/* Betula. One stylesheet for server-rendered and app-rendered pages.
   Tokens first; everything below the token block uses tokens only. One look, light and dark. */

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

:root {
  color-scheme: light dark;
  --font: "Inter", "Inter Variable", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, "JetBrains Mono", "Cascadia Mono", Consolas, monospace;

  --bg: #f1f2f4;
  --panel: #ffffff;
  --panel-2: #f6f7f8;
  --panel-3: #eceef1;
  --line: rgba(15, 23, 42, .08);
  --line-strong: rgba(15, 23, 42, .15);
  /* A shaded row of a list of modules (every other one), and a row pointed at, a step above it. */
  --row-shade: color-mix(in oklab, var(--text) 2.6%, var(--panel));
  --row-hover: color-mix(in oklab, var(--text) 5.2%, var(--panel));
  --text: #10151f;
  --text-2: #4b5565;
  --text-3: #8790a0;
  --invert: #161b26;
  --on-invert: #ffffff;
  --bark: #ffffff;
  --bark-ink: #10151f;
  /* The frame of a phone in a picture of the app: dark in both themes, as phones are. */
  --device: #1b2130;

  /* The accent is a muted birch-leaf green (owner, 2026-09-22): the hue of their tone
     oklch(.6867 .0996 149), deep enough for white labels (5.1:1) and quieter in chroma. The same
     green in both themes; text in the accent is --accent-ink (6.9:1 on white, 10:1 on dark). */
  --accent: oklch(.53 .07 149);
  --accent-ink: oklch(.46 .07 149);
  --on-accent: #ffffff;
  --accent-soft: color-mix(in srgb, var(--accent) 8%, var(--panel));
  --accent-line: color-mix(in srgb, var(--accent) 30%, transparent);
  --ok: #0b7f58;      --ok-soft: color-mix(in srgb, #0b7f58 10%, var(--panel));
  --no: #b3261e;      --no-soft: color-mix(in srgb, #b3261e 7%, var(--panel));
  --warn: #8f5a0a;    --warn-soft: color-mix(in srgb, #c98a1a 13%, var(--panel));
  /* The six faculties on the map of the programs: one lightness and a quiet chroma (OKLCH), so
     no faculty shouts and each one carries the highlight of its programs. */
  --fac-1: oklch(.5 .12 262); --fac-2: oklch(.52 .09 158); --fac-3: oklch(.56 .11 62);
  --fac-4: oklch(.5 .11 330); --fac-5: oklch(.52 .08 212); --fac-6: oklch(.52 .12 30);
  /* The ways into the catalog on the start page: a soft hue each, at the faculties' lightness. */
  --t-ice: oklch(.55 .09 240); --t-sun: oklch(.64 .12 75); --t-violet: oklch(.53 .1 300); --t-teal: oklch(.56 .08 190);
  --t-green: oklch(.56 .1 150); --t-coral: oklch(.6 .12 35); --t-rose: oklch(.56 .1 355); --t-slate: oklch(.55 .04 260);

  --shadow-panel: 0 0 0 1px var(--line), 0 1px 2px rgba(15, 23, 42, .04), 0 10px 28px -16px rgba(15, 23, 42, .16);
  --shadow-pop: 0 0 0 1px var(--line), 0 12px 40px -10px rgba(15, 23, 42, .28);
  /* A skeleton (app/src/skeleton.rs): the grey of its bars and the light that sweeps over it. */
  --sk: rgba(15, 23, 42, .07);
  --sk-shine: rgba(255, 255, 255, .6);
  --r-panel: 9px; --r-ctl: 9px; --r-row: 7px; --r-s: 5px;
  --ease: cubic-bezier(.2, .7, .2, 1);
  --spring: cubic-bezier(.3, 1.35, .5, 1); /* quick, a little past the mark and back */
  --rail: 52px; --w-filters: 272px; --gap: 8px;
  /* Widths of the filter panel and the module preview; the visitor's own widths come from
     localStorage (enhance.js). The filter panel stays inside its limits whatever is stored. */
  --filters: clamp(232px, var(--w-filters), min(440px, 42vw));
  --preview-w: 480px;

  /* The birch around the app (owner, 2026-09-25; design/birch, app/src/ground.rs). The crown is
     a cut-out silhouette in the tone of the season (`data-season` from the script in <head>;
     summer without it); what hangs behind is the same tone at half strength. The ground at the end
     of a page is Lausitz sand, a little deeper towards the bottom; the roots are a step darker. */
  --crown: oklch(.716 .073 148);
  --crown-ck: var(--crown);
  --ground: linear-gradient(180deg, oklch(.9 .026 84), oklch(.8 .036 74));
  --ground-text: oklch(.27 .02 60);
  --ground-muted: oklch(.39 .022 62);
  --ground-root: oklch(.76 .038 80);
  /* How high the ground is on a wide screen. */
  --ground-h: 200px;
  /* The wood behind every page (`.wood`): the background's grey a few steps darker, mixed in
     OKLab (in OKLCH a grey's hue is `none`, and Chromium draws the mix reddish). */
  --wood-front: color-mix(in oklab, var(--text) 8%, var(--bg));
  --wood-back: color-mix(in oklab, var(--text) 3.4%, var(--bg));
  /* The ring of a panel (--line) as it looks over what lies behind it, opaque: for the edges the
     catalog's list and the ground draw where a panel only seems to end („the one scroll area"). */
  --ring-b: color-mix(in srgb, rgb(15 23 42) 8%, var(--bg));
  --ring-w: color-mix(in srgb, rgb(15 23 42) 8%, var(--wood-front));
  --ring-p: color-mix(in srgb, rgb(15 23 42) 8%, var(--panel));
  --ring-pw: var(--ring-w);
  /* The trunk of the first panel and the branches of its figures, and the trunk of the birch
     further down the start page (`.trunk`): white bark by day. */
  --hero-bark: var(--bark);
  --hero-bark-ink: var(--bark-ink);
  --hero-limb: color-mix(in oklch, var(--ground-text) 74%, var(--crown));
}
:root[data-season="spring"] { --crown: oklch(.765 .094 130); --crown-ck: oklch(.79 .113 100); }
:root[data-season="autumn"] { --crown: oklch(.765 .105 80); }
:root[data-season="winter"] { --crown: oklch(.575 .029 40); }

/* Dark: the system setting decides, the switch in the rail overrides it (data-theme on <html>). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0a0c11;
    --panel: #12151c;
    --panel-2: #171b24;
    --panel-3: #1e2330;
    --line: rgba(255, 255, 255, .07);
    --line-strong: rgba(255, 255, 255, .15);
    --row-shade: color-mix(in oklab, var(--text) 3.4%, var(--panel));
    --row-hover: color-mix(in oklab, var(--text) 6.5%, var(--panel));
    --text: #eceff5;
    --text-2: #a4adc0;
    --text-3: #6a748a;
    --invert: #e9edf5;
    --on-invert: #0d1017;
    --bark: #e9edf5;
    --bark-ink: #0d1017;
    --accent: oklch(.53 .07 149);
    --fac-1: oklch(.76 .1 262); --fac-2: oklch(.77 .09 158); --fac-3: oklch(.79 .1 70); --fac-4: oklch(.76 .1 330); --fac-5: oklch(.77 .08 212); --fac-6: oklch(.75 .1 30);
    --t-ice: oklch(.78 .08 240); --t-sun: oklch(.82 .1 80); --t-violet: oklch(.78 .09 300); --t-teal: oklch(.8 .07 190); --t-green: oklch(.8 .09 150); --t-coral: oklch(.8 .09 35); --t-rose: oklch(.78 .09 355); --t-slate: oklch(.78 .04 260);
    --accent-ink: oklch(.8 .06 149);
    --on-accent: #ffffff;
    --accent-soft: color-mix(in srgb, var(--accent) 16%, var(--panel));
    --ok: #4fd1a1;   --warn: #f0b861;   --no: #ff8f85;
    --no-soft: color-mix(in srgb, #ff8f85 12%, var(--panel));
    --shadow-panel: 0 0 0 1px var(--line), 0 20px 40px -22px rgba(0, 0, 0, .7);
    --shadow-pop: 0 0 0 1px var(--line-strong), 0 16px 48px -8px rgba(0, 0, 0, .8);
    --sk: rgba(255, 255, 255, .065);
    --sk-shine: rgba(255, 255, 255, .045);
    /* The crown darker than its day, the ground a cool depth from the tone of the panels to
       almost black (warm soil fights the cool dark: owner, 2026-09-25), the roots a step lighter. */
    --crown: oklch(.381 .061 150);
    --ground: linear-gradient(180deg, oklch(.215 .014 262), oklch(.075 .008 265));
    --ground-text: oklch(.93 .006 262);
    --ground-muted: oklch(.72 .014 262);
    --ground-root: oklch(.25 .02 262);
    /* The wood a few steps lighter than the background (nothing is darker than it); the trunks of
       the start page in the moon's grey, their marks dark (owner, 2026-09-29: white bark glared). */
    --wood-front: color-mix(in oklab, var(--text) 6.5%, var(--bg));
    --wood-back: color-mix(in oklab, var(--text) 2.7%, var(--bg));
    --ring-b: color-mix(in srgb, #fff 7%, var(--bg));
    --ring-w: color-mix(in srgb, #fff 7%, var(--wood-front));
    --ring-p: color-mix(in srgb, #fff 15%, var(--panel));
    --ring-pw: color-mix(in srgb, #fff 15%, var(--wood-front));
    --hero-bark: oklch(.34 .014 255);
    --hero-bark-ink: oklch(.15 .01 260);
    --hero-limb: oklch(.34 .014 255);
  }
  :root:not([data-theme="light"])[data-season="spring"] { --crown: oklch(.418 .073 134); --crown-ck: oklch(.495 .083 100); }
  :root:not([data-theme="light"])[data-season="autumn"] { --crown: oklch(.44 .08 75); }
  :root:not([data-theme="light"])[data-season="winter"] { --crown: oklch(.488 .025 45); }
}
:root[data-theme="dark"] {
  --bg: #0a0c11;
  --panel: #12151c;
  --panel-2: #171b24;
  --panel-3: #1e2330;
  --line: rgba(255, 255, 255, .07);
  --line-strong: rgba(255, 255, 255, .15);
  --row-shade: color-mix(in oklab, var(--text) 3.4%, var(--panel));
  --row-hover: color-mix(in oklab, var(--text) 6.5%, var(--panel));
  --text: #eceff5;
  --text-2: #a4adc0;
  --text-3: #6a748a;
  --invert: #e9edf5;
  --on-invert: #0d1017;
  --bark: #e9edf5;
  --bark-ink: #0d1017;
  --accent: oklch(.53 .07 149);
  --fac-1: oklch(.76 .1 262); --fac-2: oklch(.77 .09 158); --fac-3: oklch(.79 .1 70); --fac-4: oklch(.76 .1 330); --fac-5: oklch(.77 .08 212); --fac-6: oklch(.75 .1 30);
  --t-ice: oklch(.78 .08 240); --t-sun: oklch(.82 .1 80); --t-violet: oklch(.78 .09 300); --t-teal: oklch(.8 .07 190); --t-green: oklch(.8 .09 150); --t-coral: oklch(.8 .09 35); --t-rose: oklch(.78 .09 355); --t-slate: oklch(.78 .04 260);
  --accent-ink: oklch(.8 .06 149);
  --on-accent: #ffffff;
  --accent-soft: color-mix(in srgb, var(--accent) 16%, var(--panel));
  --ok: #4fd1a1;   --warn: #f0b861;   --no: #ff8f85;
  --no-soft: color-mix(in srgb, #ff8f85 12%, var(--panel));
  --shadow-panel: 0 0 0 1px var(--line), 0 20px 40px -22px rgba(0, 0, 0, .7);
  --shadow-pop: 0 0 0 1px var(--line-strong), 0 16px 48px -8px rgba(0, 0, 0, .8);
  --sk: rgba(255, 255, 255, .065);
  --sk-shine: rgba(255, 255, 255, .045);
  --crown: oklch(.381 .061 150);
  --ground: linear-gradient(180deg, oklch(.215 .014 262), oklch(.075 .008 265));
  --ground-text: oklch(.93 .006 262);
  --ground-muted: oklch(.72 .014 262);
  --ground-root: oklch(.25 .02 262);
  /* The wood a few steps lighter than the background (nothing is darker than it); the trunks of
     the start page in the moon's grey, their marks dark (owner, 2026-09-29: white bark glared). */
  --wood-front: color-mix(in oklab, var(--text) 6.5%, var(--bg));
  --wood-back: color-mix(in oklab, var(--text) 2.7%, var(--bg));
  --ring-b: color-mix(in srgb, #fff 7%, var(--bg));
  --ring-w: color-mix(in srgb, #fff 7%, var(--wood-front));
  --ring-p: color-mix(in srgb, #fff 15%, var(--panel));
  --ring-pw: color-mix(in srgb, #fff 15%, var(--wood-front));
  --hero-bark: oklch(.34 .014 255);
  --hero-bark-ink: oklch(.15 .01 260);
  --hero-limb: oklch(.34 .014 255);
}
:root[data-theme="dark"][data-season="spring"] { --crown: oklch(.418 .073 134); --crown-ck: oklch(.495 .083 100); }
:root[data-theme="dark"][data-season="autumn"] { --crown: oklch(.44 .08 75); }
:root[data-theme="dark"][data-season="winter"] { --crown: oklch(.488 .025 45); }
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

/* ---------- base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { height: 100%; }
/* The rail and the view in the first row, as high as the window; in the second whatever comes
   after the view (the ground, see „the birch"). The body grows past the window with it, so that the
   rail and the view can stay where they are while the window scrolls. */
body {
  margin: 0; background: var(--bg); color: var(--text);
  font: 450 14px/1.45 var(--font); letter-spacing: -.006em;
  -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: 100%;
  display: grid; grid-template-columns: var(--rail) minmax(0, 1fr); grid-template-rows: 100vh auto; min-height: 100%;
}
/* The fade between pages. Its opt-in (@view-transition) is written inline into the head of each
   page (app::VIEW_TRANSITION_STYLE): from this file it can come too late for the browser. */
::view-transition-old(root), ::view-transition-new(root) { animation-duration: .16s; }
h1, h2, h3, p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }
a { color: var(--accent-ink); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }
button, input, select { font: inherit; color: inherit; letter-spacing: inherit; }
button { cursor: pointer; border: 0; background: none; padding: 0; }
table { border-collapse: collapse; width: 100%; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-s); }
.icon { width: 18px; height: 18px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.num { font-variant-numeric: tabular-nums; }
.mono { font-family: var(--mono); font-size: 12px; letter-spacing: 0; }
.unknown { color: var(--text-3); font-style: italic; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.skip-link { position: absolute; left: 12px; top: -100px; z-index: 60; background: var(--panel); padding: 10px 14px; border-radius: var(--r-ctl); box-shadow: var(--shadow-pop); }
.skip-link:focus { top: 10px; }
/* A side panel keeps its wheel: only the end of the page itself (`.page`, the list) goes on to
   the window, where the ground comes (see „the birch"). */
.scroll { overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--line-strong) transparent; overscroll-behavior-y: contain; }
.rows.scroll { overscroll-behavior-y: auto; }

/* ---------- virtual oversizing ----------
   Small controls react to the pointer in an area larger than they look: an invisible layer
   around them that belongs to them (a click on it is a click on the control). Per side:
   --hit for all four, --hit-x / --hit-y for an axis, --hit-t / -r / -b / -l for one side; the
   more specific wins. The rule that keeps areas from overlapping: towards a neighbour at most
   half the gap to it, so that two areas meet in the middle; more only into space that holds no
   control of its own. `ui::Hit` writes the properties for a single element (class `hit`). */
.hit, .chip, .seg a, .seg button, .tag a, .cols a, .ghost, .icon-btn, .nav { position: relative; }
.hit::after, .chip::after, .seg a::after, .seg button::after, .tag a::after, .cols a::after, .ghost::after, .icon-btn::after, .nav::after, .combo-clear::after {
  content: ""; position: absolute;
  top: calc(-1 * var(--hit-t, var(--hit-y, var(--hit, 0px))));
  right: calc(-1 * var(--hit-r, var(--hit-x, var(--hit, 0px))));
  bottom: calc(-1 * var(--hit-b, var(--hit-y, var(--hit, 0px))));
  left: calc(-1 * var(--hit-l, var(--hit-x, var(--hit, 0px))));
}
.chip { --hit: 2.5px; }                         /* chips are 5px apart */
.seg a, .seg button { --hit-y: 3px; }                        /* the padding of the track */
.seg a:first-child, .seg button:first-child { --hit-l: 3px; }
.seg a:last-child, .seg button:last-child { --hit-r: 3px; }
.tag a { --hit-y: 5px; --hit-l: 6px; --hit-r: 6px; }
.cols a { --hit-y: 8px; --hit-x: 8px; }          /* columns are 16px apart */
.ghost { --hit-y: 4px; --hit-x: 1px; }
.rail .nav, .rail .icon-btn { --hit-x: 6px; --hit-y: 1px; }   /* the rail is 52px wide, its items 2px apart */
.combo-clear { --hit-y: 6px; --hit-x: 3px; }

/* ---------- rail ---------- */
.rail { grid-row: 1 / span 2; position: sticky; top: 0; height: 100vh; display: flex; flex-direction: column; align-items: center; gap: 2px; padding: var(--gap) 0; }
/* The mark: birch bark that also reads as the rows of a list; light in both themes (--bark). Drawn
   on a 32px grid (bars 3px, 2px apart, 7px to the edges). Its middle sits on the middle of the top
   bar, so mark and wordmark read as one on the start page. */
.logo { --hit: 4px; width: 32px; height: 32px; margin: 4px 0 10px; border-radius: 7px; display: block; flex: none; box-shadow: var(--shadow-panel); }
.mark { display: block; width: 100%; height: 100%; }
.mark rect { fill: var(--bark); }
.mark path { fill: var(--bark-ink); }
/* The wordmark: B, T and U in full, E, L and A as small capitals, the E under the bar of the T.
   Measured on Inter 800: the stem of the T starts .252em from its origin, its bar ends .587em above
   the baseline. The small capitals are sized so that the air above the E equals the air between
   the E and the stem (.063em; .09em in the small cut, for sizes under 28px). design/logo/logo.html */
.wordmark { display: inline-block; font: 800 22px/1 var(--font); letter-spacing: -.01em; white-space: nowrap; color: var(--text); }
.wordmark .sc { font-size: .72em; font-weight: 400; letter-spacing: .02em; }
.wordmark .e { margin: 0 -.347em 0 -.022em; }
.wordmark .u { margin-left: -.03em; }
.wordmark .la { margin-left: -.055em; }
.wordmark.small .sc { font-size: .68em; font-weight: 500; }
.wordmark.small .e { margin: 0 -.319em 0 .02em; }
.wordmark.small .la { margin-left: -.03em; }
/* Where the rail and the title are hidden (phone), the start page carries the brand itself. */
.brand-phone { display: none; }
.nav { width: 40px; display: grid; justify-items: center; color: var(--text-3); font-size: 0; text-decoration: none; }
.nav .ind { width: 40px; height: 40px; border-radius: var(--r-ctl); display: grid; place-items: center; transition: background .15s, color .15s; }
.nav .icon { width: 20px; height: 20px; }
.nav:hover { text-decoration: none; color: var(--text); }
.nav:hover .ind { background: var(--line); }
.nav[aria-current="page"] { color: var(--text); }
.nav[aria-current="page"] .ind { background: var(--invert); color: var(--on-invert); }
.rail nav { display: grid; gap: 2px; }
.nav.soon { opacity: .4; pointer-events: none; }
.rail-end { margin-top: auto; display: grid; gap: 6px; justify-items: center; }
/* The switch between the languages (app::languages): in the rail above the theme's and as large,
   the icon of languages over the code of the language it leads to. A new page load. */
.languages { display: grid; gap: 2px; justify-items: center; }
.lang-switch { align-content: center; gap: 2px; }
.lang-switch .icon { width: 17px; height: 17px; }
.lang-switch b { font-size: 9.5px; font-weight: 700; letter-spacing: .08em; line-height: 1; }
.lang-switch:hover { text-decoration: none; }
.theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-sun { display: block; }
  :root:not([data-theme="light"]) .theme-toggle .icon-moon { display: none; }
}
/* What only works with JavaScript (ui::JsOnly): hidden until the script in the head has run. */
html:not(.js) .js-only { display: none !important; }
span.js-only { display: contents; }

/* ---------- frame ---------- */
/* The view is as high as the window; less what shows of the ground (see „the birch"). */
.main { position: relative; grid-column: 2; grid-row: 1; display: grid; grid-template-rows: auto minmax(0, 1fr); gap: var(--gap); padding: var(--gap) var(--gap) var(--gap) 0; min-width: 0; height: 100vh; }
.topbar { display: flex; align-items: center; gap: var(--gap); height: 40px; }
/* The title sits over the filter panel (its text over the panel's text), the search over the list. */
.crumb { display: flex; align-items: baseline; gap: 10px; flex: none; width: var(--filters); padding-left: 14px; min-width: 0; }
.crumb h1 { font-size: 17px; font-weight: 680; letter-spacing: -.025em; white-space: nowrap; }
.crumb small { color: var(--text-3); font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* With the wordmark: its baseline lies 28px into the 40px bar (9px + .8633em of 22px), so the 16px
   capitals sit on the middle of the bar and of the mark in the rail; the subtitle keeps the baseline. */
.crumb.brand { align-self: stretch; padding-top: 9px; }
.crumb.brand h1 { line-height: 1; }
.search { flex: 1; max-width: 760px; height: 40px; display: flex; align-items: center; gap: 10px; padding: 0 8px 0 14px; background: var(--panel); border-radius: var(--r-ctl); box-shadow: var(--shadow-panel); color: var(--text-3); cursor: text; }
.search:focus-within { box-shadow: 0 0 0 2px var(--accent); }
.search input { flex: 1; min-width: 0; border: 0; outline: 0; background: none; font-size: 15px; color: var(--text); }
.search input::placeholder { color: var(--text-3); }
.ghost kbd { margin-left: 2px; padding: 2px 6px; }
kbd { font: 500 11px var(--mono); color: var(--text-3); padding: 4px 7px; border-radius: var(--r-s); background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line); }
.top-end { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.pill { height: 38px; display: inline-flex; align-items: center; gap: 8px; padding: 0 12px; border-radius: var(--r-ctl); background: var(--panel); box-shadow: var(--shadow-panel); font-weight: 550; font-size: 13px; color: var(--text-2); white-space: nowrap; }
.pill b { color: var(--text); font-weight: 600; }
.pill .icon { width: 16px; height: 16px; }
.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 3px var(--ok-soft); }
/* How wide a module or an area is that floats beside the page (`.work > .detail`): here and not on
   `.work`, because „Nach oben" stands beside it too and is no part of `.work`. Its 100 % is the
   width of what uses it: `.work`, or the view for the button, and the two are as wide. */
.content { position: relative; min-height: 0; min-width: 0; --preview: clamp(360px, var(--preview-w), calc(100% - var(--filters) - var(--gap) - 120px)); }
/* A page ends where the panels beside it end: the same 8 px above the window's edge or the ground
   (1 px keeps the ring of the panels in it). */
.page { height: 100%; overflow-y: auto; padding: 1px; scrollbar-width: thin; }

/* ---------- the birch: the crown along the top, the ground at the end ---------- */
/* The crown hangs in from above along the whole top, on every screen the same (owner, 2026-09-25:
   „durchgehend und auf allen Geräten“): fixed to the window, under the rail and the view, so the
   mark and the search stand in front of it. Two masks per season in the season's tone
   (design/birch): at the left the head — the mark, and over the title a clearing where only the
   crown's edge hangs in, so the name stands free („ein natürlicher Platz für die Schrift“) — and
   from 420 px on a tile that repeats to the right edge, cut to its own box (content-box), so the
   two never draw over each other. Spring's catkins have a colour of their own. The masks stand in
   the rules as they are, not handed down in custom properties: an image out of a custom property
   is taken anew with every restyle, and the browser draws it again — dragging a panel's edge
   restyles the whole page in every frame, and the crown was drawn again with each one. */
.crown { position: fixed; z-index: 0; top: 0; left: 0; right: 0; height: 64px; pointer-events: none; }
.crown::before, .crown::after {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 64px; padding-left: 420px; background: var(--crown);
  -webkit-mask-image: url("/assets/birch/summer-crown-head.svg"), url("/assets/birch/summer-crown.svg"); mask-image: url("/assets/birch/summer-crown-head.svg"), url("/assets/birch/summer-crown.svg");
  -webkit-mask-repeat: no-repeat, repeat-x; mask-repeat: no-repeat, repeat-x;
  -webkit-mask-position: left top; mask-position: left top;
  -webkit-mask-origin: border-box, content-box; mask-origin: border-box, content-box;
  -webkit-mask-clip: border-box, content-box; mask-clip: border-box, content-box;
}
.crown::after { display: none; background: var(--crown-ck); -webkit-mask-image: url("/assets/birch/spring-crown-head-ck.svg"), url("/assets/birch/spring-crown-ck.svg"); mask-image: url("/assets/birch/spring-crown-head-ck.svg"), url("/assets/birch/spring-crown-ck.svg"); }
:root[data-season="spring"] .crown::before { -webkit-mask-image: url("/assets/birch/spring-crown-head.svg"), url("/assets/birch/spring-crown.svg"); mask-image: url("/assets/birch/spring-crown-head.svg"), url("/assets/birch/spring-crown.svg"); }
:root[data-season="spring"] .crown::after { display: block; }
:root[data-season="autumn"] .crown::before { -webkit-mask-image: url("/assets/birch/autumn-crown-head.svg"), url("/assets/birch/autumn-crown.svg"); mask-image: url("/assets/birch/autumn-crown-head.svg"), url("/assets/birch/autumn-crown.svg"); }
:root[data-season="winter"] .crown::before { -webkit-mask-image: url("/assets/birch/winter-crown-head.svg"), url("/assets/birch/winter-crown.svg"); mask-image: url("/assets/birch/winter-crown-head.svg"), url("/assets/birch/winter-crown.svg"); }

/* The ground: the footer after all of a page, across everything right of the rail. The roots
   grow into it from its edge and fade downwards (a mask, like the crown); in autumn the fallen
   leaves lie on its edge, in the gap above it. As it stands here it lies after the view, and the
   window scrolls to it: so it follows the page on a phone. On a wide screen every page has its own
   at the end of its scroll area instead („one scroll area"). */
.ground { position: relative; z-index: 1; grid-column: 2; grid-row: 2; margin: 0 var(--gap) var(--gap) 0; min-height: var(--ground-h); border-radius: 12px; background: var(--ground); color: var(--ground-text); box-shadow: inset 0 1px 0 color-mix(in srgb, var(--ground-text) 6%, transparent); }
.ground::before { content: ""; position: absolute; inset: 0; border-radius: inherit; background: var(--ground-root); -webkit-mask: url("/assets/birch/roots.svg") repeat-x left top; mask: url("/assets/birch/roots.svg") repeat-x left top; pointer-events: none; }
.ground::after { content: ""; display: none; position: absolute; left: 0; right: 0; top: -12px; height: 24px; background: var(--crown); -webkit-mask: url("/assets/birch/litter.svg") repeat-x left top; mask: url("/assets/birch/litter.svg") repeat-x left top; pointer-events: none; }
:root[data-season="autumn"] .ground::after { display: block; }
.ground-top { position: relative; display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; padding: 26px 28px 0; }
.ground-name { display: flex; align-items: baseline; gap: 10px; }
.ground .wordmark { color: var(--ground-text); }
.ground-name small { color: var(--ground-muted); font-size: 13px; }
.ground-note { margin-top: 8px; color: var(--ground-muted); font-size: 12.5px; }
.ground-legal { display: flex; gap: 6px; }
.ground-legal a { display: inline-flex; align-items: center; min-height: 34px; padding: 0 12px; border-radius: var(--r-ctl); background: color-mix(in srgb, var(--ground-text) 7%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ground-text) 10%, transparent); color: var(--ground-text); font-size: 13px; font-weight: 560; }
.ground-legal a:hover { background: color-mix(in srgb, var(--ground-text) 12%, transparent); text-decoration: none; }
.ground-foot { position: absolute; left: 28px; right: 28px; bottom: 22px; display: flex; justify-content: space-between; gap: 16px; color: var(--ground-muted); font-size: 12px; }
.ground-foot p { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 14px; }
.ground-foot a { display: inline-flex; align-items: center; gap: 3px; color: var(--ground-muted); }
.ground-foot .icon { width: 13px; height: 13px; }
@media (max-width: 900px) {
  /* On a phone the crown is the ground of the bar at the top: it carries the bar's frosted
     background, over the page and under the search. The ground follows the page, full width, the
     bottom bar floating over its lower part; under a page shorter than the window it lies at the
     window's lower edge all the same (the body's column, „phone and tablet"). */
  .crown { z-index: 20; height: var(--bar); background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(14px); }
  /* No title in the bar, so no clearing: the tile alone, from the left edge (the head's layer at
     no size, which draws nothing, as `none` would). */
  .crown::before, .crown::after { padding-left: 0; -webkit-mask-size: 0 0, auto; mask-size: 0 0, auto; }
  .ground { margin: 12px 0 0; min-height: 0; border-radius: 18px 18px 0 0; padding-bottom: calc(104px + env(safe-area-inset-bottom)); }
  .ground-top { flex-direction: column; gap: 14px; padding: 22px 18px 0; }
  .ground-foot { position: relative; left: auto; right: auto; bottom: auto; flex-direction: column; gap: 6px; padding: 18px 18px 0; }
}

/* ---------- one scroll area ----------
   Owner, 2026-09-29: the two steps — first the page scrolled, then the window for the ground — felt
   „unfassbar janky". Every page (`.work.flowing`: ui::Frame, ui::Plain, the catalog, the Merkliste,
   the start page) is ONE scroll area right
   of the rail under the top bar, with one scrollbar at the window's right edge: the page and the
   ground after it scroll as one, natively, and the ground is simply the end of the page. What stands
   beside the page (the filter panel, the module beside the list) is pinned: as high as the area
   shows, its place running through the ground's row, so the ground slides over its lower end; from
   8 px above the ground down, all across, the background is the wood's front colour (a band behind
   the ground, `::after`, and the wood's own below it), which cuts the panels off straight and shows
   in the ground's round corners (owner, 2026-09-30: the panels' corners drawn along with the ground
   looked off); once the page stands still its content ends above the ground
   (`--cover`, enhance.js: one layout nobody sees, the ground covers exactly what goes). The list's
   head and the heads of its columns stay at the top; where the area cuts the list off at the
   bottom, the list keeps its box (`.list-cap`), and its head's round corners let the page behind
   show (a mask on the area, which stands still while its content scrolls). The wood goes up with
   the ground in the same frame (a scroll-driven animation; enhance.js where there is none). The
   area has 1 px above and below the panels for their ring: they end where they ended before, 8 px
   above the window's edge; `100vh - 64px` is the height of a pinned panel (56 px above, 8 below). A
   sticky `top: 0` already stands inside the area's 1 px of padding. The scrollbar stands in the
   8 px right of the page (`--bar`, its width, enhance.js), not beside them (owner, 2026-09-30). */
.work > .ground { display: none; }
@media (min-width: 901px) {
  body { timeline-scope: --page; }
  :root:has(#content > .work.flowing) body > .ground { display: none; }
  .work.flowing {
    display: grid; grid-template-columns: var(--filters) minmax(0, 1fr); grid-template-rows: max-content var(--ground-h);
    --pad-r: max(0px, calc(var(--gap) - var(--bar, 0px)));
    height: calc(100% + 2px); margin: -1px calc(-1 * var(--gap)) -1px 0; padding: 1px var(--pad-r) 1px 0;
    overflow-x: hidden; overflow-y: auto; overscroll-behavior-y: none; scrollbar-width: thin; scrollbar-color: var(--line-strong) transparent; scroll-timeline: --page block;
  }
  /* A page without a frame (ui::Plain, the start page) takes the whole width. */
  .work.flowing.solo { grid-template-columns: minmax(0, 1fr); }
  /* A list's box keeps its round corners at the top: the area lets the page behind them show. A
     page of panels is cut straight at the top, as it was. */
  .work.flowing:has(> .list) {
    mask-image: linear-gradient(#000 0 0), radial-gradient(circle at 10px 10px, transparent 9.6px, #000 10.4px), radial-gradient(circle at 0 10px, transparent 9.6px, #000 10.4px);
    mask-origin: border-box, border-box, content-box; mask-clip: border-box;
    mask-size: 100% 100%, 10px 10px, 10px 10px; mask-repeat: no-repeat;
    mask-position: 0 0, calc(var(--filters) + var(--gap) - 1px) 0, right -1px top -1px;
    mask-composite: subtract, add, add;
  }
  .work.flowing:focus { outline: none; }
  /* The first row is never shorter than the area shows, whatever flows in it (a pinned short list
     stands in it and the ground's row at once). */
  .work.flowing::before { content: ""; grid-row: 1; grid-column: 1; height: calc(100vh - 64px); pointer-events: none; }
  .work.flowing > .filters, .work.flowing > .sidebar, .work.flowing > .detail {
    position: sticky; inset: auto; top: 0; grid-row: 1 / 3; align-self: start; height: calc(100vh - 64px);
    padding-bottom: var(--cover, 0px);
  }
  .work.flowing > .filters, .work.flowing > .sidebar { grid-column: 1; }
  /* A list short enough to stand whole above the ground is pinned as well (enhance.js). */
  .work.flowing > .list.short { position: sticky; top: 0; grid-row: 1 / 3; align-self: start; height: calc(100vh - 64px); min-height: 0; padding-bottom: var(--cover, 0px); }
  .work.flowing > .detail { grid-column: 1 / -1; justify-self: end; }
  /* The area has a height of its own, so its first row is as tall as what it holds (`max-content`,
     not `auto`, which would only grow into room the area has left): at least as high as it shows. */
  .work.flowing > .list { grid-column: 2; grid-row: 1; min-height: calc(100vh - 64px); overflow: visible; }
  .work.flowing > .page { grid-column: 1 / -1; grid-row: 1; height: auto; min-height: calc(100vh - 64px); overflow: visible; }
  .work.flowing.framed > .page { grid-column: 2; }
  .work.flowing > .resizer { position: sticky; top: 0; grid-row: 1 / 3; grid-column: 1 / -1; align-self: start; height: calc(100vh - 64px); }
  .work.flowing > .resizer.between { justify-self: start; left: auto; margin-left: var(--at, var(--filters)); }
  .work.flowing > .resizer.preview-edge { justify-self: end; right: auto; margin-right: calc(var(--at, var(--preview)) - 12px); }
  .work.flowing .rows.scroll { overflow: visible; }
  .work.flowing .list[data-pending] .rows { overflow: clip; }
  /* The list's head stays at the top, its ring as its top edge — in the area's 1 px above the
     panels, where the other panels have theirs, so nothing shows through above it —; the heads of
     the columns stay under it (--list-head-h, its height with the ring, enhance.js); the note of a
     semester between them scrolls away. */
  html.js .work.flowing > .list > .list-head { position: sticky; top: -1px; z-index: 6; margin: -1px -1px 0; border: 1px solid var(--ring-b); border-bottom: 0; border-radius: calc(var(--r-panel) + 1px) calc(var(--r-panel) + 1px) 0 0; background: var(--panel); }
  html.js .work.flowing .rows > .cols { top: calc(var(--list-head-h, 1px) - 1px); }
  html.js .work.flowing > .list > .cols { position: sticky; top: calc(var(--list-head-h, 1px) - 1px); z-index: 4; }
  html.js .work.flowing .rows-pending { top: calc(var(--list-head-h, 1px) - 1px + var(--cols)); }
  html.js .work.flowing .row { scroll-margin: calc(var(--list-head-h, 0px) + var(--cols) + 8px) 0 16px; }
  .list-cap { display: none; }
  .work.flowing > .list > .list-cap {
    position: sticky; bottom: -1px; z-index: 5; display: block; height: 10px; margin: -9px -1px -1px; pointer-events: none;
    background:
      radial-gradient(circle at 10px 0, transparent 8.6px, var(--ring-w) 9.3px, var(--ring-w) 9.7px, var(--wood-front) 10.4px) left top / 10px 10px no-repeat,
      radial-gradient(circle at 0 0, transparent 8.6px, var(--ring-w) 9.3px, var(--ring-w) 9.7px, var(--wood-front) 10.4px) right top / 10px 10px no-repeat,
      linear-gradient(var(--ring-w), var(--ring-w)) center bottom / calc(100% - 20px) 1px no-repeat;
  }
  /* The ground as the end of the page, on a band in the wood's front colour from 8 px above it
     and across the whole area (to the scrollbar): over the pinned panels, under the ground. */
  .work.flowing > .ground { display: block; position: relative; z-index: 20; grid-column: 1 / -1; grid-row: 2; height: var(--ground-h); min-height: 0; margin: 0; }
  .work.flowing::after { content: ""; position: relative; z-index: 19; grid-column: 1 / -1; grid-row: 2; margin: calc(-1 * var(--gap)) calc(-1 * var(--pad-r)) 0 0; background: var(--wood-front); pointer-events: none; }
  /* The wood stands on the ground as it comes: its last 208 px of the scroll are the ground's 200
     and the gap above it. */
  @keyframes wood-rise { to { transform: translateY(-208px); } }
  @supports (animation-timeline: scroll()) and (timeline-scope: --page) {
    :root:has(#content > .work.flowing) .wood { animation: wood-rise linear both; animation-timeline: --page; animation-range: calc(100% - 208px) 100%; }
  }
  /* „Nach oben" stays above the ground once the page stands still (enhance.js). */
  .to-top { translate: 0 calc(-1 * var(--lift, 0px)); }
}

/* ---------- back to the top ---------- */
/* „Nach oben" (`ui::ToTop`; owner, 2026-09-26: after a while in the catalog's list it was hard to
   get back to its top). Round and floating like the arrows of the start page's carousel, in the
   corner of what scrolls; enhance.js shows it once the page is more than a screen down
   (`data-shown`) and takes the page up. On a wide screen it stands in the corner of the view,
   16 px inside the page's panel, and left of a module or an area that floats beside the page,
   moving along as that one comes and goes (at once while its edge is dragged); when the ground
   comes up, the view gets shorter and the button goes up with it. On a phone it floats above the
   bottom bar, a finger's size, under the bars, the sheets and the skeleton of a page on its way. */
.to-top {
  position: absolute; z-index: 12; right: 16px; bottom: 16px; width: 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center; background: var(--panel); box-shadow: var(--shadow-pop); color: var(--text-2);
  opacity: 0; visibility: hidden; transform: translateY(10px) scale(.9);
  transition: opacity .15s, transform .2s var(--ease), visibility 0s .2s, right .18s var(--ease), background .12s, color .12s;
}
.to-top[data-shown] { opacity: 1; visibility: visible; transform: none; transition: opacity .15s, transform .3s var(--spring), right .18s var(--ease), background .12s, color .12s; }
.to-top:hover { background: var(--panel-2); color: var(--text); }
.to-top:active { transform: scale(.94); }
.resizing .to-top { transition: none; }
/* A keyboard that pressed it goes on from the start of the page (enhance.js): the view itself
   takes the focus there, and a frame around all of it would say nothing. */
.content:focus { outline: none; }
@media (min-width: 901px) {
  .content:has(> .work > .detail) > .to-top { right: calc(var(--preview) + 16px); }
}
@media (max-width: 900px) {
  /* 12 px above the bottom bar (10 px from the edge, 62 px high), at its right edge. */
  .to-top { position: fixed; z-index: 18; right: 10px; bottom: calc(84px + env(safe-area-inset-bottom)); width: 44px; height: 44px; }
}

.panel { background: var(--panel); border-radius: var(--r-panel); box-shadow: var(--shadow-panel); display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
.panel-head { display: flex; align-items: center; gap: 8px; min-height: 50px; padding: 8px 8px 6px 14px; }
.panel-head h2 { font-size: 15px; font-weight: 650; letter-spacing: -.015em; margin-right: auto; }
.ghost { min-height: 36px; padding: 0 10px; border-radius: var(--r-ctl); color: var(--text-2); font-weight: 550; font-size: 13px; display: inline-flex; align-items: center; gap: 6px; text-decoration: none; }
.ghost:hover { background: var(--panel-3); color: var(--text); text-decoration: none; }
.ghost .icon { width: 16px; height: 16px; }
.icon-btn { width: 40px; height: 40px; border-radius: var(--r-ctl); display: grid; place-items: center; color: var(--text-3); text-decoration: none; transition: background .12s, color .12s, transform .12s; }
.icon-btn:hover { background: var(--panel-3); color: var(--text); }
.icon-btn:active { transform: scale(.94); }
.btn { min-height: 44px; padding: 0 16px; border-radius: var(--r-ctl); display: inline-flex; align-items: center; justify-content: center; gap: 8px; font-weight: 620; font-size: 14px; text-decoration: none; transition: transform .12s, filter .12s, background .12s; }
.btn:hover { text-decoration: none; }
.btn:active { transform: scale(.98); }
.btn.primary { background: var(--accent); color: var(--on-accent); }
.btn.primary:hover { filter: brightness(1.08); }
.btn.secondary { background: var(--panel); color: var(--text); box-shadow: inset 0 0 0 1px var(--line-strong); }
.btn.secondary:hover { background: var(--panel-2); }
.button { min-height: 44px; padding: 0 16px; border-radius: var(--r-ctl); display: inline-flex; align-items: center; gap: 8px; font-weight: 620; background: var(--accent); color: var(--on-accent); text-decoration: none; }
.button:hover { filter: brightness(1.08); text-decoration: none; }
.button-quiet { background: var(--panel); color: var(--text); box-shadow: inset 0 0 0 1px var(--line-strong); }
.label { font-size: 11.5px; font-weight: 650; letter-spacing: .05em; text-transform: uppercase; color: var(--text-3); }

/* ---------- catalog: three panels ---------- */
.work { position: relative; height: 100%; display: grid; grid-template-columns: var(--filters) minmax(0, 1fr); gap: var(--gap); min-height: 0; }
/* The preview does not take room from the list: it floats above it, docked to the right edge. */
.work > .detail { position: absolute; z-index: 10; top: 0; right: 0; bottom: 0; width: var(--preview); box-shadow: var(--shadow-pop); animation: preview-in .18s var(--ease); }
/* Resize handles: an invisible zone much wider than the grip it shows, around the edge it moves.
   Both are children of .work, so no panel clips them. */
.resizer { position: absolute; top: 0; bottom: 0; cursor: col-resize; touch-action: none; }
.resizer::after { content: ""; position: absolute; top: 50%; width: 4px; height: 44px; margin-top: -22px; border-radius: 2px; background: var(--line-strong); transition: background .12s, height .12s, margin .12s, opacity .12s; }
.resizer:hover::after, .resizer:focus-visible::after, .resizing .resizer::after { background: var(--accent); height: 72px; margin-top: -36px; opacity: 1; }
.resizer:focus-visible { outline: 0; }
/* Filter panel: the gap between the two boxes and the empty left margin of the list. Nothing of
   it lies over the filter panel, whose scrollbar is at that edge. */
/* `--at`: where the handle stands while only its panel follows a drag (enhance.js). */
.resizer.between { z-index: 4; left: var(--at, var(--filters)); width: calc(var(--gap) + 8px); }
.resizer.between::after { left: calc(var(--gap) / 2 - 1.5px); width: 3px; opacity: .55; }
/* Preview: 8px of the list in front of its left edge and its own empty left margin. */
.resizer.preview-edge { z-index: 11; right: calc(var(--at, var(--preview)) - 12px); width: 20px; }
.resizer.preview-edge::after { left: 11px; }
.resizing, .resizing * { cursor: col-resize !important; user-select: none !important; }
/* While a handle is dragged only its panel follows (above its neighbour); the page is laid out
   again once, when the handle is let go (enhance.js). */
.resizing .filters, .resizing .sidebar { position: relative; z-index: 6; }
.resizing .work > .detail { animation: none; transition: none; }

.filters form { display: flex; flex-direction: column; min-height: 0; flex: 1; }
.filters .body { padding: 0 10px 12px; }
.fgroup { padding: 10px 2px 0; }
.fgroup + .fgroup { border-top: 1px solid var(--line); margin-top: 10px; }
details.fgroup > summary { cursor: pointer; min-height: 24px; display: flex; align-items: center; justify-content: space-between; list-style: none; }
details.fgroup > summary::-webkit-details-marker { display: none; }
details.fgroup > summary::after { content: "+"; font-size: 15px; font-weight: 500; color: var(--text-3); letter-spacing: 0; }
details.fgroup[open] > summary::after { content: "–"; }
details.fgroup[open] > summary { margin-bottom: 7px; }
details.fgroup.more > summary { display: flex; color: var(--text-3); font-weight: 650; font-size: 11.5px; }
.flabel { display: flex; align-items: center; justify-content: space-between; margin-bottom: 7px; }
.flabel > span { text-transform: none; letter-spacing: 0; font-weight: 550; color: var(--text-2); }
.fgroup > .flabel:not(:first-child) { margin-top: 12px; }
.flabel .legend { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; color: var(--text-3) !important; font-weight: 500 !important; }
.legend .box { margin-left: 5px; }
.more .flabel { margin-top: 12px; }
.select-wrap { position: relative; display: block; }
.select-wrap select { appearance: none; width: 100%; min-height: 40px; padding: 0 34px 0 40px; border: 0; border-radius: var(--r-ctl); background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line-strong); font-weight: 600; cursor: pointer; text-overflow: ellipsis; }
.select-wrap select:hover { background: var(--panel-3); }
.select-wrap > .icon:first-child { position: absolute; left: 12px; top: 11px; color: var(--text-2); pointer-events: none; }
.select-wrap > .icon:last-child { position: absolute; right: 10px; top: 12px; width: 16px; height: 16px; color: var(--text-3); pointer-events: none; }
.select-wrap.plain select { padding-left: 12px; font-weight: 520; min-height: 36px; font-size: 13px; }

/* One of a few: the row fills the width, every choice the same share of it. Where a choice's words
   (span.seg-label) are wider than its share they end in „…"; what follows them (a count, „bald")
   stays whole. The label cuts, not the choice: a choice that clipped its content would clip its
   hit area (R14) with it. */
.seg { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); padding: 3px; border-radius: var(--r-ctl); background: var(--panel-3); margin-top: 8px; }
.flabel + .seg { margin-top: 0; }
.seg a, .seg button { min-width: 0; padding: 0 2px; min-height: 30px; border-radius: 7px; display: flex; align-items: center; justify-content: center; font-weight: 580; font-size: 13px; color: var(--text-2); text-decoration: none; white-space: nowrap; user-select: none; }
.seg a:hover, .seg button:hover { color: var(--text); }
.seg a[aria-checked="true"], .seg button[aria-checked="true"] { background: var(--panel); color: var(--text); box-shadow: 0 1px 2px rgba(15, 23, 42, .14), 0 0 0 1px var(--line); }
/* An option there is no room for now (the matrix on a narrow page): quieter, and not a target. */
.seg button[aria-disabled="true"], .seg button[aria-disabled="true"]:hover { color: var(--text-3); cursor: default; }
.seg a .num { color: var(--text-3); font-weight: 500; margin-left: 5px; }
.seg-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.seg-label ~ * { flex: none; }
/* Choices as wide as their words (.hug), where they differ much in length („Standort",
   „Importieren"): each the room it needs, what is left of the row shared. Where the row is too
   narrow for all of them, the short ones keep theirs and the long ones share the rest, cut off. */
.seg.hug { grid-auto-columns: minmax(0, auto); }

/* Toggles: the chips of a row share its whole width; the box on the left says "switch" and
   shows the state: empty, with (ticked), without (crossed). */
.chips { display: flex; flex-wrap: wrap; gap: 5px; }
.chips + .chips { margin-top: 5px; }
.chip { position: relative; flex: 1 1 auto; min-width: 0; max-width: 100%; min-height: 32px; padding: 0 10px 0 8px; border-radius: var(--r-s); display: inline-flex; align-items: center; gap: 7px; background: var(--panel); box-shadow: inset 0 0 0 1px var(--line-strong); color: var(--text-2); font-weight: 550; font-size: 13px; cursor: pointer; user-select: none; text-decoration: none; transition: background .12s, color .12s, box-shadow .12s, transform .12s; }
.chip > .icon { width: 15px; height: 15px; color: var(--text-3); }
.chip:hover { box-shadow: inset 0 0 0 1px var(--text-3); color: var(--text); text-decoration: none; }
.chip:active { transform: scale(.98); }
.chip-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* „Passt in meinen Stundenplan", the longest label of the panel: where the panel is too narrow for
   it on one line (at its narrowest), it takes a second one instead of being cut off. */
.chip.fit-chip .chip-label { white-space: normal; padding: 5px 0; line-height: 1.3; }
/* A chip that opens more under it while it is on (the classes the finder compares): a chevron at
   its end, like the head of an accordion, pointing down while they show. */
.chip > .chip-more { margin-left: auto; transition: transform .2s var(--ease); }
.chip[data-state="with"] > .chip-more { transform: rotate(90deg); }
.box { flex: none; width: 15px; height: 15px; border-radius: 4px; display: inline-grid; place-items: center; background: var(--panel); box-shadow: inset 0 0 0 1.5px var(--text-3); color: transparent; transition: background .12s, box-shadow .12s; }
.box .icon { grid-area: 1 / 1; width: 11px; height: 11px; stroke-width: 3.2; display: none; }
.chip[data-state="with"] { background: var(--invert); color: var(--on-invert); box-shadow: none; }
.chip[data-state="with"] > .icon { color: inherit; }
.chip[data-state="with"] .box, .box.with { background: var(--on-invert); box-shadow: none; color: var(--invert); }
.box.with { background: var(--invert); color: var(--on-invert); }
.chip[data-state="with"] .box .icon:first-child, .box.with .icon { display: block; }
.chip[data-state="without"] { background: var(--no-soft); color: var(--no); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--no) 45%, transparent); }
.chip[data-state="without"] .chip-label { text-decoration: line-through; text-decoration-thickness: 1.5px; }
.chip[data-state="without"] > .icon { color: inherit; }
.chip[data-state="without"] .box, .box.without { background: var(--no); box-shadow: none; color: var(--panel); }
.chip[data-state="without"] .box .icon:last-child, .box.without .icon { display: block; }
/* A chip that has to stay as it is for now (the last class „Passt in meinen Stundenplan"
   compares), or that is only a picture of one (on the start page's board): no link, and nothing
   that looks like one. */
.chip[aria-disabled="true"] { cursor: default; }
.chip[aria-disabled="true"]:active { transform: none; }
.chip[aria-disabled="true"][data-state="off"]:hover { box-shadow: inset 0 0 0 1px var(--line-strong); color: var(--text-2); }
/* The chips of „Passt in meinen Stundenplan": part of the server's page (R9, the same for
   everybody), kept in their place but not shown until the app runs, and gone without JavaScript
   (R15) — the pattern of .mark-toggle, so the filters below them do not move at the takeover. */
.fit-chip { visibility: hidden; }
.app .fit-chip { visibility: visible; }
html:not(.js) .fit-chip { display: none; }
/* A chosen lecturer: the switch between + (wanted) and × (unwanted), the name with the
   academic title under it, and the button that takes the person out again. */
.people { display: grid; gap: 5px; margin-top: 6px; }
.person { display: flex; align-items: center; gap: 9px; min-height: 42px; padding: 3px 3px 3px 5px; border-radius: var(--r-s); background: var(--panel); box-shadow: inset 0 0 0 1px var(--line-strong); }
.person[data-state="without"] { background: var(--no-soft); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--no) 45%, transparent); }
.person-name { flex: 1; min-width: 0; display: grid; line-height: 1.25; }
.person-name b { font-weight: 580; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.person-name small { color: var(--text-3); font-size: 11.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.person[data-state="without"] .person-name b { color: var(--no); text-decoration: line-through; text-decoration-thickness: 1.5px; }
.seg.mini { flex: none; margin: 0; padding: 2px; grid-auto-columns: 26px; }
.seg.mini a { min-height: 26px; padding: 0; border-radius: 5px; --hit-y: 8px; }
.seg.mini a:first-child { --hit-l: 7px; }
.seg.mini a .icon { width: 14px; height: 14px; stroke-width: 2.6; }
.seg.mini a.plus[aria-checked="true"] { background: var(--invert); color: var(--on-invert); box-shadow: none; }
.seg.mini a.cross[aria-checked="true"] { background: var(--no); color: var(--panel); box-shadow: none; }
.person .remove { flex: none; width: 34px; height: 34px; }
.person .remove .icon { width: 15px; height: 15px; }
.person .remove:hover { color: var(--no); }
.people-hint span { display: block; }
.people-hint b { display: inline-block; width: 14px; color: var(--text-2); font-weight: 700; }

/* Pickers with a search (app/src/combobox.rs). The popup is fixed to the window, so the
   panel does not clip it; on a phone it opens in place, across its panel. */
.combo { position: relative; }
.combo-trigger { width: 100%; min-height: 40px; display: flex; align-items: center; gap: 10px; padding: 0 10px 0 12px; border-radius: var(--r-ctl); background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line-strong); font-weight: 600; text-align: left; transition: background .12s; }
.combo-trigger:hover { background: var(--panel-3); }
.combo[data-open] .combo-trigger { background: var(--panel); box-shadow: inset 0 0 0 2px var(--accent); }
.combo-trigger > .icon:first-child { color: var(--text-2); }
.combo-value { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.combo-value small { color: var(--text-3); font-weight: 500; font-size: 12.5px; margin-left: 8px; }
.combo-value.placeholder { color: var(--text-2); }
.combo-chevron { width: 16px; height: 16px; color: var(--text-3); }
.combo-clear { position: absolute; right: 30px; top: 6px; width: 28px; height: 28px; border-radius: 6px; display: grid; place-items: center; color: var(--text-3); }
.combo-clear:hover { background: var(--line-strong); color: var(--text); }
.combo-clear .icon { width: 14px; height: 14px; }
.combo:has(.combo-clear) .combo-value { margin-right: 26px; }
.more .combo-trigger { min-height: 36px; font-size: 13px; font-weight: 560; }
.more .combo-clear { top: 4px; }
.combo-pop { position: fixed; z-index: 60; left: var(--x, 8px); top: var(--y, auto); bottom: var(--b, auto); width: var(--w, 340px); max-height: var(--h, 420px); display: flex; flex-direction: column; overflow: hidden; background: var(--panel); border-radius: var(--r-panel); box-shadow: var(--shadow-pop); animation: pop-in .12s var(--ease); }
.combo-search { flex: none; height: 44px; display: flex; align-items: center; gap: 9px; padding: 0 12px; border-bottom: 1px solid var(--line); color: var(--text-3); }
.combo-search .icon { width: 16px; height: 16px; }
.combo-search input { flex: 1; min-width: 0; height: 100%; border: 0; outline: 0; background: none; font-size: 14px; color: var(--text); }
.combo-search input::placeholder { color: var(--text-3); }
.combo-list { position: relative; flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 4px; }
.combo-option { display: flex; align-items: baseline; gap: 10px; min-height: 34px; padding: 7px 10px; border-radius: var(--r-row); cursor: pointer; font-size: 13.5px; font-weight: 560; }
.combo-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.combo-option small { margin-left: auto; color: var(--text-3); font-weight: 450; font-size: 12px; white-space: nowrap; }
.combo-option .icon { align-self: center; width: 15px; height: 15px; }
.combo-option.active { background: var(--panel-3); }
.combo-option[aria-selected="true"] { color: var(--accent-ink); }
.combo-empty { padding: 12px 10px; color: var(--text-3); font-size: 12.5px; text-align: center; }
.combo-group { padding: 10px 10px 3px; color: var(--text-3); font-size: 11px; font-weight: 650; letter-spacing: .05em; text-transform: uppercase; }
.combo-group:first-child { padding-top: 6px; }
.combo-rule { padding: 0; margin: 6px 10px; border-top: 1px solid var(--line); }
.combo-foot { flex: none; display: flex; align-items: center; gap: 4px; padding: 6px 10px; border-top: 1px solid var(--line); color: var(--text-3); font-size: 11.5px; white-space: nowrap; overflow: hidden; }
.combo-foot kbd { padding: 2px 5px; margin-left: 6px; }
.combo-foot kbd:first-child, .combo-foot kbd + kbd { margin-left: 0; }

.field { display: block; margin-top: 8px; }
.field > span { display: block; color: var(--text-2); font-size: 12px; margin-bottom: 3px; }
.field input { width: 100%; min-height: 36px; font-size: 13px; padding: 0 12px; border: 0; border-radius: var(--r-ctl); background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line-strong); }

/* Credits: two thumbs on one track (--from and --to are fractions of it), exact numbers below. */
/* The knob that is seen (--knob) is drawn inside a larger thumb that is grabbed (--thumb, virtual
   oversizing); the bar lies under the knobs and ends at their outer edges. */
.credits { --knob: 18px; --thumb: calc(var(--knob) + 10px); }
.slider { --ring: var(--invert); position: relative; height: var(--thumb); }
.slider::before, .slider::after { content: ""; position: absolute; z-index: 0; top: calc(50% - 2px); height: 4px; border-radius: 2px; pointer-events: none; }
.slider::before { left: 5px; right: 5px; background: var(--panel-3); box-shadow: inset 0 0 0 1px var(--line); }
.slider::after { left: calc(var(--thumb) / 2 + (100% - var(--thumb)) * var(--from, 0)); right: calc(var(--thumb) / 2 + (100% - var(--thumb)) * (1 - var(--to, 1))); background: var(--invert); }
.slider input { position: absolute; z-index: 1; inset: 0; width: 100%; height: var(--thumb); margin: 0; padding: 0; appearance: none; -webkit-appearance: none; background: none; pointer-events: none; outline: 0; }
.slider[data-low-on-top] input:first-of-type { z-index: 2; }
.slider input::-webkit-slider-runnable-track { height: var(--thumb); background: none; }
.slider input::-moz-range-track { height: var(--thumb); background: none; }
.slider input::-webkit-slider-thumb { -webkit-appearance: none; pointer-events: auto; width: var(--thumb); height: var(--thumb); border-radius: 50%; cursor: grab; filter: drop-shadow(0 1px 1.5px rgba(0, 0, 0, .3)); background: radial-gradient(circle, var(--panel) 0 calc(var(--knob) / 2 - 2px), var(--ring) calc(var(--knob) / 2 - 1.5px) calc(var(--knob) / 2), transparent calc(var(--knob) / 2 + .5px)); }
.slider input::-moz-range-thumb { pointer-events: auto; width: var(--thumb); height: var(--thumb); border: 0; border-radius: 50%; cursor: grab; filter: drop-shadow(0 1px 1.5px rgba(0, 0, 0, .3)); background: radial-gradient(circle, var(--panel) 0 calc(var(--knob) / 2 - 2px), var(--ring) calc(var(--knob) / 2 - 1.5px) calc(var(--knob) / 2), transparent calc(var(--knob) / 2 + .5px)); }
.slider input:active::-webkit-slider-thumb { cursor: grabbing; }
.slider input:focus-visible { --ring: var(--accent); }
.scale span { position: absolute; left: calc(var(--thumb) / 2 + (100% - var(--thumb)) * var(--at, 0)); transform: translateX(-50%); }
.scale { position: relative; height: 14px; margin: -3px 0 7px; font-size: 10.5px; color: var(--text-3); font-variant-numeric: tabular-nums; }
.range { display: grid; grid-template-columns: 1fr auto 1fr; gap: 8px; align-items: center; color: var(--text-3); }
.range input { width: 100%; min-height: 34px; padding: 0 12px; border: 0; border-radius: var(--r-ctl); background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line-strong); text-align: center; font-weight: 600; font-size: 13px; }
.hint { color: var(--text-3); font-size: 12.5px; margin-top: 8px; }
.filter-actions { padding: 10px 12px 12px; border-top: 1px solid var(--line); display: flex; gap: 8px; }
.filter-actions .btn { flex: 1; }
/* The app applies every change at once: its sheet only needs a way back to the list. */
.filter-actions .show, .app .filter-actions .apply { display: none; }
.app .filter-actions .show { display: inline-flex; }
.js .filter-actions { display: none; }
.sheet-toggle, .sheet-close { display: none; }

/* What stands above the rows scrolls with them (`List`): the note of a semester scrolls away, the
   heads of the columns stay at the top while the rows scroll under them (owner, 2026-09-23: the
   note fixed above the list left room for two rows). The heads are as tall as --cols says, so
   that what sticks below them (`.rows-pending`) knows where they end. */
.list { container: list / inline-size; --cols: 35px; }
.list-head { padding: 8px 16px 0; }
.rows > .cols { position: sticky; top: 0; z-index: 4; height: var(--cols); margin: 0 -8px; }
.count-row { display: flex; align-items: center; gap: 10px; min-height: 40px; }
.count { font-size: 22px; font-weight: 700; letter-spacing: -.035em; line-height: 1; }
.count-label { color: var(--text-2); font-weight: 520; }
.list-tools { margin-left: auto; display: flex; gap: 4px; align-items: center; }
.active-filters { display: flex; flex-wrap: wrap; gap: 5px; padding: 4px 0 8px; min-height: 8px; }
/* „Passt in meinen Stundenplan" against a semester without dates: nothing could be checked. */
.list-head > .fit-line { margin: 0 0 8px; }
.tag { min-height: 26px; padding: 0 4px 0 10px; border-radius: 7px; display: inline-flex; align-items: center; gap: 4px; background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line); font-size: 12.5px; font-weight: 560; color: var(--text); }
/* With a semester chosen: what the plan asks for in it besides the modules it places there, a
   line a row. */
.plan-note { display: flex; gap: 10px; margin: 0 8px 10px; padding: 10px 12px; border-radius: var(--r-row); background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line); color: var(--text-2); font-size: 12.5px; line-height: 1.45; }
.plan-note > .icon { width: 16px; height: 16px; margin-top: 1px; flex: none; color: var(--text-3); }
.plan-note > div { min-width: 0; }
.plan-note ul { margin-top: 3px; padding-left: 16px; list-style: disc; }
.plan-note li + li { margin-top: 2px; }
.plan-note li::marker { color: var(--text-3); }
.plan-note b { color: var(--text); font-weight: 600; }
.plan-note .plan-note-lead { font-weight: 600; color: var(--text); }
.plan-note .plan-note-hint { color: var(--text-3); margin-top: 6px; }
.tag em { font-style: normal; color: var(--text-3); font-weight: 450; }
.tag a { width: 24px; height: 24px; border-radius: 5px; display: grid; place-items: center; color: var(--text-3); }
.tag a:hover { background: var(--line-strong); color: var(--text); }
.tag .icon { width: 13px; height: 13px; }
.cols, .row { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) 150px 56px 110px 46px 100px; align-items: center; column-gap: 16px; }
.cols { padding: 8px 58px 8px 23px; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--panel-2); }
.cols a { color: inherit; text-decoration: none; }
.cols a:hover, .cols a[aria-current="true"] { color: var(--text); }
.cols .c-lp, .row .lp { text-align: right; }
.narrow-only { display: none; }
@container list (max-width: 1080px) {
  .cols, .row { grid-template-columns: minmax(0, 1fr) 150px 56px 110px 46px 100px; }
  .c-resp, .row .resp { display: none; }
}
@container list (max-width: 860px) {
  .cols, .row { grid-template-columns: minmax(0, 1fr) 56px 110px 46px 100px; }
  .c-exam, .row .exam { display: none; }
}
@container list (max-width: 660px) {
  .cols, .row { grid-template-columns: minmax(0, 1fr) 54px 30px; }
  .c-lang, .c-events, .row .lang, .row .events, .turnus .txt { display: none; }
  .c-turnus { visibility: hidden; }
  .narrow-only { display: inline; }
}
.rows { padding: 0 8px 12px; flex: 1; }
.sem { position: sticky; top: 0; z-index: 2; display: flex; align-items: center; gap: 10px; padding: 10px 12px 4px 15px; background: linear-gradient(var(--panel) 78%, transparent); font-weight: 650; font-size: 13px; }
.sem small { color: var(--text-3); font-weight: 500; }
.sem::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.row { position: relative; min-height: 54px; padding: 6px 50px 6px 15px; border-radius: var(--r-row); color: inherit; text-decoration: none; transition: background .1s; }
.row::before { content: ""; position: absolute; left: 4px; top: 14px; bottom: 14px; width: 3px; border-radius: 3px; background: transparent; }
/* Every other row is shaded, so the eye keeps to its line across the columns (owner, 2026-09-29:
   „alternating row color"). Which rows the list says (`.shaded`, `Row`); the row pointed at, the
   focused one and the open one stand above the shade (the rules after this one, as specific as
   it). On a phone the rows are cards of their own and stay plain. */
@media (min-width: 901px) { .row-wrap:where(.shaded) > .row { background: var(--row-shade); } }
.row:hover { background: var(--row-hover); text-decoration: none; }
.row[aria-current="true"] { background: var(--panel-3); }
.row[aria-current="true"]::before { background: var(--accent); }
.row .t { min-width: 0; }
.row .t b { font-weight: 600; font-size: 14.5px; line-height: 1.3; letter-spacing: -.012em; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.row .t small { display: flex; flex-wrap: wrap; gap: 2px 10px; align-items: center; color: var(--text-3); font-size: 12.5px; margin-top: 3px; }
.row .lp { font-weight: 650; font-size: 15px; }
.row .lp small { font-weight: 500; font-size: 11px; color: var(--text-3); margin-left: 2px; }
.resp, .exam { color: var(--text-2); font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kind { display: inline-flex; align-items: center; gap: 5px; font-weight: 560; color: var(--text-2); }
.kind i { width: 7px; height: 7px; border-radius: 50%; background: var(--text-2); }
.kind.k-elective i, .kind.k-fues i { background: transparent; box-shadow: inset 0 0 0 2px var(--text-2); }
.kind.k-none { color: var(--text-3); font-style: italic; font-weight: 450; }
.kind.k-none i { background: transparent; box-shadow: inset 0 0 0 1px var(--text-3); }
.flag { font-size: 11.5px; font-weight: 600; padding: 1px 6px; border-radius: 5px; background: var(--warn-soft); color: var(--warn); }
.flag.neutral { background: var(--panel-3); color: var(--text-2); }
.turnus { display: inline-flex; align-items: center; gap: 6px; color: var(--text-2); font-weight: 520; font-size: 13px; }
.turnus .icon { width: 15px; height: 15px; color: var(--text-3); }
.lang { font-size: 11.5px; font-weight: 650; color: var(--text-2); padding: 3px 7px; border-radius: 5px; background: var(--panel-3); justify-self: start; }
.events { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-2); font-weight: 520; }
.events.none { color: var(--text-3); font-weight: 450; }
.events .icon { width: 15px; height: 15px; }
.chunk { display: contents; }
/* The endless list belongs to the browser app; without it the pager links do the job. */
.load-more, .list-end, .keys { display: none; }
.app .load-more { display: flex; width: calc(100% - 16px); margin: 10px 8px; }
.app .list-end { display: block; text-align: center; color: var(--text-3); font-size: 12.5px; padding: 16px 0 6px; }
.app .pager { display: none; }
.app .keys { display: inline-flex; align-items: center; gap: 4px; color: var(--text-3); font-size: 12px; margin-right: 6px; white-space: nowrap; }
.keys kbd { padding: 2px 5px; }
.row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; background: var(--row-hover); }
.row { scroll-margin: 44px 0 16px; }
/* Rows that are off screen are not laid out (their last height is remembered). What the page
   costs to lay out no longer grows with the rows that are loaded: a resize stays live and an
   endless list stays quick (measured with 350 rows: 50 ms a frame before, 17 ms after). */
.rows .row-wrap { content-visibility: auto; contain-intrinsic-size: auto 58px; }
/* The virtual list of the browser app (`VirtualRows`): an element as tall as the whole list, so
   the scrollbar has the list's length from the start, and inside it only the rows that are on
   screen, each at its offset. They are measured, so they need no reserved size. */
.vlist { position: relative; height: var(--h, 0px); }
.vlist > .vrow { position: absolute; left: 0; right: 0; top: var(--top, 0px); }
.rows.virtual .row-wrap { content-visibility: visible; contain-intrinsic-size: none; }
.plan-sem { white-space: nowrap; }
.pager { display: flex; align-items: center; justify-content: center; gap: 12px; padding: 14px 0 6px; color: var(--text-3); font-size: 13px; }
.state { max-width: 520px; margin: 56px auto; padding: 0 20px; text-align: center; }
.state h1, .state .state-title { font-size: 17px; font-weight: 650; margin-bottom: 6px; }
.state p { color: var(--text-2); margin-bottom: 14px; }
.state-detail { font-family: var(--mono); font-size: 11.5px; color: var(--text-3); }

/* ---------- marked modules („Merken", app/src/bookmarks.rs) ----------
   Marking belongs to the browser app, so its buttons stay out of sight until the app runs (.app):
   server HTML shows every module unmarked, to everybody (R9). Where a button is part of server
   HTML its place is kept (visibility, not display), so nothing moves at the takeover; without
   JavaScript it is gone altogether (R15). A marked module is neutral and strong, like a chosen
   chip: the accent stays with primary actions and the open row. */
.mark-toggle { visibility: hidden; }
.app .mark-toggle { visibility: visible; }
html:not(.js) .mark-toggle { display: none; }
.mark-toggle .icon { transition: fill .12s, color .12s; }
.mark-toggle[aria-pressed="true"] .icon { fill: currentColor; }
/* At the end of a row: a button next to the row's link (inside the link it would be neither a
   link nor a button). Its glyph (18px) ends where the content of the list ends, 12px from the
   row's edge, like the rule of a group header; the button around it is 32px and reaches from 37px
   to 5px before the edge, and the row's columns end 13px before the button (50px of padding).
   It is quiet until the row is pointed at or the module is marked, and takes the pointer over
   the whole height of the row and up to the row's right edge (R14). */
.row-wrap { position: relative; }
.row-wrap > .mark-toggle { --hit-y: 11px; --hit-l: 6px; --hit-r: 5px; position: absolute; right: 5px; top: 50%; width: 32px; height: 32px; margin-top: -16px; border-radius: var(--r-row); color: var(--line-strong); }
.row-wrap:focus-within > .mark-toggle[aria-pressed="false"] { color: var(--text-3); }
.row-wrap > .mark-toggle:focus-visible { background: var(--line); }
.row-wrap > .mark-toggle[aria-pressed="false"]:focus-visible { color: var(--text); }
.row-wrap > .mark-toggle[aria-pressed="true"] { color: var(--text); }
@media (hover: none) { .row-wrap > .mark-toggle[aria-pressed="false"] { color: var(--text-3); } }
/* Where a pointer hovers (a finger leaves its hover behind): the row stays lit while the pointer
   is on its button. A marked module keeps its strength whatever the pointer does. */
@media (hover: hover) {
  .row-wrap:hover > .mark-toggle[aria-pressed="false"] { color: var(--text-3); }
  .row-wrap > .mark-toggle:hover { background: var(--line); }
  .row-wrap > .mark-toggle[aria-pressed="false"]:hover { color: var(--text); }
  .row-wrap:hover > .row { background: var(--row-hover); }
  .row-wrap:hover > .row[aria-current="true"] { background: var(--panel-3); }
}
/* The list of marked modules: a module whose mark was taken away stays until the page is left. */
.row-wrap > .row { transition: background .1s, opacity .15s; }
.row-wrap.unmarked > .row { opacity: .42; }
.row-wrap > .row.missing, .row-wrap:hover > .row.missing { background: none; cursor: default; }
.row.missing .t b { font-weight: 560; color: var(--text-2); }
.count-more { color: var(--text-3); font-weight: 450; }
/* The line under a module's heading, in the preview and on the module's page (`.hero-line`,
   markup: div.hero-line > p.badges > span.badge… + div.switches > button.plan-toggle +
   button.mark-toggle): the badges (credits, turnus, language) on the left, and at its right end
   „Einplanen" and „Merken", switches with their shortcuts, as tall as the toggles of the filter
   panel. Where the line has room the two stand side by side; where it has not they stand one
   over the other, and the badges have the rest of the line and wrap in it (owner, 2026-09-26:
   the pair taking a line of its own under the badges made the heading jump about). The badges
   keep the width of their one line while that leaves room for the stack (flex-grow 0); the
   switches take what is left (flex-basis 0) and wrap in it, so they are side by side exactly
   where both fit. Where the stack does not fit beside the badges, the switches keep its width,
   their automatic minimum, and the badges give way. A label keeps its width, so a switch does
   not change its size when „Merken" becomes „Gemerkt", and both are as wide, so the stack is
   straight. */
.hero-line { display: flex; align-items: flex-start; gap: 6px; margin-top: 12px; }
/* The badges stand on the middle of the switches (26px in 32px), a line of them beside each
   switch of the stack. A badge wider than their column (the narrowest preview has 176px for „jedes
   Semester (ungerade Jahre)") breaks into two lines rather than push the stack out. */
.hero-line > .badges { margin-top: 0; padding: 3px 0; align-items: center; row-gap: 12px; }
.hero-line > .badges > .badge { padding-top: 3px; padding-bottom: 3px; line-height: 1.25; white-space: normal; }
/* Its right edge is the right edge of what stands under the heading (the body of a module is
   inset by 20px, the heading of the preview by 14px and that of the page by 18px). */
.hero-line > .switches { flex: 1 1 0; margin-right: 6px; display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: 6px; }
.module-page > .hero .hero-line > .switches { margin-right: 2px; }
html:not(.js) .hero-line > .switches { display: none; }
html:not(.js) .hero-line > .badges { padding: 0; row-gap: 6px; }
.mark-switch { --hit-y: 5px; --hit-x: 4px; min-height: 32px; padding: 0 7px 0 10px; border-radius: var(--r-ctl); display: inline-flex; align-items: center; gap: 8px; background: var(--panel); box-shadow: inset 0 0 0 1px var(--line-strong); color: var(--text); font-weight: 600; font-size: 13.5px; transition: background .12s, color .12s, box-shadow .12s, transform .12s; }
.mark-switch:hover { box-shadow: inset 0 0 0 1px var(--text-3); }
.mark-switch:active { transform: scale(.98); }
.mark-switch .icon { width: 16px; height: 16px; }
.mark-switch > span:first-of-type { min-width: 70px; text-align: left; }
.mark-switch kbd { padding: 2px 6px; }
.mark-switch[aria-pressed="true"] { background: var(--invert); color: var(--on-invert); box-shadow: none; }
.mark-switch[aria-pressed="true"] kbd { background: color-mix(in srgb, var(--on-invert) 16%, transparent); box-shadow: none; color: inherit; }
/* „Einplanen" keeps the width of „Eingeplant". What it says beyond that in the app (the
   placeholder it plans for, the other semesters the module is planned in) is its small line: one
   line that gives way first. The two are 6px apart, side by side or one over the other: towards
   each other their areas meet in the middle. */
.switches > .mark-toggle { flex: none; --hit-l: 3px; --hit-t: 3px; }
.switches > .plan-toggle { min-width: 0; --hit-r: 3px; --hit-b: 3px; }
.plan-toggle.mark-switch > span:first-of-type { flex: none; }
.plan-toggle.mark-switch > small { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 12px; font-weight: 500; opacity: .8; }
@media (min-width: 901px) {
  /* The small line has a column of its own, which the switch's smallest width leaves out: one over
     the other, the two are as narrow as without it, and it takes what the line leaves. Side by
     side only where it fits whole. Its gap is inside the column (::before), so that an empty
     column adds none. */
  .plan-toggle.mark-switch:has(> small) { display: inline-grid; grid-template-columns: auto auto minmax(0, max-content) auto; gap: 0; }
  .plan-toggle.mark-switch:has(> small) > .icon { margin-right: 8px; }
  .plan-toggle.mark-switch:has(> small) > small { grid-column: 3; }
  .plan-toggle.mark-switch:has(> small) > small::before { content: ""; padding-left: 8px; }
  .plan-toggle.mark-switch:has(> small) kbd { grid-column: 4; margin-left: 8px; }
  /* On a module's page its sidebar says that under „Einplanen": the switch keeps the width the
     server gave it, so the line does not break anew at the takeover (R15). */
  .module-page > .hero .plan-toggle.mark-switch > small { display: none; }
}
/* On a phone the pair takes a line of its own under the badges and fills it, each switch half of
   it with its content in the middle (owner, 2026-09-27: kept at the line's right end, the two
   stood oddly under the badges). The small line is the switch's second line, in the switch's
   height: it has more room there than beside the label. It gives way on that line instead of
   breaking the pair, so nothing moves when the app adds it (R15). */
@media (max-width: 900px) {
  .hero-line { flex-wrap: wrap; row-gap: 10px; }
  .hero-line > .badges { padding: 0; row-gap: 6px; }
  .hero-line > .switches { flex-basis: 100%; flex-wrap: nowrap; }
  .switches > .plan-toggle, .switches > .mark-toggle { flex: 1 1 0; justify-content: center; }
  .plan-toggle.mark-switch > span:first-of-type { min-width: 77px; }
  .plan-toggle.mark-switch:has(> small) { display: inline-grid; grid-template-columns: auto minmax(0, max-content); justify-content: center; gap: 0 8px; align-content: center; }
  .plan-toggle.mark-switch:has(> small) > .icon { grid-row: span 2; }
  .plan-toggle.mark-switch:has(> small) > span:first-of-type { line-height: 19px; }
  .plan-toggle.mark-switch > small { font-size: 12.5px; line-height: 16px; }
}
/* Among the actions of a sidebar, and what else a sidebar's action can be: a button, a question. */
button.action { width: 100%; text-align: left; }
button.action:hover { background: var(--panel-3); }
.action.mark-toggle[aria-pressed="true"] .icon { color: var(--text); }
.note-action { flex-wrap: wrap; gap: 6px; padding-top: 4px; padding-bottom: 4px; }
.note-action > span { flex: 1 1 auto; min-width: 0; }
/* A question before something is thrown away: on a line of its own, the answers under it. */
.note-action.ask > span { flex-basis: 100%; }
.mini { --hit-y: 4px; min-height: 28px; padding: 0 10px; border-radius: var(--r-s); background: var(--panel); box-shadow: inset 0 0 0 1px var(--line-strong); font-weight: 600; font-size: 12.5px; }
.mini:hover { background: var(--panel-2); }
.mini.danger { background: var(--no); color: var(--panel); box-shadow: none; }
.mini.danger:hover { filter: brightness(1.08); background: var(--no); }
.mini.primary { background: var(--accent); color: var(--on-accent); box-shadow: none; }
.mini.primary:hover { filter: brightness(1.08); background: var(--accent); }
/* A list that arrives in a link from another device: the page asks before it adds anything.
   The box starts where the number of the list's head starts. */
.offer { flex: none; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 12px 16px 0; padding: 8px 8px 8px 12px; border-radius: var(--r-row); background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line-strong); font-size: 13px; }
.offer > .icon { width: 16px; height: 16px; color: var(--text-2); }
.offer p { flex: 1 1 260px; min-width: 0; color: var(--text-2); }
.offer p b { color: var(--text); font-weight: 620; }
.side-facts dd small { color: var(--text-3); font-weight: 450; font-size: 12px; }
.storage-hint { display: flex; gap: 8px; margin-top: 2px; }
.storage-hint .icon { width: 15px; height: 15px; margin-top: 2px; }
/* How many modules are marked, at the rail's bookmark (the app alone knows). */
.nav .ind { position: relative; }
.nav-count { position: absolute; top: 3px; right: 2px; min-width: 15px; height: 15px; padding: 0 4px; border-radius: 8px; display: grid; place-items: center; background: var(--invert); color: var(--on-invert); box-shadow: 0 0 0 2px var(--bg); font-size: 10px; font-weight: 700; line-height: 1; letter-spacing: 0; }
.nav[aria-current="page"] .nav-count { background: var(--on-invert); color: var(--invert); box-shadow: 0 0 0 1px var(--invert); }
@media (max-width: 900px) {
  /* 44px for a finger; the glyph ends at the card's inner edge (14px), like the card's text. */
  .row-wrap > .mark-toggle { --hit-y: 8px; --hit-l: 6px; --hit-r: 1px; right: 1px; width: 44px; height: 44px; margin-top: -22px; border-radius: var(--r-ctl); }
  .row-wrap > .mark-toggle[aria-pressed="false"] { color: var(--text-3); }
  .mark-switch { --hit-y: 2px; min-height: 44px; padding: 0 16px 0 13px; font-size: 15px; }
  .mark-switch .icon { width: 18px; height: 18px; }
  .mark-switch > span:first-of-type { min-width: 64px; }
  .mini { min-height: 38px; padding: 0 14px; font-size: 14px; }
  .bottomnav .nav-count { top: -1px; right: 9px; box-shadow: 0 0 0 2px var(--panel); }
}

/* ---------- detail panel ---------- */
.hero { padding: 12px 14px 16px 20px; border-bottom: 1px solid var(--line); }
.hero-top { display: flex; align-items: center; gap: 2px; color: var(--text-2); min-height: 40px; }
.hero-top .mono { margin-right: auto; padding: 4px 8px; border-radius: var(--r-s); background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line); color: var(--text-2); }
.hero h2 { margin: 10px 0 4px; font-size: 24px; line-height: 1.16; font-weight: 700; letter-spacing: -.03em; text-wrap: pretty; }
.hero .en { color: var(--text-2); font-size: 14px; }
.badges { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.badge { min-height: 26px; padding: 0 9px; border-radius: var(--r-s); display: inline-flex; align-items: center; gap: 6px; font-weight: 600; font-size: 12.5px; background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line); color: var(--text-2); white-space: nowrap; }
.badge .icon { width: 14px; height: 14px; }
.badge.strong { background: var(--invert); color: var(--on-invert); box-shadow: none; }
.badge.warn { background: var(--warn-soft); color: var(--warn); box-shadow: none; }
.badge-unknown { background: transparent; box-shadow: inset 0 0 0 1px var(--line-strong); color: var(--text-3); font-style: italic; font-weight: 500; }
.dbody { padding: 0 20px 24px; }
.section { padding-top: 20px; }
.section > .label { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.section > .label span { margin-left: auto; text-transform: none; letter-spacing: 0; font-weight: 550; color: var(--text-2); font-size: 12.5px; }
.facts { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 0; }
.fact { display: flex; align-items: center; gap: 11px; padding: 10px 12px; border-radius: var(--r-row); background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line); min-width: 0; }
.fact.wide { grid-column: 1 / -1; }
.fact .ico { width: 32px; height: 32px; border-radius: 8px; display: grid; place-items: center; background: var(--panel); color: var(--text-2); box-shadow: 0 0 0 1px var(--line); flex: none; }
.fact .ico .icon { width: 16px; height: 16px; }
.fact dt { color: var(--text-3); font-size: 11.5px; }
.fact dd { margin: 0; font-weight: 600; font-size: 13.5px; overflow-wrap: anywhere; }
/* A column per day, equal ones; a fitted grid (the Stundenplan's) gives each day its share of the
   week instead, more where modules stand side by side (--cols, app/src/week.rs). */
.week { display: grid; grid-template-columns: 30px var(--cols, repeat(var(--days, 5), minmax(0, 1fr))); gap: 0 5px; border-radius: var(--r-row); background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line); padding: 10px 10px 12px 4px; }
.week .d { text-align: center; font-size: 11.5px; font-weight: 650; color: var(--text-3); padding-bottom: 8px; }
.week .hours { grid-row: 2; display: grid; grid-auto-rows: 48px; font-size: 10.5px; color: var(--text-3); text-align: right; padding-right: 4px; }
.week .col { grid-row: 2; position: relative; background: repeating-linear-gradient(var(--line) 0 1px, transparent 1px 48px); height: calc(var(--span) * 24px); }
/* Slots that overlap on a day stand side by side: `--lane` of `--lanes` (week.rs), `--wide` lanes
   wide. One lane is the whole column less 1px each side, as before lanes; neighbours keep 2px
   between them. Where the module's own slots meet another planned module (`--mine` own lanes,
   `--theirs` planned ones), the column has two parts, and `--lane`, `--lanes` and `--wide` count
   in the slot's part: the planned part (`--pw`, the slots with `--beside:1`) on the right, its
   lanes half as wide as an own one, but never so wide that an own lane is left narrower than
   42px („Prak" and its padding) while a planned lane is wider than 10px, an outline alone. The
   side padding is at most a quarter of the slot's width (a percentage of the column, like `left`
   and `width`): a border box is never narrower than its padding, so a fixed 6px made the slots of
   five or more lanes 12px wide, over their neighbours and out of the column. */
.slot, .slot-acts, .slot-frame {
  --pw: min(100% * var(--theirs, 0) / (2 * var(--mine, 1) + var(--theirs, 0)), max(var(--theirs, 0) * 10px, 100% - var(--mine, 1) * 42px));
  --at: calc(var(--beside, 0) * (100% - var(--pw)));
  --in: calc(var(--beside, 0) * var(--pw) + (1 - var(--beside, 0)) * (100% - var(--pw)));
}
.slot {
  position: absolute; left: calc(1px + var(--at) + var(--in) * var(--lane, 0) / var(--lanes, 1)); width: calc(var(--in) * var(--wide, 1) / var(--lanes, 1) - 2px); top: calc((var(--from) - var(--first)) * 24px); height: calc((var(--to) - var(--from)) * 24px - 2px); border-radius: 6px; padding: 4px clamp(0px, calc((var(--in) * var(--wide, 1) / var(--lanes, 1) - 2px) / 4), 6px); font-size: 11px; line-height: 1.25; font-weight: 600; overflow: hidden; background: var(--accent-soft); color: var(--accent-ink); box-shadow: inset 2.5px 0 0 var(--accent); container-type: inline-size; overflow-wrap: normal; word-break: normal; hyphens: manual;
}
a.slot { text-decoration: none; }
/* The buttons the page puts on a slot (div.slot-acts > button.slot-act, week.rs): a box of the
   slot's size and place (the slot's style), which lets every click but those on its buttons
   through to the slot's link; the buttons in a row at its bottom right, „✓" green, „×" red when
   pointed at. They come when the slot is pointed at, and stand in view where a decision is due
   (.asks) and for a finger. */
.slot-acts { position: absolute; z-index: 2; left: calc(1px + var(--at) + var(--in) * var(--lane, 0) / var(--lanes, 1)); width: calc(var(--in) * var(--wide, 1) / var(--lanes, 1) - 2px); top: calc((var(--from) - var(--first)) * 24px); height: calc((var(--to) - var(--from)) * 24px - 2px); display: flex; justify-content: flex-end; align-items: flex-end; gap: 3px; padding: 3px; pointer-events: none; opacity: 0; transition: opacity .12s; }
.week.fit .slot-acts { top: calc((var(--from) - var(--first)) * 100% / var(--span)); height: calc((var(--to) - var(--from)) * 100% / var(--span) - 2px); }
.slot:hover + .slot-acts, .slot-acts:hover, .slot-acts:focus-within, .slot-acts.asks { opacity: 1; }
@media (hover: none) { .slot-acts { opacity: 1; } }
.slot-act { flex: none; pointer-events: auto; width: 20px; height: 20px; display: grid; place-items: center; border-radius: 6px; background: var(--panel); color: var(--text-2); box-shadow: 0 0 0 1px var(--line-strong), 0 1px 2px rgba(15, 23, 42, .12); }
.slot-act .icon { width: 13px; height: 13px; stroke-width: 2.6; }
.slot-act[data-icon="check"] { color: var(--ok); }
.slot-act[data-icon="check"]:hover { background: var(--ok); color: var(--panel); box-shadow: none; }
.slot-act[data-icon="x"]:hover { background: var(--no); color: var(--panel); box-shadow: none; }
/* Slots that stand together and clash (a module's A-week and B-week Termine at one time,
   week.rs): one red frame around them (div.slot-frame, the slot style of their lanes and times),
   1px outside them, with the red mark at its top right; each slot in it (.framed) keeps its own
   look (owner, 2026-09-25: together they compete with the other). */
.slot-frame { position: absolute; z-index: 1; pointer-events: none; left: calc(var(--at) + var(--in) * var(--lane, 0) / var(--lanes, 1)); width: calc(var(--in) * var(--wide, 1) / var(--lanes, 1)); top: calc((var(--from) - var(--first)) * 24px - 1px); height: calc((var(--to) - var(--from)) * 24px); border-radius: 7px; box-shadow: inset 0 0 0 2px var(--no); }
.week.fit .slot-frame { top: calc((var(--from) - var(--first)) * 100% / var(--span) - 1px); height: calc((var(--to) - var(--from)) * 100% / var(--span)); }
.slot-frame::after { content: "!"; position: absolute; top: 4px; right: 4px; width: 13px; height: 13px; border-radius: 50%; background: var(--no); color: var(--panel); font-size: 10px; font-weight: 800; line-height: 13px; text-align: center; }
.sp-body .slot.clash.framed { box-shadow: inset 2.5px 0 0 var(--tint, var(--accent)); }
.sp-body .slot.clash.framed::after { content: none; }
/* A „✓" that is on (aria-pressed): what was taken, filled; its click takes it back. */
.slot-act[aria-pressed="true"] { background: var(--ok); color: var(--panel); box-shadow: none; }
.slot-act[aria-pressed="true"]:hover { filter: brightness(.9); }
/* The first line (span.l): whole words only, two lines at most, then „…" (owner review
   2026-09-25: „Softwaresystem-en" broke inside a word); one in a slot under an hour (.brief),
   whose second line would show only its top edge. The height caps what the line clamp counts,
   for the first word below. */
.slot > .l { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; max-height: 2.5em; overflow: hidden; }
.slot .w { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: top; }
.slot.brief { padding-top: 3px; padding-bottom: 3px; }
.slot.brief > .l { -webkit-line-clamp: 1; line-clamp: 1; max-height: 1.25em; }
/* A first word wider than the slot ends the label, cut with „…": the words after it would stand
   under a cut word („Mathe…" over „IT-1", review 2026-09-25). Its outer box keeps the word's whole
   width (a percentage width counts as none there), its text is as wide as the slot (100cqw), and
   an empty line after the text is as tall as the word is wider than the slot, times a thousand,
   at most one line: a word that fits adds nothing, one that does not makes the first line two
   lines tall, and the height above leaves the rest out. */
.slot > .l > .w:first-child { max-width: none; overflow: visible; }
.slot > .l > .w:first-child > span { display: block; width: min(100%, 100cqw); overflow: hidden; text-overflow: ellipsis; }
.slot > .l > .w:first-child:not(:last-child)::after { content: ""; display: block; width: calc(100% - 100cqw); aspect-ratio: 1 / 1000; max-height: 1.25em; }
/* A slot a sliver wide (a planned module's slim lane in a narrow week) is an outline alone; its
   tooltip and the line under the week name it. */
@container (max-width: 22px) {
  .slot.planned > .l { visibility: hidden; }
}
.slot small { display: block; font-weight: 500; opacity: .85; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.slot.other { background: var(--panel-3); color: var(--text-2); box-shadow: inset 2.5px 0 0 var(--text-3); }
/* A module of the Studienplan, in its tint (`t-…`). */
.slot.tinted { background: color-mix(in oklch, var(--tint) 16%, var(--panel)); color: var(--text); box-shadow: inset 2.5px 0 0 var(--tint); }
/* Single dates: they do not happen every week, so they do not look like what does. */
.slot.once { background: var(--panel-2); color: var(--text-2); box-shadow: inset 0 0 0 1px var(--line-strong); }
/* Another planned module beside a module's own slots: a quiet outline in a slim lane (week.rs),
   its short name in a lighter weight than theirs. */
.slot.planned { background: transparent; color: var(--text-2); box-shadow: inset 0 0 0 1px var(--line-strong); font-weight: 500; }
/* An option of a choice not yet made. */
.slot.alt { background: transparent; box-shadow: none; outline: 1px dashed var(--tint, var(--line-strong)); outline-offset: -1px; }
.slot.clash { box-shadow: inset 2.5px 0 0 var(--no), inset 0 0 0 1.5px var(--no); }
/* The slot of what is open beside the grid. */
.slot[aria-current="true"] { outline: 2px solid var(--accent); outline-offset: -2px; }
.evlist { display: grid; gap: 4px; margin-top: 10px; }
.ev { display: grid; grid-template-columns: 112px minmax(0, 1fr); gap: 2px 12px; padding: 9px 12px; border-radius: var(--r-row); box-shadow: inset 0 0 0 1px var(--line); font-size: 13px; color: inherit; text-decoration: none; }
a.ev:hover { background: var(--panel-2); text-decoration: none; }
.ev .when { font-weight: 600; font-variant-numeric: tabular-nums; }
.ev small { color: var(--text-3); grid-column: 2; }
.ev .when + b { font-weight: 560; }
.ev .odd { display: flex; gap: 5px; color: var(--warn); }
.ev .odd .icon { flex: none; width: 13px; height: 13px; margin-top: 2px; }
.note { display: flex; gap: 10px; margin-top: 10px; padding: 10px 12px; border-radius: var(--r-row); background: var(--warn-soft); color: var(--warn); font-size: 12.5px; font-weight: 520; }
.note .icon { width: 16px; height: 16px; margin-top: 1px; }
/* An explanation, not a warning: why a row of the plan names no module. */
.note.quiet { background: var(--panel-2); color: var(--text-3); margin-top: 20px; }
/* A soft exam warning under a module's exams (the plan beside it): as close as a warning. */
#pruefungstermine > .note.quiet { margin-top: 10px; }
/* The first thing under the head of a plan row or an area: where its modules are chosen. */
.row-action { width: 100%; margin-top: 20px; }
/* How one got to an area („Anwendungsfach / Mathematik"): each step leads back. */
.hero .trail a { color: inherit; text-decoration: underline; text-decoration-color: var(--line-strong); text-underline-offset: 3px; }
.hero .trail a:hover { color: var(--text); text-decoration-color: currentColor; }
.hero .trail .sep { color: var(--text-3); }
.linklist { display: grid; gap: 4px; }
.pre { min-height: 46px; display: flex; align-items: center; gap: 10px; padding: 6px 10px 6px 12px; border-radius: var(--r-row); box-shadow: inset 0 0 0 1px var(--line); color: inherit; text-decoration: none; }
.pre:hover { background: var(--panel-2); text-decoration: none; }
.pre b { flex: 1 1 auto; font-weight: 570; font-size: 13.5px; min-width: 0; }
/* The note at the right end gives way before the name does. */
.pre small { flex: 0 1 auto; min-width: 0; margin-left: auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-3); font-size: 12px; }
.pre > .icon { color: var(--text-3); width: 16px; height: 16px; }
.prose { color: var(--text-2); font-size: 14px; line-height: 1.6; white-space: pre-line; max-width: 72ch; }
.prose + .prose { margin-top: 10px; }
details.more > summary { cursor: pointer; color: var(--text-2); font-weight: 560; font-size: 13px; min-height: 36px; display: inline-flex; align-items: center; }
.plain th, .plain td { padding: 8px 10px; text-align: left; border-bottom: 1px solid var(--line); font-size: 13px; }
.plain th { color: var(--text-3); font-weight: 600; font-size: 12px; }
.source { margin-top: 22px; padding-top: 14px; border-top: 1px solid var(--line); display: flex; align-items: center; gap: 8px; color: var(--text-3); font-size: 12.5px; }
.source a { margin-left: auto; font-weight: 560; display: inline-flex; gap: 4px; align-items: center; min-height: 36px; }
.source .icon { width: 14px; height: 14px; }

/* ---------- other pages (one scrolling column of panels) ---------- */
.page-inner { display: grid; gap: var(--gap); container: page / inline-size; }
/* The head of a program: three lines that all start on the same left edge — where the visitor
   is, the name with the numbers right of it on its baseline, and under which regulations. */
.prog-head { padding: 12px 24px 16px; }
.prog-head .hero-top { margin: 0 0 2px -10px; min-height: 34px; }
.crumbs { margin-left: auto; display: flex; align-items: center; color: var(--text-3); font-size: 12.5px; }
.crumbs a { color: var(--text-3); }
.crumbs a + a::before { content: "/"; margin: 0 7px; color: var(--line-strong); }
.prog-title { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; gap: 4px 24px; }
.prog-title h1 { min-width: 0; font-size: 26px; line-height: 1.15; font-weight: 700; letter-spacing: -.03em; text-wrap: pretty; }
.prog-facts { display: flex; align-items: baseline; color: var(--text-3); font-size: 12.5px; white-space: nowrap; }
.pfact { white-space: nowrap; }
.pfact + .pfact { margin-left: 16px; padding-left: 16px; border-left: 1px solid var(--line); }
.pfact b { margin-right: 1px; color: var(--text); font-size: 16px; font-weight: 680; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.prog-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 0; margin-top: 7px; color: var(--text-2); font-size: 13.5px; }
.prog-meta > * + *::before { content: "·"; margin: 0 8px; color: var(--text-3); }
.prog-meta .degree { font-weight: 650; color: var(--text); }
.prog-meta .current { color: var(--text-3); }
.prog-meta .flag { margin-left: 10px; }
.prog-meta .flag::before { content: none; }
@container page (max-width: 720px) {
  .prog-title { grid-template-columns: minmax(0, 1fr); }
  .prog-facts { flex-wrap: wrap; white-space: normal; margin-top: 4px; }
  .pfact + .pfact { margin-left: 14px; padding-left: 14px; }
}
.eyebrow { font-size: 12.5px; color: var(--text-3); margin-bottom: 6px; }
.subtitle { display: block; color: var(--text-3); font-size: 12.5px; }
/* A program's line under its name (PO, the semester of its plan, area) wraps in the room the name
   leaves: unlike the name, it does not push the kind at the right end out of the row. */
.pre .subtitle { contain: inline-size; }
.total { color: var(--text-2); }
.tab-count { color: var(--text-3); font-weight: 500; font-size: 13px; }
.block { padding: 20px 24px; }
.block > h2 { font-size: 16px; font-weight: 650; letter-spacing: -.015em; margin-bottom: 12px; }
.block h3 { font-size: 14px; font-weight: 650; margin: 0 0 8px; }
.actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.inline-links { display: flex; flex-wrap: wrap; gap: 4px 12px; }
.tabs { display: flex; gap: 4px; padding: 6px; }
.tab { min-height: 40px; padding: 0 16px; border-radius: var(--r-ctl); display: inline-flex; align-items: center; gap: 6px; color: var(--text-2); font-weight: 600; font-size: 13.5px; text-decoration: none; }
.tab:hover { background: var(--panel-3); color: var(--text); text-decoration: none; }
.tab.active { background: var(--invert); color: var(--on-invert); }
/* A page of sections opens like the catalog's list: the number, then what it counts. */
.summary { display: flex; align-items: center; gap: 12px; min-height: 40px; padding: 4px 8px 2px 16px; }
.summary h1 { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-right: auto; font-size: inherit; font-weight: inherit; letter-spacing: inherit; }
/* „Mein Studiengang" at the right of the program overview's head (markup: header.summary > h1 +
   a.mine-line > .icon + span > small + text, + .icon): the app's. The server writes an empty
   .mine-room in its place, which keeps the line's room from the first paint in a browser that
   keeps a program (html.mine, set by the head script), so nothing moves at the takeover. The
   count does not give way to it: the count stays on its line, the same beside the empty room as
   beside the line, and the line ends in „…" instead, down to its padding (with the gap 30px). The
   count wraps only where it alone is wider than the head. */
.summary:has(> .mine-line) > h1 { flex-shrink: 0; max-width: calc(100% - 30px); }
.mine-line { flex: 0 1 auto; min-width: 0; min-height: 34px; padding: 0 8px 0 10px; border-radius: var(--r-ctl); display: inline-flex; align-items: center; gap: 7px; overflow: hidden; white-space: nowrap; color: var(--text); font-size: 13px; font-weight: 580; text-decoration: none; }
a.mine-line:hover { background: var(--panel-3); text-decoration: none; }
.mine-line > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.mine-line small { color: var(--text-3); font-size: inherit; font-weight: 480; }
.mine-line .icon { flex: none; width: 15px; height: 15px; color: var(--text-3); }
.mine-line > .icon:first-child { fill: currentColor; color: var(--text-2); }
.mine-room { display: none; }
html.mine .mine-room { display: inline-flex; visibility: hidden; }
.sheet-only { display: none; }
/* The program overview is a matrix per faculty: a row per subject, a column per cycle of study.
   The columns are the same in every section, so everything lines up down the whole page. */
.faculty { padding: 16px 0 6px; container: faculty / inline-size; }
.faculty-head { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 4px 12px; padding: 0 24px 14px; }
.faculty-code { font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; padding: 3px 8px; border-radius: var(--r-s); background: var(--invert); color: var(--on-invert); white-space: nowrap; }
.faculty-head h2 { min-width: 0; font-size: 16px; font-weight: 650; letter-spacing: -.015em; text-wrap: pretty; }
.faculty > .hint { padding: 0 24px 12px; margin: 0; }
/* One column by default (a phone, a narrow window); the matrix where there is room for it. */
.matrix-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px 20px; padding: 0 24px; }
@container faculty (min-width: 761px) {
  .matrix-row { grid-template-columns: minmax(0, 25fr) minmax(0, 75fr); }
  .faculty.has-bachelor .matrix-row, .faculty.has-master .matrix-row { grid-template-columns: minmax(0, 25fr) minmax(0, 75fr) 176px; }
  .faculty.has-bachelor.has-master .matrix-row { grid-template-columns: minmax(0, 25fr) minmax(0, 38fr) minmax(0, 37fr) 176px; }
}
.matrix-head { padding-top: 8px; padding-bottom: 8px; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); background: var(--panel-2); }
.subject { align-items: start; min-height: 46px; padding-top: 7px; padding-bottom: 7px; border-bottom: 1px solid var(--line); transition: background .1s; }
.subject:last-child { border-bottom: 0; }
/* Every other subject is shaded, as the rows of the catalog's list (`--row-shade`). Nothing here
   renders only what is on screen, so the stylesheet counts, the subjects of each faculty anew. */
.subject:where(:nth-child(even of .subject)) { background: var(--row-shade); }
.subject:hover { background: var(--row-hover); }
/* The name stands next to the first of its programs, however many lines of them follow. */
.subject-name { padding-top: 6px; font-weight: 620; font-size: 14.5px; line-height: 20px; letter-spacing: -.012em; text-wrap: pretty; }
.cell { display: flex; flex-wrap: wrap; align-content: start; gap: 6px 0; min-width: 0; }
/* A program and the other forms of study it is offered in are one control of several segments:
   the segments share their borders, only the ends are rounded. The group has no box of its own,
   so in a narrow column the segments wrap like words and every line stays as wide as its text. */
.pill-group { display: contents; }
.program-pill { --hit-y: 3px; position: relative; min-height: 32px; margin-right: -1px; padding: 0 10px; display: inline-flex; align-items: center; gap: 6px; background: var(--panel); box-shadow: inset 0 0 0 1px var(--line-strong); color: var(--text-2); font-size: 13px; font-weight: 520; text-decoration: none; white-space: nowrap; transition: background .12s, color .12s; }
.pill-group > .program-pill:first-child { border-top-left-radius: var(--r-s); border-bottom-left-radius: var(--r-s); }
.pill-group > .program-pill:last-child { margin-right: 6px; border-top-right-radius: var(--r-s); border-bottom-right-radius: var(--r-s); }
.program-pill::after { content: ""; position: absolute; inset: calc(-1 * var(--hit-y)) 0; }
.program-pill b { color: var(--text); font-weight: 650; }
.program-pill .variant { color: var(--text-3); }
.program-pill .variant::before { content: "· "; }
.program-pill.form { padding: 0 9px; font-weight: 500; }
/* Four of five programs have a validated study plan; the ones without are the quieter ones. */
.program-pill.no-plan { color: var(--text-3); }
.program-pill.no-plan b { color: var(--text-2); font-weight: 600; }
.program-pill:hover { z-index: 1; background: var(--invert); box-shadow: none; color: var(--on-invert); text-decoration: none; }
.program-pill:hover b, .program-pill:hover .variant { color: inherit; }
@container faculty (max-width: 760px) {
  .matrix-head, .cell.empty { display: none; }
  .subject { padding-top: 10px; padding-bottom: 10px; }
  .subject-name { padding-top: 0; }
  .faculty-head h2 { grid-column: 1 / -1; grid-row: 2; }
  .faculty-head .tab-count { grid-column: 3; }
}
/* ---------- landing page ----------
   One column of panels like every other page. Everything stands on the same lines: the text of
   all panels starts 24px in, lists are rows with continuous rules, numbers are right-aligned.
   Colour is quiet and says something: the faculties' palette behind the pictures and on the
   faculties, a soft hue per way into the catalog; the accent stays with the main button. */
/* No frame (owner, 2026-09-28: the sidebar made the page confusing): the page scrolls on its own,
   its panels in one column in the middle, no wider than still reads well. What is left beside it
   the wood behind the page fills (`.wood`). 1600 px since 2026-09-29 (owner: at 1280 a large
   screen had „links und rechts wirklich noch zu viel Platz"): every text of the page keeps its own
   measure, and the one without, the points of a chapter of „Betula im Detail", is then as wide as
   the chapter's lead; a screen of 1920 px still shows a strip of the wood on either side. */
.home { container: home / inline-size; width: 100%; max-width: 1600px; margin: 0 auto; }
@media (min-width: 901px) { .home-page { overflow-x: hidden; } }
.home > .panel, .home-lists { scroll-margin-top: 14px; }
.block-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 2px 12px; padding: 18px 12px 14px 24px; }
.block-head h2 { font-size: 16px; font-weight: 650; letter-spacing: -.015em; }
.block-head p { grid-column: 1 / -1; color: var(--text-2); max-width: 86ch; text-wrap: pretty; }
.block-head .ghost { margin: -8px 0; }

/* The first panel, flat: the text and the two ways in on the left, the figures on the right
   (under the text on a narrow page). */
.home-hero { display: grid; grid-template-columns: minmax(0, 1fr); }
.home-hero-text { min-width: 0; padding: 24px 24px 22px; }
.eyebrow-pill { display: inline-flex; align-items: center; gap: 8px; height: 28px; padding: 0 12px 0 10px; border-radius: 999px; background: var(--accent-soft); color: var(--accent-ink); font-size: 12.5px; font-weight: 600; box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 18%, transparent); }
.eyebrow-pill i { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
.home-hero h1 { margin-top: 14px; font-size: clamp(26px, 2.9cqi, 34px); line-height: 1.12; font-weight: 720; letter-spacing: -.034em; text-wrap: balance; }
.home-hero h1 .nowrap { white-space: nowrap; }
.home-hero .lead { margin-top: 12px; color: var(--text-2); font-size: 15.5px; line-height: 1.55; max-width: 64ch; text-wrap: pretty; }
.intro-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 20px; }
.intro-actions .btn { min-height: 46px; padding: 0 18px; font-size: 14.5px; }
.intro-actions .btn .icon { width: 17px; height: 17px; }
/* „Studiengang wählen" as a picker (`start::ProgramPick`): its button in the look of the button it
   stands for. The server's link says the words the browser app will show: „Studiengang wählen"
   where it will be a picker (JavaScript, and no program kept: `html.mine` from the head script). */
.program-pick .if-choose { display: none; }
html.js:not(.mine) .program-pick .if-choose { display: inline; }
html.js:not(.mine) .program-pick .if-all { display: none; }
.intro-actions .pick-combo .combo-trigger { width: auto; min-height: 46px; gap: 8px; padding: 0 14px 0 18px; background: var(--panel); box-shadow: inset 0 0 0 1px var(--line-strong); color: var(--text); font-size: 14.5px; font-weight: 620; }
.intro-actions .pick-combo .combo-trigger:hover { background: var(--panel-2); }
.intro-actions .pick-combo .combo-trigger > .icon:first-child { width: 17px; height: 17px; color: var(--text); }
.intro-actions .pick-combo .combo-value.placeholder { color: var(--text); }
/* Its words take a second line where there is no room for them (a narrow phone, in English), as
   those of the button it stands for do, instead of pushing it out of the panel. */
.pick-combo .combo-value { white-space: normal; }
/* The birch of the first panel (owner, 2026-09-28: „rechts am Rand so ein dickerer Birkenstamm und
   dann nach links die Stats"): a trunk of bark down the panel's right edge, the whole height, and
   each figure a tag on a branch of it, the branches of two lengths. Flat, as the mark is (the first
   trunk, shaded at its edges, looked round: „warum ist das Ding jetzt plötzlich 3D?"), and only
   from a notebook's width on: on a phone it took the height the way in needs, and a trunk down a
   phone's edge looked wrong (owner, the same day), so there the first panel is its text alone. */
.hero-trunk { display: none; position: relative; overflow: hidden; border-radius: 0 var(--r-panel) var(--r-panel) 0; background: var(--hero-bark); box-shadow: inset 1px 0 0 var(--line-strong); }
/* The marks of the bark, as the mark of Betula draws them (dashes from either edge or in the
   middle, and two dark chevrons where branches were): a drawing 48 × 400 as the trunk's width,
   repeated down it. */
.hero-trunk::after { content: ""; position: absolute; inset: 0; background: var(--hero-bark-ink); opacity: .82; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 400' width='48' height='400'%3E%3Cpath d='M0 14h14v3h-14zM34 33h14v3h-14zM16 47h12v2h-12zM0 66h20v4h-20zM30 84h18v3h-18zM0 99h8v3h-8zM20 117h16v3h-16zM38 136h10v3h-10zM0 150h17v3h-17zM27 171h21v4h-21zM12 188h10v2h-10zM0 206h11v3h-11zM33 224h15v3h-15zM0 243h22v4h-22zM22 260h14v3h-14zM36 279h12v3h-12zM0 296h15v3h-15zM26 314h22v4h-22zM0 333h9v3h-9zM17 350h15v3h-15zM31 369h17v3h-17zM0 386h19v3h-19zM8 160Q24 150 40 160Q24 155 8 160zM12 303Q27 294 42 303Q27 298 12 303z'/%3E%3C/svg%3E") center top / 100% auto repeat-y; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 400' width='48' height='400'%3E%3Cpath d='M0 14h14v3h-14zM34 33h14v3h-14zM16 47h12v2h-12zM0 66h20v4h-20zM30 84h18v3h-18zM0 99h8v3h-8zM20 117h16v3h-16zM38 136h10v3h-10zM0 150h17v3h-17zM27 171h21v4h-21zM12 188h10v2h-10zM0 206h11v3h-11zM33 224h15v3h-15zM0 243h22v4h-22zM22 260h14v3h-14zM36 279h12v3h-12zM0 296h15v3h-15zM26 314h22v4h-22zM0 333h9v3h-9zM17 350h15v3h-15zM31 369h17v3h-17zM0 386h19v3h-19zM8 160Q24 150 40 160Q24 155 8 160zM12 303Q27 294 42 303Q27 298 12 303z'/%3E%3C/svg%3E") center top / 100% auto repeat-y; }
.tree-figures { display: none; position: relative; z-index: 1; justify-items: end; align-content: center; gap: 8px; margin: 0; padding: 20px 0; }
.tree-figures > div { --reach: 22px; position: relative; max-width: calc(100% - var(--reach)); margin-right: var(--reach); display: flex; flex-wrap: wrap; align-items: baseline; justify-content: flex-end; gap: 0 8px; padding: 6px 12px 7px; border-radius: 9px; background: var(--panel); box-shadow: inset 0 0 0 1px var(--line-strong); }
.tree-figures > div:nth-child(even) { --reach: 46px; }
/* The branch from the trunk to the tag: thick where it leaves the trunk (and into its edge), thin
   at the tag. */
.tree-figures > div::after { content: ""; position: absolute; top: 50%; left: 100%; width: calc(var(--reach) + 6px); height: 7px; margin-top: -3.5px; background: var(--hero-limb); clip-path: polygon(0 42%, 100% 0, 100% 100%, 0 58%); }
.tree-figures dd { order: -1; margin: 0; color: var(--text); font-size: 22px; font-weight: 700; line-height: 1.15; letter-spacing: -.03em; }
.tree-figures dt { color: var(--text-2); font-size: 13px; font-weight: 580; }
.tree-figures dt small { margin-left: 5px; color: var(--text-3); font-size: 12px; font-weight: 500; }

/* The way in for a first visit (home/start.rs): three steps, one under the other on a narrow page
   and side by side on a wide one, joined through their marks by a line, as a path. Each has one
   button (owner, 2026-09-28: one click a step) and names where the navigation keeps it, as chips
   with the rail's icon and name. The next step's button wears the accent; a step done wears a
   tick, the line from it to the next one turns green, and what was done stands in place of the
   chips. Only words and colours follow the visitor, never a height. */
.start-steps { display: grid; grid-template-columns: minmax(0, 1fr); margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--line); --track: var(--line-strong); --track-done: color-mix(in srgb, var(--accent) 60%, transparent); }
.start-step { --mark-x: 40px; --mark-y: 34px; position: relative; min-width: 0; display: grid; grid-template-columns: 32px minmax(0, 1fr); align-content: start; gap: 4px 14px; padding: 18px 24px 20px; }
.start-step > * { grid-column: 2; min-width: 0; }
/* The line through the marks: to the one before (::before) and on to the next (::after). */
.start-step::before, .start-step::after { content: ""; position: absolute; left: calc(var(--mark-x) - 1px); width: 2px; background: var(--track); }
.start-step::before { top: 0; height: var(--mark-y); }
.start-step::after { top: var(--mark-y); bottom: 0; }
.start-step:first-child::before, .start-step:last-child::after { content: none; }
.start-step.is-done::after, .start-step.is-done + .start-step::before { background: var(--track-done); }
.start-step .start-mark { grid-column: 1; grid-row: 1; position: relative; z-index: 1; width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center; background: var(--panel); color: var(--text-2); box-shadow: inset 0 0 0 1.5px var(--line-strong), 0 0 0 5px var(--panel); }
.start-mark b { font-size: 14px; font-weight: 680; }
.start-mark .icon { width: 17px; height: 17px; stroke-width: 2.6; }
.start-step.is-next .start-mark { background: var(--accent); color: var(--on-accent); box-shadow: 0 0 0 5px var(--panel); }
.start-step.is-done .start-mark { background: var(--accent-soft); color: var(--accent-ink); box-shadow: inset 0 0 0 1.5px var(--accent-line), 0 0 0 5px var(--panel); }
/* The heading on the middle of its mark, the rest under the heading. */
.start-step h3 { grid-row: 1; min-height: 32px; display: flex; align-items: center; margin: 0; font-size: 15px; font-weight: 650; letter-spacing: -.015em; }
.start-step p { color: var(--text-2); font-size: 13.5px; line-height: 1.5; max-width: 62ch; text-wrap: pretty; }
/* The step's one button (a link, or the picker of „Studiengang wählen"); the next step's in the accent. */
.start-act { display: flex; margin-top: 10px; }
.start-btn, .start-act .pick-combo .combo-trigger { width: auto; min-height: 40px; display: inline-flex; align-items: center; gap: 8px; padding: 0 14px 0 12px; border-radius: var(--r-ctl); background: var(--panel); box-shadow: inset 0 0 0 1px var(--line-strong); color: var(--text); font-size: 13.5px; font-weight: 620; text-decoration: none; transition: background .12s, filter .12s; }
.start-btn:hover, .start-act .pick-combo .combo-trigger:hover { background: var(--panel-2); text-decoration: none; }
.start-btn .icon, .start-act .pick-combo .combo-trigger > .icon:first-child { width: 16px; height: 16px; color: var(--text-2); }
.start-act .pick-combo .combo-value.placeholder { color: inherit; }
.start-step.is-next .start-btn, .start-step.is-next .pick-combo .combo-trigger { background: var(--accent); box-shadow: none; color: var(--on-accent); }
.start-step.is-next .start-btn:hover, .start-step.is-next .pick-combo .combo-trigger:hover { background: var(--accent); filter: brightness(1.08); }
.start-step.is-next .start-btn .icon, .start-step.is-next .pick-combo .combo-trigger .icon { color: inherit; }
/* Where the navigation keeps the step, and its items as the rail shows them; once the step is
   done, what was done in their place, on the same line. (A `span` of the items can be `.js-only`,
   whose `display: contents` would leave no box: hence the longer selector.) */
.start-step .start-where { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; min-height: 24px; margin-top: 10px; color: var(--text-3); font-size: 12.5px; }
.start-where .start-place { display: inline-flex; align-items: center; gap: 5px; height: 24px; padding: 0 8px 0 6px; border-radius: var(--r-s); background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line); color: var(--text-2); font-weight: 580; }
.start-place .icon { width: 14px; height: 14px; }
.start-state a, .start-state > span { display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 100%; color: var(--text); font-weight: 620; text-decoration: none; }
.start-state a:hover { text-decoration: underline; }
.start-state a > span, .start-state > span > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.start-state .icon { flex: none; width: 15px; height: 15px; color: var(--accent-ink); }
.start-state a > .icon:last-child { color: var(--text-3); }
/* For those who want to read first: the ways to the sections that explain Betula. */
.start-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 4px; padding: 10px 16px 12px 24px; border-top: 1px solid var(--line); color: var(--text-3); font-size: 13px; }
.start-foot > span { margin-right: 6px; }
.start-foot a { display: inline-flex; align-items: center; gap: 2px; min-height: 34px; padding: 0 6px 0 10px; border-radius: var(--r-ctl); color: var(--text-2); font-weight: 580; text-decoration: none; }
.start-foot a:hover { background: var(--panel-3); color: var(--text); text-decoration: none; }
.start-foot .icon { width: 15px; height: 15px; color: var(--text-3); }
/* Side by side: the mark over the heading, the line through the marks across the row, and the
   button and the chips of each on the same lines as the others'. */
@container home (min-width: 900px) {
  .start-steps { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .start-step { --mark-y: 36px; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto auto 1fr auto auto; gap: 6px; padding: 20px 24px 22px; }
  .start-step > * { grid-column: 1; }
  .start-step .start-mark { grid-row: 1; margin-bottom: 8px; }
  .start-step h3 { grid-row: 2; min-height: 0; }
  .start-step::before, .start-step::after { top: calc(var(--mark-y) - 1px); bottom: auto; width: auto; height: 2px; }
  .start-step::before { left: 0; width: var(--mark-x); }
  .start-step::after { left: var(--mark-x); right: 0; }
}

/* The wood behind every page (`ground::Wood`; owner, 2026-09-29: „so ein Scherenschnitt, der auch
   grau nur etwas dunkler wie der Hintergrund ist und nur etwas Struktur hinzufügt", then „bring den
   Wald auf alle Seiten"; design/forest): birches cut out of the background's grey in two layers,
   the trees further back fainter, with the leaves of the season and none in winter. It stands
   still behind the page, beneath everything (the crown and the ground included), from the rail to
   the window's right edge; the rail stays free. Beside the start page's column it fills the room a
   wide window leaves; on every page it shows wherever the panels leave a gap (between two of them,
   above the ground under a column shorter than the one beside it). Each mask
   (`<season>-wood-back.svg`, `-front.svg`, 3400 × 1600) has the start page's column (1600 px) in
   its middle, with wood under it too, and that middle lies under the column's: the page is the
   window less the rail and the gap on its right, so its middle is 4 px left of the wood's. The
   masks stand on the window's bottom, and when the ground comes up at the end of a page they go
   up with its edge (enhance.js), so that the birches stand on it; a window taller than they are
   sees the trunks fade out at their top. Their trees walk outwards from the column's edges, so the
   ones next to it are the same on every screen and a wider one only adds trees further out. Not
   on a phone, where the page scrolls with the window and the ground follows it. A layer of its
   own, so that going up with the ground moves it and does not draw its masks again. Nor does
   anything else: each mask is one image, the fade drawn into it (design/forest), and it stands in
   the rules as it is. A gradient over it, or an image out of a custom property, is made anew with
   every restyle, and the browser draws it again — dragging a panel's edge restyles the whole page
   in every frame, and redrawing the wood with each one was most of what the drag cost. */
.wood { display: none; }
@media (min-width: 901px) {
  .wood { display: block; position: fixed; z-index: -1; left: var(--rail); right: 0; bottom: 0; height: 1600px; pointer-events: none; will-change: transform; }
  /* Under the wood, as it goes up with the ground, its front colour goes on down to the window's
     edge: a shadow only draws outside the box. */
  :root:has(#content > .work.flowing) .wood { box-shadow: 0 208px 0 var(--wood-front); }
  .wood::before, .wood::after { content: ""; position: absolute; inset: 0; background: var(--wood-back); -webkit-mask: url("/assets/birch/summer-wood-back.svg") calc(50% - 4px) 100% / 3400px 1600px no-repeat; mask: url("/assets/birch/summer-wood-back.svg") calc(50% - 4px) 100% / 3400px 1600px no-repeat; }
  .wood::after { background: var(--wood-front); -webkit-mask-image: url("/assets/birch/summer-wood-front.svg"); mask-image: url("/assets/birch/summer-wood-front.svg"); }
  :root[data-season="spring"] .wood::before { -webkit-mask-image: url("/assets/birch/spring-wood-back.svg"); mask-image: url("/assets/birch/spring-wood-back.svg"); }
  :root[data-season="spring"] .wood::after { -webkit-mask-image: url("/assets/birch/spring-wood-front.svg"); mask-image: url("/assets/birch/spring-wood-front.svg"); }
  :root[data-season="autumn"] .wood::before { -webkit-mask-image: url("/assets/birch/autumn-wood-back.svg"); mask-image: url("/assets/birch/autumn-wood-back.svg"); }
  :root[data-season="autumn"] .wood::after { -webkit-mask-image: url("/assets/birch/autumn-wood-front.svg"); mask-image: url("/assets/birch/autumn-wood-front.svg"); }
  :root[data-season="winter"] .wood::before { -webkit-mask-image: url("/assets/birch/winter-wood-back.svg"); mask-image: url("/assets/birch/winter-wood-back.svg"); }
  :root[data-season="winter"] .wood::after { -webkit-mask-image: url("/assets/birch/winter-wood-front.svg"); mask-image: url("/assets/birch/winter-wood-front.svg"); }
}

/* The pictures, as a carousel for pictures: the current one in front and in the middle, its
   neighbours behind it, half of each showing at its sides (their middle on its edge), going round.
   The slides lie on top of each other and are moved by their place (--at, -2 … 2); the layers
   swap halfway through a move. The frame takes its height from a slide nobody sees. */
.showcase { padding: 16px 0 14px; }
/* The frame reaches past its place above and below (padding, taken back by negative margins) so
   that the shadows, the lift under the pointer and the spring of a move are not cut off; it lets
   the pointer through to what lies under that edge (the tabs), the pictures take it. The current
   picture is 52 % of the frame wide, so that its neighbours reach out to the panel's edges; at
   most 832 px, 52 % of the start page's widest column (1600 px). */
.carousel { --room-t: 14px; --room-b: 44px; position: relative; container-type: inline-size; overflow: hidden; margin: calc(-1 * var(--room-t)) 0 calc(-1 * var(--room-b)); padding: var(--room-t) 0 var(--room-b); pointer-events: none; touch-action: pan-y; outline-offset: -2px; --w: min(52cqw, 832px); }
.cslide, .show-arrow { pointer-events: auto; }
.cslide { position: absolute; top: var(--room-t); left: 50%; width: var(--w); display: flex; flex-direction: column; border-radius: 10px; overflow: hidden; background: var(--panel); box-shadow: 0 0 0 1px var(--line), 0 14px 34px -24px rgba(15, 23, 42, .55); color: inherit; text-decoration: none; z-index: var(--z, 3); transform: translateX(calc(-50% + var(--at, 0) * var(--w) / 2)) scale(var(--scale, 1)); transition: transform .5s var(--spring), translate .25s var(--spring), opacity .3s, box-shadow .2s, z-index .5s linear; user-select: none; -webkit-user-drag: none; }
/* What lies behind is veiled in the colour of the panel. */
.cslide::after { content: ""; position: absolute; inset: 0; z-index: 4; border-radius: inherit; background: var(--panel); opacity: 0; pointer-events: none; transition: opacity .3s; }
.cslide:hover { text-decoration: none; }
.cslide.spacer { position: relative; top: 0; left: auto; margin: 0 auto; transform: none; visibility: hidden; }
.cslide.is-side { --scale: .84; --z: 2; cursor: pointer; }
.cslide.is-side::after { opacity: .42; }
.cslide.is-side:hover::after { opacity: .22; }
.cslide.is-far { --scale: .7; --z: 1; opacity: 0; pointer-events: none; }
.cslide.is-current { box-shadow: 0 0 0 1px var(--line), 0 26px 50px -26px rgba(15, 23, 42, .65); }
.cslide.teleport { transition: none; }
.cslide.is-current:hover { translate: 0 -3px; box-shadow: 0 0 0 1px var(--line-strong), 0 30px 54px -24px rgba(15, 23, 42, .7); }
.stage { position: relative; aspect-ratio: 16 / 10; overflow: hidden; }
/* Each picture stands on a faint wash of two hues of the faculties' palette. */
.wash-map .stage, .stage.wash-map { background: radial-gradient(90% 70% at 0% 0%, color-mix(in oklch, var(--fac-5) 9%, transparent), transparent 70%), radial-gradient(80% 80% at 100% 100%, color-mix(in oklch, var(--fac-2) 9%, transparent), transparent 70%), var(--panel); }
.wash-catalog .stage { background: radial-gradient(100% 90% at 0% 0%, color-mix(in oklch, var(--fac-1) 22%, transparent), transparent 70%), radial-gradient(90% 90% at 100% 100%, color-mix(in oklch, var(--fac-4) 16%, transparent), transparent 70%), var(--panel-2); }
.wash-program .stage { background: radial-gradient(100% 90% at 0% 0%, color-mix(in oklch, var(--fac-3) 22%, transparent), transparent 70%), radial-gradient(90% 90% at 100% 100%, color-mix(in oklch, var(--fac-2) 18%, transparent), transparent 70%), var(--panel-2); }
.wash-module .stage { background: radial-gradient(100% 90% at 0% 0%, color-mix(in oklch, var(--fac-2) 20%, transparent), transparent 70%), radial-gradient(90% 90% at 100% 100%, color-mix(in oklch, var(--fac-5) 20%, transparent), transparent 70%), var(--panel-2); }
/* A screenshot lies on the wash like a window, cut off at the right and at the bottom. The one of
   the theme that is not shown stays hidden, and a hidden lazy picture is never loaded. */
.stage-shot img { position: absolute; left: 7%; top: 8%; width: 104%; height: auto; border-radius: 8px 0 0 0; box-shadow: 0 0 0 1px var(--line-strong), 0 18px 40px -18px rgba(15, 23, 42, .45); background: var(--bg); }
.shot-dark { display: none; }
:root[data-theme="dark"] .shot-light { display: none; }
:root[data-theme="dark"] .shot-dark { display: inline; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .shot-light { display: none; }
  :root:not([data-theme="light"]) .shot-dark { display: inline; }
}
/* On the current picture, under the pointer: what a click does. */
.stage-go { position: absolute; right: 12px; top: 12px; z-index: 2; width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; background: color-mix(in srgb, var(--panel) 92%, transparent); box-shadow: 0 0 0 1px var(--line), 0 4px 12px -6px rgba(15, 23, 42, .4); color: var(--text); opacity: 0; transform: scale(.6); transition: opacity .15s, transform .3s var(--spring); }
.stage-go .icon { width: 16px; height: 16px; }
.cslide.is-current:hover .stage-go, .cslide.is-current:focus-visible .stage-go { opacity: 1; transform: none; }
/* Under each picture one line: what it shows, and what a click on it does. */
.cap { flex: none; display: flex; align-items: center; gap: 8px; height: 50px; padding: 0 12px 0 16px; border-top: 1px solid var(--line); background: var(--panel); font-size: 13px; }
.cap-text { flex: 1 1 auto; min-width: 0; display: flex; align-items: baseline; gap: 8px; white-space: nowrap; overflow: hidden; }
.cap-text b { flex: none; font-weight: 650; }
.cap-text span { min-width: 0; color: var(--text-2); overflow: hidden; text-overflow: ellipsis; }
.cap-text .quiet { color: var(--text-3); }
.cap-go { flex: none; display: inline-flex; align-items: center; gap: 2px; color: var(--text-3); font-weight: 600; white-space: nowrap; transition: color .15s; }
.cap-go .icon { width: 16px; height: 16px; transition: transform .2s var(--spring); }
.cslide.is-current:hover .cap-go .icon { transform: translateX(3px); }
/* What comes to the front says what it is, line by line. */
.cslide.is-current .cap-text > *, .cslide.is-current .cap-go { animation: cap-in .45s var(--spring) both; }
.cslide.is-current .cap-text > span { animation-delay: .06s; }
.cslide.is-current .cap-go { animation-delay: .12s; }
@keyframes cap-in { from { opacity: 0; transform: translateY(8px); } }
.cslide.is-current:hover .cap-go { color: var(--text); }
.cap-link { flex: none; display: inline-flex; align-items: center; gap: 2px; min-height: 34px; padding: 0 8px 0 12px; border-radius: var(--r-ctl); color: var(--text); font-weight: 600; white-space: nowrap; background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line); text-decoration: none; }
.cap-link:hover { background: var(--invert); color: var(--on-invert); box-shadow: none; text-decoration: none; }
.cap-link .icon { width: 16px; height: 16px; }
.cap-link.picked { background: var(--invert); color: var(--on-invert); box-shadow: none; }
.cap-link.picked:hover { background: var(--pick, var(--accent)); }
.cap-close { flex: none; width: 34px; height: 34px; border: 0; border-radius: var(--r-ctl); display: grid; place-items: center; background: transparent; color: var(--text-2); cursor: pointer; }
.cap-close:hover { background: var(--panel-3); color: var(--text); }
.cap-close .icon { width: 16px; height: 16px; }
/* The arrows stand on the edges of the current picture, at the height of its middle. */
.show-arrow { position: absolute; top: calc(var(--room-t) + var(--w) * 5 / 16); z-index: 5; width: 42px; height: 42px; margin-top: -21px; border: 0; border-radius: 50%; display: grid; place-items: center; background: var(--panel); box-shadow: 0 0 0 1px var(--line), 0 6px 16px -8px rgba(15, 23, 42, .45); color: var(--text); cursor: pointer; transition: background .12s, transform .25s var(--spring); }
.show-arrow:hover { background: var(--panel-2); transform: scale(1.08); }
.show-arrow:active { transform: scale(.88); transition-duration: .08s; }
.show-arrow .icon { width: 20px; height: 20px; transition: transform .2s var(--spring); }
.show-arrow.previous:hover .icon { transform: translateX(-2px); }
.show-arrow.next:hover .icon { transform: translateX(2px); }
.show-arrow.previous { left: calc(50% - var(--w) / 2 - 21px); }
.show-arrow.next { right: calc(50% - var(--w) / 2 - 21px); }
/* The tabs name the pictures; the mark of the current one slides to it (--i of --n). In it a bar
   fills until the next picture comes. The pause button beside them stops that (then the bar stands
   still), and so does the open map. Where less motion is wanted the bar runs too, and the pictures
   change without moving. */
.show-bar { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: 10px; margin-top: 12px; padding: 0 16px; }
.show-tabs { grid-column: 2; position: relative; width: min(460px, calc(100vw - 140px)); margin: 0; }
.show-mark { position: absolute; top: 3px; bottom: 3px; left: 3px; width: calc((100% - 6px) / var(--n, 4)); border-radius: 7px; background: var(--panel); box-shadow: 0 1px 2px rgba(15, 23, 42, .14), 0 0 0 1px var(--line); transform: translateX(calc(var(--i, 0) * 100%)); transition: transform .4s var(--spring); }
.show-tabs button { position: relative; z-index: 1; overflow: hidden; border: 0; background: transparent; font-family: var(--font); cursor: pointer; transition: color .15s; }
.show-tabs button[aria-checked="true"] { background: transparent; box-shadow: none; }
.show-progress { position: absolute; left: 10px; right: 10px; bottom: 3px; height: 2px; border-radius: 2px; background: var(--accent); transform-origin: left; transform: scaleX(0); animation: show-progress 7s linear forwards; }
@keyframes show-progress { to { transform: scaleX(1); } }
.showcase.paused .show-progress { animation-play-state: paused !important; }
.show-play { justify-self: start; width: 36px; height: 36px; border-radius: 50%; display: grid; place-items: center; background: var(--panel-3); color: var(--text-2); transition: background .12s, color .12s, transform .25s var(--spring); }
.show-play:hover { background: var(--invert); color: var(--on-invert); }
.show-play:active { transform: scale(.86); transition-duration: .08s; }
.show-play .icon { width: 14px; height: 14px; fill: currentColor; }
@media (prefers-reduced-motion: reduce) { .show-progress { animation: show-progress 7s linear forwards !important; } }

/* The map, large: a dialog over the page (Escape, the ✕ or a click beside it closes it). Its grid
   places the head, the map and the caption one under the other, the legend on the map's upper
   left corner; on a wide screen the legend and the caption stand beside the map, under the head. */
.map-dialog { width: min(1120px, calc(100vw - 48px)); max-width: none; max-height: calc(100vh - 48px); padding: 0; border: 0; border-radius: 14px; background: var(--panel); color: var(--text); box-shadow: var(--shadow-pop); overflow: hidden; overscroll-behavior: contain; }
.map-dialog[open] { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr) auto; grid-template-areas: "head" "map" "cap"; animation: dialog-in .34s var(--spring); }
.map-dialog[open]::backdrop { animation: fade-in .2s ease-out; }
.map-dialog::backdrop { background: rgba(10, 12, 17, .5); }
@keyframes dialog-in { from { opacity: 0; transform: translateY(14px) scale(.97); } }
@keyframes fade-in { from { opacity: 0; } }
.map-dialog-head { grid-area: head; display: flex; align-items: flex-start; gap: 16px; padding: 18px 12px 14px 24px; border-bottom: 1px solid var(--line); }
.map-dialog-head > div { flex: 1 1 auto; min-width: 0; }
.map-dialog-head h2 { font-size: 17px; font-weight: 680; letter-spacing: -.015em; }
.map-dialog-head p { margin-top: 4px; color: var(--text-2); font-size: 13.5px; line-height: 1.5; max-width: 80ch; text-wrap: pretty; }
.map-dialog-close { flex: none; }
/* 4:3 as wide as the dialog, unless the height of the screen (less head and caption) is shorter. */
.map-dialog .map-live { grid-area: map; aspect-ratio: auto; height: min(calc(min(1120px, 100vw - 48px) * 3 / 4), calc(100vh - 48px - 170px)); }
.map-dialog .map-cap { grid-area: cap; height: 54px; }
.map-dialog .live-key { grid-area: map; position: relative; left: auto; top: auto; align-self: start; justify-self: start; margin: 10px 12px; }
.live-key .label, .live-key .key-links, .cap-relatives { display: none; }
.link-key { width: 18px; height: 2px; border-radius: 1px; background: var(--text); opacity: .5; }
/* While the map is open the page behind it stands still (a wheel over the dim backdrop would
   scroll it); its scrollbar keeps its room, so nothing behind moves sideways either. */
:root:has(.map-dialog[open]) { overflow: hidden; }
:root:has(.map-dialog[open]) .page { overflow: hidden; scrollbar-gutter: stable; }

/* The map of the programs. The server lays it out (catalog::graph); here it is only drawn, below
   its legend, which lies on the paper in the upper left corner. */
.map-key { position: absolute; left: 12px; top: 10px; z-index: 1; display: flex; gap: 12px; margin: 0; padding: 5px 10px; border-radius: 999px; background: color-mix(in srgb, var(--panel) 86%, transparent); box-shadow: 0 0 0 1px var(--line); color: var(--text-2); font-size: 12px; font-weight: 550; pointer-events: none; }
.map-key > span { display: inline-flex; align-items: center; gap: 6px; }
.map-key .dot-key { width: 9px; height: 9px; }
.map-holder { position: absolute; inset: 40px 6px 6px; }
.fac-1 { --fac: var(--fac-1); } .fac-2 { --fac: var(--fac-2); } .fac-3 { --fac: var(--fac-3); }
.fac-4 { --fac: var(--fac-4); } .fac-5 { --fac: var(--fac-5); } .fac-6 { --fac: var(--fac-6); }
.map-region { fill: color-mix(in oklch, var(--fac, var(--text-3)) 7%, transparent); stroke: color-mix(in oklch, var(--fac, var(--text-3)) 55%, transparent); stroke-width: 1.25; stroke-linejoin: round; vector-effect: non-scaling-stroke; opacity: 0; transition: opacity .2s; pointer-events: none; }
.map-faculty { font: 650 calc(var(--map-font, 12px) * .8) var(--font); text-transform: uppercase; letter-spacing: .07em; fill: var(--fac, var(--text-2)); paint-order: stroke; stroke: var(--panel); stroke-width: 4px; stroke-linejoin: round; pointer-events: none; opacity: 0; transition: opacity .2s; }
.map-region.shown, .map-faculty.shown { opacity: 1; }
.map-dot.picked circle { stroke: var(--pick, var(--accent)); stroke-width: 3; }
.dot-key { width: 10px; height: 10px; border-radius: 50%; box-shadow: inset 0 0 0 2px var(--text); }
.dot-key.bachelor { background: var(--text); }
.dot-key.other { background: var(--text-3); box-shadow: none; }
.map { position: absolute; inset: 0; display: block; width: 100%; height: 100%; overflow: visible; }
.map-tall { display: none; }
.map-preview { pointer-events: none; }
.map-links { fill: none; stroke: var(--text); stroke-opacity: .2; stroke-width: 1; stroke-linecap: round; vector-effect: non-scaling-stroke; transition: stroke-opacity .15s; }
.map-links.medium { stroke-opacity: .34; stroke-width: 1.5; }
.map-links.strong { stroke-opacity: .55; stroke-width: 2.2; }
.map-dot circle { stroke: var(--text); stroke-width: 1.6; vector-effect: non-scaling-stroke; transition: fill .12s, stroke .12s, opacity .15s; }
.map-dot.bachelor circle { fill: var(--text); }
.map-dot.master circle { fill: var(--panel); }
.map-dot.other circle { fill: var(--text-3); stroke: var(--text-3); }
.map-halo circle { fill: transparent; }
.map-dot, .map-halo { cursor: pointer; outline: 0; }
.map-dot:hover circle, .map-dot:focus-visible circle { fill: var(--pick, var(--accent)); stroke: var(--pick, var(--accent)); }
.map-name { font: 560 var(--map-font, 12px) var(--font); letter-spacing: -.01em; fill: var(--text-2); paint-order: stroke; stroke: var(--panel); stroke-width: 4px; stroke-linejoin: round; pointer-events: none; transition: opacity .15s; }
/* With a program under the pointer (the app): the rest steps back, its links and relatives stand out. */
.map.has-hot .map-links { stroke-opacity: .08; }
.map.has-hot .map-dot circle { opacity: .35; }
.map.has-hot .map-name { opacity: .3; }
/* A picked program: its faculty stays, the programs of the others step back a little. */
.map.has-pick .map-dot circle { opacity: 1; }
.map.has-pick .map-dot.outside circle { opacity: .4; }
.map-hot { pointer-events: none; }
.map-hot path { fill: none; stroke: var(--pick, var(--accent)); stroke-width: 2; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.map-hot circle { fill: var(--panel); stroke: var(--pick, var(--accent)); stroke-width: 2; vector-effect: non-scaling-stroke; }
.map-hot circle.bachelor { fill: var(--text); }
.map-hot circle.other { fill: var(--text-3); }
.map-hot circle.self { fill: var(--pick, var(--accent)); }

/* Two lists side by side, their rows on the same lines (every row is 56px). */
.home-lists { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap); }
.rowlist { border-top: 1px solid var(--line); padding-bottom: 6px; }
.rowlist li + li { border-top: 1px solid var(--line); }
.rowlink { display: grid; grid-template-columns: 32px minmax(0, 1fr) auto 16px; align-items: center; gap: 12px; height: 56px; padding: 0 16px 0 24px; color: inherit; text-decoration: none; transition: background .1s; }
/* Every other row is shaded, as the rows of the catalog's list; the two lists side by side keep
   their stripes on the same lines as their rows. */
.rowlink:where(.rowlist li:nth-child(even) > *) { background: var(--row-shade); }
.rowlink:hover { background: var(--row-hover); text-decoration: none; }
.rowlink .ico, .ability .ico { width: 32px; height: 32px; border-radius: 8px; display: grid; place-items: center; background: var(--panel-2); color: var(--text-2); box-shadow: inset 0 0 0 1px var(--line); }
.rowlink .ico .icon, .ability .ico .icon { width: 16px; height: 16px; }
.rowlink .ico.code { font-size: 11.5px; font-weight: 700; letter-spacing: .02em; color: var(--text); }
.rowlink:hover .ico { background: var(--panel); }
/* The ways into the catalog in their soft hues, the faculties in their colours from the map. */
.t-ice { --tint: var(--t-ice); } .t-sun { --tint: var(--t-sun); } .t-violet { --tint: var(--t-violet); } .t-teal { --tint: var(--t-teal); }
.t-green { --tint: var(--t-green); } .t-coral { --tint: var(--t-coral); } .t-rose { --tint: var(--t-rose); } .t-slate { --tint: var(--t-slate); }
.rowlink .ico[class*="t-"] { background: color-mix(in oklch, var(--tint) 14%, var(--panel)); color: var(--tint); box-shadow: none; }
.rowlink:hover .ico[class*="t-"] { background: color-mix(in oklch, var(--tint) 22%, var(--panel)); }
.rowlink .ico.code[class*="fac-"] { background: color-mix(in oklch, var(--fac) 14%, var(--panel)); color: var(--fac); box-shadow: none; }
.rowlink:hover .ico.code[class*="fac-"] { background: color-mix(in oklch, var(--fac) 22%, var(--panel)); }
.rowlink-text { min-width: 0; display: grid; line-height: 1.3; }
.rowlink-text b { font-weight: 600; font-size: 14.5px; letter-spacing: -.012em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rowlink-text small { color: var(--text-3); font-size: 12.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rowlink-count { font-weight: 650; font-size: 15px; }
.rowlink > .icon { width: 16px; height: 16px; color: var(--text-3); }

/* What the app does: a grid of equal cells with rules, not cards. */
.ability-grid { display: grid; grid-template-columns: minmax(0, 1fr); border-top: 1px solid var(--line); }
.ability { display: grid; grid-template-columns: 32px minmax(0, 1fr); align-content: start; gap: 2px 14px; padding: 18px 24px 20px; border-bottom: 1px solid var(--line); }
.ability .ico { background: var(--accent-soft); color: var(--accent-ink); box-shadow: none; }
/* The heading stands on the middle of its icon, the text under the heading. */
.ability h3 { min-height: 32px; display: flex; align-items: center; font-size: 14.5px; font-weight: 640; letter-spacing: -.012em; margin: 0; }
.ability p { grid-column: 2; color: var(--text-2); font-size: 13.5px; line-height: 1.5; text-wrap: pretty; }
.soon-line { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 22px; padding: 14px 24px 16px; color: var(--text-3); font-size: 13px; }
.soon-line > span:not(.label) { display: inline-flex; align-items: center; gap: 7px; }
.soon-line .icon { width: 15px; height: 15px; }

/* Questions and answers, in groups: the questions are the list, an answer opens in place. */
.questions { padding-bottom: 6px; }
.faq { border-top: 1px solid var(--line); }
.faq-group { padding: 16px 24px 8px; }
.faq details { border-top: 1px solid var(--line); }
.faq summary { list-style: none; display: flex; align-items: center; gap: 16px; min-height: 54px; padding: 0 16px 0 24px; cursor: pointer; font-weight: 620; font-size: 14.5px; letter-spacing: -.01em; transition: background .1s; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary:hover { background: var(--panel-2); }
.faq summary .icon { margin-left: auto; width: 18px; height: 18px; color: var(--text-3); transition: transform .2s var(--ease); }
.faq details[open] summary .icon { transform: rotate(45deg); color: var(--text); }
.faq details p { padding: 0 24px 18px; max-width: 80ch; color: var(--text-2); font-size: 14px; line-height: 1.6; text-wrap: pretty; }

/* ---------- „Betula im Detail" (pages/home/detail.rs; owner, 2026-09-28) ---------- */
/* After the questions: a panel that names the chapters, then a panel per chapter. A chapter is its
   words — its name in its tint, a headline, the line under it, four points — and a picture made of
   the app's own parts (chips, fields, the week grid, the bark of the mark, the crown and the
   ground) on a wash of its tint, as the carousel's pictures stand on the faculties' washes. On a
   wide page the picture stands beside the words, every other one on the left; below that under
   them. The pictures lay themselves out by their own width (container `picture`). */
.detail-nav { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 24px 20px; }
.detail-nav a { display: inline-flex; align-items: center; gap: 7px; min-height: 32px; padding: 0 13px 0 10px; border-radius: 999px; background: color-mix(in oklch, var(--tint) 8%, var(--panel)); box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--tint) 24%, transparent); color: var(--text); font-size: 13px; font-weight: 580; text-decoration: none; transition: background .12s; }
.detail-nav a:hover { background: color-mix(in oklch, var(--tint) 15%, var(--panel)); text-decoration: none; }
.detail-nav .icon { width: 15px; height: 15px; color: var(--tint); }

/* `clip`, not the panel's `hidden`: a panel that hides its overflow is a scroll container of its
   own, and the board's count (`.board-foot`) would stick to it instead of to the view. */
.feature { display: grid; grid-template-columns: minmax(0, 1fr); gap: 24px; padding: 30px 24px; align-items: center; overflow: clip; }
.feature-text { min-width: 0; }
.feature-name { display: flex; align-items: center; gap: 10px; color: var(--text-2); font-size: 11.5px; font-weight: 650; letter-spacing: .06em; text-transform: uppercase; }
.feature-name .ico { width: 30px; height: 30px; border-radius: 8px; display: grid; place-items: center; background: color-mix(in oklch, var(--tint) 14%, var(--panel)); color: var(--tint); }
.feature-name .icon { width: 16px; height: 16px; }
.feature h3 { margin-top: 14px; font-size: clamp(21px, 2.4cqi, 28px); line-height: 1.15; font-weight: 720; letter-spacing: -.03em; text-wrap: balance; }
.feature-lead { margin-top: 10px; color: var(--text-2); font-size: 15px; line-height: 1.55; max-width: 62ch; text-wrap: pretty; }
.points { display: grid; gap: 14px; margin: 22px 0 0; padding: 0; list-style: none; }
.points li { display: grid; grid-template-columns: 18px minmax(0, 1fr); gap: 12px; }
.points li > .icon { width: 18px; height: 18px; margin-top: 1px; color: var(--tint); }
.points b { display: block; color: var(--text); font-size: 14px; font-weight: 640; letter-spacing: -.01em; }
.points p { margin-top: 1px; color: var(--text-2); font-size: 13.5px; line-height: 1.5; text-wrap: pretty; }
.feature-link { margin: 18px 0 0 -10px; color: var(--accent-ink); }
.feature-picture { container: picture / inline-size; position: relative; min-width: 0; padding: 22px; border-radius: 12px; background: radial-gradient(110% 90% at 0% 0%, color-mix(in oklch, var(--tint) 18%, transparent), transparent 70%), radial-gradient(90% 90% at 100% 100%, color-mix(in oklch, var(--tint) 11%, transparent), transparent 70%), var(--panel-2); box-shadow: inset 0 0 0 1px var(--line); }
.feature-picture.bare { padding: 0; overflow: hidden; background: var(--panel-2); }

/* Figures in the look of the start page's: bare numbers over thin rules, each with a stroke of
   birch bark on its rule. */
.figures { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 18px; margin: 20px 0 0; --rule: color-mix(in srgb, var(--text) 11%, transparent); }
.figures.one { grid-template-columns: minmax(0, 1fr); }
.figures > div { position: relative; display: flex; flex-direction: column-reverse; justify-content: flex-end; gap: 3px; padding: 11px 0 10px; border-top: 1px solid var(--rule); }
.figures > div::before { content: ""; position: absolute; top: -2px; left: 0; width: 30%; height: 3px; border-radius: 0 0 2px 0; background: var(--text); }
.figures > div:nth-child(2n)::before { left: auto; right: 0; width: 18%; border-radius: 0 0 0 2px; }
.figures > div:nth-child(3)::before { width: 14%; }
.figures dd { margin: 0; color: color-mix(in srgb, var(--text) 86%, var(--panel)); font-size: 34px; font-weight: 640; line-height: 1; letter-spacing: -.035em; }
.figures.one dd { font-size: 46px; }
.figures dt { color: var(--text-2); font-size: 12.5px; font-weight: 560; line-height: 1.35; }
.figures dt small { display: block; color: var(--text-3); font-size: 12px; font-weight: 500; }

/* „Klausur" in its three steps, each a chip of the panel with what it means under it. */
.tri { display: flex; align-items: flex-start; gap: 10px; margin-top: 20px; }
.tri figure { margin: 0; display: grid; gap: 6px; justify-items: center; }
.tri figcaption { color: var(--text-3); font-size: 12px; font-weight: 560; }
.tri > .icon { flex: none; width: 16px; height: 16px; margin-top: 8px; color: var(--text-3); }
/* On a phone the three steps stand one under the other, what each means beside it. */
@container home (max-width: 560px) {
  .tri { flex-direction: column; align-items: stretch; gap: 4px; }
  .tri figure { grid-template-columns: auto minmax(0, 1fr); align-items: center; justify-items: start; gap: 14px; }
  .tri > .icon { margin: 0 0 0 26px; transform: rotate(90deg); }
}

/* The way to a module: four questions, each a lane of the app's own steps, and the module page
   they end on — beside them where the picture is wide enough, a dashed line from each lane to it. */
.flow { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; }
.lanes { display: grid; gap: 10px; margin: 0; padding: 0; list-style: none; }
.lane { position: relative; display: grid; gap: 8px; padding: 12px 14px; border-radius: 10px; background: var(--panel); box-shadow: var(--shadow-panel); }
.lane-ask { display: flex; align-items: center; gap: 9px; color: var(--text); font-size: 13.5px; font-weight: 620; }
.lane-ask .icon { flex: none; width: 16px; height: 16px; color: var(--tint); }
.steps { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; padding-left: 25px; }
.steps > .icon { width: 13px; height: 13px; color: var(--text-3); }
.step { display: inline-flex; align-items: center; min-height: 24px; padding: 0 8px; border-radius: 6px; background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line); color: var(--text-2); font-size: 12px; font-weight: 580; }
.flow { row-gap: 26px; }
.goal { position: relative; align-self: stretch; display: flex; flex-direction: column; justify-content: center; padding: 14px; border-radius: 10px; background: var(--panel); box-shadow: inset 0 0 0 1.5px color-mix(in oklch, var(--tint) 45%, transparent), var(--shadow-panel); }
.goal::before { content: ""; position: absolute; bottom: 100%; left: 50%; height: 26px; border-left: 1.5px dashed color-mix(in oklch, var(--tint) 65%, transparent); }
.goal-head { display: flex; align-items: center; gap: 9px; color: var(--text); font-size: 14px; font-weight: 680; }
.goal-head .ico { flex: none; width: 26px; height: 26px; border-radius: 7px; display: grid; place-items: center; background: color-mix(in oklch, var(--tint) 14%, var(--panel)); color: var(--tint); }
.goal-head .icon { width: 15px; height: 15px; }
.mini-mark { flex: none; width: 18px; height: 18px; border-radius: 5px; overflow: hidden; box-shadow: 0 0 0 1px var(--line-strong); }
.goal-badges { display: flex; gap: 4px; margin-top: 10px; }
.goal-badges i { height: 16px; width: 34px; border-radius: 4px; background: var(--panel-3); }
.goal-badges i:first-child { background: var(--invert); width: 26px; }
.goal ul { display: grid; gap: 5px; margin: 10px 0 0; padding: 0; list-style: none; }
.goal li { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 6px; background: var(--panel-2); color: var(--text-2); font-size: 12.5px; font-weight: 560; }
.goal li .icon { flex: none; width: 14px; height: 14px; color: var(--tint); }
@container picture (min-width: 500px) {
  .flow { grid-template-columns: minmax(0, 1fr) minmax(184px, 37%); column-gap: 26px; align-items: center; }
  .lane::after { content: ""; position: absolute; top: 50%; right: -26px; width: 26px; border-top: 1.5px dashed color-mix(in oklch, var(--tint) 65%, transparent); }
  .goal::before { display: none; }
}

/* The filter panel laid out as a board: its groups in columns, each a small panel in the panel's
   own parts (labels, chips with their boxes, segmented rows); fields and the slider only look like
   theirs. In the browser app the chips that filter by themselves, and the rows of the duration and
   the years, switch in place (buttons); the others are a picture (`aria-disabled`). */
.board { columns: 3 220px; column-gap: 12px; }
.bgroup { break-inside: avoid; display: grid; gap: 8px; margin-bottom: 12px; padding: 14px; border-radius: 10px; background: var(--panel); box-shadow: var(--shadow-panel); }
.bgroup > .flabel { margin: 0; }
.bgroup .flabel.sub { margin: 4px 0 -2px; }
.bgroup .seg { margin: 0; }
.bgroup .chips + .chips { margin-top: 0; }
.bhint { color: var(--text-3); font-size: 12px; line-height: 1.45; text-wrap: pretty; }
.bhint.people { display: flex; flex-wrap: wrap; gap: 2px 12px; }
.bhint.people b { margin-right: 4px; color: var(--text-2); }
.mock-field { display: flex; align-items: center; gap: 8px; min-width: 0; min-height: 36px; padding: 0 10px; border-radius: var(--r-ctl); background: var(--panel); box-shadow: inset 0 0 0 1px var(--line-strong); color: var(--text-3); font-size: 13px; }
.mock-field > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mock-field .icon { flex: none; width: 15px; height: 15px; }
.mock-field kbd { margin-left: auto; }
.mock-field.typed > span { color: var(--text); font-weight: 520; }
.mock-field.typed > span::after { content: ""; display: inline-block; width: 1.5px; height: 1.1em; margin-left: 1px; vertical-align: -3px; background: var(--accent); animation: caret 1.1s steps(1) infinite; }
@keyframes caret { 50% { opacity: 0; } }
.mock-hit { display: flex; align-items: center; gap: 8px; min-height: 32px; padding: 0 10px; border-radius: 7px; background: var(--accent-soft); color: var(--text); font-size: 13px; font-weight: 600; }
.mock-hit .icon { width: 14px; height: 14px; color: var(--accent-ink); }
.mock-seg { cursor: default; }
.mock-seg > span, .mock-seg > button { min-height: 28px; padding: 0 6px; border-radius: 7px; display: flex; align-items: center; justify-content: center; color: var(--text-2); font-size: 12.5px; font-weight: 580; white-space: nowrap; }
.mock-seg > span[aria-checked="true"], .mock-seg > button[aria-checked="true"] { background: var(--panel); color: var(--text); box-shadow: 0 1px 2px rgba(15, 23, 42, .14), 0 0 0 1px var(--line); }
.mock-slider { position: relative; display: block; height: 22px; margin: 2px 9px 0; --from: .2; --to: .4; }
.mock-slider::before, .mock-slider::after { content: ""; position: absolute; top: 9px; height: 4px; border-radius: 2px; }
.mock-slider::before { left: 0; right: 0; background: var(--panel-3); box-shadow: inset 0 0 0 1px var(--line); }
.mock-slider::after { left: calc(var(--from) * 100%); right: calc((1 - var(--to)) * 100%); background: var(--invert); }
.mock-slider i { position: absolute; z-index: 1; top: 2px; width: 18px; height: 18px; margin-left: -9px; border-radius: 50%; background: var(--panel); box-shadow: inset 0 0 0 2px var(--invert), 0 1px 2px rgba(0, 0, 0, .25); }
.mock-slider i:first-child { left: calc(var(--from) * 100%); }
.mock-slider i:last-child { left: calc(var(--to) * 100%); }
.mock-range { color: var(--text); font-size: 13px; font-weight: 600; }
/* What is chosen, counted, and the way to it: it stays at the bottom of the view while the board is
   in it, so the number that follows a click is in sight wherever the chip was (12 px above the
   bottom bar on a phone, like „Nach oben"). Only its button takes the pointer: beside it the chips
   under it stay reachable. */
.board-foot { position: sticky; z-index: 2; bottom: 16px; display: flex; justify-content: center; margin-top: 4px; pointer-events: none; }
.board-foot .btn { pointer-events: auto; box-shadow: inset 0 0 0 1px var(--line-strong), var(--shadow-pop); }
@media (max-width: 900px) {
  .board-foot { bottom: calc(84px + env(safe-area-inset-bottom)); }
}
/* „Alle Filter zeigen" (a checkbox and its label, the same with and without JavaScript): only where
   the board is a single column, a phone's, which shows the groups of a first look (`.key`) first. */
.board-all, .board-more { display: none; }
@container picture (max-width: 480px) {
  .board-all { display: block; position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
  .board-all:not(:checked) ~ .board > .bgroup:not(.key) { display: none; }
  .board-more { display: flex; margin: 0 0 12px; cursor: pointer; }
  .board-all:checked ~ .board-more { display: none; }
  .board-all:focus-visible ~ .board-more { outline: 2px solid var(--accent); outline-offset: 2px; }
  /* A phone's count stands at the left, clear of „Nach oben" in the corner: that one reaches 54 px
     in from the edge of the view, the picture 32 px. Its words may take two lines there. */
  .board-foot { justify-content: flex-start; padding-right: 30px; }
  .board-foot .btn { padding-top: 6px; padding-bottom: 6px; line-height: 1.3; text-align: left; }
}
.board-foot .btn { min-height: 40px; font-size: 13.5px; }
.board-foot .icon { width: 16px; height: 16px; }

/* A page of a regulation, read by a line of light, and the plan it becomes in Betula: modules by
   semester, the credits in their cells, a module over two semesters, a range, the sums checked. */
.planscan { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; align-items: center; }
.plansheet { margin: 0; padding: 12px; border-radius: 10px; background: var(--panel); box-shadow: var(--shadow-panel); }
.plansheet figcaption { display: flex; align-items: center; gap: 7px; margin-bottom: 10px; color: var(--text-2); font-size: 12px; font-weight: 620; }
.plansheet figcaption .icon { width: 15px; height: 15px; color: var(--tint); }
.paper { position: relative; overflow: hidden; }
.paper-table { display: grid; grid-template-columns: 2.4fr repeat(6, minmax(0, 1fr)); gap: 4px 3px; }
.paper-table i { height: 11px; border-radius: 2px; background: color-mix(in srgb, var(--text) 5%, transparent); }
.paper-table i.head { height: 7px; margin-bottom: 3px; background: color-mix(in srgb, var(--text) 22%, transparent); }
.paper-table i.name { background: color-mix(in srgb, var(--text) 12%, transparent); }
.paper-table i.on { background: color-mix(in oklch, var(--tint) 55%, transparent); }
.beam { position: absolute; left: 0; right: 0; top: 30px; height: 30px; background: linear-gradient(transparent, color-mix(in oklch, var(--tint) 30%, transparent) 50%, transparent); animation: scan 3.4s var(--ease) infinite alternate; pointer-events: none; }
@keyframes scan { to { transform: translateY(78px); } }
.planscan-arrow { display: grid; place-items: center; color: var(--tint); }
.planscan-arrow .icon { width: 20px; height: 20px; transform: rotate(90deg); }
.mx { display: grid; gap: 3px; font-size: 12px; }
.mx-row { display: grid; grid-template-columns: minmax(0, 2.6fr) repeat(6, minmax(22px, 1fr)); gap: 3px; }
.mx-row > span { min-height: 25px; display: flex; align-items: center; justify-content: center; border-radius: 5px; }
.mx-head > span { min-height: 20px; color: var(--text-3); font-size: 11px; font-weight: 650; }
.mx-head > span:first-child, .mx-row > .mx-name { justify-content: flex-start; padding-left: 4px; }
.mx-name { gap: 6px; min-width: 0; color: var(--text); font-weight: 580; }
.mx-name i { flex: none; } /* the dot of the kind, as the app's `.kind` draws it */
.mx-cell { grid-column: var(--c) / span var(--n); background: color-mix(in oklch, var(--tint) 18%, var(--panel)); box-shadow: inset 2.5px 0 0 var(--tint); color: var(--text); font-weight: 650; }
.mx-sums { margin-top: 3px; padding-top: 5px; border-top: 1px solid var(--line); }
.mx-sums > span:not(:first-child) { gap: 2px; color: var(--ok); font-size: 11.5px; font-weight: 700; }
.mx-sums .icon { width: 11px; height: 11px; stroke-width: 3; }
.mx-sums .mx-name { color: var(--text-3); font-size: 11px; font-weight: 600; }
.mx-key { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 10px; color: var(--text-3); font-size: 11.5px; }
.mx-key .kind { color: inherit; font-weight: 500; }
/* A narrow matrix gives the names what the semesters do not need. */
@container picture (max-width: 459px) {
  .mx, .mx-row { gap: 2px; }
  .mx-row { grid-template-columns: minmax(0, 1fr) repeat(6, 24px); }
  .mx-row > .mx-name { font-size: 11.5px; }
  .mx-sums > span:not(:first-child) { gap: 1px; font-size: 11px; }
  .mx-sums .icon { width: 10px; height: 10px; }
}
/* The page and the matrix stand one under the other: beside the page the matrix is too narrow
   for its names, which then break over several lines. Only a picture wide enough for both puts
   them side by side. */
.planscan > .paper { justify-self: center; width: 100%; max-width: 400px; }
@container picture (min-width: 1000px) {
  .planscan { grid-template-columns: minmax(0, 4fr) auto minmax(0, 6fr); }
  .planscan > .paper { max-width: none; }
  .planscan-arrow .icon { transform: none; }
}

/* A week of the Stundenplan in its own grid, fitted to a fixed height, and the ways into a
   calendar under it. */
.demo-week { display: flex; flex-direction: column; height: 380px; }
.demo-week-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 6px 12px; margin-bottom: 10px; }
.demo-week .week { background: var(--panel); box-shadow: var(--shadow-panel); }
.clashline { display: inline-flex; align-items: center; gap: 7px; color: var(--no); font-size: 12.5px; font-weight: 620; }
.clashline i { width: 8px; height: 8px; border-radius: 50%; background: var(--no); }
.calendars { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.calendars .badge { background: var(--panel); }

/* Where things live: the browser holds a visitor's, the server what is the same for everybody,
   and the link that carries the Merkliste between devices keeps it behind its „#". */
.places { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; align-items: center; }
.place { padding: 14px; border-radius: 10px; background: var(--panel); box-shadow: var(--shadow-panel); }
.place-head { display: flex; align-items: center; gap: 8px; color: var(--text); font-size: 13.5px; font-weight: 680; }
.place-head .icon { width: 16px; height: 16px; color: var(--tint); }
.place.mine ul { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; margin: 10px 0 0; padding: 0; list-style: none; }
.place.mine li { display: flex; align-items: center; gap: 7px; min-width: 0; padding: 7px 9px; border-radius: 7px; background: color-mix(in oklch, var(--tint) 11%, var(--panel)); color: var(--text); font-size: 12.5px; font-weight: 580; }
.place.mine li .icon { flex: none; width: 14px; height: 14px; color: var(--tint); }
.place.server { background: var(--panel); }
.place.server > p:not(.place-head) { margin-top: 8px; color: var(--text-2); font-size: 12.5px; line-height: 1.45; }
.place .nothing { display: flex; align-items: center; gap: 6px; color: var(--text-3) !important; font-weight: 600; }
.place .nothing .icon { width: 14px; height: 14px; }
.place-link { display: grid; justify-items: center; gap: 5px; text-align: center; color: var(--text-3); font-size: 11.5px; line-height: 1.35; }
.place-link > .icon { width: 16px; height: 16px; color: var(--tint); }
.place-link code { max-width: 100%; padding: 3px 6px; border-radius: 5px; background: var(--panel); box-shadow: inset 0 0 0 1px var(--line); color: var(--text-2); font: 500 11px var(--mono); overflow-wrap: anywhere; }
@container picture (min-width: 560px) {
  .places { grid-template-columns: minmax(0, 1.25fr) minmax(0, 140px) minmax(0, 1fr); }
}

/* The site without JavaScript and with it, the second raised in the tint, and what it is made of. */
.modes { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; }
.mode { padding: 14px; border-radius: 10px; background: var(--panel); box-shadow: var(--shadow-panel); }
.mode.on { box-shadow: inset 0 0 0 1.5px color-mix(in oklch, var(--tint) 55%, transparent), var(--shadow-panel); }
.mode-head { display: flex; align-items: center; gap: 8px; color: var(--text); font-size: 14px; font-weight: 680; }
.mode-head .icon { width: 16px; height: 16px; color: var(--tint); }
.mode ul { display: grid; gap: 7px; margin: 12px 0 0; padding: 0; list-style: none; }
.mode li { display: grid; grid-template-columns: 15px minmax(0, 1fr); gap: 8px; color: var(--text-2); font-size: 13px; line-height: 1.4; }
.mode li .icon { width: 15px; height: 15px; margin-top: 1px; color: var(--ok); stroke-width: 2.6; }
.stack { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.stack .badge { background: var(--panel); font-family: var(--mono); font-size: 12px; font-weight: 550; }
@container picture (min-width: 500px) {
  .modes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* The birch Betula is named after, as the site draws its frame: the season's crown on top (Folia),
   one trunk in the first panel's bark from under it down to the ground (the Datenstand on a twig
   beside it), the ground with its roots (Radix) holding the sources. The crown's tile is the
   season's, as along the top of every page. The trunk is placed in its grid area (lines on both
   sides: an `auto` end of an absolute item would be the container's edge). */
.tree { --crown-mask: url("/assets/birch/summer-crown.svg"); position: relative; display: grid; grid-template-columns: 84px minmax(0, 1fr); grid-template-rows: 52px auto auto auto; }
:root[data-season="spring"] .tree { --crown-mask: url("/assets/birch/spring-crown.svg"); }
:root[data-season="autumn"] .tree { --crown-mask: url("/assets/birch/autumn-crown.svg"); }
:root[data-season="winter"] .tree { --crown-mask: url("/assets/birch/winter-crown.svg"); }
.tree::before { content: ""; grid-column: 1 / -1; grid-row: 1; align-self: start; z-index: 1; height: 64px; background: var(--crown); -webkit-mask: var(--crown-mask) repeat-x left top; mask: var(--crown-mask) repeat-x left top; pointer-events: none; }
.trunk { position: absolute; grid-column: 1 / 2; grid-row: 1 / 4; top: 20px; left: calc(50% - 15px); width: 30px; height: calc(100% - 20px); border-radius: 5px 5px 0 0; box-shadow: 0 0 0 1px var(--line-strong); }
.trunk rect { fill: var(--hero-bark); }
.trunk path { fill: var(--hero-bark-ink); }
.tree > .tree-tag { grid-column: 2; z-index: 2; margin: 0 20px 0 0; }
.tree > .tree-tag::before { content: ""; position: absolute; top: 50%; right: 100%; width: 27px; border-top: 1.5px solid var(--line-strong); }
.tree > .tree-tag:nth-child(2) { grid-row: 2; margin-top: 12px; }
.tree > .tree-tag:nth-child(3) { grid-row: 3; margin-top: 34px; margin-bottom: 46px; }
.tree-ground { grid-column: 1 / -1; grid-row: 4; position: relative; display: grid; gap: 12px; padding: 22px 20px 20px; background: var(--ground); color: var(--ground-text); }
.tree-ground::before { content: ""; position: absolute; inset: 0; background: var(--ground-root); -webkit-mask: url("/assets/birch/roots.svg") repeat-x left top; mask: url("/assets/birch/roots.svg") repeat-x left top; pointer-events: none; }
.tree-tag { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; justify-self: start; max-width: 100%; padding: 8px 12px; border-radius: 9px; background: var(--panel); box-shadow: var(--shadow-panel); color: var(--text-2); font-size: 13px; line-height: 1.4; }
.tree-tag b { color: var(--text); font-weight: 700; }
.tree-tag .icon { flex: none; width: 15px; height: 15px; color: var(--tint); }
.sources { position: relative; display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; list-style: none; }
.sources li { display: inline-flex; align-items: baseline; gap: 6px; padding: 5px 9px; border-radius: 7px; background: color-mix(in srgb, var(--ground-text) 8%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ground-text) 10%, transparent); font-size: 12px; font-weight: 600; }
.sources em { color: var(--ground-muted); font-style: normal; font-weight: 500; }

/* The catalog on a computer and a module on a phone standing in front of it, then the keys. */
.devices { position: relative; padding: 6px 17% 26px 0; }
.screen { margin: 0; }
/* No `display` here: .shot-light and .shot-dark decide which theme's picture shows. */
.screen img { width: 100%; height: auto; vertical-align: top; }
.screen.desktop img { border-radius: 10px; box-shadow: 0 0 0 1px var(--line-strong), 0 18px 40px -18px rgba(15, 23, 42, .45); }
.screen.phone { position: absolute; right: 0; bottom: 0; width: 31%; padding: 5px; border-radius: 20px; background: var(--device); box-shadow: 0 0 0 1px var(--line-strong), 0 18px 40px -14px rgba(15, 23, 42, .55); }
.screen.phone img { border-radius: 15px; }
.device-keys { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 14px; }
.device-keys kbd { font-size: 11.5px; background: var(--panel); }
.device-keys .badge { background: var(--panel); }

@container home (min-width: 1000px) {
  .feature { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: 48px; padding: 38px 32px; }
  .feature.flip { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); }
  .feature.flip > .feature-text { order: 2; }
  /* The board takes the whole width under the words, which stand in two columns: the chapter
     on the left, its points on the right. */
  .feature.wide { grid-template-columns: minmax(0, 1fr); gap: 28px; }
  .feature.wide > .feature-text { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: 48px; align-items: start; }
  .feature.wide > .feature-text > * { grid-column: 1; }
  .feature.wide .points { grid-column: 2; grid-row: 1 / span 5; align-self: center; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px 28px; margin: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .beam, .mock-field.typed > span::after { animation: none; }
}

/* A table of contents that follows the page (`data-spy`, the legal pages): the part the page is
   at inverted. */
.toc a[aria-current="location"] { background: var(--invert); color: var(--on-invert); }
.legal-toc a > .icon { width: 16px; height: 16px; color: var(--text-3); }
.legal-toc a[aria-current] > .icon { color: inherit; }

/* Impressum and Datenschutz (pages/legal.rs): one panel of text, its parts on continuous rules.
   What belongs together (which data, why, how long) stands as label | text rows, the label above
   the text where the panel is narrow. */
.legal { container: legal / inline-size; }
.legal-head { padding: 26px 24px 18px; }
.legal-head h1 { font-size: 26px; line-height: 1.15; font-weight: 720; letter-spacing: -.03em; }
.legal-head p { margin-top: 8px; color: var(--text-2); font-size: 15px; line-height: 1.55; max-width: 72ch; text-wrap: pretty; }
.legal-head .legal-stand { margin-top: 4px; color: var(--text-3); font-size: 13px; }
.legal-part { padding: 20px 24px 22px; border-top: 1px solid var(--line); scroll-margin-top: 8px; }
.legal-part h2 { font-size: 16px; font-weight: 650; letter-spacing: -.015em; }
/* One measure for the text, the label | text rows included, so the parts share their right edge. */
.legal-part > * { max-width: 704px; }
.legal-part > * + *, .legal-facts dd > * + * { margin-top: 10px; }
.legal-part p, .legal-part li, .legal-facts dd { color: var(--text-2); font-size: 14.5px; line-height: 1.6; text-wrap: pretty; }
.legal-part ul { display: grid; gap: 3px; }
.legal-part li { position: relative; padding-left: 18px; }
.legal-part li::before { content: ""; position: absolute; left: 5px; top: .7em; width: 5px; height: 5px; border-radius: 50%; background: var(--text-3); }
.legal-part code { padding: 1px 5px; border-radius: 5px; background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line); color: var(--text); font-family: var(--mono); font-size: 12.5px; }
.legal-address { display: grid; font-style: normal; color: var(--text); font-size: 14.5px; line-height: 1.6; }
.legal-facts { display: grid; margin-bottom: 0; }
.legal-facts > div { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2px; padding: 10px 0; border-top: 1px solid var(--line); }
.legal-facts > div:last-child { padding-bottom: 0; }
.legal-facts dt { color: var(--text); font-size: 13.5px; font-weight: 620; line-height: 1.6; }
.legal-facts dd { margin: 0; }
@container legal (min-width: 720px) {
  .legal-facts > div { grid-template-columns: 140px minmax(0, 1fr); gap: 20px; }
  .legal-facts dt { padding-top: 1px; }
}
/* On a phone the text starts 16px in, as on every other page, and a part a link jumps to
   (/datenschutz#kalender) stops below the sticky search bar. */
@media (max-width: 900px) {
  .legal-head { padding: 20px 16px 14px; }
  .legal-part { padding: 18px 16px 20px; scroll-margin-top: 76px; }
}

/* Two abilities a row, and an odd last one takes the whole row: no empty cell at the end (nine
   are three rows of three on a wide page). */
@container home (min-width: 720px) {
  .ability-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ability:nth-child(odd) { border-right: 1px solid var(--line); }
  .ability:nth-child(odd):last-child { grid-column: 1 / -1; border-right: 0; }
  .ability p { max-width: 80ch; }
}
@container home (min-width: 900px) {
  .home-lists { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* From a notebook's width on the birch with the figures stands beside the text. */
@container home (min-width: 700px) {
  .home-hero { grid-template-columns: minmax(0, 1fr) auto 46px; }
  .tree-figures { display: grid; }
  .hero-trunk { display: block; }
}
@container home (min-width: 1180px) {
  .ability-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .ability:nth-child(odd) { border-right: 0; }
  .ability:nth-child(odd):last-child { grid-column: auto; }
  .ability:not(:nth-child(3n)) { border-right: 1px solid var(--line); }
}
/* A wide screen: the map as large as the height allows (4:3), the head, the legend and what is
   shown in a column on its left, the ✕ in the dialog's corner. */
@media (min-width: 1100px) and (min-aspect-ratio: 7 / 5) {
  .map-dialog { --side: 340px; width: min(calc(100vw - 48px), calc((100vh - 48px) * 4 / 3 + var(--side))); height: min(calc(100vh - 48px), calc((100vw - 48px - var(--side)) * 3 / 4)); }
  .map-dialog[open] { grid-template-columns: var(--side) minmax(0, 1fr); grid-template-rows: auto auto minmax(0, 1fr); grid-template-areas: "head map" "key map" "cap map"; }
  .map-dialog-head { padding: 24px 24px 16px; border-bottom: 0; }
  .map-dialog-head h2 { font-size: 19px; line-height: 1.25; text-wrap: balance; }
  .map-dialog-head p { margin-top: 8px; font-size: 14px; line-height: 1.55; }
  .map-dialog-close { position: absolute; top: 12px; right: 12px; z-index: 2; background: color-mix(in srgb, var(--panel) 90%, transparent); box-shadow: 0 0 0 1px var(--line); }
  .map-dialog .map-live { height: auto; border-left: 1px solid var(--line); }
  .map-dialog .map-holder { inset: 20px; }
  .map-dialog .live-key { grid-area: key; align-self: auto; justify-self: stretch; display: grid; gap: 9px; margin: 0 24px; padding: 16px 0 18px; border-radius: 0; border-top: 1px solid var(--line); background: none; box-shadow: none; color: var(--text-2); font-size: 13px; }
  .map-dialog .live-key .label { display: block; margin-bottom: 1px; }
  .map-dialog .live-key .key-links { display: inline-flex; }
  .map-dialog .live-key > span { gap: 10px; }
  .map-dialog .live-key .dot-key { width: 10px; height: 10px; margin: 0 4px; }
  .map-dialog .map-cap { grid-area: cap; align-self: start; height: auto; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 12px 6px; margin: 0 16px 16px; padding: 16px; border: 0; border-radius: 12px; background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line); font-size: 13.5px; }
  .map-dialog .map-cap .cap-text { grid-column: 1 / -1; flex-direction: column; align-items: stretch; gap: 3px; white-space: normal; }
  .map-dialog .map-cap .cap-text b { font-size: 15.5px; line-height: 1.3; letter-spacing: -.012em; }
  .map-dialog .cap-shares { display: none; }
  .map-dialog .cap-relatives { display: block; grid-column: 1 / -1; }
  .cap-relatives ol { margin: 6px 0 0; padding: 0; list-style: none; }
  .cap-relatives li { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 6px 0; border-top: 1px solid var(--line); color: var(--text-2); font-size: 13px; line-height: 1.35; }
  .cap-relatives li b { flex: none; color: var(--text); font-weight: 650; }
  .map-dialog .map-cap .cap-link { justify-content: center; min-height: 38px; }
}
/* On a phone the pictures stand upright (the tall map, the phone's own screenshots) and are
   swiped; the figures stand two by two, the caption in two lines. */
@media (max-width: 600px) {
  .carousel { --w: 66cqw; }
  .stage { aspect-ratio: 3 / 4; }
  .stage-shot img { left: 8%; top: 6%; width: 96%; border-radius: 12px 12px 0 0; }
  .map-wide { display: none; }
  .map-tall { display: block; }
  .show-arrow, .cap-go { display: none; }
  .show-bar { grid-template-columns: minmax(0, 1fr) auto; padding: 0 12px; }
  .show-bar .show-tabs { grid-column: 1; width: auto; grid-auto-columns: minmax(0, auto); }
  .show-bar .show-tabs button { padding: 0 6px; font-size: 13.5px; }
  /* „Regelstudienplan" („Standard study plan") is the longest tab: on a small phone the four still
     fit side by side, and on the smallest (320px) as well. */
  @media (max-width: 400px) { .show-bar .show-tabs button { padding: 0 3px; font-size: 12.5px; } }
  @media (max-width: 359px) {
    .show-bar { gap: 6px; }
    .show-bar .show-tabs button { padding: 0 2px; font-size: 11px; }
  }
  .show-bar .show-tabs button[aria-checked="true"] { background: var(--panel); box-shadow: 0 1px 2px rgba(15, 23, 42, .14), 0 0 0 1px var(--line); }
  .show-mark { display: none; }
  .show-play { width: 40px; height: 40px; }
  .cslide.is-current .stage-go { opacity: 1; transform: none; }
  .cap { height: auto; min-height: 50px; padding: 8px 12px 8px 14px; }
  .cap-text { flex-direction: column; align-items: stretch; gap: 0; }
  .map-dialog { width: 100vw; height: 100dvh; max-height: none; border-radius: 0; }
  .map-dialog .map-live { height: auto; }
}
.freshness { color: var(--text-3); font-size: 12.5px; padding: 4px 6px; }
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 8px; }
.card-link { display: block; height: 100%; min-height: 76px; padding: 14px 16px; border-radius: var(--r-row); box-shadow: inset 0 0 0 1px var(--line); color: inherit; text-decoration: none; }
.card-link:hover { background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line-strong); text-decoration: none; }
.card-title { display: block; font-weight: 620; font-size: 14.5px; }
.card-meta { display: block; color: var(--text-3); font-size: 12.5px; margin-top: 2px; }
.chip-links { display: flex; flex-wrap: wrap; gap: 6px; }
.table-scroll { overflow-x: auto; }

/* ---------- the tables of a program ----------
   Study plan, areas and all modules are one table with the same columns and continuous rules:
   semesters and areas are rows inside it, never boxes beside it. The first and the last column
   keep the 24px of the panel, so every line starts under the heading. */
.ptable { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.ptable th, .ptable td { padding: 8px 10px; text-align: left; font-size: 13.5px; font-weight: 450; vertical-align: baseline; border-top: 1px solid var(--line); }
.ptable > thead th, .ptable > thead td { border-top: 0; padding-top: 2px; padding-bottom: 7px; color: var(--text-3); font-size: 11px; font-weight: 650; letter-spacing: .05em; text-transform: uppercase; }
.ptable th:first-child, .ptable td:first-child { padding-left: 24px; }
.ptable th:last-child, .ptable td:last-child { padding-right: 24px; }
/* Every other module is shaded, as the rows of the catalog's list (`--row-shade`), counted anew
   in every group and without its head (`of :not(.group)`). `:where` keeps the rule as little
   specific as `.ptable tbody tr`, so that the row pointed at and the open one stand above it. */
.ptable tbody tr:where(:nth-child(even of :not(.group))) { background: var(--row-shade); }
.ptable tbody tr:not(.group):hover { background: var(--row-hover); }
/* The row whose module stands beside the page, marked as the catalog marks its open row. */
.ptable tbody tr.open { background: var(--panel-3); }
.ptable tbody tr.open > :first-child { position: relative; }
.ptable tbody tr.open > :first-child::before { content: ""; position: absolute; left: 8px; top: 5px; bottom: 5px; width: 3px; border-radius: 3px; background: var(--accent); }
.ptable tbody tr.open .c-name a { font-weight: 650; }
.ptable .c-name { font-weight: 500; }
.ptable:not(.matrix):not(.planlist) .c-name { width: 100%; }
.ptable .c-name a { color: inherit; font-weight: 600; text-decoration-color: var(--line-strong); }
.ptable .c-name a:hover { text-decoration: underline; }
/* A row of the plan that names no module of the catalog: still a link (it says what the plan
   states about it), but quieter than a module. */
.ptable .c-name a.unstated { color: var(--text-2); font-weight: 520; }
.ptable .c-name .flag { margin-left: 6px; }
.ptable .c-id { font-family: var(--mono); font-size: 12px; color: var(--text-3); white-space: nowrap; }
.ptable .num { text-align: right; white-space: nowrap; }
.ptable .c-kind, .ptable .c-turnus, .ptable .c-lp, .ptable .c-sem { white-space: nowrap; color: var(--text-2); }
.ptable .c-area span { display: block; max-width: 24ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-3); }
.ptable .c-lp { font-weight: 600; color: var(--text); }
/* A group of rows: an area, a semester, a section of the plan. */
.ptable tr.group > th { padding: 0; border-top: 1px solid var(--line-strong); color: var(--text); font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
.ptable tbody:first-of-type tr.group > th { border-top: 0; }
/* An area is a link: it shows beside the page what it holds. The whole line is the target. */
.ptable tr.group a.ginner { color: inherit; text-decoration: none; transition: background .1s; }
.ptable tr.group a.ginner:hover { background: var(--panel-2); text-decoration: none; }
.ptable tr.group a.ginner:hover .gname { text-decoration: underline; }
.ptable tr.group > td { padding-top: 20px; padding-bottom: 6px; border-top: 1px solid var(--line-strong); font-weight: 700; }
.ptable tbody:first-of-type tr.group > td { border-top: 0; padding-top: 10px; }
.ptable tr.group .ginner { display: flex; align-items: baseline; gap: 10px; padding: 19px 24px 7px; }
.ptable tbody:first-of-type tr.group .ginner { padding-top: 10px; }
.ptable tr.group .gname { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ptable tr.group .gpath, .ptable tr.group .gcount { text-transform: none; letter-spacing: 0; font-weight: 500; font-size: 12.5px; color: var(--text-3); white-space: nowrap; }
.ptable tr.group .gcount { margin-left: auto; padding-left: 10px; }
.ptable tr.group.depth-2 .gname { padding-left: 14px; }
.ptable tr.group.depth-3 .gname { padding-left: 28px; }
.ptable tr.group.depth-4 .gname { padding-left: 42px; }
/* The plan as its regulations print it: fixed columns, equal for every semester, and the name
   takes what is left — never less than --name-min, so a name never stands a word to a line and
   no column runs into the next one. Where the page is too narrow for that, „Art" gives way;
   where it is too narrow even without it, the table scrolls sideways in its panel and keeps its
   lines. A word longer than its column breaks instead of running into the next one. */
.matrix, .planlist {
  table-layout: fixed; width: 100%; overflow-wrap: break-word;
  --name-min: 200px;
  /* „Art" is as wide as its longest label („Abschlussarbeit"), or not there at all: a step,
     taken where the name would get less than its minimum. 100cqi is the page, which the table
     fills. --rest is what the columns right of it take. */
  --kind: clamp(0px, (100cqi - var(--name-min) - 140px - var(--rest)) * 1000, 140px);
  min-width: calc(var(--name-min) + var(--kind) + var(--rest));
}
/* Every semester as wide as every other, and all of them never narrower than three, so that
   „Leistungspunkte" fits above a plan of two. The page sets --sems. Where the page is narrower
   than the names at their minimum and the semesters, the app draws the list instead
   (`matrix_min_width` in program.rs, with the same numbers). */
.matrix { --sem: max(48px, 144px / var(--sems)); --rest: calc(var(--sems) * var(--sem)); }
.planlist { --rest: 80px; }
.matrix col:first-child, .planlist col:first-child { width: auto; }
.matrix col.c-kind-col, .planlist col.c-kind-col { width: var(--kind); }
.matrix .c-kind, .planlist .c-kind { overflow: hidden; padding-left: min(10px, var(--kind)); padding-right: min(10px, var(--kind)); }
.matrix col.c-sem-col { width: var(--sem); }
.planlist col.c-area-col { width: 200px; }
.planlist col.c-lp-col { width: 80px; }
/* Where the page shares its room with the panel on the right, the columns that carry the least
   give way first — never the name, the credits or the semester. In the fixed columns of the
   plan a column gives way by getting narrow, not by leaving: a cell that left would move the
   ones after it into the column before (the credits under „Bereich", away from the sums of the
   semesters). After the widths above, so that the column gives its room back. */
@container page (max-width: 860px) {
  .ptable:not(.planlist) .c-area { display: none; }
  .planlist col.c-area-col { width: 0; }
  .planlist .c-area { overflow: hidden; padding-left: 0; padding-right: 0; }
}
@container page (max-width: 700px) { .ptable .c-turnus { display: none; } }
@container page (max-width: 560px) { .ptable .c-id { display: none; } }
.matrix .c-group { text-align: center; color: var(--text-3); }
/* A semester is a box between two lines with its figure in the middle: 10px on both sides, also
   where the box is the first cell of its row (the „1" of the head) or the last one. The line
   stands left of every semester and nowhere else, also in the sums. */
.matrix th.c-sem, .matrix th.c-group, .matrix td.lp, .matrix td.empty { padding-left: 10px; padding-right: 10px; }
.matrix .c-sem, .matrix td.lp, .matrix td.empty { text-align: center; border-left: 1px solid var(--line); }
.matrix .c-group { border-left: 1px solid var(--line); }
.matrix td.lp { font-weight: 650; color: var(--text); }
.matrix td.spans { background: var(--panel-2); }
.matrix td.spans::before { content: "↔"; margin-right: 6px; color: var(--text-3); font-weight: 500; }
.matrix td.loose { text-align: left; color: var(--text-3); font-weight: 500; }
.matrix td.differs::after { content: "*"; color: var(--warn); }
.matrix tfoot th, .matrix tfoot td { border-top: 2px solid var(--line-strong); padding-top: 9px; padding-bottom: 12px; font-weight: 700; color: var(--text); }
.matrix tfoot th { text-align: left; }
.footnote { padding: 8px 24px 14px; margin: 0; }
.variants { padding: 0 24px 12px; }
.variants .flabel { margin-bottom: 6px; }
.doclist { padding-bottom: 6px; }
.docrow { display: flex; align-items: center; gap: 10px; min-height: 46px; padding: 0 24px; border-top: 1px solid var(--line); color: inherit; text-decoration: none; }
.doclist li:first-child .docrow { border-top: 0; }
/* Every other document is shaded, as the rows of the catalog's list. */
.docrow:where(.doclist li:nth-child(even) > *) { background: var(--row-shade); }
.docrow:hover { background: var(--row-hover); text-decoration: none; }
.docrow > .icon { width: 16px; height: 16px; color: var(--text-3); flex: none; }
.docname { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 560; font-size: 13.5px; }
.docrow small { margin-left: auto; color: var(--text-3); font-size: 12.5px; white-space: nowrap; }
.fues > summary { cursor: pointer; display: flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 24px; border-top: 1px solid var(--line); color: var(--text-2); font-weight: 560; font-size: 13px; list-style: none; }
.fues > summary::-webkit-details-marker { display: none; }
.fues > summary::after { content: "+"; margin-left: auto; font-size: 15px; color: var(--text-3); }
.fues[open] > summary::after { content: "–"; }
.fues > summary:hover { background: var(--panel-2); }
/* „Mein Plan" of a program: a placeholder that leads on, to the Stundenplan and the catalog. */
.my-plan-links { display: grid; gap: 6px; max-width: 460px; padding: 0 24px 22px; }
.my-plan-links .action { box-shadow: inset 0 0 0 1px var(--line-strong); }
.my-plan-links .action:hover { background: var(--panel-2); }
.my-plan-links .action > span { flex: 1; }
.list-plain li { padding: 10px 0; border-bottom: 1px solid var(--line); display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center; }
.list-plain li:last-child { border-bottom: 0; }

/* The frame of every page (ui::Frame): a sidebar as wide as the catalog's filter panel, with
   the same handle and remembered width, and the page next to it. A page can add a panel on the
   right (a module, the numbers of the view): unlike the catalog's preview it is a column of the
   layout, so it covers nothing and the page keeps the room that is left. */
.framed > .page { padding: 0 1px 1px 0; }
/* A frame's panel on the right is the catalog's preview (`.work > .detail`): it floats over the
   page, docked to the right edge, and is there only while something is picked (ui::Frame). */
.detail.aside .hero { padding-top: 18px; }
.detail.aside .hero h2 { margin-top: 0; font-size: 19px; }
/* „Ein Modul anklicken …": what the panel shows once something is picked. */
.sidebar .panel-head .mono { padding: 3px 7px; border-radius: var(--r-s); background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line); color: var(--text-2); margin-right: 6px; }
.sidebar .body { padding: 0 10px 12px; overflow-x: hidden; }
.sidebar .fgroup.first { padding-top: 4px; }
.toc { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1px; padding: 4px 0 0; }
.toc.fgroup { padding-top: 10px; }
.toc .flabel { padding-left: 2px; }
.toc a { min-height: 32px; display: flex; align-items: center; gap: 8px; padding: 0 10px; border-radius: var(--r-s); color: var(--text-2); font-weight: 550; font-size: 13px; text-decoration: none; }
.toc a:hover { background: var(--panel-3); color: var(--text); text-decoration: none; }
.toc a[aria-current="page"] { background: var(--invert); color: var(--on-invert); }
.toc a b { flex: none; font-weight: 650; color: var(--text); white-space: nowrap; }
.toc a .toc-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.toc a .toc-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text-3); font-weight: 450; font-size: 12.5px; }
.toc a .num { flex: none; margin-left: auto; color: var(--text-3); font-weight: 500; font-size: 12px; }
/* The Studienplan's semesters: „jetzt" (small.num) readable on the inverted current one, and a
   semester that is over (a.past) steps back. */
.toc a[aria-current="page"] small.num { color: inherit; opacity: .72; }
.toc a.past:not([aria-current="page"]) { color: var(--text-3); }
.toc .hint { padding: 0 2px; font-size: 12px; }
.chip-count { margin-left: auto; padding-left: 6px; color: var(--text-3); font-weight: 500; font-size: 12px; }
.chip[data-state="with"] .chip-count { color: inherit; opacity: .7; }
.side-facts { display: grid; gap: 10px; margin: 4px 0 0; padding: 0 2px; }
.side-facts dt { color: var(--text-3); font-size: 11.5px; }
.side-facts dd { margin: 0; font-weight: 620; font-size: 14px; }
.actions { display: grid; gap: 2px; }
.action { min-height: 36px; display: flex; align-items: center; gap: 9px; padding: 0 10px; border-radius: var(--r-s); color: var(--text); font-weight: 560; font-size: 13px; text-decoration: none; }
a.action:hover { background: var(--panel-3); text-decoration: none; }
.action .icon { flex: none; width: 16px; height: 16px; color: var(--text-2); }
.action > span { min-width: 0; }
.action small { display: block; color: var(--text-3); font-weight: 450; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.action:has(small) { min-height: 46px; }
.action.soon, .action.soon .icon { color: var(--text-3); }
/* The way from a program's page into the catalog: first in the sidebar, set off by a line. */
.catalog-jump { margin-bottom: 6px; }
.catalog-jump .action { min-width: 0; box-shadow: inset 0 0 0 1px var(--line-strong); }
.catalog-jump .action > span { flex: 1; overflow: hidden; }
.catalog-jump .action > .icon:last-child { margin-left: auto; }
.action em { margin-left: auto; font-style: normal; font-size: 11px; font-weight: 600; padding: 1px 6px; border-radius: 5px; background: var(--panel-3); color: var(--text-3); }
.section { scroll-margin-top: 14px; }
.module-page { display: grid; gap: var(--gap); container: module / inline-size; }
.module-page > .hero { border-bottom: 0; padding: 10px 18px 18px; }
.module-page > .hero h2 { font-size: 30px; }
/* The same parts in the same order as the preview (times and facts, then the description);
   with room for it the two halves stand side by side, the description on the left. */
.module-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap); align-items: start; }
@container module (min-width: 900px) {
  .module-grid { grid-template-columns: minmax(0, 1fr) minmax(360px, 36%); }
  .module-grid > aside { grid-column: 2; grid-row: 1; }
  .module-grid > div { grid-column: 1; grid-row: 1; }
}
.module-grid .prose { max-width: 80ch; }
/* The state of the local data while it loads (boot.js): gone as soon as the app runs. `hidden`
   has to win over the pill's own display, and the text gives way before the bar grows. */
.db-status { margin-left: auto; height: 32px; font-size: 12.5px; flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; display: block; line-height: 32px; }
.db-status[hidden] { display: none; }
.fatal { position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%); z-index: 100; padding: 12px 16px; border-radius: var(--r-ctl); background: var(--invert); color: var(--on-invert); box-shadow: var(--shadow-pop); }
.fatal a { color: inherit; font-weight: 650; text-decoration: underline; }

.bottomnav { display: none; }

/* ---------- studyplan (app/src/pages/studyplan, app/src/studyplan.rs) ----------
   The Studienplan: the frame of every page, the sidebar „Anpassen", and the plan in one panel of
   the main column (.sp-body) that fills the page's height. The views are built by the parts of
   app/src/pages/studyplan/*.rs; the markup each rule expects is named beside it. A module's tone is
   a `t-…` class (--tint), as on the start page and in the week grid. */
/* One column that never grows past the page: its grid items hold lines that do not wrap. */
.page-inner.sp { min-height: 100%; grid-template-columns: minmax(0, 1fr); }
/* The fold measures itself by what the area shows of the page (`100vh - 64px`, app.css „one scroll
   area"), not by the page, which is as tall as its content. A module opened from the plan floats
   over the page at its right edge, as on every other page (owner, 2026-09-25: „nicht tiling,
   sondern attached"). */
.sp-body { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; align-content: start; padding: 16px 24px 24px; overflow: visible; overflow-wrap: break-word; }
/* The semester in three parts (SemesterView): above the fold (div.sp-fold) the head, the red
   alerts, the quiet line and the view (div.sp-view); the planned modules (div.sp-side); under the
   fold what has no fixed time and the line of what is derived (div.sp-below). Where the page is
   wide enough, the modules are a column of their own at the right, from the head down and as long
   as their list: the fold keeps its height however long the list is, and what is under the fold
   follows it on the left. Narrower, the modules follow the fold.
   The fold is as tall as the page shows, and a fitted week (div.sp-week > .week.fit) takes what
   the view has left of it, from 340px up to 800px (owner, 2026-09-25: always the whole week on the
   screen, smaller on a small one, larger on a large one „bis zu einem sinnvollen Limit"). Its
   height is never the hours': more hours squeeze the week („einfach gesquished"). The slots stand
   in fractions of the day's height, the hours share it. A phone lists the days instead. */
.sp-fold { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.sp-fold > .hint, .sp-fold > .note { margin-top: 0; }
/* A plan from a link (`share=`) offered at the top of the plan, as the Merkliste offers a list. */
.sp-fold > .offer { margin: 0; }
.sp-view { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.sp-side { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; align-content: start; min-width: 0; }
.sp-below { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; min-width: 0; }
.sp-week { position: relative; display: flex; flex-direction: column; min-width: 0; }
@media (min-width: 901px) {
  .sp-fold { min-height: min(calc(100vh - 64px - 41px), 960px); }
  .sp-view, .sp-week { flex: 1 1 auto; }
  .sp-week { min-height: 340px; max-height: 800px; }
}
.week.fit { flex: 1 1 auto; grid-template-rows: auto minmax(0, 1fr); }
.week.fit .hours { grid-auto-rows: minmax(0, 1fr); }
.week.fit .col { height: auto; background: repeating-linear-gradient(var(--line) 0 1px, transparent 1px calc(200% / var(--span))); }
.week.fit .slot { top: calc((var(--from) - var(--first)) * 100% / var(--span)); height: calc((var(--to) - var(--from)) * 100% / var(--span) - 2px); }
/* On a phone and a small screen the week is a grid as well (div.sp-carousel > div.sp-slide >
   div.sp-week > .week.fit, owner 2026-09-27), as tall as the screen leaves it under the bar of the
   search and over the tabs and the bar at the bottom, over the list of its days. It alone takes
   the whole width of the screen (owner: „den ganzen horizontalen Platz", the rest of the page as
   it is): out of the page's margin and the panel's (12px + 14px, `--bleed`), a band without the
   frame of a box, its top and its bottom a line. Where the plan has A or B weeks the three weeks
   are a carousel as the pictures of the start page are (`--at`, -2 … 2, week.rs `WeekCarousel`):
   the week shown fills the band, its neighbours wait beside it out of sight, smaller and veiled,
   and come in with the finger that carries the weeks sideways (`--drag`, without their spring
   while it does; up and down it scrolls the page). The tabs under them (div.seg.sp-weektabs) name
   the weeks, the mark of the one shown slides to it (--i). */
@property --drag { syntax: "<length>"; inherits: false; initial-value: 0px; }
.sp-carousel { --bleed: 26px; --w: 100cqw; --side: .92; --step: calc(var(--w) * (1 + var(--side)) / 2 + 6px); position: relative; container-type: inline-size; height: clamp(340px, calc(100vh - 240px), 560px); height: clamp(340px, calc(100svh - 240px), 560px); margin: 0 calc(-1 * var(--bleed)); overflow: hidden; touch-action: pan-y pinch-zoom; user-select: none; -webkit-user-select: none; }
.sp-slide { position: absolute; top: 0; left: 50%; width: var(--w); height: 100%; display: flex; flex-direction: column; z-index: var(--z, 3); transform: translateX(calc(-50% + var(--at, 0) * var(--step) + var(--drag, 0px))) scale(var(--scale, 1)); transition: transform .5s var(--spring), opacity .3s, z-index .5s linear; }
.sp-carousel.dragging > .sp-slide { transition: none; }
.sp-slide > .sp-week { flex: 1 1 auto; min-height: 0; }
.sp-slide::after { content: ""; position: absolute; inset: 0; z-index: 4; background: var(--panel); opacity: 0; pointer-events: none; transition: opacity .3s; }
.sp-slide.is-side { --scale: var(--side); --z: 2; }
.sp-slide.is-side::after { opacity: .45; }
.sp-slide.is-far { --scale: var(--side); --z: 1; opacity: 0; }
/* The width goes to the days: a narrower column of hours, closer days, and slots whose text starts
   right after their bar (at most a fifth of a slot's width, as the base rule keeps it). */
.sp-slide .week { grid-template-columns: 22px var(--cols, repeat(var(--days, 5), minmax(0, 1fr))); gap: 0 3px; padding: 8px 8px 10px 4px; border-radius: 0; box-shadow: inset 0 1px 0 var(--line), inset 0 -1px 0 var(--line); }
.sp-slide .slot { padding-left: clamp(0px, calc((var(--in) * var(--wide, 1) / var(--lanes, 1) - 2px) / 5), 5px); padding-right: clamp(0px, calc((var(--in) * var(--wide, 1) / var(--lanes, 1) - 2px) / 8), 3px); }
/* A slot says where it is held, under its name (owner, 2026-09-27: „zeige auch die location an"),
   and its time only where it is wide enough for it (a tablet's): the grid's hours say it, and the
   list under the grid. Its height still decides, as above. */
@container (min-height: 38.51px) { .sp-slide .slot > small.p { display: block; } }
@container (min-width: 80px) and (min-height: 26.5px) { .sp-slide .slot > small:not(.p) { display: block; } }
/* The red mark of a clash stands in the bottom corner, which the lines of a slot leave free: in a
   day's narrow lane the name reaches the top corner. */
.sp-carousel .sp-slide .slot.clash::after, .sp-carousel .sp-slide .slot-frame::after { top: auto; bottom: 3px; }
.sp-weektabs { position: relative; width: min(100%, 380px); margin: 0 auto; }
.sp-weekmark { position: absolute; top: 3px; bottom: 3px; left: 3px; width: calc((100% - 6px) / 3); border-radius: 7px; background: var(--panel); box-shadow: 0 1px 2px rgba(15, 23, 42, .14), 0 0 0 1px var(--line); transform: translateX(calc(var(--i, 0) * 100%)); transition: transform .4s var(--spring); }
.seg.sp-weektabs button { position: relative; z-index: 1; }
.seg.sp-weektabs button[aria-checked="true"] { background: transparent; box-shadow: none; }
@container page (min-width: 900px) {
  .sp-body { grid-template-columns: minmax(0, 1fr) clamp(250px, 28%, 340px); grid-template-rows: auto 1fr; gap: 0 24px; }
  .sp-body > .sp-fold { grid-area: 1 / 1; }
  .sp-body > .sp-below { grid-area: 2 / 1; }
  .sp-body > .sp-below > :first-child { margin-top: 14px; }
  .sp-body > .sp-side { grid-column: 2; grid-row: 1 / span 2; }
  /* A semester whose data failed has no modules to list: its error takes the whole width. */
  .sp-body:not(:has(> .sp-side)) > .sp-fold { grid-column: 1 / -1; }
}
/* A semester without a module (div.sp-empty, head.rs NothingPlanned): „Noch keine Termine" and the
   ways to modules in a row under it. In the week a card in the middle of the empty days (owner,
   2026-09-25: „in der Mitte"); in „Termine" and „Prüfungen" alone in the middle of the view. */
.sp-empty { display: grid; justify-items: center; gap: 14px; margin: 48px auto; padding: 22px 26px; max-width: 100%; text-align: center; }
.sp-empty .state-title { font-size: 17px; font-weight: 650; }
.sp-view > .sp-empty { margin: auto; }
.sp-week > .sp-empty { position: absolute; top: calc(50% + 14px); left: calc(50% + 15px); width: max-content; max-width: calc(100% - 64px); margin: 0; transform: translate(-50%, -50%); border-radius: 14px; background: var(--panel); box-shadow: var(--shadow-pop); }
/* The server's explanation: what is missing, the ways on in a row under it. */
.sp-body > .state { margin: 64px auto; }
.sp-body > .state p { text-wrap: balance; }
.state-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
/* The head: „Stundenplan WiSe 2026/27" (h1, the semester in its span), on a phone „Anpassen" at
   the right, and „A-Woche · B-Woche · A/B" where it applies, which the head is always as tall as:
   the week does not move when it comes. Under it the program and the numbers: „Informatik B.Sc.
   · 5 Module" (p.sp-sum). The import's head (h2) has ‹ › around it. */
.sp-head { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; min-height: 36px; margin-left: -8px; }
.sp-head h2 { font-size: 21px; font-weight: 700; letter-spacing: -.025em; line-height: 1.2; padding: 0 4px; }
.sp-head h1 { margin: 0; padding-left: 8px; font-size: 21px; font-weight: 700; letter-spacing: -.025em; line-height: 1.2; }
.sp-head h1 span { color: var(--text-3); font-weight: 600; }
.sp-head .icon-btn { width: 34px; height: 34px; --hit: 3px; }
.sp-head .icon-btn[aria-disabled="true"] { opacity: .35; pointer-events: none; }
.sp-head .sheet-toggle { margin-left: auto; }
.sp-head .sp-weeks, .sp-head .sp-all { margin-left: auto; }
.sp-head .sp-all + .sp-weeks { margin-left: 6px; }
.sp-sum { margin-top: -8px; color: var(--text-2); font-size: 13.5px; font-variant-numeric: tabular-nums; }
.sp-sum small { color: var(--text-3); font-size: 12.5px; }
/* The planned modules (section.sp-list, modules.rs), as the catalog's list and the Merkliste have
   them (owner, 2026-09-25): a head („Module" and the credits of the planned modules at the right,
   above theirs), then a row per module (div.sp-row-wrap > a.sp-row.t-… > span.t > b + small,
   span.lp; and button.sp-remove), a link to the module beside the plan, the open one marked as the
   catalog marks it. Under the title the key the week names the module by, in its tone as a slot
   of the week (span.sp-key, empty where the week says its name), its number and what else is to
   know; its credits at the right, left of the ×. The placeholders no module fills are dashed
   (a.open-slot), and so is „Modul hinzufügen" at the end (a.sp-add). */
.sp-list { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1px; align-content: start; }
.sp-list-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 30px; padding: 0 42px 0 12px; margin-bottom: 3px; }
.sp-list-head .lp { font-weight: 700; font-size: 15px; white-space: nowrap; letter-spacing: -.01em; }
.sp-list-head .lp small { margin-left: 2px; color: var(--text-3); font-weight: 500; font-size: 11px; }
.sp-row { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; column-gap: 12px; min-height: 50px; padding: 6px 42px 6px 12px; border-radius: var(--r-row); color: inherit; text-decoration: none; transition: background .1s; }
.sp-row::before { content: ""; position: absolute; left: 3px; top: 12px; bottom: 12px; width: 3px; border-radius: 3px; background: transparent; }
/* Every other module is shaded, as the rows of the catalog's list (`--row-shade`): the list's own
   modules, and anew those in each placeholder's box, a module taken out keeping its place in the
   count; not the dashed rows, which hold none. The shade of a row with its × lies on the row's
   wrap, so the row pointed at and the open one lie above it; a module of the row's other semester
   is a row of its own, shaded before the rules that stand above it. */
.sp-row-wrap:where(:nth-child(even of .sp-row-wrap, .sp-row.other)) { border-radius: var(--r-row); background: var(--row-shade); }
.sp-row.other:where(:nth-child(even of .sp-row-wrap, .sp-row.other)) { background: var(--row-shade); }
.sp-row:hover { background: var(--row-hover); text-decoration: none; }
.sp-row[aria-current="true"] { background: var(--panel-3); }
.sp-row[aria-current="true"]::before { background: var(--accent); }
.sp-row .t { min-width: 0; }
.sp-row .t b { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; font-weight: 600; font-size: 13.5px; line-height: 1.3; letter-spacing: -.01em; }
.sp-row .t small { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 8px; margin-top: 3px; color: var(--text-3); font-size: 12px; }
.sp-row .lp { font-weight: 650; font-size: 14px; white-space: nowrap; }
.sp-row .lp small { margin-left: 2px; color: var(--text-3); font-weight: 500; font-size: 11px; }
.sp-key { flex: none; display: inline-flex; align-items: center; min-width: 14px; height: 18px; padding: 0 6px 0 8px; border-radius: 4px; background: color-mix(in oklch, var(--tint, var(--text-3)) 16%, var(--panel)); box-shadow: inset 2.5px 0 0 var(--tint, var(--text-3)); color: var(--text); font-weight: 650; font-size: 11.5px; font-style: normal; letter-spacing: 0; }
i.sp-key { width: 14px; padding: 0; }
.sp-row.open-slot { outline: 1px dashed var(--line-strong); outline-offset: -1px; }
.sp-row.open-slot .t b { color: var(--text-2); font-weight: 560; }
.sp-row.open-slot .lp { color: var(--text-2); font-weight: 600; }
.sp-row.sp-add { min-height: 44px; margin-top: 2px; }
.sp-row.sp-add .t b { display: flex; align-items: center; gap: 8px; }
.sp-row.sp-add .icon { width: 16px; height: 16px; color: var(--text-3); }
.sp-row.sp-add:hover .t b, .sp-row.sp-add:hover .icon { color: var(--text); }
/* A placeholder a module counts for (div.sp-area, owner 2026-09-26: „sehen, dass das ein Bereich
   ist, wo ein Modul ausgewählt ist"): the dashed line of the open one around its head (the row's
   name, what it asks for at the right, under it the Fachsemester of a row over several and what
   counts for it so far), its modules as rows of the list, those of its row's other semesters
   quiet (a.sp-row.other), and „Weiteres Modul" while the row takes more (a.sp-area-add). */
.sp-area { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1px; margin: 2px 0; padding: 0 3px 3px; border-radius: var(--r-row); outline: 1px dashed var(--line-strong); outline-offset: -1px; }
.sp-area-head { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; column-gap: 12px; min-height: 40px; padding: 7px 42px 2px 12px; }
.sp-area-head .t { min-width: 0; }
.sp-area-head .t b { display: block; color: var(--text-2); font-weight: 560; font-size: 13px; line-height: 1.3; letter-spacing: -.01em; }
.sp-area-head .t small { display: block; margin-top: 2px; color: var(--text-3); font-size: 12px; }
.sp-area-head .lp { color: var(--text-2); font-weight: 600; font-size: 14px; white-space: nowrap; }
.sp-area-head .lp small { margin-left: 2px; color: var(--text-3); font-weight: 500; font-size: 11px; }
.sp-row.other .t b { color: var(--text-2); font-weight: 560; }
.sp-row.other .lp { color: var(--text-2); font-weight: 600; }
.sp-area-add { display: flex; align-items: center; gap: 8px; min-height: 36px; padding: 4px 12px; border-radius: var(--r-row); color: var(--text-2); font-weight: 560; font-size: 13px; text-decoration: none; transition: background .1s; }
.sp-area-add:hover { background: var(--panel-2); color: var(--text); text-decoration: none; }
.sp-area-add .icon { width: 15px; height: 15px; color: var(--text-3); }
.sp-area-add:hover .icon { color: var(--text); }
/* The × of a row (owner, 2026-09-25: „gleich da auch wieder raus nehmen"): beside the row's link,
   not inside it, at its right end as the mark of the Merkliste's rows, quiet until the row is
   pointed at; a finger has it at once. The row taken out stays in its place, its title struck,
   with „Rückgängig" (div.sp-row.gone). */
.sp-row-wrap { position: relative; }
.sp-remove { --hit-y: 9px; --hit-l: 4px; --hit-r: 4px; position: absolute; right: 4px; top: 50%; width: 30px; height: 30px; margin-top: -15px; border-radius: var(--r-row); color: var(--line-strong); }
.sp-remove .icon { width: 15px; height: 15px; }
.sp-row-wrap:hover > .sp-remove, .sp-row-wrap:focus-within > .sp-remove { color: var(--text-3); }
.sp-remove:hover { background: var(--line); color: var(--text); }
@media (hover: none) { .sp-remove { color: var(--text-3); } }
.sp-row.gone { padding-right: 6px; }
.sp-row.gone:hover { background: none; }
.sp-row.gone .t b { color: var(--text-3); font-weight: 560; text-decoration: line-through; text-decoration-color: var(--line-strong); }
/* „Aus der Merkliste (n)": closed until asked, a line per module with its „Einplanen"; after the
   empty week's „übernehmen" its note with „Rückgängig" (p.sp-marked-note). */
.sp-marked-note { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 4px 9px; margin: 0; padding: 4px 4px 4px 10px; color: var(--text-2); }
.sp-marked > summary { cursor: pointer; min-height: 28px; display: inline-flex; align-items: center; gap: 6px; color: var(--text-2); font-weight: 580; font-size: 13px; }
.sp-marked > summary::after { content: "+"; color: var(--text-3); font-weight: 500; }
.sp-marked[open] > summary::after { content: "–"; }
.sp-marked ul { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2px; margin-top: 6px; }
.sp-marked li { display: flex; align-items: center; gap: 10px; min-height: 36px; padding: 0 4px 0 10px; border-radius: var(--r-s); font-size: 13px; }
.sp-marked li:where(:nth-child(even)) { background: var(--row-shade); }
.sp-marked li:hover { background: var(--row-hover); }
.sp-marked li .mini { margin-left: auto; }
/* The notes of a semester: warnings (p.note, a triangle) first, then quiet lines; each a link to
   the module beside the plan. At most four, „+2 weitere" (button.mini) shows the rest in place. */
.sp-notes { display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; justify-items: start; }
.sp-notes .note { margin: 0; width: 100%; align-items: flex-start; }
.sp-notes a.note { text-decoration: none; }
.sp-notes a.note:hover { text-decoration: underline; text-underline-offset: 3px; }
.sp-notes .note.quiet { color: var(--text-2); }
/* Exams that collide (div.sp-alerts > a.sp-alert > svg + span > b + small), red and large, directly
   under the modules in every view: what collides, then when. */
.sp-alerts { display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; }
.sp-alert { display: flex; align-items: flex-start; gap: 10px; padding: 10px 14px; border-radius: var(--r-row); background: var(--no-soft); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--no) 40%, transparent); color: var(--no); text-decoration: none; }
.sp-alert:hover { text-decoration: none; filter: brightness(.98); }
.sp-alert .icon { flex: none; width: 18px; height: 18px; margin-top: 1px; }
.sp-alert b { display: block; font-size: 14.5px; font-weight: 680; line-height: 1.35; }
.sp-alert small { display: block; margin-top: 2px; color: var(--text-2); font-size: 12.5px; font-variant-numeric: tabular-nums; }
/* Overlaps and open choices: one quiet line, closed until asked (details.sp-overlaps > summary
   > span, span.no an overlap in red); opened, a compact line each (ul > li > a, a.warn with a red
   mark), each a link to its module beside the plan. */
.sp-overlaps > summary { cursor: pointer; min-height: 28px; display: inline-flex; align-items: center; flex-wrap: wrap; gap: 0 5px; color: var(--text-2); font-weight: 580; font-size: 13px; font-variant-numeric: tabular-nums; }
.sp-overlaps > summary .no { color: var(--no); font-weight: 650; }
.sp-overlaps > summary::after { content: "+"; color: var(--text-3); font-weight: 500; }
.sp-overlaps[open] > summary::after { content: "–"; }
.sp-overlaps ul { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1px; margin-top: 4px; }
.sp-overlaps li > a, .sp-overlaps li > span { display: block; padding: 4px 10px 4px 20px; border-radius: var(--r-s); color: var(--text-2); font-size: 13px; text-decoration: none; position: relative; font-variant-numeric: tabular-nums; }
.sp-overlaps li > a:hover { background: var(--panel-2); text-decoration: none; }
.sp-overlaps li > .warn { color: var(--text); }
.sp-overlaps li > .warn::before { content: ""; position: absolute; left: 8px; top: calc(4px + .65em - 3px); width: 6px; height: 6px; border-radius: 50%; background: var(--no); }
/* What is hidden (li.hidden > span + button.mini): the line, its „Einblenden" at its end. */
.sp-overlaps li.hidden { display: flex; align-items: center; gap: 10px; min-height: 32px; padding: 2px 4px 2px 20px; color: var(--text-2); font-size: 13px; }
.sp-overlaps li.hidden > span { flex: 1; min-width: 0; }
/* „Plan · Alle Termine" and „A-Woche · B-Woche · A/B" above the week: as wide as their words. */
.seg.sp-weeks, .seg.sp-all { justify-self: start; margin-top: 0; }
.seg.sp-weeks button, .seg.sp-all button { padding: 0 12px; }
/* A slot that overlaps another in the week shown: a red frame and a red mark at its top right. */
.sp-body .slot.clash { box-shadow: inset 3px 0 0 var(--no), inset 0 0 0 1.5px var(--no); }
.sp-body .slot.clash::after { content: "!"; position: absolute; top: 3px; right: 3px; width: 13px; height: 13px; border-radius: 50%; background: var(--no); color: var(--panel); font-size: 10px; font-weight: 800; line-height: 13px; text-align: center; }
/* What the plan leaves out, in „Alle Termine" (.slot.faint): pale and dashed in its tone; where it
   would meet a Termin of the plan, dashed red with the mark (.clash). */
.sp-body .slot.faint { background: color-mix(in oklch, var(--tint) 6%, var(--panel)); color: var(--text-3); font-weight: 560; box-shadow: none; outline: 1px dashed color-mix(in oklch, var(--tint) 60%, var(--line-strong)); outline-offset: -1px; }
.sp-body .slot.faint.clash { box-shadow: none; outline-color: var(--no); }
.sp-body .slot.faint.clash::after { opacity: .7; }
/* What the page derived, under the calendar: small and muted. */
.sp-body > .sp-derived { margin-top: 0; font-size: 12px; }
/* Where the plan has no fixed time: „Ohne feste Zeit" (h3.label) and its lines. */
.sp-body h3.label { margin-top: 4px; }
.sp-loose { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2px; font-size: 13px; color: var(--text-2); }

/* The Regelwoche in the plan: a slot is a link to its module beside the plan (a.slot.tinted.t-…),
   an option of a choice not made is dashed (.alt). Single dates keep their tone quietly. */
/* A slot of the plan in three lines (owner, 2026-09-25): type and key, time and week (small),
   building and room (small.p), the two small ones at 10px. What gives way in a short slot, by the
   height of its content (the slot is its own container): the label's second line where it would
   push the third out (27.5 + 2 x 12.5px), the third line where three do not fit (13.75 + 25px),
   and in a slot shorter still the second one. */
.sp-body .slot { container-type: size; }
.sp-body .slot small { font-size: 10px; }
@container (max-height: 52px) { .slot > .l { -webkit-line-clamp: 1; line-clamp: 1; max-height: 1.25em; } }
@container (max-height: 38.5px) { .slot > small.p { display: none; } }
@container (max-height: 26px) { .slot > small { display: none; } }
.slot.once[class*=" t-"] { background: color-mix(in oklch, var(--tint) 7%, var(--panel)); color: var(--text); box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--tint) 45%, transparent); }
a.slot { cursor: pointer; transition: filter .12s; }
a.slot:hover { filter: brightness(.96); }
/* On a phone the week's grid has its days as a list under it, closed until its line opens it
   (button.sp-days-toggle[aria-expanded] > span.num, owner 2026-09-27): quiet like the line of
   overlaps, „+" while closed, „–" while open, the whole line a finger's target. */
.sp-days-toggle { display: flex; align-items: center; gap: 5px; width: 100%; min-height: 44px; margin: -4px 0; color: var(--text-2); font-weight: 580; font-size: 13.5px; text-align: left; }
.sp-days-toggle:hover { color: var(--text); }
.sp-days-toggle .num { color: var(--text-3); font-weight: 500; font-variant-numeric: tabular-nums; }
.sp-days-toggle::after { content: "+"; color: var(--text-3); font-weight: 500; }
.sp-days-toggle[aria-expanded="true"]::after { content: "–"; }
/* The list of days (div.sp-daylist > section > h3.label + a …): the time, then what it is; the
   tone as a bar on the left, a choice not made dashed, a clash red. */
.sp-daylist { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; }
.sp-daylist section { display: grid; grid-template-columns: minmax(0, 1fr); gap: 4px; }
.sp-daylist a { min-height: 44px; display: flex; align-items: center; gap: 12px; padding: 8px 12px; border-radius: var(--r-row); background: var(--panel-2); box-shadow: inset 3px 0 0 var(--tint, var(--text-3)); color: var(--text); font-size: 14px; text-decoration: none; }
.sp-daylist a .t { flex: none; min-width: 92px; color: var(--text-2); font-size: 13px; font-variant-numeric: tabular-nums; }
.sp-daylist a small { display: block; color: var(--text-3); font-size: 12.5px; }
/* The text gives way (and breaks a long word with a hyphen) rather than run under the buttons. */
.sp-daylist a > span:not(.t) { min-width: 0; }
.sp-daylist a.alt { background: transparent; box-shadow: inset 3px 0 0 var(--tint, var(--text-3)), inset 0 0 0 1px var(--line-strong); }
.sp-daylist a.clash { background: var(--no-soft); box-shadow: inset 3px 0 0 var(--no); }
.sp-daylist a[aria-current="true"] { outline: 2px solid var(--accent); outline-offset: -2px; }
.sp-daylist a.faint { background: transparent; box-shadow: inset 3px 0 0 color-mix(in oklch, var(--tint, var(--text-3)) 45%, transparent), inset 0 0 0 1px var(--line); color: var(--text-2); }
.sp-daylist a.faint.clash { box-shadow: inset 3px 0 0 var(--no), inset 0 0 0 1px var(--line); }
/* A row's buttons (div.sp-dayrow > a + div.slot-acts, --acts of them) at its right end, each as
   tall as a finger. */
.sp-dayrow { position: relative; }
.sp-dayrow > a { padding-right: calc(8px + var(--acts, 0) * 42px); }
.sp-dayrow > .slot-acts { left: auto; right: 4px; top: 50%; width: auto; height: auto; padding: 0; gap: 2px; transform: translateY(-50%); opacity: 1; }
.sp-dayrow .slot-act { width: 40px; height: 40px; border-radius: var(--r-s); background: transparent; box-shadow: none; }
.sp-dayrow .slot-act[data-icon="x"] { color: var(--text-3); }
.sp-dayrow .slot-act[aria-pressed="true"] { background: var(--ok); color: var(--panel); }
.sp-dayrow .slot-act .icon { width: 18px; height: 18px; }

/* „Termine": a week a section (section.agenda-week#kw-<year>-<week> > h3 + .agenda-day …), a day a
   row: the day on the left (.when), its items on the right (.agenda-item > i + span.t + span, the
   time, then what and where); a cancelled item strikes its span through, and says why in a small
   beside it (a sibling, so the reason is not struck). Off-screen weeks are not laid out (R18:
   about 450 items a semester). */
.agenda { display: grid; grid-template-columns: minmax(0, 1fr); gap: 18px; }
.agenda-week { content-visibility: auto; contain-intrinsic-size: auto 240px; scroll-margin-top: 12px; }
.agenda-week > h3 { margin-bottom: 4px; color: var(--text-2); font-size: 12.5px; font-weight: 650; font-variant-numeric: tabular-nums; }
.agenda-week > h3 small { color: var(--text-3); font-weight: 500; font-size: 12px; }
.agenda-day { display: grid; grid-template-columns: 76px minmax(0, 1fr); gap: 2px 12px; padding: 6px 0; border-top: 1px solid var(--line); }
.agenda-day > .when { padding-top: 1px; color: var(--text-3); font-size: 12.5px; font-variant-numeric: tabular-nums; }
.agenda-day.today > .when { color: var(--accent-ink); font-weight: 650; }
.agenda-day > div { display: grid; grid-template-columns: minmax(0, 1fr); gap: 3px; min-width: 0; }
.agenda-day .holiday { color: var(--text-3); font-size: 12.5px; }
.agenda-item { display: flex; align-items: baseline; gap: 10px; min-width: 0; font-size: 13px; color: var(--text); text-decoration: none; }
a.agenda-item:hover { text-decoration: none; color: var(--text); }
a.agenda-item:hover > span:not(.t) { text-decoration: underline; text-underline-offset: 3px; }
.agenda-item > i { flex: none; align-self: center; width: 8px; height: 8px; border-radius: 2px; background: var(--tint, var(--text-3)); }
.agenda-item .t { flex: none; min-width: 84px; color: var(--text-2); font-size: 12.5px; font-variant-numeric: tabular-nums; }
.agenda-item > span:not(.t) { min-width: 0; }
.agenda-item small { color: var(--text-3); font-size: 12px; }
.agenda-item > small { flex: none; max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* An exam of the agenda: bold. The catalog row's `.exam` (one line, clipped, muted) is another
   thing of the same name, so its rule is undone here. */
.agenda-item.exam { font-weight: 650; font-size: 13px; color: var(--text); white-space: normal; overflow: visible; }
.agenda-item.exam .icon { width: 14px; height: 14px; align-self: center; color: var(--warn); }
.agenda-item.alt { color: var(--text-2); }
.agenda-item.cancelled > span:not(.t) { color: var(--text-3); text-decoration: line-through; }
.agenda-break { padding: 8px 12px; border-radius: var(--r-row); background: var(--panel-2); color: var(--text-3); font-size: 12.5px; scroll-margin-top: 12px; }

/* „Prüfungen": a sitting a row (.sp-exam: .when, what and where, the eye), „2. Termin" small;
   a hidden sitting steps back ([data-hidden]). */
.sp-exams { display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; }
.sp-exam { display: grid; grid-template-columns: 160px minmax(0, 1fr) auto; gap: 2px 12px; align-items: center; min-height: 48px; padding: 6px 6px 6px 12px; border-radius: var(--r-row); background: var(--panel-2); font-size: 13px; }
.sp-exam .when { font-weight: 620; font-variant-numeric: tabular-nums; }
.sp-exam small { display: block; color: var(--text-3); font-weight: 450; font-size: 12px; }
.sp-exam[data-hidden] { opacity: .5; }

/* The module beside the plan (PlanModulePanel, section.panel.detail.aside): its events
   (.sp-event > h3 + .sp-rowline …), each Termin a line with its eye, the one the address points
   at current; an open choice under „1 von 4 wählen" (.sp-choice); what is hidden dimmed. */
.sp-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 12px; }
.sp-actions select { min-height: 32px; max-width: 100%; padding: 0 8px; border: 0; border-radius: var(--r-s); background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line-strong); font-size: 13px; }
.sp-event { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2px; padding-top: 12px; margin-top: 12px; border-top: 1px solid var(--line); }
/* The first event stands right under the head, whose rule is enough. */
.dbody > .sp-event:first-child { border-top: 0; margin-top: 0; }
.sp-event > h3 { display: flex; align-items: center; gap: 6px; min-height: 32px; font-size: 13.5px; font-weight: 640; }
.sp-event > h3 > span { min-width: 0; flex: 1; }
.sp-event > h3 > a { flex: none; font-weight: 520; font-size: 12.5px; color: var(--text-3); }
.sp-event > h3 > a:hover { color: var(--text); }
.sp-choice { display: flex; align-items: center; gap: 8px; min-height: 30px; padding-left: 8px; color: var(--text-3); font-size: 12.5px; font-weight: 560; }
.sp-choice .mini { margin-left: auto; }
.sp-rowline { display: flex; align-items: center; gap: 6px; min-height: 34px; padding: 2px 2px 2px 10px; border-radius: var(--r-s); color: var(--text-2); font-size: 13px; font-variant-numeric: tabular-nums; }
.sp-rowline > .rl { flex: 1; min-width: 0; }
/* A Termin in two lines: day and time bold, the room and the weeks legible; how many, the campus
   and first–last quieter under it. */
.sp-rowline b { color: var(--text); font-weight: 650; }
.sp-rowline small { color: var(--text-3); font-size: 12px; }
/* Every other Termin of an event is shaded, as the rows of the catalog's list; the current one
   stands above it. */
.sp-rowline:where(:nth-child(even of .sp-rowline)) { background: var(--row-shade); }
.sp-rowline[aria-current="true"] { background: var(--panel-3); color: var(--text); box-shadow: inset 2.5px 0 0 var(--accent); }
.sp-rowline[data-hidden], .sp-rowline[data-hidden] b { color: var(--text-3); }
.sp-rowline[data-hidden] > .rl { text-decoration: line-through; text-decoration-color: var(--line-strong); }
/* Only the first line (when) is struck; the line under it (how many, where) is an inline block,
   which a line-through does not reach. */
.sp-rowline .rt > small { display: inline-block; }
.sp-rowline .rt > small > span { white-space: nowrap; }
/* An option of a choice is its own line's choice: a radio mark at the left, filled when chosen;
   the options not chosen step back (not struck: they are not hidden, only not chosen). */
.sp-rowline > .pick { display: flex; align-items: flex-start; gap: 8px; padding: 3px 0; border-radius: var(--r-s); text-align: left; }
.sp-rowline > .pick:disabled { cursor: default; }
.sp-rowline > .pick > .rt { min-width: 0; }
.radio { flex: none; width: 14px; height: 14px; margin-top: 2px; border-radius: 50%; box-shadow: inset 0 0 0 1.5px var(--text-3); }
.pick:not(:disabled):hover .radio { box-shadow: inset 0 0 0 1.5px var(--text); }
.pick[aria-pressed="true"] .radio { box-shadow: inset 0 0 0 1.5px var(--accent), inset 0 0 0 4px var(--panel), inset 0 0 0 8px var(--accent); }
.pick:disabled .radio { opacity: .5; }
.sp-rowline[data-off], .sp-rowline[data-off] b { color: var(--text-3); }
.sp-option b { font-weight: 620; }
/* An event hidden for any reason (the eye, a kind, the Standort) steps back as its Termine do. */
.sp-event[data-hidden] > h3 { color: var(--text-3); }
.sp-rowline .clash { color: var(--no); font-weight: 600; }
/* The eye: hide or show an event, a Termin, a sitting (aria-pressed: hidden). */
.eye { --hit: 3px; flex: none; width: 30px; height: 30px; display: grid; place-items: center; border-radius: var(--r-s); color: var(--text-3); transition: background .12s, color .12s; }
.eye:hover { background: var(--panel-3); color: var(--text); }
.eye .icon { width: 16px; height: 16px; }
.eye[aria-pressed="true"] { color: var(--text); }

/* A module's title in a row of „Prüfungen" reads as text, as in the legend. */
.sp-exam a { color: var(--text); text-decoration: none; }
.sp-exam a:hover { text-decoration: underline; text-underline-offset: 3px; }
/* The Übersicht's head holds its h2 alone, without the arrows the head's margin is for. */
.sp-head > h2:first-child { padding-left: 8px; }
.btn[aria-busy="true"] { opacity: .7; cursor: progress; }
.btn[disabled] { opacity: .5; cursor: default; filter: none; }
/* A note among the sittings keeps to their gap. */
.sp-exams .note { margin-top: 0; }

/* The Stundenplan's sidebar (side.rs, import.rs): the program picker with „Studium planen →"
   under it; „Importieren" a source switch, the plan's select where it has several, then the
   Fachsemester and „Übernehmen" on one line, what it adds under them; „Plan" the name field of
   „Plan speichern" and one line per saved plan (name, modules, ×), the loaded one marked. */
.sp-program .combo { width: 100%; }
.sp-more { display: inline-block; margin: 6px 2px 0; font-size: 13px; font-weight: 560; }
.sp-import > .select-wrap { margin-top: 8px; }
.seg button small { margin-left: 5px; color: var(--text-3); font-size: 11px; font-weight: 500; }
.sp-import-go { display: flex; gap: 8px; margin-top: 8px; }
.sp-import-go > .select-wrap { flex: 1; min-width: 0; }
.sp-import-go > .btn { flex: none; min-height: 36px; }
.sp-import > .hint, .sp-import > .note-action { margin-top: 6px; }
.sp-save { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 4px 0 6px; }
.sp-save input { flex: 1 1 100%; min-width: 0; min-height: 36px; padding: 0 12px; border: 0; border-radius: var(--r-ctl); background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line-strong); font-size: 13px; }
.sp-saved { display: flex; align-items: center; gap: 2px; }
.sp-saved > .action { flex: 1; min-width: 0; }
.sp-saved > .action > span { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sp-saved > .action > small { display: inline; flex: none; }
.sp-saved > .action[aria-current="true"] { background: var(--panel-3); box-shadow: inset 2.5px 0 0 var(--accent); }
.sp-saved > .icon-btn { flex: none; width: 32px; height: 32px; color: var(--text-3); }
.sp-saved > .icon-btn:hover { color: var(--text); }

/* A list that opens under an action: the four ways of „Abonnieren" in the sidebar's „Kalender",
   and the semesters of „Anderes Semester" in a module page's sidebar. */
.sp-sub { display: grid; grid-template-columns: minmax(0, 1fr); gap: 2px; margin: 2px 0 4px 17px; padding-left: 8px; border-left: 2px solid var(--line); }
.sp-sub .hint { margin: 4px 0 2px 10px; }
.sidebar .note { margin-top: 6px; }
/* On a phone „Kalender" stands under the Termine (section.sp-export > div.fgroup.actions, the
   sidebar's group, export.rs), set off from them by a rule: its two ways as framed buttons across
   the page, what „Abonnieren" opens under them as in the sidebar. */
.sp-export { padding-top: 14px; border-top: 1px solid var(--line); }
.sp-export > .fgroup.actions { gap: 6px; padding: 0; }
.sp-export > .fgroup > .flabel { margin-bottom: 2px; }
.sp-export > .fgroup > a.action, .sp-export > .fgroup > button.action { box-shadow: inset 0 0 0 1px var(--line-strong); }
.sp-export > .fgroup > button.action[aria-expanded="true"] { background: var(--panel-2); }
.sp-export .note { margin: 0; }
/* „.ics herunterladen" without a file: an action that does nothing looks like none. */
.action[aria-disabled="true"], .action[aria-disabled="true"] .icon { color: var(--text-3); cursor: default; }
.action[aria-disabled="true"]:hover { background: none; }
/* A button in a note („Neue Adresse kopieren", „Alle einblenden") is a button, not warning text. */
.note .mini { color: var(--text); }
/* „Zeigen" without a Termin to show: the line in the chips' place. */
.sidebar .sp-none { margin-top: 0; }

/* „Einplanen" on a module's page and „Mein Studiengang" on a program's: part of server HTML (R9,
   unpressed), kept in place but not shown until the app runs, and gone without JavaScript (R15) —
   the pattern of .mark-toggle, so nothing moves at the takeover. */
.plan-toggle, .mine-toggle { visibility: hidden; }
.app .plan-toggle, .app .mine-toggle { visibility: visible; }
html:not(.js) .plan-toggle, html:not(.js) .mine-toggle { display: none; }
.plan-toggle[aria-busy="true"] { cursor: progress; }
/* The line under „Einplanen" among a sidebar's actions (its semester, the placeholder, the other
   semesters) ends in „…" where it is longer than the sidebar is wide; it never widens the list. */
.actions > .plan-toggle { min-width: 0; }
.mine-toggle[aria-pressed="true"] .icon { fill: currentColor; color: var(--text); }
/* In a sidebar of 248px „Als meinen Studiengang setzen" takes two lines, pressed „Mein
   Studiengang" one. Two lines this close fit the action's height, so the button keeps it in both
   and the actions below do not move when the app shows the pressed one (R15). */
.action.mine-toggle > span { line-height: 1.3; }

/* ---------- closed testing: the login page ----------
   A document of its own (server/src/access.rs): no rail, no app, one column in the middle of the
   screen. The lockup stands on the page as in the app (on a panel the light mark would vanish),
   mark and panel share the left edge; the field is 16px so phones do not zoom in. */
/* `display` again, because phones turn the body of the app into a column; more room below than
   above, so the column sits a little over the middle, where it looks centred. */
body.gate { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: none; place-items: center; padding: 24px 16px 12vh; }
.gate-main { width: min(100%, 400px); }
.gate-panel { padding: 26px 28px 28px; border-radius: var(--r-panel); background: var(--panel); box-shadow: var(--shadow-panel); }
.gate-brand { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
.gate-brand .logo { margin: 0; }
.gate-brand > span:last-child { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
.gate-brand small { color: var(--text-3); font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gate-panel h1 { font-size: 20px; font-weight: 650; letter-spacing: -.015em; margin-bottom: 6px; }
.gate-panel > p { color: var(--text-2); margin-bottom: 22px; }
.gate-panel label { display: block; color: var(--text-2); font-size: 12px; font-weight: 550; margin-bottom: 5px; }
.gate-panel input[type="password"] { width: 100%; height: 48px; padding: 0 14px; border: 0; outline: 0; border-radius: var(--r-ctl); background: var(--panel-2); box-shadow: inset 0 0 0 1px var(--line-strong); font-size: 16px; }
.gate-panel input[type="password"]:focus { box-shadow: 0 0 0 2px var(--accent); }
.gate-panel input[aria-invalid="true"]:not(:focus) { box-shadow: inset 0 0 0 1px var(--no); }
.gate-problem { margin-top: 8px; padding: 9px 12px; border-radius: var(--r-ctl); background: var(--no-soft); color: var(--no); font-size: 13px; font-weight: 550; }
.gate-panel .button { width: 100%; min-height: 48px; margin-top: 14px; justify-content: center; font-size: 15px; }

/* ---------- waiting: the frame of what comes (app/src/pending.rs, app/src/skeleton.rs) ----------
   A click answers in the next frame; what still has to be computed stands there as a skeleton, in
   the frame of what comes (the layout classes of the page, so every panel is where the page will
   put its own): grey bars where the text will be. A band of light sweeps over it once the wait is
   long enough to be noticed; the compositor moves it, so it keeps moving while the page is built. */
.pending-page { position: absolute; inset: 0; z-index: 30; background: var(--bg); }
.pending-page > .work { height: 100%; }
/* Another view of the same page: its column waits, the sidebar stays as it is and takes clicks. */
.pending-page.columns { background: none; pointer-events: none; }
.pending-page.columns > .work > .sidebar { visibility: hidden; }
.pending-page.columns > .work > .page { background: var(--bg); pointer-events: auto; }
.pending-page.columns > .work > .aside { pointer-events: auto; }
.sk { display: block; flex: none; height: 10px; width: 60%; border-radius: 5px; background: var(--sk); }
.sk.sk-tall { height: 14px; }
.sk.sk-big { height: 24px; border-radius: 7px; }
.sk.sk-w1 { width: 36px; }
.sk.sk-w2 { width: 24%; }
.sk.sk-w3 { width: 38%; }
.sk.sk-w4 { width: 56%; }
.sk.sk-w5 { width: 74%; }
.sk.sk-w6 { width: 92%; }
.sk.sk-count { width: 58px; height: 22px; border-radius: 7px; }
.sk.sk-chip { width: 76px; height: 32px; border-radius: var(--r-ctl); }
.sk.sk-pill { width: 92px; height: 32px; border-radius: var(--r-s); }
.sk.sk-wide-pill { width: 260px; height: 28px; border-radius: 14px; }
.sk.sk-badge { width: 56px; height: 26px; border-radius: var(--r-s); }
.sk.sk-code { width: 56px; height: 24px; border-radius: var(--r-s); }
.sk.sk-button { width: 190px; height: 46px; border-radius: var(--r-ctl); }
.sk.sk-fact { width: auto; height: 56px; border-radius: var(--r-ctl); }
.sk.sk-week { width: auto; height: 250px; border-radius: var(--r-ctl); }
.sk.sk-week-tall { height: 420px; }
.sk.sk-figure { width: 110px; height: 34px; border-radius: 8px; }
.sk.sk-tools { width: 250px; margin-left: auto; }
.sk-frame .panel-head { min-height: 50px; }
.sk-lines { display: grid; gap: 12px; padding: 4px 0; }
.sk-toc { gap: 20px; padding: 6px 8px; }
.sk-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 4px; }
.sk-block { padding: 22px 24px; display: grid; gap: 14px; align-content: start; }
.sk-head { padding: 18px 24px 16px; display: grid; gap: 12px; }
.sk-title-row { display: flex; align-items: center; gap: 24px; padding: 4px 0; }
.sk-fill { min-height: calc(100vh - 180px); }
/* The Stundenplan's: the head over the week, the modules beside them where the page is wide. */
.sk-plan-main { display: grid; gap: 14px; align-content: start; min-width: 0; }
@container page (min-width: 900px) { .sk-plan { grid-template-columns: minmax(0, 1fr) clamp(250px, 28%, 340px); column-gap: 24px; } }
.sk-stage { min-height: 480px; }
.sk-home { display: grid; grid-template-columns: minmax(0, 1fr) 290px; min-height: 285px; }
.sk-figures { display: grid; border-left: 1px solid var(--line); }
.sk-figures > div { display: flex; align-items: center; gap: 12px; padding: 0 16px; border-bottom: 1px solid var(--line); }
.sk-figures > div:last-child { border-bottom: 0; }
.sk-summary { display: flex; align-items: center; gap: 12px; min-height: 40px; padding: 4px 8px 2px 16px; }
.sk-faculty { padding: 16px 0 6px; }
.sk-faculty-head { display: flex; align-items: center; gap: 12px; padding: 0 24px 14px; }
.sk-subject { display: grid; grid-template-columns: minmax(0, 25fr) minmax(0, 38fr) minmax(0, 37fr) 176px; align-items: center; gap: 6px 20px; min-height: 46px; padding: 7px 24px; border-bottom: 1px solid var(--line); }
.sk-subject:last-child { border-bottom: 0; }
.sk-subject-head { min-height: 32px; border-top: 1px solid var(--line); background: var(--panel-2); }
.sk-table { display: grid; }
.sk-trow { display: grid; grid-template-columns: minmax(0, 1fr) 48px 48px; gap: 16px; align-items: center; min-height: 38px; border-bottom: 1px solid var(--line); }
.sk-facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; margin: 6px 0; }
.sk-module { display: grid; gap: var(--gap); container: module / inline-size; }
.sk-row .t { display: grid; gap: 8px; }
.sk-row:hover { background: none; }
.sk-row > span { display: flex; }
.sk-row > .lp { justify-content: flex-end; }
.sk-row > .lang { padding: 0; background: none; }
.sk-sweep { position: relative; overflow: hidden; }
.sk-sweep::after { content: ""; position: absolute; inset: 0; background: linear-gradient(100deg, transparent 25%, var(--sk-shine) 50%, transparent 75%); transform: translateX(-100%); animation: sk-sweep 1.5s ease-in-out .35s infinite; pointer-events: none; }
@keyframes sk-sweep { to { transform: translateX(100%); } }
/* The catalog's list while another filter is applied: skeleton rows over the old ones, at the top
   of what is visible of the list; the number waits, the tags of the old filter step back. */
.list[data-pending] .count { color: transparent; background: var(--sk); border-radius: 7px; }
.list[data-pending] .plan-note { opacity: .4; }
.list[data-pending] .rows { overflow: hidden; }
.rows-pending { position: sticky; top: var(--cols, 0px); z-index: 3; height: 0; }
.rows-pending-fill { position: absolute; top: 0; left: -8px; right: -8px; height: 100vh; padding: 0 8px; background: var(--panel); }
/* A module opened beside something that was already there comes without sliding in; neither does
   the module that replaces its skeleton (`data-settling`: the frame in which the page that was
   waited for comes). */
.work > .sk-detail.calm, [data-settling] .work > .detail { animation: none; }

/* ---------- phone and tablet ---------- */
@media (max-width: 1180px) and (min-width: 901px) {
  :root { --w-filters: 248px; }
  .top-end .pill:first-child { display: none; }
}
@media (max-width: 900px) {
  /* The bar at the top: 20 px above the search for the crown, 48 for the search, 10 below. */
  :root { --bar: 78px; }
  /* The view and the ground one under the other, at least as high as the window: a page shorter
     than the window leaves its room above the ground, which stays at the window's lower edge
     instead of floating halfway up the screen (see „the birch"). `dvh`: the window as it is now,
     with the browser's bars shown or hidden; where it is unknown, the base rule's 100 % stays. */
  body { display: flex; flex-direction: column; min-height: 100dvh; }
  .rail { display: none; }
  .main { position: static; display: block; flex-grow: 1; height: auto; margin: 0; padding: 0; }
  /* The crown carries the bar's frosted ground (see „the birch"), so the bar itself lets it through. */
  .topbar { position: sticky; top: 0; z-index: 21; height: auto; padding: 20px 12px 10px; }
  .crumb, .top-end, kbd { display: none; }
  .brand-phone { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
  .brand-phone .logo { margin: 0; }
  /* „inoffiziell" stays with the name, whole: where the line has no room for it beside the
     wordmark (the switch of the languages at its end, a longer language), it goes under it. */
  .brand-phone > span { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px; min-width: 0; }
  .brand-phone small { color: var(--text-3); font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* The switch between the languages at the end of the brand's line: a target of its own, framed so
     that it reads as a button where nothing hovers. */
  .brand-phone .languages { margin-left: auto; }
  .brand-phone .lang-switch { width: 44px; height: 44px; box-shadow: inset 0 0 0 1px var(--line); color: var(--text-2); }
  .search { height: 48px; }
  .content, .page { height: auto; overflow: visible; }
  .page { padding: 0 12px 12px; }
  .work, .work.no-detail { display: block; height: auto; }
  .work > .panel.list { border-radius: 0; box-shadow: none; background: transparent; overflow: visible; }
  .list-head { padding: 4px 16px 0; }
  .count { font-size: 22px; }
  .cols, .list-tools .ghost { display: none; }
  .sheet-toggle { display: inline-flex; min-height: 42px; padding: 0 14px; border-radius: var(--r-ctl); background: var(--panel); box-shadow: var(--shadow-panel); align-items: center; gap: 8px; font-weight: 600; color: var(--text); text-decoration: none; }
  .sheet-toggle em { font-style: normal; min-width: 20px; height: 20px; border-radius: 10px; background: var(--invert); color: var(--on-invert); font-size: 11.5px; display: grid; place-items: center; }
  .rows.scroll { overflow: visible; padding: 0 12px 12px; display: grid; gap: 8px; }
  /* The virtual list places its rows itself; the gap between the cards is part of every row. */
  .rows.virtual { display: block; }
  .vlist > .vrow { padding-bottom: 8px; }
  /* The note stands in line with the cards, as far above them in either list. */
  .plan-note { margin: 0 0 10px; }
  .rows.scroll:not(.virtual) > .plan-note { margin-bottom: 2px; }
  .app .keys { display: none; }
  .sem { top: var(--bar); background: var(--bg); padding: 12px 4px 4px; }
  .rows .row-wrap { contain-intrinsic-size: auto 84px; }
  .row { grid-template-columns: minmax(0, 1fr) auto; min-height: 72px; padding: 12px 52px 12px 18px; background: var(--panel); box-shadow: var(--shadow-panel); border-radius: var(--r-panel); }
  .row .t b { font-size: 15.5px; }
  .row > .resp, .row > .exam, .row > .turnus, .row > .lang, .row > .events { display: none; }
  .narrow-only { display: inline; }
  .filters { position: fixed; inset: auto 0 0 0; z-index: 40; max-height: 84vh; border-radius: 18px 18px 0 0; box-shadow: var(--shadow-pop); transform: translateY(105%); transition: transform .3s var(--ease); }
  .filters:target, .filters.open { transform: none; }
  .filters .panel-head::before { content: ""; position: absolute; left: 50%; top: 8px; width: 40px; height: 4px; margin-left: -20px; border-radius: 2px; background: var(--line-strong); }
  .filters .panel-head { position: relative; padding-top: 22px; }
  /* The sheet is closed the way one expects of a sheet, too: swiped down (anywhere its list is
     at the top; the head never scrolls, so the browser must not take a touch there), or with a
     tap beside it, on the page it dims. */
  .filters .panel-head, .sidebar.sheet .panel-head { touch-action: none; cursor: grab; }
  .filters.dragging, .sidebar.sheet.dragging { transition: none; }
  html.sheet-open::after { content: ""; position: fixed; inset: 0; z-index: 39; background: rgba(15, 23, 42, .34); }
  /* Open, a sheet holds the page behind it still: the page does not scroll (a touch on it moves
     nothing, enhance.js), and the sheet's list keeps its scroll to itself at either end. */
  html.sheet-open { overflow: hidden; overscroll-behavior: none; }
  .filters .body, .sidebar.sheet .body { overscroll-behavior: contain; }
  /* What was picked beside a program (a module, an area, a row of the plan) has no room beside
     the page on a phone: it is the page until it is closed. The browser app renders it as a page
     of its own; this is for the same HTML without it. */
  .framed.aside-picked > .page, .framed.aside-picked > .sidebar, .framed.aside-picked > .resizer { display: none; }
  .framed.with-aside.aside-picked > .detail.aside { margin: 0 12px 12px; }
  .sheet-close { display: grid; }
  .chip { min-height: 44px; padding: 0 14px 0 12px; font-size: 14.5px; border-radius: 8px; gap: 9px; }
  .box { width: 18px; height: 18px; border-radius: 5px; }
  .box .icon { width: 13px; height: 13px; }
  .seg a, .seg button { min-height: 40px; font-size: 14.5px; }
  .combo-trigger, .more .combo-trigger { min-height: 46px; font-size: 15px; }
  .combo-clear, .more .combo-clear { top: 9px; }
  /* The popup of a picker opens under its button and across the panel it stands in (owner,
     2026-09-29: „Komboboxen auf dem Handy sollen immer die volle Breite einnehmen (mit margin
     etc) aber sie sollen halt von ganz links bis nach ganz rechts"): out past its button to
     16 px from the panel's edges, a panel's padding on a phone, and never narrower than the
     button (`--pl`, `--pr`: how far in the picker stands, `nav::popup_place`). The names it
     lists never widen it: a long one pushed the popup and the picker's button out of the panel.
     Above what it covers, the line through the marks of the way in. */
  .combo-pop { position: relative; z-index: 1; inset: auto; width: auto; max-height: 46vh; margin: 6px min(0px, 16px - var(--pr, 16px)) 0 min(0px, 16px - var(--pl, 16px)); contain: inline-size; box-shadow: inset 0 0 0 1px var(--line-strong); animation: none; }
  .combo-option { min-height: 44px; align-items: center; font-size: 15px; }
  .combo-foot { display: none; }
  .credits { --knob: 24px; --thumb: 40px; }
  .slider::before { left: 8px; right: 8px; }
  .select-wrap select, .select-wrap.plain select, .field input, .range input { min-height: 46px; font-size: 15px; }
  details.fgroup > summary { min-height: 40px; }
  .framed { display: flex; flex-direction: column; }
  .framed > .page { padding: 0 12px 12px; }
  .sidebar { order: 2; margin: 0 12px 24px; }
  .framed.sidebar-first .sidebar { order: 0; margin: 0 12px 12px; }
  /* A sidebar of filters is a sheet from below, opened by the page's „Filter" button. */
  .sidebar.sheet { position: fixed; inset: auto 0 0 0; z-index: 40; order: 0; margin: 0; max-height: 84vh; border-radius: 18px 18px 0 0; box-shadow: var(--shadow-pop); transform: translateY(105%); transition: transform .3s var(--ease); }
  .sidebar.sheet:target, .sidebar.sheet.open { transform: none; }
  .sidebar.sheet .panel-head { display: flex; position: relative; padding-top: 22px; }
  .sidebar.sheet .panel-head::before { content: ""; position: absolute; left: 50%; top: 8px; width: 40px; height: 4px; margin-left: -20px; border-radius: 2px; background: var(--line-strong); }
  .sidebar.sheet .body { overflow-y: auto; padding: 0 12px 12px; }
  .sidebar.sheet .sheet-only { display: flex; position: sticky; bottom: 0; margin: 12px -12px -12px; background: var(--panel); }
  .summary { padding: 4px 16px 0; }
  /* On a phone „Mein Studiengang" is a line of its own under the count and „Filter". The grid
     lays out the first line as the flex row does. */
  .summary { display: grid; grid-template-columns: minmax(0, 1fr) auto; row-gap: 2px; }
  .summary:has(> .mine-line) > h1 { max-width: none; }
  .summary > .mine-line { grid-column: 1 / -1; grid-row: 2; justify-self: start; max-width: calc(100% + 10px); min-height: 40px; margin-left: -10px; }
  .summary > .mine-line > .icon:last-child { display: none; }
  /* The program page's „Darstellung": the plan is always the list on a phone, and the app leaves
     the switch out; the server's copy goes too, so nothing moves at the takeover. */
  .plan-shapes { display: none; }
  .toc.views + .plan-shapes + .fgroup { margin-top: 16px; border-top: 0; }
  .sidebar .panel-head, .toc.jumps { display: none; }
  .sidebar .body { padding: 2px 8px 8px; overflow: visible; }
  .toc.views { grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 3px; padding: 6px 0 2px; }
  .toc.views .flabel { display: none; }
  .toc.views a { justify-content: center; min-height: 42px; padding: 0 6px; text-align: center; font-size: 13.5px; background: var(--panel-3); }
  /* The view one is on stays the inverted one; the row above must not take its background. */
  .toc.views a[aria-current="page"] { background: var(--invert); color: var(--on-invert); }
  .program-pill { min-height: 40px; padding: 0 12px; font-size: 14.5px; }
  .faculty-head, .faculty > .hint, .matrix-row { padding-left: 16px; padding-right: 16px; }
  .actions.fgroup { padding-top: 6px; }
  .action { min-height: 46px; font-size: 15px; }
  .person { min-height: 50px; }
  .seg.mini { grid-auto-columns: 34px; }
  .seg.mini a { min-height: 34px; }
  .module-page > .hero h2 { font-size: 24px; }
  .filter-actions, .js .filter-actions { display: flex; padding-bottom: calc(12px + env(safe-area-inset-bottom)); }
  .filter-actions .btn { min-height: 50px; }
  .work > .detail:not(.aside) { position: fixed; inset: 0; z-index: 45; width: auto; border-radius: 0; overflow-y: auto; display: block; box-shadow: none; animation: slide-in .28s var(--ease); }
  .work > .detail.aside { position: static; width: auto; box-shadow: var(--shadow-panel); animation: none; }
  .framed.with-aside > .detail.aside { margin: 12px 12px 0; }
  .resizer { display: none; }
  .detail .scroll { overflow: visible; }
  .back { display: grid !important; }
  .pending-page, .pending-page.columns { position: fixed; z-index: 19; padding: var(--bar) 0 92px; overflow: hidden; background: var(--bg); pointer-events: auto; }
  .pending-page .sidebar { display: none; }
  .sk-home, .sk-subject { grid-template-columns: minmax(0, 1fr); }
  .sk-figures { grid-template-columns: repeat(2, minmax(0, 1fr)); border-left: 0; border-top: 1px solid var(--line); }
  .sk-figures > div { min-height: 56px; }
  .sk-fill { min-height: 0; }
  .sk.sk-tools { width: 112px; height: 42px; border-radius: var(--r-ctl); }
  .sk-row { min-height: 124px; }
  .sk-subject-head { display: none; }
  .sk-stage { min-height: 300px; }
  .rows-pending { top: var(--bar); }
  .rows-pending-fill { left: 0; right: 0; padding: 0; display: grid; gap: 8px; align-content: start; background: var(--bg); }
  .bottomnav { position: fixed; left: 10px; right: 10px; bottom: calc(10px + env(safe-area-inset-bottom)); z-index: 30; display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; height: 62px; padding: 5px; border-radius: 16px; background: color-mix(in srgb, var(--panel) 90%, transparent); backdrop-filter: blur(18px); box-shadow: var(--shadow-pop); }
  .bottomnav .nav { width: auto; font-size: 11px; font-weight: 560; gap: 0; align-content: center; }
  .bottomnav .nav .ind { height: 30px; width: 52px; }
  .facts { grid-template-columns: 1fr; }
  .prog-head { padding: 10px 16px 14px; }
  .block { padding: 18px 16px; }
  .ptable th:first-child, .ptable td:first-child, .variants, .footnote, .docrow, .fues > summary, .my-plan-links { padding-left: 16px; }
  .ptable th:last-child, .ptable td:last-child, .variants, .footnote, .docrow, .fues > summary, .my-plan-links { padding-right: 16px; }
  .prog-title h1 { font-size: 23px; }
  .home-hero-text { padding: 20px 16px 18px; }
  .home-hero .lead { font-size: 15.5px; }
  .intro-actions .btn { flex: 1 1 auto; min-height: 50px; }
  /* Where the two share a line, an open picker's popup runs on under the other button, which
     keeps its height. */
  .intro-actions { align-items: flex-start; }
  .faq-group { padding-left: 16px; }
  .block-head { padding: 16px 8px 12px 16px; }
  .rowlink { height: 60px; padding: 0 12px 0 16px; }
  .ability { padding-left: 16px; padding-right: 16px; }
  .start-step { --mark-x: 32px; padding-left: 16px; padding-right: 16px; }
  .intro-actions .pick-combo { flex: 1 1 auto; }
  .intro-actions .pick-combo .combo-trigger { width: 100%; min-height: 50px; justify-content: center; }
  .intro-actions .pick-combo .combo-value { flex: 0 1 auto; }
  .start-foot { padding-left: 16px; padding-right: 8px; }
  .faq summary { padding-left: 16px; }
  .faq details p { padding: 0 16px 16px; }
  .soon-line { padding: 14px 16px 16px; }
  .detail-nav { padding: 0 16px 16px; }
  /* The board is a picture of the sheet, not the sheet: its chips keep about their size from the
     computer (and still a finger's, 34 px). */
  .board .chip { min-height: 34px; padding: 0 10px 0 8px; font-size: 13.5px; border-radius: var(--r-s); gap: 7px; }
  .board .box { width: 16px; height: 16px; border-radius: 4px; }
  .board .box .icon { width: 12px; height: 12px; }
  .board .mock-seg > button { min-height: 34px; font-size: 13px; }
  .feature { padding: 26px 16px 18px; scroll-margin-top: 76px; }
  .feature-picture { padding: 16px; }
  .feature-picture.bare { padding: 0; }
  /* The Studienplan on a phone: the panel and its parts in finger sizes. */
  /* Long German words („Programmierpraktikum") break with a hyphen rather than past the edge. */
  .sp-body { padding: 14px 14px 18px; gap: 12px; hyphens: auto; }
  .sp-rowline, .sp-event > h3 { hyphens: auto; }
  .sp-body > .state { margin: 36px auto; }
  .sp-view > .sp-empty { margin: 28px auto; padding: 0; }
  .sp-head { gap: 0; }
  .sp-head h2, .sp-head h1 { font-size: 19px; }
  .sp-alert b { font-size: 14px; }
  .sp-head .icon-btn { width: 40px; height: 44px; --hit: 0px; }
  .sp-head .sheet-toggle, .sp-body > .sheet-toggle { padding: 0 12px; }
  /* An empty plan taking a Regelstudienplan over has no head: „Anpassen" stands where one has it. */
  .sp-body > .sheet-toggle { justify-self: end; }
  .sp-head .seg { flex: 1 1 100%; margin-top: 4px; }
  .sp-sum { margin-top: -8px; }
  .sp-row { min-height: 56px; padding-right: 50px; }
  .sp-list-head, .sp-area-head { padding-right: 50px; }
  .sp-area-add { min-height: 44px; }
  .sp-remove { right: 2px; width: 44px; height: 44px; margin-top: -22px; --hit-y: 6px; --hit-l: 2px; --hit-r: 2px; }
  .slot small { display: none; }
  .eye { width: 44px; height: 44px; --hit: 0px; }
  /* A Termin's line: its text across the panel, its controls under it at the right. */
  .sp-rowline { min-height: 44px; flex-wrap: wrap; row-gap: 0; padding-top: 4px; padding-bottom: 2px; }
  .sp-rowline > .rl { flex-basis: 100%; }
  .sp-rowline > :nth-child(2) { margin-left: auto; }
  /* A lone eye stays beside its Termin: a row of its own would hold nothing else. */
  .sp-rowline:has(> :nth-child(2):last-child) { flex-wrap: nowrap; }
  .sp-rowline:has(> :nth-child(2):last-child) > .rl { flex-basis: 0; flex-grow: 1; }
  .sp-rowline > .pick { min-height: 40px; }
  .sp-row .icon-btn { width: 44px; height: 44px; }
  .sp-daylist a { min-height: 48px; }
  /* The day in two short lines („Mo" over „05.10."), the items get the room. */
  .agenda-day { grid-template-columns: 44px minmax(0, 1fr); gap: 2px 8px; }
  .agenda-item { flex-wrap: wrap; gap: 0 8px; }
  .agenda-item .t { min-width: 76px; }
  .agenda-item > span:not(.t) { flex: 1 1 150px; }
  .agenda-item > small { flex-basis: 100%; max-width: none; padding-left: 92px; white-space: normal; }
  .sp-exam { grid-template-columns: minmax(0, 1fr) auto; }
  .sp-exam .eye { grid-column: 2; grid-row: 1 / span 2; }
  .sp-field { grid-template-columns: minmax(0, 1fr); }
  .sp-field > .seg, .sp-field > select { max-width: none; width: 100%; }
  .sp-row { min-height: 48px; }
  .sp-actions select { min-height: 44px; }
  .sk.sk-week-tall { height: 300px; }
}
.back { display: none; }
@keyframes slide-in { from { transform: translateX(24px); opacity: 0; } }
@keyframes preview-in { from { transform: translateX(16px); opacity: 0; } }
@keyframes pop-in { from { transform: translateY(-4px); opacity: 0; } }
/* The wood keeps its animation: it is tied to the scroll (the ground's), it moves only as the
   visitor scrolls (owner, 2026-09-30: with less motion it stayed down behind the ground). */
@media (prefers-reduced-motion: reduce) { *:not(.wood), *::before, *::after { animation: none !important; transition: none !important; } }
@media print { .rail, .topbar, .filters, .bottomnav, .pager, .crown, .ground, .to-top, .wood { display: none !important; } body { display: block; background: #fff; } .main { height: auto; display: block; } }
