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

:root {
  --bg: oklch(1 0 0);
  /* 2026-10-17 hockey palette (owner): white cards on a very light ice-blue
     page, cool neutrals instead of the old warm beige-grey, one accent family
     taken from the rink's own line colours. */
  --page-bg: oklch(0.975 0.008 240);
  /* The rink renderer's line colours (renderer.js RED_LINE_COLOR #c0392b /
     BLUE_LINE_COLOR #2980b9), converted to OKLCh. The blue line itself gives
     white text only ~4:1, so --primary is the same hue a little darker. */
  --rink-blue: oklch(0.576 0.119 242.3);
  --rink-red: oklch(0.543 0.174 29.7);
  --surface: oklch(0.967 0.01 240);
  --surface-strong: oklch(0.935 0.015 240);
  --ink: oklch(0.23 0.035 255);
  --muted: oklch(0.47 0.03 250);
  --line: oklch(0.905 0.014 240);
  --primary: oklch(0.52 0.12 242);
  --primary-dark: oklch(0.44 0.12 246);
  --accent: oklch(0.36 0.07 255);
  --danger: oklch(0.543 0.174 29.7);
  --warning-bg: oklch(0.95 0.045 85);
  --warning-ink: oklch(0.34 0.07 70);
  --success: oklch(0.47 0.12 155);
  --control-bg: oklch(0.998 0.002 240);
  --control-hover: oklch(0.975 0.01 240);
  --editor-tool-selected-bg: oklch(0.94 0.035 245);
  /* WCAG 2.1 SC 1.4.11 (non-text contrast) needs 3:1 for a control boundary.
     The old 0.76 gave 2.16:1 on white and failed. 0.62 is the LIGHTEST value that
     still clears 3:1 against the worst realistic panel (--surface-strong, 3.06:1),
     so the borders stay visually light without failing. Verified by
     test/design/token_contrast_test.rb -- do not raise this without rerunning it. */
  --control-border: oklch(0.61 0.03 245);
  --control-border-hover: oklch(0.52 0.06 245);
  /* --line stays light: it is for genuine dividers, which 1.4.11 exempts. Any
     border that is the sole boundary of an interactive control uses this instead. */
  --line-strong: oklch(0.6 0.03 245);
  --focus-ring: oklch(0.62 0.13 242 / .34);
  --danger-bg: oklch(0.965 0.028 25);
  --success-bg: oklch(0.955 0.035 155);
  --shadow-control: 0 1px 1px oklch(0.2 0.02 250 / .04), 0 1px 3px oklch(0.2 0.02 250 / .03);
  --shadow-switch: 0 1px 4px oklch(0.2 0.03 250 / .1);
  --shadow-card: 0 1px 2px oklch(0.2 0.03 250 / .04), 0 10px 30px oklch(0.2 0.03 250 / .05);
  --shadow-popover: 0 14px 38px oklch(0.15 0.03 250 / .16), 0 2px 8px oklch(0.15 0.03 250 / .08);
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --z-builder-save: 15;
  /* One rink height for every surface that draws one: the segment ice map
     (which set this 22rem precedent), both diagram editors, and the drill
     detail page. Without it the editors run ~30rem and the drill page ~39rem
     at .page-wide, which is more scrolling than a rink is worth. */
  --rink-max-height: 22rem;
  /* Editor-only, deliberately NOT a change to --rink-max-height above: that
     one is also what caps a practice-session segment's ice map (see the two
     other rules reading it) and must stay where it is. */
  --editor-rink-max-height: min(72vh, 46rem);

  /* Forgejo #95. Promoted from raw literals that were hand-written in two or
     more places each. Names say what the colour is FOR, not what hue it is, so
     a future dark-mode pass has something meaningful to redefine. Values are
     byte-identical to what they replaced -- this is a pure refactor with no
     rendering change, which is what makes it safe to do with no browser here.
     --shadow-tint collapses `oklch(0 0 0 / 0.12)` and `oklch(0 0 0 / .12)`,
     the same colour that had drifted into two spellings. */
  /* 2026-10-07: white. The renderer now paints white ice inside the boards
     and a grey surround outside them, so these only still show behind a
     whiteboard diagram (no rink drawn) and in a letterbox margin. */
  --ice-surface: var(--bg);
  --ice-surface-alt: var(--bg);
  --diagram-line: oklch(0.87 0.028 215);
  --diagram-tab-bg: oklch(0.955 0.022 215);
  --diagram-tab-hover: oklch(0.925 0.038 215);
  --diagram-tab-active-border: oklch(0.63 0.09 220);
  --diagram-tab-ink: oklch(0.5 0.035 222);
  --diagram-tab-ink-active: oklch(0.38 0.085 222);
  --status-ready-bg: oklch(0.93 0.04 155);
  --status-ready-ink: oklch(0.32 0.09 155);
  --status-planned-bg: oklch(0.93 0.035 280);
  --status-planned-ink: oklch(0.35 0.09 280);
  /* 2026-09-22: a session status has to look the same everywhere it appears
     -- the badge, the show page's status control, and the calendar chip. It
     did not: .status-published was --status-ready-* on the badge but
     --success-* on the month chip, and .status-completed was a raw literal on
     one and --surface-strong/--ink + opacity on the other. These four pairs
     are now the ONLY definition; every call site reads them. --status-draft-*
     deliberately aliases existing neutrals rather than inventing a colour --
     draft is "not decided yet", and the neutral is what said that already. */
  --status-mark-published: oklch(0.7 0.085 155);
  --status-mark-draft: oklch(0.77 0.09 82);
  --status-mark-archived: oklch(0.72 0.02 250);
  --status-draft-bg: var(--surface-strong);
  --status-draft-ink: var(--muted);
  --status-published-bg: var(--status-ready-bg);
  --status-published-ink: var(--status-ready-ink);
  --status-completed-bg: oklch(0.93 0.035 205);
  --status-completed-ink: oklch(0.32 0.09 205);
  /* 2026-09-22: drills and rink layouts have draft/published/ARCHIVED, and
     .status-archived had no rule at all -- archived rendered as an uncoloured
     pill. Its own warm grey rather than draft's neutral one: archived means
     "deliberately put away", draft means "not finished yet", and the two must
     not look the same. */
  --status-archived-bg: oklch(0.93 0.015 70);
  --status-archived-ink: oklch(0.36 0.045 70);
  --focus-ring-strong: oklch(0.72 0.11 242 / .45);
  --surface-subtle: oklch(0.984 0.006 240);
  --danger-bg-hover: oklch(0.95 0.04 25);
  /* The accent chip: token field values and the library's active-filter chips. */
  --chip-bg: oklch(0.95 0.03 245);
  --chip-border: oklch(0.84 0.06 245);
  /* Forgejo #115 named these after the Atlas' trajectory curves; since #246
     the chart is gone and they only carry the role stripes below. */
  --axis-curve-3: oklch(0.55 0.17 55);
  --axis-curve-4: oklch(0.5 0.18 320);
  /* The Academy page's four tactical roles: an accent stripe only, the role
     name is always text beside it. Aliases, so each is covered by its
     target's pinned pair. */
  /* Forgejo #254: roster chip badges, one colour family per attribute. Each
     badge is a filled pill, so it reads the same on all three chip
     backgrounds. Spielstärke is a symbol whose SATURATION is the level
     (more saturated = stronger): about the same lightness, rising chroma
     (grey, blue, vivid blue), so every level clears 3:1 on its pill
     (token_contrast_test). */
  --tag-skill-bg: oklch(0.975 0.008 250);
  --tag-skill-1: oklch(0.55 0.01 255);
  --tag-skill-2: oklch(0.52 0.09 248);
  --tag-skill-3: oklch(0.45 0.13 258);
  --tag-adult-bg: oklch(0.95 0.03 70);
  --tag-adult-ink: oklch(0.44 0.07 60);
  --tag-junior-bg: oklch(0.95 0.03 150);
  --tag-junior-ink: oklch(0.42 0.06 150);
  --tag-position-bg: oklch(0.96 0.015 280);
  --tag-position-ink: oklch(0.44 0.07 280);
  --tag-flex-bg: oklch(0.95 0.004 250);
  --tag-flex-ink: oklch(0.42 0.01 250);
  /* Forgejo #257: how well a Team slot suits a dragged or seated player. */
  --fit-preferred: oklch(0.5 0.15 150);
  --fit-alternate: oklch(0.62 0.14 90);
  --fit-flex: oklch(0.6 0.16 55);
  --fit-off: oklch(0.52 0.2 25);
  --role-offense-with-puck: var(--rink-red);
  --role-offense-without-puck: var(--axis-curve-3);
  --role-defense-on-puck: var(--rink-blue);
  --role-defense-off-puck: var(--axis-curve-4);
  /* Was 0.58 = 4.30:1 on white, below the 4.5:1 AA floor for text -- and a
     placeholder IS text (SC 1.4.3). The failure was invisible while this was a
     raw literal inside `.form-control::placeholder`; promoting it to a token is
     what surfaced it. 0.549 is the darkest-permitted... i.e. the LIGHTEST value
     that still clears 4.5:1 on --surface and --surface-subtle both. */
  /* Forgejo #96: 42 hand-written font sizes, ten of them inside a 0.18rem
     band, collapsed onto a nine-step scale. Every literal moved to its
     NEAREST step, so most rules did not move at all and none moved more than
     1.6px. This is the axis a theme or a future density setting has to be
     able to reach; a literal is unreachable by definition. */
  --text-2xs: .6875rem;
  --text-xs: .72rem;
  --text-sm: .8rem;
  --text-base: .88rem;
  --text-md: 1rem;
  --text-lg: 1.15rem;
  --text-xl: 1.35rem;
  --text-2xl: 1.6rem;
  --text-3xl: 2rem;
  --placeholder-ink: oklch(0.53 0.025 250);
  --success-bright: oklch(0.48 0.13 155);
  --immersive-bg: oklch(0.18 0.035 255);
  --scrim: oklch(0 0 0 / 0.4);
  --shadow-tint-strong: oklch(0 0 0 / 0.2);
  --shadow-tint: oklch(0 0 0 / 0.12);
  /* The 1px seam an active tab paints over its strip's bottom border, so the
     tab reads as joined to the panel below it. Five identical copies before
     #96; one meaning, so one token. */
  --shadow-tab-seam: 0 1px 0 var(--bg);
  /* The editor's overlap chooser popover. Was the file's only rgba() -- now an
     exact oklch equivalent (black is oklch(0 0 0), so this is a respelling, not
     an approximation) and a token, because token_discipline_test correctly
     refused to let the respelling land as one more raw literal. */
  --shadow-overlap-chooser: 0 .25rem .75rem oklch(0 0 0 / .15);

  /* Forgejo #96. 21 distinct border-radius values collapsed to six steps. The
     two heaviest clusters keep their own step (--radius-md at 31 call sites,
     --radius-lg at 28), so most rules did not move; 33 sites shifted, nearly
     all by 0.8px. Safe to sweep without a browser precisely because
     border-radius cannot change an element's box size -- it can never reflow
     anything, only round a corner differently. A font-size sweep CAN reflow,
     which is why the type scale was deliberately left for a pass that can look
     at a screen (see the issue). */
  /* UI design brief 2026-11-03 (owner: the app looks coarse and old next to the
     Lineup mockup): one softer family, small 5-9px, mid 10-13px, large 16px.
     Radii never reflow anything, so the whole scale moves at once. */
  --radius-xs: .3rem;
  --radius-sm: .4rem;
  --radius-md: .55rem;
  --radius-lg: .65rem;
  --radius-xl: .8rem;
  --radius-2xl: 1rem;
  /* The two colours of a session's roster (owner 2026-11-03: the old pale yellow
     and near-black were too hard). Soft ice and slate from the app's own blue
     hue; ink contrast is above 7:1 on both. roster_team_image.js mirrors the
     hex values for its canvas. */
  --zone-light-bg: oklch(0.955 0.012 235);
  --zone-light-ink: oklch(0.3 0.04 255);
  --zone-light-line: oklch(0.88 0.02 235);
  --zone-light-mark: oklch(0.72 0.05 235);
  --zone-dark-bg: oklch(0.36 0.05 255);
  --zone-dark-ink: oklch(0.97 0.005 250);
  /* Forgejo #303 (brief §4.1): the spacing scale, 4 px base and 8 px rhythm.
     Every padding/margin/gap from 4 px up lies on it (spacing_grid_test). */
  --space-1: .25rem;
  --space-2: .5rem;
  --space-3: .75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --control-height: 44px;
  --form-field-gap: 1rem;
  --form-section-gap: 1.25rem;
}

* { box-sizing: border-box; }

body.app-body {
  margin: 0;
  background: var(--page-bg);
  color: var(--ink);
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased;
}

.app-header {
  align-items: center;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  display: flex;
  height: 56px;
  padding: 0 max(1rem, calc((100vw - 80rem) / 2));
  position: sticky;
  top: 0;
  z-index: 20;
}

.app-mark {
  align-items: baseline;
  color: var(--ink);
  display: flex;
  gap: .4rem;
  margin-right: 2rem;
  text-decoration: none;
}

.app-mark span {
  background: var(--primary);
  border-radius: var(--radius-sm);
  color: white;
  font-size: var(--text-sm);
  font-weight: 800;
  padding: .2rem .35rem;
}

.app-mark strong { font-size: var(--text-md); }
.app-header nav {
  display: flex;
  gap: .25rem;
  overflow-x: auto;
  scrollbar-width: none;
}

.app-header nav::-webkit-scrollbar { display: none; }

.app-header nav a {
  align-items: center;
  border-radius: var(--radius-md);
  color: var(--ink);
  display: inline-flex;
  flex-shrink: 0;
  min-height: 44px;
  padding: 0 .8rem;
  text-decoration: none;
}
.app-header nav a:hover { background: var(--surface); }
/* The current page is marked like a blue line on the ice: a bar under the
   label, not a grey block. */
.app-header nav a.is-current { box-shadow: inset 0 -3px 0 var(--primary); color: var(--primary-dark); font-weight: 700; }
.app-header-right { align-items: center; display: flex; gap: .75rem; margin-left: auto; min-width: 0; }
/* Forgejo #290: the bar is the primary navigation from 1280 px (measured: it
   needs 742 px and fits from about 1250; owner 2026-11-02 picked 1280); below it a
   menu (summary = the hamburger) replaces it and the header names the area
   you are in. The account menu is the mirror image: a name with a chevron
   from 1280 px, its entries at the foot of the hamburger menu below. */
.app-current-area { color: var(--muted); display: none; flex: 0 1 auto; margin-right: .5rem; font-size: var(--text-base); font-weight: 650; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.app-current-area::before { content: "· "; }
.app-nav-menu, .account-menu { position: relative; }
.app-menu-button, .account-menu-button { align-items: center; border-radius: var(--radius-md); color: var(--ink); gap: .3rem; min-height: 44px; }
.app-menu-button { justify-content: center; min-width: 44px; }
.account-menu-button { color: var(--muted); font-size: var(--text-base); padding: 0 .4rem; }
.app-menu-button:hover, .account-menu-button:hover { background: var(--surface); }
.app-nav-menu[open] > summary svg { transform: none; }
.action-menu-panel.app-menu-panel { gap: .1rem; max-height: calc(100dvh - 4.5rem); padding: .5rem; width: min(16rem, calc(100vw - 2rem)); }
.app-menu-panel a, .app-menu-item {
  align-items: center;
  background: none;
  border: 0;
  border-radius: var(--radius-md);
  color: var(--ink);
  cursor: pointer;
  display: flex;
  font: inherit;
  min-height: 44px;
  padding: 0 .7rem;
  text-align: left;
  text-decoration: none;
  width: 100%;
}
.app-menu-panel a:hover, .app-menu-item:hover { background: var(--surface); }
.app-menu-panel a.is-current { box-shadow: inset 3px 0 0 var(--primary); color: var(--primary-dark); font-weight: 700; }
.app-menu-form { margin: 0; }
.app-menu-rule { border: 0; border-top: 1px solid var(--line); margin: .3rem 0; }
.app-menu-user { color: var(--muted); font-size: var(--text-sm); margin: 0; overflow: hidden; padding: .2rem .7rem; text-overflow: ellipsis; white-space: nowrap; }
/* .action-menu (further down) sets display, so these need its specificity. */
.action-menu.app-nav-menu { display: none; }
@media (max-width: 1279px) {
  .app-header nav, .action-menu.account-menu { display: none; }
  .action-menu.app-nav-menu { display: inline-flex; }
  .app-current-area { display: block; }
}
.club-switcher .form-control { font-size: var(--text-base); max-width: 13rem; min-width: 0; width: auto; }
.club-switcher { min-width: 0; }
/* The club's identity colour (ClubTheme, --club-color): only this dot. */
.club-switcher, .club-identity { align-items: center; display: inline-flex; gap: .45rem; }
.club-identity { color: var(--ink); font-size: var(--text-base); font-weight: 650; white-space: nowrap; }
.club-dot { background: var(--club-color, var(--line-strong)); border-radius: 50%; box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--line); flex: 0 0 auto; height: .7rem; width: .7rem; }
/* Forgejo #276: the club logo replaces dot and name; with several clubs it opens
   the club list. Positioned so the panel hangs under the logo, not the header edge. */
.club-logo { display: block; height: 40px; max-width: 9rem; object-fit: contain; width: auto; }
.club-menu { position: relative; }
.club-logo-button { align-items: center; border-radius: var(--radius-md); color: var(--muted); gap: .25rem; min-height: 44px; padding: 0 .25rem; }
.club-logo-button:hover { background: var(--surface); }
.club-menu-panel { gap: .15rem; padding: .4rem; width: min(18rem, calc(100vw - 2rem)); }
.club-menu-form { margin: 0; }
.club-menu-option {
  align-items: center;
  background: none;
  border: 0;
  border-radius: var(--radius-md);
  color: var(--ink);
  cursor: pointer;
  display: flex;
  font: inherit;
  gap: .55rem;
  min-height: 44px;
  padding: .3rem .5rem;
  text-align: left;
  width: 100%;
}
.club-menu-option:hover { background: var(--surface); }
.club-menu-option.is-current { font-weight: 700; }
.club-menu-option svg { color: var(--primary); margin-left: auto; }
.club-menu-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.club-logo-small { flex: 0 0 auto; height: 28px; object-fit: contain; width: 3rem; }
/* A dot row lines its name up with the logo rows. */
.club-menu-option .club-dot { margin: 0 calc((3rem - .7rem) / 2); }
/* On a phone a banner-shaped logo must not squeeze the nav to one item (measured: 70px nav at 390). */
@media (max-width: 767px) { .club-logo { height: 32px; max-width: 5rem; } }
.app-main { min-height: calc(100vh - 56px); padding: 2rem 1rem 4rem; }

.page-wide, .page-narrow { margin: 0 auto; width: 100%; }
.page-wide { max-width: 80rem; }
.page-narrow { max-width: 48rem; }
.page-heading {
  align-items: end;
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  margin-bottom: 1.75rem;
}
.page-heading h1, .builder-header h1 {
  font-size: var(--text-2xl);
  font-weight: 750;
  letter-spacing: -.025em;
  line-height: 1.2;
  margin: .25rem 0;
}
.page-heading p, .builder-header p, .section-heading p {
  color: var(--muted);
  margin: 0;
}

/* Buttons are DaisyUI's (btn, btn-primary, btn-soft btn-error, btn-sm,
   btn-square, btn-ghost) since 2026-10-17; only the press feedback and the
   app's own focus ring stay here, shared with the icon and text buttons. */
.btn:active, .icon-button:active, .text-button:active { transform: scale(.97); }
.btn:focus-visible, .icon-button:focus-visible, .text-button:focus-visible, .text-link:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}
.text-link, .text-button { color: var(--primary-dark); font-weight: 650; text-decoration: none; }
/* Forgejo #302: a link that stands on its own ("Filter zurücksetzen", "Assign one")
   is a 24 px target; inside running text it stays inline (WCAG exception). */
.text-link:not(:is(p, li, td, dd, small) .text-link) { align-items: center; display: inline-flex; min-height: 24px; }
.text-button { background: none; border: 0; cursor: pointer; min-height: 44px; padding: .5rem; }

/* Forgejo #1: shared action-group/icon-button components — introduced for
   #7/#9/#17/#20 to build on, not yet adopted by the feature pages those
   issues will rewrite (kept out of scope here to avoid double-touching). */
.action-group { align-items: center; display: flex; flex-wrap: wrap; gap: .5rem; }
/* Glyph-replacement icons (Forgejo icon pass): these live in buttons that
   used to hold a single text character, so they need an explicit inline box
   rather than inheriting a text baseline. */
.drill-variation-move button svg,
.drill-variation-handle svg, .segment-tab-handle svg,
.segment-expand-button b svg,
.station-expand-button b svg { display: block; }
.station-add-button { gap: .5rem; }
/* 2026-10-07: "Stations from preset…" -- a details menu of preset buttons,
   each with an ice-map preview of the areas it will create. It opens IN
   FLOW, taking its own full-width line in the row: the segment article is
   overflow: clip, and a floating panel hanging below the Stations row was
   cut off there (measured live: the last option unreachable). Same fix as
   the session index's status menu inside .data-table-wrap. */
.station-preset-menu[open] { flex-basis: 100%; }
.station-preset-menu > summary { cursor: pointer; list-style: none; }
.station-preset-menu > summary::-webkit-details-marker { display: none; }
.station-preset-menu-panel {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  display: grid;
  gap: .5rem;
  grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
  margin-top: .5rem;
  max-width: 48rem;
  padding: .5rem;
}
.station-preset-menu-panel form { margin: 0; }
.station-preset-option {
  align-items: stretch;
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  color: var(--ink);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 700;
  gap: .25rem;
  padding: .5rem;
  text-align: left;
  width: 100%;
}
.station-preset-option:hover { background: var(--control-hover); }
.station-preset-option:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 1px; }
.station-preset-preview { display: block; height: auto; pointer-events: none; width: 100%; }
/* .icon-button is DaisyUI's btn btn-square (app/assets/tailwind/design_system.css). */
/* Forgejo #101: the Ausbildungsstufen toggle's "off" state. The state is also
   in title and aria-label, so the colour is a second signal, never the only
   one -- the same rule every status marker in this app follows. */
.icon-button.is-off { color: var(--muted); }
.icon-button.icon-button-danger { color: var(--danger); }
.icon-button.icon-button-danger:hover { background: var(--danger-bg-hover); }

.panel, .builder-section, .segment, .add-segment {
  border: 1px solid var(--line);
  border-radius: var(--radius-2xl);
}
.panel { background: var(--bg); padding: clamp(1rem, 3vw, 2rem); }
.form-grid {
  display: grid;
  align-items: start;
  gap: var(--form-field-gap);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.form-label { color: var(--ink); display: block; font-size: var(--text-sm); font-weight: 740; letter-spacing: .005em; margin-bottom: .5rem; }
/* Deliberately NOT DaisyUI's .input: its default field border is 20 % ink on
   white (~1.6:1) and fails SC 1.4.11, which --control-border is pinned to pass
   (token_contrast_test). Same radius as DaisyUI's fields (--radius-field). */
.form-control {
  background: var(--control-bg);
  border: 1px solid var(--control-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-control);
  color: var(--ink);
  font: inherit;
  line-height: 1.35;
  min-height: var(--control-height);
  padding: .75rem .75rem;
  transition: background-color 140ms ease, border-color 140ms ease, box-shadow 140ms ease, opacity 140ms ease;
  width: 100%;
}
textarea.form-control { display: block; line-height: 1.45; min-height: 5.5rem; resize: vertical; }
select.form-control {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 1rem) calc(50% - 2px), calc(100% - .7rem) calc(50% - 2px);
  background-repeat: no-repeat;
  background-size: .32rem .32rem, .32rem .32rem;
  padding-right: 2.25rem;
}
.form-control::placeholder { color: var(--placeholder-ink); opacity: 1; }
.form-control:hover:not(:disabled):not(:focus) { border-color: var(--control-border-hover); }
.form-control:focus, .form-control:focus-visible, .color-control:focus, .color-control:focus-visible {
  background-color: var(--bg);
  border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--focus-ring), var(--shadow-control);
  outline: 0;
}
.form-control:disabled, .form-control[readonly] {
  background-color: var(--surface);
  color: var(--muted);
}
.form-control:disabled { cursor: not-allowed; opacity: .72; }
.field_with_errors .form-control, .form-control[aria-invalid="true"] {
  border-color: var(--danger);
  box-shadow: 0 0 0 2px oklch(0.72 0.15 25 / .16);
}
.form-hint { color: var(--muted); font-size: var(--text-sm); margin: .25rem 0 0; }
.field-error { align-items: center; color: var(--danger); display: flex; font-size: var(--text-sm); gap: .25rem; margin: .25rem 0 0; }
.field-error > span { align-items: center; background: var(--danger); border-radius: 50%; color: white; display: inline-flex; font-size: var(--text-2xs); font-weight: 800; height: 1rem; justify-content: center; width: 1rem; }
.field-optional { color: var(--muted); font-size: var(--text-xs); font-weight: 550; margin-left: auto; }
.form-section { background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-2xl); padding: clamp(1rem, 2.5vw, 1.5rem); }
.form-section + .form-section { margin-top: 1rem; }
.form-section-heading { margin-bottom: 1rem; }
.form-section-heading h2, .form-section-heading h3 { font-size: var(--text-md); margin: 0; }
.form-section-heading p { color: var(--muted); font-size: var(--text-sm); margin: .25rem 0 0; }
.form-actions { align-items: center; display: flex; flex-wrap: wrap; gap: .75rem; margin-top: 1rem; }
.form-section-form { display: grid; gap: var(--form-section-gap); }
.form-section-form > .form-actions { margin-top: 0; }
.save-state { align-items: center; color: var(--muted); display: inline-flex; font-size: var(--text-sm); gap: .25rem; min-height: 1.25rem; }
.save-state:not(:empty)::before { background: var(--success); border-radius: 50%; content: ""; height: .42rem; width: .42rem; }
.input-with-suffix { position: relative; }
.input-with-suffix .form-control { padding-right: 3.25rem; }
.input-with-suffix > span { color: var(--muted); font-size: var(--text-sm); pointer-events: none; position: absolute; right: .8rem; top: 50%; transform: translateY(-50%); }

.token-control {
  align-items: center;
  background: var(--control-bg);
  border: 1px solid var(--control-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-control);
  display: flex;
  flex-wrap: wrap;
  gap: .25rem;
  min-height: 44px;
  padding: .25rem .5rem;
  transition: border-color 140ms ease, box-shadow 140ms ease;
}
.token-control:focus-within { background: var(--bg); border-color: var(--primary); box-shadow: 0 0 0 3px var(--focus-ring), var(--shadow-control); }
.token-list { display: contents; }
.token-chip { align-items: center; background: var(--chip-bg); border: 1px solid var(--chip-border); border-radius: 999px; color: var(--primary-dark); display: inline-flex; font-size: var(--text-sm); font-weight: 650; gap: .2rem; min-height: 30px; padding: .2rem .25rem .2rem .75rem; }
.token-chip button { align-items: center; background: transparent; border: 0; border-radius: 50%; color: currentColor; cursor: pointer; display: inline-flex; font-size: var(--text-md); height: 1.75rem; justify-content: center; padding: 0; width: 1.75rem; }
.token-chip button:hover { background: oklch(0.8 0.06 245 / .3); }
.token-chip button:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.token-entry { background: transparent; border: 0; color: var(--ink); flex: 1 1 9rem; font: inherit; min-height: 32px; min-width: 8rem; outline: 0; padding: .2rem; }
.token-entry::placeholder { color: var(--placeholder-ink); }

.token-field { position: relative; }
.token-field-suggestions {
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-md);
  box-shadow: 0 8px 24px var(--shadow-tint); left: 0; list-style: none; margin: .25rem 0 0;
  max-height: 12rem; overflow-y: auto; padding: .25rem; position: absolute; right: 0; z-index: 20;
}
.token-field-suggestions li { border-radius: var(--radius-sm); cursor: pointer; font-size: var(--text-base); padding: .5rem .5rem; }
.token-field-suggestions li.is-active { background: var(--primary); color: white; }

.segmented-control { background: var(--surface-strong); border: 0; border-radius: var(--radius-xl); display: inline-flex; gap: .2rem; padding: .25rem; }
.segmented-control { flex-wrap: wrap; max-width: 100%; }
.segmented-control label { cursor: pointer; flex: 1 1 auto; min-width: 0; position: relative; }
.segmented-control input { height: 1px; opacity: 0; position: absolute; width: 1px; }
.segmented-control span { justify-content: center; text-align: center; align-items: center; border-radius: var(--radius-md); color: var(--muted); display: inline-flex; font-size: var(--text-sm); font-weight: 700; min-height: 36px; padding: .5rem .75rem; }
.segmented-control input:checked + span { background: var(--bg); box-shadow: var(--shadow-switch); color: var(--primary-dark); }
.segmented-control input:focus-visible + span { outline: 3px solid var(--focus-ring); outline-offset: 1px; }

/* The link equivalent of .segmented-control, for a Configuration section whose
   pages are peers (Drill-Konfiguration: Kataloge / Kategorien). Links rather
   than radios because each one is a real page with its own URL -- the
   segmented control is for choosing a value inside a form. */
.section-tabs {
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-xl);
  display: inline-flex;
  gap: .2rem;
  margin-bottom: 1rem;
  max-width: 100%;
  overflow-x: auto;
  padding: .2rem;
  scrollbar-width: none;
}
.section-tab {
  flex: none;
  white-space: nowrap;
  align-items: center;
  border-radius: var(--radius-md);
  color: var(--muted);
  display: inline-flex;
  font-size: var(--text-sm);
  font-weight: 700;
  min-height: 36px;
  padding: .5rem .75rem;
  text-decoration: none;
}
.section-tab.is-current { background: var(--bg); color: var(--primary-dark); }
.section-tab:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 1px; }

/* The category tree editor. Two levels, as Category itself enforces, so the
   nesting is a plain nested <ol> rather than a generic recursive renderer. */
.category-scopes { flex-wrap: wrap; }
.category-tree, .category-children { list-style: none; margin: 0; padding: 0; }
.category-children { margin-left: 1.75rem; }
.category-node.is-sorting { opacity: .5; }
.category-row {
  align-items: center;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-bottom: .5rem;
  min-height: 48px;
  padding: .5rem .5rem;
}
.category-row.is-child { background: var(--bg); }
.category-grip { color: var(--muted); cursor: grab; display: inline-flex; touch-action: none; }
.category-name { flex: 1 1 auto; min-width: 0; }
.category-name-form { align-items: center; display: flex; flex: 1 1 14rem; gap: .5rem; min-width: 0; }
.category-name-form .form-control-compact { flex: 1 1 auto; min-width: 0; }
.category-name-form:has(.tactical-fields) { flex-wrap: wrap; }
.tactical-fields { border: 0; display: flex; flex: 1 1 100%; flex-wrap: wrap; font-size: var(--text-sm); gap: .25rem 1rem; margin: 0; min-width: 0; padding: 0; }
.tactical-fields label { align-items: center; display: inline-flex; gap: .25rem; }
.category-usage { flex: 0 0 auto; }
.category-add { margin-top: 1rem; }

.choice-chip { cursor: pointer; transition: background-color 140ms ease, border-color 140ms ease, transform 140ms var(--ease-out); }
.choice-chip:has(input:checked) { background: var(--chip-bg); border-color: var(--primary); color: var(--primary-dark); }
.choice-chip:active { transform: scale(.98); }
.choice-chip input { accent-color: var(--primary); height: 1rem; width: 1rem; }
.color-control { border: 1px solid var(--line-strong); border-radius: var(--radius-md); height: 44px; width: 100%; }

/* Forgejo #13: compact swatch picker -- shrinks the (previously oversized,
   full-width) native color_field down to a small "custom color" escape
   hatch once the picker mounts, sitting alongside real swatch buttons. */
.color-control.color-swatch-native { height: 1.75rem; padding: 0; width: 1.75rem; }
.color-swatch-grid { align-items: center; display: flex; flex-wrap: wrap; gap: .25rem; margin-top: .5rem; }
.color-swatch {
  background: var(--swatch-color);
  border: 2px solid transparent;
  border-radius: 50%;
  cursor: pointer;
  height: 1.75rem;
  width: 1.75rem;
}
.color-swatch:hover { border-color: var(--line); }
.color-swatch:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.color-swatch.is-selected { border-color: var(--ink); }

/* Forgejo #5: per-field help trigger + essential/collapsible field groups. */
.field-label-row { align-items: center; display: flex; gap: .5rem; margin-bottom: .25rem; }
.field-label-row .form-label { margin-bottom: 0; }
.field-help { display: inline-flex; position: relative; }
.field-help-trigger {
  align-items: center;
  background: none;
  border: 0;
  border-radius: 50%;
  color: var(--muted);
  cursor: pointer;
  display: inline-flex;
  height: 24px;
  justify-content: center;
  padding: 0;
  width: 24px;
}
.field-help-trigger:hover, .field-help-trigger[aria-expanded="true"] { background: var(--surface); color: var(--primary-dark); }
.field-help-text { background: var(--surface); border-radius: var(--radius-md); margin: .25rem 0 0; padding: .5rem .5rem; }
/* Forgejo #275 (owner 2026-10-27, "kompakter, aber gut lesbar"): two columns
   on a phone (start and ice time share a row), twelve from 768px --
   title | team; date with its buttons | start | ice time; status | location |
   season block (wider: "Not assigned" was cut on the new-session page). Each field is placed by its class (_essentials_fields). */
.field-essentials { align-items: start; display: grid; gap: .75rem 1rem; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-bottom: 1rem; }
.field-essentials > :is(.field-title, .field-team, .field-date, .field-status, .field-location, .field-block) { grid-column: 1 / -1; }
.field-essentials .form-label { margin-bottom: .25rem; }
@media (min-width: 768px) {
  .field-essentials { grid-template-columns: repeat(12, minmax(0, 1fr)); }
  .field-essentials > .field-title { grid-column: span 7; }
  .field-essentials > .field-team { grid-column: span 5; }
  .field-essentials > .field-date { grid-column: span 6; }
  .field-essentials > :is(.field-start, .field-duration) { grid-column: span 3; }
  .field-essentials > .field-status { grid-column: span 5; }
  .field-essentials > .field-location { grid-column: span 3; }
  .field-essentials > .field-block { grid-column: span 4; }
}
/* #275: the builder's Coaching intent side by side -- objective | focus
   points | private notes from 1024px, the notes below the two from 768px --
   rather than three full-width rows. */
.session-intent-form .field-group-body { gap: .75rem 1rem; padding-top: 0; }
.session-intent-form .field-group-body .form-label { margin-bottom: .25rem; }
@media (min-width: 768px) {
  .session-intent-form .field-group-body > .field-full { grid-column: auto; }
  .session-intent-form .field-group-body > .field-full:last-child { grid-column: 1 / -1; }
}
@media (min-width: 1024px) {
  .session-intent-form .field-group-body { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .session-intent-form .field-group-body > .field-full:last-child { grid-column: auto; }
}
/* The date keeps room for its whole value (DEV, 390px: "10/27/202"); the
   two buttons wrap below only where even that does not fit. */
.date-with-actions { align-items: center; display: flex; flex-wrap: wrap; gap: .5rem; }
.date-with-actions > .form-control { flex: 1 1 9.5rem; min-width: 9.5rem; }
.date-with-actions .date-quick-actions { flex: none; margin: 0; }
/* The coaches beside the fields once both fit (#275); stacked below. */
.essentials-layout { display: grid; gap: 1.25rem 2rem; }
.essentials-layout .field-essentials { margin-bottom: 0; }
.essentials-layout > .session-coaches { margin-top: 0; }
@media (min-width: 1180px) {
  .essentials-layout { align-items: start; grid-template-columns: minmax(0, 2fr) minmax(17rem, 1fr); }
}
/* Form sub-sections are .ds-collapse-sub (design_system.css). */
.field-group { margin-top: 1rem; }
.field-group > summary small { margin-left: auto; }
.field-group-body { padding: 1rem 0 0; }
.field-group-body { align-items: start; display: grid; gap: var(--form-field-gap); grid-template-columns: repeat(2, minmax(0, 1fr)); }
.field-group-body > .field-full { grid-column: 1 / -1; }

/* Forgejo #10: quick-set shortcuts alongside the native (large, keyboard-
   accessible) date input -- not a replacement for it. */
.date-quick-actions { display: flex; gap: .5rem; }
.date-quick-actions .btn { font-size: var(--text-sm); min-height: 36px; padding: .5rem .75rem; }

@media (max-width: 767px) {
  .field-group-body { grid-template-columns: 1fr; }
}

/* Forgejo #6: spatial rink-area picker — thumbnail grid over the (hidden
   once JS runs) real <select>, reused by every rink-area field. */
.rink-area-picker-grid {
  display: grid;
  gap: .5rem;
  grid-template-columns: repeat(auto-fill, minmax(6rem, 1fr));
  margin-top: .35rem;
}
.rink-area-picker-grid button {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  min-height: 44px;
  padding: .35rem;
  text-align: center;
}
.rink-area-picker-grid button.is-selected {
  border-color: var(--primary);
  outline: 2px solid var(--primary);
  outline-offset: -1px;
}
.rink-area-picker-grid button:focus-visible {
  outline: 3px solid var(--focus-ring-strong);
  outline-offset: 1px;
}
.rink-area-picker-grid svg {
  background: var(--ice-surface);
  border-radius: var(--radius-sm);
  display: block;
  height: 3.25rem;
  margin: 0 auto .3rem;
  width: 100%;
}
.rink-area-picker-grid span {
  display: block;
  font-size: var(--text-xs);
  line-height: 1.2;
}

/* Forgejo #30: popover variant for the dense editor toolbar -- same grid
   styles above, just positioned as a floating panel behind a trigger
   button instead of always inline. */
.rink-view-popover { position: relative; }
.rink-view-popover-trigger { cursor: pointer; list-style: none; }
.rink-view-popover-trigger::-webkit-details-marker { display: none; }
.rink-view-popover-trigger::marker { content: ""; }
.rink-view-popover-grid {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: 0 8px 24px var(--shadow-tint);
  max-height: 20rem;
  max-width: 22rem;
  overflow-y: auto;
  padding: .5rem;
  position: absolute;
  z-index: 20;
}

.custom-rink-area-preview {
  background: var(--ice-surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  display: block;
  height: 8rem;
  margin-top: .5rem;
  width: 100%;
}

/* Station form "Ice area" picker: a persistent full-rink SVG with hover/tap
   targets overlaid, replacing a flat 19-thumbnail grid. See
   rink_area_map_controller.js for the interaction design. */
.rink-area-map { margin-top: .35rem; }
.rink-area-map-stage {
  background: var(--ice-surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  max-width: 26rem;
  overflow: hidden;
  position: relative;
  width: 100%;
}
.rink-area-map-stage svg { display: block; height: 100%; width: 100%; }
.rink-area-map-hint { color: var(--muted); font-size: var(--text-sm); margin: .35rem 0 0; }
.rink-area-map-highlight {
  fill: var(--primary);
  fill-opacity: .28;
  pointer-events: none;
  stroke: var(--primary);
  stroke-width: .3;
}
.rink-area-map-highlight[hidden] { display: none; }
.rink-area-map-cells { inset: 0; position: absolute; }
.rink-area-map-cell {
  background: oklch(0.52 0.12 242 / .05);
  border: 1px dashed oklch(0.52 0.12 242 / .35);
  border-radius: var(--radius-xs);
  padding: 0;
  position: absolute;
}
.rink-area-map-cell:hover, .rink-area-map-cell:focus-visible {
  background: oklch(0.52 0.12 242 / .12);
}
.rink-area-map-cell:focus-visible {
  outline: 3px solid var(--focus-ring-strong);
  outline-offset: -2px;
}
.rink-area-map-chips {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem;
  margin-top: .5rem;
}
.rink-area-map-chip {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-size: var(--text-sm);
  min-height: 36px;
  padding: .35rem .8rem;
}
.rink-area-map-chip.is-selected {
  background: var(--surface-strong);
  border-color: var(--primary);
  color: var(--primary-dark);
  font-weight: 600;
}
.rink-area-map-mobile-controls { margin-top: .5rem; }
.rink-area-map-breadcrumb { color: var(--muted); font-size: var(--text-sm); margin: 0 0 .4rem; }
.rink-area-map-mobile-actions { display: flex; gap: .5rem; }
.rink-area-map-mobile-actions [data-area-key].is-selected {
  border-color: var(--primary);
  color: var(--primary-dark);
}
.rink-area-map-extra { margin-top: .5rem; }
.rink-area-map-extra summary { cursor: pointer; font-size: var(--text-base); font-weight: 600; }

/* Forgejo #11: drill search combobox */
.drill-picker { position: relative; }
.drill-picker-results, .coach-picker-results {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: 0 8px 24px var(--shadow-tint);
  left: 0;
  list-style: none;
  margin: .25rem 0 0;
  max-height: 16rem;
  overflow-y: auto;
  padding: .25rem;
  position: absolute;
  right: 0;
  z-index: 20;
}
.drill-picker-results li, .coach-picker-results li {
  border-radius: var(--radius-sm);
  cursor: pointer;
  padding: .5rem .5rem;
}
.drill-picker-results li.is-active, .coach-picker-results li.is-active { background: var(--primary); color: white; }
.drill-picker-results li strong, .coach-picker-results li strong { display: block; font-size: var(--text-base); }
.drill-picker-results li span, .coach-picker-results li span { display: block; font-size: var(--text-xs); opacity: .8; }

/* The "a real drill hangs on this station" marker. Deliberately the only
   decorated state in this field -- free text is the normal case and gets
   nothing. --line-strong, not --line: the X is an interactive control whose
   border is its only boundary (WCAG 1.4.11). */
.drill-attached {
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  display: flex;
  font-size: var(--text-sm);
  gap: .5rem;
  justify-content: space-between;
  margin-top: .5rem;
  padding: .25rem .5rem .25rem .5rem;
}
.drill-attached[hidden] { display: none; }
.drill-attached > span { align-items: center; color: var(--muted); display: flex; gap: .25rem; min-width: 0; }
.drill-attached svg { flex: 0 0 auto; }
.drill-attached strong { color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.drill-attached-remove {
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  color: var(--ink);
  cursor: pointer;
  flex: 0 0 auto;
  font: inherit;
  font-size: var(--text-md);
  line-height: 1;
  min-height: 32px;
  min-width: 32px;
}
.drill-attached-remove:hover { background: var(--control-hover); }
.drill-attached-remove:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 1px; }

/* Shown in place of an empty result list: the typed text is already saved as
   free text, so this states that and offers the one deliberate alternative.
   Not absolutely positioned like .drill-picker-results -- it is a decision
   the coach may take their time over, not a transient dropdown. */
.drill-picker-freetext {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  font-size: var(--text-sm);
  margin-top: .5rem;
  padding: .5rem .75rem;
}
.drill-picker-freetext[hidden] { display: none; }
.drill-picker-freetext p { color: var(--muted); margin: 0 0 .5rem; }
.drill-picker-freetext .btn { gap: .25rem; }
.notice-error, .conflict-warning {
  border-radius: var(--radius-lg);
  padding: .75rem 1rem;
}
.notice-error { background: var(--danger-bg-hover); color: oklch(0.37 0.14 25); }

.flash {
  border-radius: var(--radius-md);
  left: 50%;
  max-width: min(32rem, calc(100vw - 2rem));
  opacity: 1;
  padding: .7rem 1rem;
  position: fixed;
  top: 68px;
  transition: opacity 180ms var(--ease-out), transform 180ms var(--ease-out);
  transform: translateX(-50%);
  z-index: 30;
}
.flash.is-leaving { opacity: 0; transform: translate(-50%, -.35rem); }
.flash-notice { background: oklch(0.93 0.05 155); color: oklch(0.31 0.08 155); }
.flash-alert { background: oklch(0.94 0.05 25); color: oklch(0.35 0.13 25); }

/* The action bar above a listing: selection actions left, search right.
   Shared by Sessions, Drills, Rink Layouts and Spieler, so the four cannot
   drift apart. */
.bulk-bar {
  align-items: center;
  border-bottom: 1px solid var(--line);
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1rem;
  justify-content: space-between;
  margin-top: 1rem;
  padding: .5rem 0;
}
.bulk-bar-actions { align-items: center; display: flex; flex-wrap: wrap; gap: .5rem; }
.bulk-form { display: inline-flex; gap: .5rem; }
.bulk-status-form { align-items: center; }
.bulk-status-form select { max-width: 11rem; }
.bulk-toggle-all { gap: .25rem; }
.bulk-count { color: var(--muted); font-size: var(--text-sm); }
/* A disabled action has to look unavailable, not just refuse to work -- the
   bar is mostly disabled most of the time. */
.bulk-bar button:disabled, .bulk-bar select:disabled { cursor: default; opacity: .45; }
/* Forgejo #294 (V2, owner 2026-11-02): the bar of selection actions exists only
   once something is selected, and then floats at the foot of the screen, so
   an unused row of grey buttons never sits above a list. bulk_select_controller
   puts .has-selection on the [data-controller~="bulk-select"] element (also
   when the page comes back with ticked rows: connect() syncs). The same controller
   drives the all/none switch of the drill library's column filters, which have
   no .bulk-bar, hence :has(.bulk-bar). A bar that holds nothing else (no
   "Weitere Filter" on the drill library, no search) gives up its lines and
   spacing: only the floating card is left of it. */
[data-controller~="bulk-select"] .bulk-bar-actions { display: none; }
[data-controller~="bulk-select"] .bulk-bar:not(:has(.bulk-bar-trailing, .bulk-search)) { border: 0; margin: 0; padding: 0; }
[data-controller~="bulk-select"].has-selection:has(.bulk-bar) { padding-bottom: 5rem; }
[data-controller~="bulk-select"].has-selection .bulk-bar-actions {
  animation: bulk-bar-in 160ms var(--ease-out);
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  bottom: max(1rem, env(safe-area-inset-bottom));
  box-shadow: 0 1rem 2rem var(--shadow-tint);
  display: flex;
  left: 1rem;
  margin-inline: auto;
  max-width: 40rem;
  padding: .5rem .75rem;
  position: fixed;
  right: 1rem;
  width: auto;
  z-index: 25;
}
@keyframes bulk-bar-in { from { opacity: 0; transform: translateY(.5rem); } }
@media (prefers-reduced-motion: reduce) {
  [data-controller~="bulk-select"].has-selection .bulk-bar-actions { animation: none; }
}
.bulk-search { align-items: center; display: flex; gap: .5rem; }
.bulk-search input[type=search] { min-width: 0; width: min(18rem, 48vw); }

/* Listing tables. Real headers, because a column of dates and a column of
   durations are not self-describing. */
.data-table { border-collapse: collapse; width: 100%; }
.data-table thead th {
  border-bottom: 1px solid var(--line-strong);
  color: var(--muted);
  font-size: var(--text-xs);
  font-weight: 750;
  letter-spacing: .04em;
  padding: .5rem .5rem;
  text-align: left;
  text-transform: uppercase;
}
/* A sortable header: the label sorts, the chevron beside it filters. Two
   controls, because merging them would mean every filter also re-sorts. */
.data-table-head { padding: .25rem .5rem !important; }
.data-table-head-inner { align-items: center; display: flex; gap: .15rem; }
.data-table-numeric .data-table-head-inner { justify-content: flex-end; }
.data-table-sort {
  align-items: center;
  border-radius: var(--radius-sm);
  color: inherit;
  display: inline-flex;
  gap: .25rem;
  min-height: var(--control-height);
  padding: 0 .25rem;
  text-decoration: none;
  white-space: nowrap;
}
.data-table-sort:hover { background: var(--surface-strong); color: var(--ink); }
.data-table-sort:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: -2px; }
.data-table-head.is-sorted .data-table-sort { color: var(--primary-dark); }
.data-table-sort-arrow { flex-shrink: 0; }

/* Reuses .action-menu / .action-menu-panel -- the app's own popover, not a
   browser default. The panel is anchored to the header cell, so it needs the
   cell positioned; the row is not a positioning context a <td> can rely on. */
.column-filter { position: relative; }
.column-filter-trigger {
  align-items: center;
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--muted);
  cursor: pointer;
  display: inline-flex;
  justify-content: center;
  min-height: var(--control-height);
  min-width: var(--control-height);
  padding: 0;
}
.column-filter-trigger:hover { background: var(--surface-strong); color: var(--ink); }
/* A column that is filtering says so, or the reader wonders where the rows went. */
.column-filter-trigger.is-active { color: var(--primary); }
.column-filter-trigger.is-active::after {
  background: var(--primary);
  border-radius: 50%;
  content: "";
  height: 5px;
  position: absolute;
  right: .1rem;
  top: .15rem;
  width: 5px;
}
.column-filter-panel { left: auto; right: 0; width: min(15rem, calc(100vw - 2rem)); }
.column-filter-form { display: grid; gap: .25rem; }
.column-filter-all,
.column-filter-option {
  align-items: center;
  border-radius: var(--radius-sm);
  cursor: pointer;
  display: flex;
  font-size: var(--text-sm);
  font-weight: 500;
  gap: .5rem;
  min-height: 34px;
  padding: 0 .25rem;
  text-transform: none;
}
.column-filter-all { border-bottom: 1px solid var(--line); font-weight: 750; margin-bottom: .15rem; padding-bottom: .25rem; }
.column-filter-option:hover { background: var(--surface); }
.column-filter-options { display: grid; gap: .1rem; max-height: 15rem; overflow-y: auto; }
.column-filter-form input[type=checkbox] { height: 1rem; width: 1rem; }
.column-filter-form input[type=submit] { justify-self: stretch; margin-top: .25rem; }

.data-table tbody td { border-bottom: 1px solid var(--line); padding: .5rem; vertical-align: middle; }
.data-table tbody tr:hover { background: var(--surface); }
.data-table tbody tr.is-selected { background: var(--surface-strong); }
.data-table-select { width: 2.5rem; }
/* Forgejo #302 (WCAG 2.2 target size): the row checkbox is a 24 px target. */
.data-table-select input { height: 1.5rem; width: 1.5rem; }
.data-table-numeric { text-align: right; white-space: nowrap; }
.data-table-numeric strong, .data-table-numeric small { display: block; }
.data-table-numeric small { color: var(--muted); font-size: var(--text-xs); }
.data-table-actions { text-align: right; width: 3rem; }
.data-cell-meta { font-size: var(--text-base); }
.data-cell-sub { color: var(--muted); display: block; font-size: var(--text-xs); }
/* The Spieler listing's name cell: an editable field plus the jump to that
   player's own page. The cell stopped being one big link when the name became
   editable -- a text field inside an <a> is not clickable. */
.player-name-cell { align-items: center; display: flex; gap: .5rem; }
/* Forgejo #287: the player's internal photo as a round avatar. */
.player-avatar { background: var(--line); border-radius: 999px; flex: none; object-fit: cover; }
.player-avatar-xs { height: 22px; width: 22px; }
.player-avatar-sm { height: 28px; width: 28px; }
.player-avatar-lg { height: 56px; width: 56px; }
.player-avatar[hidden] { display: none; }
/* 2026-11-02 (owner): a trial player -- equipment unknown. */
.trial-badge { align-self: center; background: var(--warning-bg); border: 1px solid oklch(0.82 0.1 75); border-radius: 999px; color: var(--warning-ink); flex: none; font-size: var(--text-xs); font-weight: 750; letter-spacing: .02em; line-height: 1.1; padding: .12rem .5rem; text-transform: uppercase; white-space: nowrap; }
.player-page-title { align-items: center; display: flex; gap: .75rem; }
.roster-player-data-head { align-items: center; display: flex; gap: .75rem; }
.roster-player-data-head h2 { margin: 0; }
.roster-player-data-photo { display: flex; flex-wrap: wrap; gap: .5rem; margin: .25rem 0 .5rem; }
.player-name-cell .form-control-compact { flex: 1 1 auto; min-width: 0; }
/* 2026-11-02: the Spieler list reads like the other "All" views. The import
   panel opens from the primary button; its own summary is gone. */
.player-import > summary { display: none; }
.player-import:not([open]) { display: none; }
.player-skill { align-items: center; display: inline-flex; gap: .5rem; white-space: nowrap; }
.player-skill[data-value="beginner"] .strength-bars { color: var(--tag-skill-1); }
.player-skill[data-value="advanced_1"] .strength-bars { color: var(--tag-skill-2); }
.player-skill[data-value="advanced_2"] .strength-bars { color: var(--tag-skill-3); }
.player-notes-cell span { -webkit-box-orient: vertical; -webkit-line-clamp: 2; display: -webkit-box; max-width: 18rem; overflow: hidden; }
/* Forgejo #203: Wunschposition, Alternative and Flexibel in one cell. */
.player-position-cell { align-items: center; display: flex; gap: .5rem; }
.player-position-cell .form-control-compact { min-width: 4.5rem; }
.player-flexible-toggle { align-items: center; display: inline-flex; font-size: var(--text-sm); gap: .25rem; white-space: nowrap; }
.player-open-link { align-items: center; color: var(--muted); display: inline-flex; flex: 0 0 auto; }
.player-open-link:hover { color: var(--primary); }
.player-open-link svg { display: block; }
.rink-layout-preview-col { width: 7rem; }
.data-table .rink-layout-preview { margin: 0; width: 6.5rem; }
.data-table .form-control-compact { min-width: 7rem; }
.player-table td { vertical-align: middle; }
/* A listing table has more columns than a phone has room for. Rather than
   hiding data, the table scrolls inside its own box at any width -- the
   action bar above it stays put, which is where the controls are. The
   wrapper is also the reason the page itself never grows wider than the
   viewport, which is what made the session page 659px once already. */
.data-table-wrap { background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-2xl); box-shadow: var(--shadow-card); overflow-x: auto; position: relative; }
/* position: relative is load-bearing: an absolutely positioned descendant (the
   visually-hidden labels in the table's cells) is only clipped by a scroll
   container that is ITS containing block. Without it, those labels in cells
   scrolled out of view stuck out to the right of the page and made the whole
   document scroll sideways (measured 78px at 1024, 334px at 768). */
@media (max-width: 767px) {
  .bulk-bar { gap: .5rem; }
  .bulk-bar-actions { width: 100%; }
  .bulk-search { width: 100%; }
  .bulk-search input[type=search] { flex: 1; width: auto; }
}
/* Forgejo #294 (V3, owner 2026-11-02): the sessions list below 768px is a stack of
   read-only cards -- the very same table markup, restyled; no second list is rendered.
   Per row a card with date, title (team, time, place) and the status switcher. The
   selection column, Dauer and the action column are hidden, so there are no bulk
   actions on a phone (checkboxes only exist on the table). The whole card opens the
   session: the title link stretches over it (::after), the status menu sits above
   that. The <thead> becomes ONE wrapping row of sort/filter buttons over the cards
   (the filters live in the column heads, 2026-10-08). */
@media (max-width: 767px) {
  .session-list-cards { background: none; border: 0; border-radius: 0; overflow: visible; }
  .session-list-cards .data-table, .session-list-cards thead, .session-list-cards tbody { display: block; }
  .session-list-cards thead tr { align-items: center; display: flex; flex-wrap: wrap; gap: .25rem .75rem; margin-bottom: .75rem; }
  .session-list-cards thead th { border: 0; display: block; padding: 0 !important; }
  .session-list-cards thead th.data-table-select,
  .session-list-cards thead th.data-table-numeric,
  .session-list-cards thead th.data-table-actions { display: none; }
  .session-list-cards tbody tr {
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: var(--radius-xl);
    margin-bottom: .5rem;
    position: relative;
  }
  .session-list-cards tbody tr[data-bulk-row] {
    align-items: center;
    display: grid;
    gap: .75rem;
    grid-template-columns: 3.25rem minmax(0, 1fr) auto;
    padding: .75rem;
  }
  .session-list-cards tbody td { border: 0; display: block; padding: 0; }
  .session-list-cards tbody td.data-table-select,
  .session-list-cards tbody td.data-table-numeric,
  .session-list-cards tbody td.data-table-actions { display: none; }
  .session-list-cards .session-title-link::after { content: ""; inset: 0; position: absolute; }
  .session-list-cards .status-menu { position: relative; z-index: 1; }
}
.session-date-link { color: inherit; text-decoration: none; }
.session-title-link { color: inherit; display: grid; text-decoration: none; }
.session-title-link strong { font-size: var(--text-md); font-weight: 750; }
.session-title-link small { color: var(--muted); font-size: var(--text-sm); }
.session-title-link:hover strong { text-decoration: underline; }

.session-list { border-top: 1px solid var(--line); }
.session-row {
  align-items: center;
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  display: flex;
  gap: 1rem;
  min-height: 88px;
  padding: 1rem .5rem;
  text-decoration: none;
}
.session-row:hover { background: var(--surface); }
.session-row-actions { flex-shrink: 0; }
.session-date { align-items: center; display: flex; flex-direction: column; width: 3.25rem; }
.session-date span { color: var(--muted); font-size: var(--text-xs); font-weight: 750; text-transform: uppercase; }
.session-date strong { font-size: var(--text-xl); }
.session-row h2 { font-size: var(--text-md); font-weight: 750; margin: 0; }
.session-row p { color: var(--muted); font-size: var(--text-base); margin: .25rem 0 0; }
.status-badge {
  border-radius: 999px;
  font-size: var(--text-xs);
  font-weight: 750;
  padding: .15rem .5rem;
}
.status-planned { background: var(--status-planned-bg); color: var(--status-planned-ink); }
.status-draft { background: var(--status-draft-bg); color: var(--status-draft-ink); }
.status-published { background: var(--status-published-bg); color: var(--status-published-ink); }
.status-completed { background: var(--status-completed-bg); color: var(--status-completed-ink); }
.status-archived { background: var(--status-archived-bg); color: var(--status-archived-ink); }

.empty-state { border-top: 1px solid var(--line); padding: 4rem 1rem; text-align: center; }
.empty-state h2 { font-size: var(--text-lg); margin: 0; }
.empty-state p { color: var(--muted); margin: .5rem auto 1.5rem; max-width: 34rem; }
/* Forgejo #202: the filtered-to-nothing row keeps the table (and its header filters) on screen. */
.data-table-empty { color: var(--muted); padding: 2rem 1rem; text-align: center; }
/* Forgejo #210: on a phone the table is wider than the screen and scrolls inside
   .data-table-wrap, so text centred across the whole row put "Alle anzeigen" out of
   view. The text is a screen-wide block that stays at the visible left edge. */
.data-table-empty-text { display: block; left: 1rem; max-width: calc(100vw - 4rem); position: sticky; }

.library-page { max-width: 64rem; }
.library-heading { align-items: center; }
.library-search {
  background: var(--surface);
  border-radius: var(--radius-2xl);
  margin-bottom: 2rem;
  padding: 1rem;
}
.library-search-row { align-items: end; display: flex; gap: .75rem; }
.library-search-field { flex: 1; }
.library-search-field label {
  display: block;
  font-size: var(--text-sm);
  font-weight: 750;
  margin-bottom: .25rem;
}
.library-search-field input { background-color: var(--bg); }
/* The drill library keeps its disclosure (it holds seven controls, not two),
   but its body now uses the same surface, labels and chips as .filter-bar on
   the Spieler and session listings. */
/* The drill library renders this panel permanently open (collapsible: false):
   its heading is a label, not a control, so it carries no disclosure marker
   and no pointer affordance -- the "+" would promise something to click. */
.favourites-filter input { height: 1.1rem; width: 1.1rem; }
/* Forgejo #101: the Ausbildungsstufen checkbox group. Wraps rather than
   overflowing -- five German stage names never fit one 390px line. */
.stage-checkboxes { display: flex; flex-wrap: wrap; gap: 0 1rem; }
.library-results-heading h2 { font-size: var(--text-md); margin: 0; }
.library-results-heading p { color: var(--muted); font-size: var(--text-sm); margin: 0; }
.signal-muted { color: var(--muted); font-weight: 500; }
.favourite-form { margin: 0; }
.favourite-button {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  color: var(--ink);
  cursor: pointer;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 700;
  min-height: 44px;
  padding: .5rem .75rem;
}
.favourite-button:hover { background: var(--surface-strong); }
.favourite-button.is-favourite {
  background: var(--chip-bg);
  border-color: var(--chip-border);
  color: var(--primary-dark);
}
/* The library's star. No border and no label: in a table of eighty rows the
   control has to read as a state, not as a button competing with the row. */
.favourite-star {
  align-items: center;
  background: none;
  border: 0;
  border-radius: var(--radius-md);
  color: var(--line-strong);
  cursor: pointer;
  display: inline-flex;
  justify-content: center;
  min-height: 44px;
  min-width: 44px;
  padding: 0;
  transition: color 140ms ease, transform 140ms var(--ease-out);
}
.favourite-star:hover { color: var(--primary); }
.favourite-star:active { transform: scale(.9); }
.favourite-star.is-favourite { color: var(--primary); }
.favourite-star:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.drill-feedback {
  align-items: center;
  background: var(--bg);
  border-radius: var(--radius-2xl);
  display: flex;
  gap: 1rem;
  margin: .25rem 0 1.5rem;
  padding: .75rem 1rem;
}
.drill-feedback-summary { display: flex; flex-direction: column; margin-right: auto; }
.drill-feedback-summary strong { font-size: var(--text-base); }
.drill-feedback-summary span { color: var(--muted); font-size: var(--text-sm); margin-top: .15rem; }
.rating-form { align-items: center; display: flex; gap: .5rem; }
.rating-form label { font-size: var(--text-sm); font-weight: 700; }
.rating-form .form-control { min-width: 10rem; width: auto; }

@media (max-width: 767px) {
  .library-heading { align-items: stretch; }
  .library-heading .btn-primary { width: 100%; }
  .library-search { margin-inline: -.75rem; border-radius: 0; }
  .library-search-row { align-items: stretch; flex-direction: column; }
  .library-search-row .btn-primary { width: 100%; }
  .filter-summary { display: none; }
  .drill-feedback { align-items: stretch; flex-direction: column; }
  .rating-form { align-items: stretch; flex-wrap: wrap; }
  .rating-form label { flex-basis: 100%; }
  .rating-form .form-control { flex: 1; min-width: 0; }
}

.builder-header {
  align-items: end;
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  margin-bottom: 1.5rem;
}
.builder-totals { align-items: center; display: flex; flex-wrap: wrap; gap: .75rem; }
.builder-totals > span { color: var(--muted); font-size: var(--text-sm); }
.builder-totals > span strong { color: var(--ink); display: block; font-size: var(--text-md); }
.builder-totals .is-mismatch strong { color: var(--danger); }
/* Forgejo #340: the workspace header -- title left, status and actions right,
   the planning progress as its own line under both. One column on a phone. */
.workspace-header { align-items: end; display: grid; gap: .5rem 1rem; grid-template-columns: minmax(0, 1fr) auto; }
.workspace-header-actions { align-items: center; display: flex; flex-wrap: wrap; gap: .5rem; justify-content: flex-end; position: relative; }
/* Forgejo #343: "Export & Teilen" -- its panel hangs right under the whole
   actions row (the row is the positioned ancestor, like .session-actions). */
.workspace-export-menu > summary { gap: .5rem; }
/* .action-menu turns every svg of an open summary; only the chevron (last) should. */
.workspace-export-menu[open] > summary svg:first-child { transform: none; }
.action-menu-panel.workspace-export-panel { width: min(24rem, calc(100vw - 2rem)); }
.workspace-print-form { display: grid; gap: .5rem; }
.workspace-export-item { align-items: center; border-radius: var(--radius-md); color: var(--ink); display: flex; font-size: var(--text-sm); font-weight: 650; gap: .5rem; padding: .5rem; text-decoration: none; }
.workspace-export-item:hover, .workspace-export-item:focus-visible { background: var(--surface); }
.workspace-export-item i { background: var(--coach-color); border-radius: 50%; flex: 0 0 auto; height: .75rem; width: .75rem; }
.workspace-export-item span { display: grid; }
.workspace-export-item small { color: var(--muted); font-size: var(--text-xs); }
.workspace-share-links > summary { align-items: center; display: flex; font-size: var(--text-sm); gap: .5rem; padding: .5rem; }
.workspace-share-links .share-manager { margin-top: .5rem; }
/* Forgejo #343: Messungen inside the Kader tab's card, not flush with its edges. */
.workspace-observations { padding: 0 1.25rem 1.25rem; }
@media (max-width: 767px) {
  .workspace-observations { padding: 0 .75rem .75rem; }
}
@media (max-width: 767px) {
  .workspace-export-label { display: none; }
}
.workspace-progress { grid-column: 1 / -1; }
.workspace-progress-items { color: var(--warning-ink); display: flex; flex-wrap: wrap; font-size: var(--text-sm); gap: .25rem .75rem; list-style: none; margin: 0; padding: 0; }
/* Forgejo #340: the sticky strip under the app header -- the two main tabs and
   the save status. The save pill sits in it instead of floating on its own. */
.workspace-strip { align-items: center; background: var(--bg); border-bottom: 1px solid var(--line); display: flex; flex-wrap: wrap; gap: .5rem 1rem; justify-content: space-between; margin: 1rem 0; padding: .5rem 0; position: sticky; top: 56px; z-index: var(--z-builder-save); }
.workspace-tabs { display: flex; gap: .25rem; }
.workspace-tab { background: transparent; border: 0; border-bottom: 3px solid transparent; color: var(--muted); cursor: pointer; font: inherit; font-weight: 650; padding: .5rem .75rem; }
.workspace-tab:hover { color: var(--ink); }
.workspace-tab.is-active { border-bottom-color: var(--primary); color: var(--ink); }
.workspace-strip .builder-save-bar { box-shadow: none; margin: 0; position: static; }
.workspace-context { align-items: center; display: flex; flex: 1 1 12rem; gap: .5rem; min-width: 0; }
.workspace-context strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.workspace-context[hidden] { display: none; }
.workspace-panel-heading { align-items: center; display: flex; gap: 1rem; justify-content: space-between; padding: 1rem 1.25rem 0; }
/* Forgejo #340 (OE-02): the session details drawer -- modal, from the right,
   full height, full width on a phone. Its sections are the builder's own
   collapsibles; inside the narrow panel the essentials stay one column. */
.workspace-drawer { background: var(--surface); border: 0; box-shadow: -1rem 0 3rem var(--shadow-tint); height: 100dvh; margin: 0 0 0 auto; max-height: 100dvh; max-width: min(38rem, 100vw); padding: 0 0 1.5rem; width: 100%; }
.workspace-drawer-head { align-items: center; background: var(--bg); border-bottom: 1px solid var(--line); display: flex; gap: 1rem; justify-content: space-between; padding: .75rem 1.25rem; position: sticky; top: 0; z-index: 1; }
.workspace-drawer-head h2 { font-size: var(--text-md); margin: 0; }
.workspace-drawer details.session-setup { margin: .75rem .75rem 0; }
.segment-settings-section { display: grid; gap: 1rem; padding: 1rem 1.25rem; }
/* Forgejo #342 (CP-EDIT-03): a session segment's editor without its drawing
   state is display and station picker -- no bars. Its command bar sits BELOW
   the canvas, so switching the tools on adds them under the rink and the
   rink keeps its place and size. "Zeichnen" exists from 1024 px only. */
.single-diagram-editor[data-drawing] { display: flex; flex-direction: column; }
.single-diagram-editor[data-drawing] > :not(.editor-workbench) { order: 2; }
.single-diagram-editor[data-drawing] > .editor-workbench { order: 1; }
.single-diagram-editor[data-drawing="false"] > .editor-command-bar,
.single-diagram-editor[data-drawing="false"] .editor-tools-bar,
.single-diagram-editor[data-drawing="false"] .editor-context-panels { display: none; }
.single-diagram-editor[data-drawing="false"] [data-editor-stations] [data-station-id] { cursor: pointer; }
.segment-drawing-toggle { display: none; }
/* Forgejo #367: while drawing, every menu of the editor is reachable. The segment no longer clips what
   reaches past it (a tool menu over a short rink), the stage stops sticking (with its tools and the
   inspector it is taller than the screen, and a sticky box's lower part never scrolls into view), and it
   lies above the station list, whose open station heads (z-index 2) would cover a wide menu. */
.segment-workspace:has(.single-diagram-editor[data-drawing="true"]) { overflow: visible; }
.segment-workspace:has(.single-diagram-editor[data-drawing="true"]) .segment-stage > .segment-body { position: relative; top: auto; z-index: 3; }
/* Forgejo #367: the cinema mode -- the segment as before the stage columns (#341, #342): the rink on the
   full width, the command bar above it, the tools below, the lists underneath, parallel stations and
   sequential drills side by side again (the rule #341 removed). */
.segment-workspace[data-cinema="true"] .segment-stage { grid-template-columns: minmax(0, 1fr); }
.segment-workspace[data-cinema="true"] .segment-stage-list { border-left: 0; }
.segment-workspace[data-cinema="true"] .segment-stage-list > .segment-stations-heading { border-top: 1px solid var(--line); }
.segment-workspace[data-cinema="true"] .single-diagram-editor[data-drawing] > .editor-command-bar { order: 0; }
[data-cinema-toggle][aria-pressed="true"] { background: var(--editor-tool-selected-bg); color: var(--primary-dark); }
@media (min-width: 1024px) {
  .segment-workspace[data-cinema="true"] .segment-stations:has(> .stations-group:first-child .stations > *):has(> .stations-group:last-child .stations > *) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
  .segment-workspace[data-cinema="true"] .segment-stations:has(> .stations-group:first-child .stations > *):has(> .stations-group:last-child .stations > *) > .stations-group + .stations-group {
    border-left: 1px solid var(--line);
    border-top: 0;
  }
}
/* Forgejo #342 (CP-RINK-02): the station's area picked on the stage's rink. */
.area-pick-trigger { display: none; margin-top: .5rem; }
@media (min-width: 900px) {
  .area-pick-trigger { display: inline-flex; }
}
.area-pick-option { cursor: pointer; fill: var(--primary); fill-opacity: .22; stroke: var(--bg); stroke-dasharray: 1 .6; stroke-width: .35; }
.area-pick-option:focus { outline: none; }
.area-pick-option:hover, .area-pick-option:focus-visible, .area-pick-option:focus { fill-opacity: .45; stroke-dasharray: none; stroke-width: .6; }
.area-pick-option[aria-pressed="true"] { fill-opacity: .35; }
@media (min-width: 1024px) {
  .segment-drawing-toggle { display: inline-flex; }
}
.segment-settings-section[hidden] { display: none; }
.segment-settings-title { font-size: var(--text-md); margin: 0; }
.segment-settings-section .segment-remove { justify-self: start; }
.workspace-drawer .essentials-layout { grid-template-columns: minmax(0, 1fr); }
@media (max-width: 767px) {
  .workspace-header { grid-template-columns: minmax(0, 1fr); }
  .workspace-header-actions { justify-content: flex-start; }
  .workspace-strip .builder-save-bar { width: 100%; }
}
/* Deliberately understated: a compact, right-aligned pill rather than a
   full-width banner. It is sticky, so at its old 58px full-bleed size it sat
   on top of whatever a station anchor scrolled to (see .station's
   scroll-margin-top below, which is the other half of that fix). */
.builder-save-bar {
  align-items: center;
  backdrop-filter: blur(6px);
  background: oklch(1 0 0 / .9);
  border: 1px solid var(--line);
  border-radius: 999px;
  box-shadow: var(--shadow-control);
  display: flex;
  gap: .5rem;
  margin: 0 0 .75rem auto;
  max-width: 100%;
  min-height: 42px;
  padding: .25rem .25rem .25rem .75rem;
  position: sticky;
  top: 68px;
  width: fit-content;
  z-index: var(--z-builder-save);
}
.builder-save-bar > div { align-items: baseline; display: flex; flex: 0 1 auto; gap: .5rem; min-width: 0; }
.builder-save-bar strong { font-size: var(--text-sm); font-weight: 650; }
.builder-save-bar small { color: var(--muted); font-size: var(--text-xs); }
.builder-save-bar .btn-primary { font-size: var(--text-sm); min-height: 34px; padding: .25rem .75rem; }
.builder-save-indicator { background: var(--success); border-radius: 50%; height: .55rem; width: .55rem; }
[data-save-state="dirty"] .builder-save-indicator { background: var(--warning-ink); }
[data-save-state="saving"] .builder-save-indicator { background: var(--accent); }
[data-save-state="error"] .builder-save-indicator { background: var(--danger); }
[data-save-state="error"] .builder-save-bar strong { color: var(--danger); }
.builder-section { margin-bottom: 1.25rem; overflow: clip; position: relative; }
.builder-section > summary {
  align-items: center;
  cursor: pointer;
  display: flex;
  gap: .75rem;
  justify-content: space-between;
  list-style: none;
  min-height: 60px;
  padding: .75rem 1rem;
}
.builder-section > summary::-webkit-details-marker { display: none; }
.builder-section > summary:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: -3px;
}
.builder-section > summary small { color: var(--muted); display: block; font-size: var(--text-sm); }
.builder-section-body { border-top: 1px solid var(--line); padding: 1.25rem; }
.session-setup-overview { min-width: 0; }
.session-setup-overview small { line-height: 1.45; text-wrap: pretty; }
/* Forgejo #272: a shut collapsible's preview line -- its parts joined by
   " · "; a hidden (blank) part leaves no stray dot behind. */
.summary-preview > :not([hidden]) ~ :not([hidden])::before,
.summary-preview-coaches > :not([hidden]) ~ :not([hidden])::before { content: " · "; }
.session-setup-actions { align-items: center; display: inline-flex; flex: 0 0 auto; gap: .5rem; }
.session-setup-content { border-top: 1px solid var(--line); }
/* The setup cards hold pickers that must overflow them (the coach field's
   list, the colour menu, the focus point suggestions) -- not clipped like
   the other builder sections (#273). */
.builder-section.session-setup { overflow: visible; }
.builder-section.session-setup:not([open]) > summary { border-radius: inherit; }
.session-setup-content > .builder-section-body { border-top: 0; }
.session-setup .station-map-lines li + li { margin-top: .25rem; }
.section-heading { align-items: end; display: flex; gap: 1rem; justify-content: space-between; padding: 0 0 .75rem; }
.section-heading h2 { margin: 0 0 .2rem; }
/* Forgejo #273: the coaches in Essentials -- one field (the drill picker's
   result list), then one row per coach: name, role, colour, X. */
.session-coaches { display: grid; gap: .5rem; margin-top: 1.25rem; }
.session-coaches > h3 { margin: 0; }
.coach-picker { max-width: 28rem; position: relative; }
.coach-rows { display: grid; list-style: none; margin: 0; padding: 0; }
.coach-row { align-items: center; border-bottom: 1px solid var(--line); display: grid; gap: .5rem; grid-template-columns: minmax(0, 1fr) auto auto auto; min-height: 44px; padding-block: .25rem; }
.coach-row-name { min-width: 0; overflow-wrap: anywhere; }
.coach-row-name small, .coach-row-role { color: var(--muted); font-size: var(--text-sm); }
.coach-row-name small { margin-left: .25rem; }
.coach-role-form .form-control { width: auto; }
.coach-color-dot { background: var(--person-color); border-radius: 50%; display: inline-block; height: 1.1rem; width: 1.1rem; }
summary.coach-color-dot { box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--line-strong); cursor: pointer; list-style: none; }
summary.coach-color-dot::-webkit-details-marker { display: none; }
summary.coach-color-dot:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 3px; }
.coach-color-menu { position: relative; }
.coach-color-panel {
  background: var(--bg); border: 1px solid var(--line-strong); border-radius: var(--radius-md); box-shadow: 0 .5rem 1rem var(--shadow-tint);
  display: grid; gap: .5rem; grid-template-columns: repeat(5, 2rem); padding: .5rem; position: absolute; right: 0; top: calc(100% + .4rem); z-index: 30;
}
.coach-color-option {
  align-items: center; background: var(--person-color); border: 0; border-radius: 50%; color: white; cursor: pointer; display: inline-flex;
  font-size: var(--text-2xs); font-weight: 750; height: 2rem; justify-content: center; width: 2rem;
}
.coach-color-option.is-current { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--ink); }
.coach-color-option:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.coach-remove[disabled], .coach-role-form select[disabled] { cursor: not-allowed; opacity: .45; }
.inline-add, .add-segment { margin-top: 1rem; }
.inline-add > summary, .add-segment > summary {
  color: var(--primary-dark);
  cursor: pointer;
  font-size: var(--text-base);
  font-weight: 700;
  list-style: none;
  min-height: 44px;
  padding: .75rem 0;
}
.inline-add > form { border-top: 1px solid var(--line); padding-top: 1rem; }

.builder-plan { margin-top: 2rem; }
.segment-tabs-shell { align-items: flex-end; border-bottom: 1px solid var(--line); display: flex; gap: .5rem; }
.segment-tabs-shell > .segment-tabs-scroll { flex: 1 1 auto; min-width: 0; }
/* Forgejo #343 (contract §10): "Gesamtübersicht" right in the segment bar; the
   overview stands in place of the stage (the segment panels), nothing else. */
.segment-overview-toggle { align-self: center; flex: 0 0 auto; }
.segment-overview-toggle[aria-pressed="true"] { background: var(--chip-bg); border-color: var(--primary); color: var(--primary-dark); }
.builder-plan.is-overview > [data-practice-session-plan-target="panel"],
.builder-plan.is-overview > #new-segment-panel { display: none; }
.workspace-overview-region[hidden] { display: none; }
.workspace-overview-region .run-sheet { margin-top: 1rem; }
.workspace-overview-open { margin-left: auto; align-self: flex-start; }
.run-stations > .run-station { align-items: center; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-lg); color: var(--ink); cursor: pointer; display: flex; font: inherit; gap: .75rem; min-height: 64px; padding: .75rem; text-align: left; }
.run-stations > .run-station:hover, .run-stations > .run-station:focus-visible { border-color: var(--primary); }
.run-station-number { align-items: center; background: var(--primary); border-radius: 50%; color: white; display: flex; flex: 0 0 auto; font-size: var(--text-xs); height: 1.75rem; justify-content: center; width: 1.75rem; }
.run-station-text { display: flex; flex: 1; flex-direction: column; font-size: var(--text-sm); min-width: 0; }
.run-station-coaches { color: var(--muted); font-size: var(--text-sm); }
.segment-empty button.text-link { background: none; border: 0; cursor: pointer; font: inherit; font-size: var(--text-sm); padding: 0; }
.segment-tabs-scroll { align-items: flex-end; display: flex; gap: .25rem; overflow-x: auto; padding: .15rem .1rem 0; scrollbar-width: thin; }
.segment-tabs { display: flex; flex: 0 0 auto; gap: .25rem; list-style: none; margin: 0; padding: 0; }
.segment-tab-item, .roster-tab-item {
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: var(--radius-md) var(--radius-md) 0 0;
  display: inline-flex;
  height: 40px;
  overflow: hidden;
  position: relative;
  transition: background-color 180ms var(--ease-out), border-color 180ms var(--ease-out);
}
.segment-tab-item.is-sorting { opacity: .6; }
.segment-tab-item.is-active, .roster-tab-item.is-active { background: var(--bg); box-shadow: var(--shadow-tab-seam); }
.segment-tab-item:not(.is-active):hover, .roster-tab-item:not(.is-active):hover { background: var(--control-hover); }
.segment-tab-handle {
  align-items: center;
  color: var(--muted);
  cursor: grab;
  display: inline-flex;
  font-size: var(--text-xs);
  height: 100%;
  justify-content: center;
  touch-action: none;
  width: 24px;
}
.segment-tab, .roster-tab {
  background: transparent;
  border: 0;
  color: var(--muted);
  cursor: pointer;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 700;
  height: 100%;
  padding: .5rem .75rem .5rem .25rem;
  white-space: nowrap;
}
.segment-tab[aria-selected="true"], .roster-tab[aria-selected="true"] { color: var(--ink); }
.segment-tab:focus-visible, .segment-add-tab:focus-visible, .segment-tab-handle:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: -2px; }

/* Same visual language as .segment-tabs/.segment-tab above, but a distinct
   class -- a page-wide test asserts there is exactly one
   button.segment-tab[aria-selected="true"] on the whole builder page, so the
   roster's own tab-pair (a separate, unrelated tab group) must not share
   that literal class. */
.roster-tabs { display: flex; flex: 0 0 auto; gap: .25rem; list-style: none; margin: 0; padding: 0; }
/* Everything else (the tab's box, active/hover states, colors) is shared with
   .segment-tab-item/.segment-tab above -- the roster pair only keeps its own
   class names so a page-wide test can still count exactly one selected
   button.segment-tab. A roster tab has no drag handle to its left, so it is
   the one declaration that genuinely differs. */
.roster-tab { padding: .5rem .75rem; }
/* Owner 2026-11-03: the roster's three views are the same neutral switch as the
   mode choice below them (mockup: one segmented language), not folder tabs. */
.roster-tabs { background: var(--surface-strong); border-radius: var(--radius-xl); flex: 1 1 auto; gap: .2rem; max-width: 30rem; min-width: 0; padding: .25rem; }
.roster-tab-item { flex: 1 1 auto; min-width: 0; }
.roster-tab-item .roster-tab { padding: .5rem .5rem; width: 100%; }
.roster-tab-item { background: transparent; border: 0; border-radius: var(--radius-md); height: 36px; }
.roster-tab-item:not(.is-active):hover { background: var(--control-hover); }
.roster-tab-item.is-active { background: var(--bg); box-shadow: var(--shadow-switch); }
.roster-tab[aria-selected="true"] { color: var(--primary-dark); }
.roster-tab:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: -2px; }
/* 2026-09-27: both roster panels are direct children of the .builder-section
   <details>, which has a border but no padding of its own -- .builder-section-body
   (the 1.25rem inset every other section's content sits in) was never applied
   here. The textarea, the buttons and all six columns therefore touched the
   section's rounded border on every side, and the tab strip started flush at
   its left edge. Same inset as .section-heading directly above them, so the
   heading, the tabs and the panel now share one left margin. */
.roster-setup > [role="tabpanel"] { display: grid; gap: .75rem; padding: 1rem 1.25rem 1.25rem; }
/* The two textareas are prose-width inputs, not full-bleed ones -- at desk
   width a 2-row box spanning the whole builder reads as a mis-sized control. */
.roster-setup > [role="tabpanel"] > textarea { max-width: 48rem; }
/* The grid above already spaces these; their own top margins would double it. */
.roster-setup .roster-zones,
.roster-setup .roster-kanban { margin: 0; }
.roster-setup .roster-group-actions { margin-top: 0; }

/* Player roster (§7/§15 amendment): "Spieler" 3-zone board + "Gruppen" Kanban. */
/* Club colors page (2026-09-20): three generated suggestions plus a free hue.
   The swatch is filled by an inline style carrying ClubTheme's derived colour --
   a runtime value that cannot live in this file, which is why that class's own
   contrast is pinned in test/design instead. (Spelling the function name out
   here would count against token_discipline_test's literal ceiling: it scans
   the file as text, comments included.) */
.club-theme-form { margin-top: 1.5rem; max-width: 34rem; }
/* Forgejo #276: the logo row on the same page -- preview, pick, remove in one line. */
.club-logo-panel { margin: 1rem 0 1.5rem; max-width: 34rem; }
.club-logo-panel h2 { margin: 0 0 .6rem; }
.club-logo-row { align-items: center; display: flex; flex-wrap: wrap; gap: .75rem; }
.club-logo-preview { align-items: center; display: inline-flex; gap: .45rem; min-height: 3.5rem; min-width: 3.5rem; }
.club-logo-preview img { height: 3.5rem; object-fit: contain; width: auto; max-width: 10rem; }
.club-logo-form label { cursor: pointer; }
.club-logo-form:focus-within label { outline: 2px solid var(--primary); outline-offset: 2px; }
.club-theme-choices { border: 0; margin: 0 0 1.25rem; padding: 0; }
.club-theme-option {
  align-items: center;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  cursor: pointer;
  display: flex;
  gap: .6rem;
  margin-top: .5rem;
  min-height: 44px;
  padding: .5rem .7rem;
}
.club-theme-option:has(input:checked) { border-color: var(--primary); }
.club-theme-option-text { display: flex; flex-direction: column; }
.club-theme-option-text small { color: var(--muted); font-size: var(--text-sm); }
.club-theme-swatch {
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  display: inline-block;
  flex: none;
  height: 1.6rem;
  width: 1.6rem;
}
.club-theme-taken { border-top: 1px solid var(--line); margin-top: 2rem; padding-top: 1rem; }
.club-theme-taken h2 { font-size: var(--text-md); }
.club-theme-taken p { color: var(--muted); font-size: var(--text-base); }
.club-theme-taken ul { list-style: none; margin: .75rem 0 0; padding: 0; }
.club-theme-taken li { align-items: center; display: flex; gap: .6rem; padding: .3rem 0; }

/* Reflection (2026-09-20): post-practice notes, only rendered on a completed
   session. Sized like the other session prose blocks rather than full width. */
.session-reflection { margin-top: 1.5rem; }
.session-reflection-form { max-width: 48rem; }
.session-reflection-form textarea { margin-bottom: .75rem; }
.session-reflection-text { color: var(--muted); line-height: 1.55; max-width: 70ch; white-space: pre-line; }
/* The session page's roster: the two panels stack here rather than sitting
   behind tabs like the builder's. */
.session-roster { margin-top: 2rem; }
.session-roster > #roster-groups-panel { margin-top: 1.5rem; }
.roster-group-actions { margin-top: .75rem; }
/* Owner 2026-11-03: adding names looks the same on Hell/Dunkel and on Gruppen --
   one quiet strip, the field and its button side by side. */
.roster-add { align-items: end; background: var(--surface-subtle); border-radius: var(--radius-xl); display: grid; gap: .5rem .75rem; grid-template-columns: minmax(0, 1fr) auto; max-width: none; padding: .75rem 1rem; }
.roster-add > .form-hint { grid-column: 1 / -1; margin: 0; }
.roster-setup > [role="tabpanel"] .roster-add > textarea { margin: 0; max-width: none; }
/* Forgejo #370: the roster's one paste box, under "+ Spieler", between the heading and the toolbar. */
.roster-add-panel { margin: .75rem 1.25rem 0; }
.roster-add-panel > .action-group { align-items: center; display: flex; flex-wrap: wrap; gap: .5rem .75rem; grid-column: 1 / -1; }
.roster-add-panel > .action-group > .form-label { margin: 0; }
.roster-add-panel > .action-group > select { flex: 0 1 16rem; width: auto; }
/* Forgejo #371: the roster check -- two lists side by side (players, trial players apart), then the delta in two
   columns (no longer on the list / new), each split into players and trial players. One column on a phone. */
.roster-check-panel { background: var(--surface-subtle); border-radius: var(--radius-xl); margin: .75rem 1.25rem 0; padding: .75rem 1rem; }
.roster-check-form, .roster-check-result { display: grid; gap: .75rem; }
.roster-check-form > .form-hint { margin: 0; }
.roster-check-fields, .roster-check-columns { display: grid; gap: .75rem 1rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.roster-check-result { border-top: 1px solid var(--line); margin-top: .75rem; padding-top: .75rem; }
.roster-check-result h3 { font-size: var(--text-md); margin: 0 0 .25rem; }
.roster-check-result h4 { color: var(--muted); font-size: var(--text-sm); margin: .5rem 0 .25rem; }
.roster-check-group ul { display: grid; gap: .25rem; list-style: none; margin: 0; padding: 0; }
.roster-check-group .form-hint { margin: 0; }
.roster-check-item { align-items: center; cursor: pointer; display: inline-flex; flex-wrap: wrap; gap: .5rem; }
.roster-check-similar { color: var(--warning-ink); display: block; margin-left: 1.5rem; }
.roster-check-status { background: var(--warning-bg); border-radius: var(--radius-md); color: var(--warning-ink); padding: .5rem .75rem; }
.roster-check-ok { align-items: center; color: var(--success); display: flex; gap: .5rem; margin: .75rem 0 0; }
@media (max-width: 767px) {
  .roster-check-fields, .roster-check-columns { grid-template-columns: minmax(0, 1fr); }
}
/* The ⋯ menu's panel hangs under its own button, right-aligned (.action-menu-panel is absolute). */
.roster-more-menu { position: relative; }
.roster-more-menu .action-menu-panel { width: max-content; }
@media (max-width: 767px) { .roster-add { grid-template-columns: minmax(0, 1fr); } }

/* Spieler detail page (2026-09-20): the same attribute row the club list
   edits inline, plus the session history. Reuses .data-list/.data-row for the
   history so a player's sessions look like every other list in the app. */
/* 2026-11-03: the page is built from the session page's own parts -- header
   with an action menu, then .session-disclosure cards. The editable fields sit
   in a grid that wraps by column width, never by breakpoint, so no width
   needs its own rule. */
.player-fields {
  display: grid;
  gap: .75rem 1rem;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 12rem), 1fr));
  padding: 1rem 1.25rem 1.25rem;
}
.player-fields .data-row-field { flex: none; }
.player-toggles { display: flex; flex-wrap: wrap; gap: .5rem; grid-column: 1 / -1; }
.player-toggle { max-width: 100%; }
.player-fields .data-row-field-wide { grid-column: 1 / -1; }
.player-attributes > summary .inline-save-status { margin-left: auto; }
.player-photo-form { display: grid; }
.player-photo-menu .action-menu-panel .btn { justify-content: center; width: 100%; }
.player-history .data-row-title { overflow-wrap: anywhere; white-space: normal; }
.player-history .data-row-field { flex: 0 1 auto; }

/* 2026-10-26 (owner: readability first): "Nicht eingeteilt" is a full-width
   strip on top whose players sit in columns (.roster-zone-pool); Hell/Dunkel
   -- or the groups -- share the rows below, as many 28rem columns as fit. At
   28rem a chip holds name, badges and controls in ONE row; only below that
   (a phone) does it take two (see the roster-column container query). */
.roster-zones, .roster-kanban { display: grid; gap: 1rem; margin: 1rem 0; }
.roster-zones { grid-template-columns: repeat(auto-fit, minmax(min(28rem, 100%), 1fr)); }
.roster-chip[hidden] { display: none; }
.roster-search { align-items: center; display: flex; gap: .5rem; margin: 1rem 0 0; }
.roster-search .form-control { flex: 1 1 auto; max-width: 28rem; }
/* Forgejo #370 (owner 2026-11-15): one toolbar for the roster -- the three views, the search (all views) and
   Einteilen -- in one row at desk width, wrapping on a phone; the board follows right below it. */
.roster-toolbar { align-items: center; display: flex; flex-wrap: wrap; gap: .5rem .75rem; padding: .75rem 1.25rem 0; }
.roster-toolbar > .segment-tabs-shell { border-bottom: 0; flex: 0 1 30rem; min-width: 0; }
.roster-toolbar > .roster-search { flex: 1 1 14rem; margin: 0; }
.roster-toolbar > .roster-distribute-toggle { margin-left: auto; }
.roster-distribute-toggle svg { transition: transform 140ms var(--ease-out); }
.roster-distribute-toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
@media (max-width: 767px) {
  .roster-toolbar > .segment-tabs-shell { flex-basis: 100%; }
  /* The heading's counts get the whole width on a phone; the actions wrap below them. */
  .roster-head { flex-wrap: wrap; }
  .roster-head > .session-setup-overview { flex: 1 1 100%; }
  .roster-head > .session-setup-actions { flex: 1 1 100%; flex-wrap: wrap; }
}
/* The Team board dims, never hides, a player the search does not find: its lines keep their shape. */
.roster-line-chip[data-search-miss] { opacity: .3; }
/* UI design brief 2026-11-03 (Lineup mockup): once the board is wide, the players
   without a colour become a column of their own beside Hell and Dunkel, so
   dragging someone in or out never means scrolling past a long pool. The pool
   keeps its place in the DOM (the controllers find it by data-zone). */
@media (min-width: 1180px) {
  .roster-zones { align-items: start; grid-template-columns: minmax(16rem, 20rem) repeat(2, minmax(0, 1fr)); }
  .roster-zones > .roster-zone-pool { display: block; grid-column: 1; max-height: 80vh; overflow-y: auto; position: sticky; top: 1rem; }
}
.roster-kanban { grid-template-columns: repeat(auto-fill, minmax(min(28rem, 100%), 1fr)); }
.roster-zone-pool {
  align-content: start; column-gap: .75rem; display: grid; grid-column: 1 / -1;
  grid-template-columns: repeat(auto-fill, minmax(min(28rem, 100%), 1fr));
}
.roster-zone-pool > .roster-zone-heading { grid-column: 1 / -1; }
/* min-width: 0 is load-bearing, not tidiness. A flex item's automatic minimum
   size is its MIN-CONTENT width, and a column's min-content is a whole chip --
   a non-wrapping name plus four buttons, ~419px. Without this the three
   columns could not shrink at all, so opening the roster on a phone made the
   document 465px wide at a 320px viewport (measured: over 145 at 320, 75 at
   390, 35 at 430). The general mobile_layout_probe never caught it because it
   does not open a <details>; script/roster_layout_probe.mjs does, and is what
   this number came from. */
.roster-zone {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  flex: 1 1 200px;
  list-style: none;
  margin: 0;
  min-height: 4rem;
  min-width: 0;
  padding: .5rem;
}
/* One row: the name-and-count label, then (real groups only) the sort select
   and the remove button. Wraps rather than squeezing the label, since the
   count is the part a coach reads. */
.roster-zone-heading {
  align-items: center;
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  font-size: var(--text-sm);
  font-weight: 700;
  gap: .25rem;
  margin: 0 0 .5rem;
  text-transform: uppercase;
}
.roster-zone-heading [data-roster-group-label], .roster-zone-heading [data-zone-heading] { flex: 1 1 auto; min-width: 0; }
.roster-sort-form { margin: 0; }
/* 2026-10-27 (owner): every column's sort select gets a visible heading. The
   column heading is uppercase and bold; the label must read as a quiet hint. */
.roster-column-sort-form, .roster-sort-label { align-items: center; display: inline-flex; gap: .25rem; }
.roster-sort-label { color: var(--muted); font-size: var(--text-xs); font-weight: 500; letter-spacing: 0; margin: 0; text-transform: none; white-space: nowrap; }
/* Forgejo #265: the roster's one copy button -- "Kopieren" plus an arrow that
   opens Farben / Gruppen / Team-Reihen. Anchored right: it is the last thing
   in the summary row. */
.roster-copy-menu { align-items: center; display: inline-flex; gap: .25rem; position: relative; }
.roster-copy-menu-toggle { padding-inline: .5rem; }
.roster-copy-menu-panel {
  background: var(--bg); border: 1px solid var(--line-strong); border-radius: var(--radius-md); box-shadow: 0 .5rem 1rem var(--shadow-tint);
  display: grid; gap: .1rem; margin-top: .25rem; min-width: 11rem; padding: .25rem; position: absolute; right: 0; top: 100%; z-index: 40;
}
.roster-copy-menu-panel[hidden] { display: none; }
.roster-copy-menu-panel button {
  background: none; border: 0; border-radius: var(--radius-sm); color: var(--ink); cursor: pointer; font: inherit;
  font-size: var(--text-sm); font-weight: 600; min-height: 36px; padding: .25rem .5rem; text-align: left; white-space: nowrap;
}
.roster-copy-menu-panel button:hover { background: var(--control-hover); }
.roster-copy-menu-panel button:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }
/* Forgejo #263: "+ Gruppe hinzufügen" in the unassigned strip's heading, and
   the group's name edited right in its heading. */
.roster-zone-heading .roster-group-add { margin-left: auto; }
.roster-zone-heading .roster-group-add .btn { text-transform: none; }
.roster-group-name-input {
  background: transparent; border: 1px solid transparent; border-radius: var(--radius-sm); color: var(--ink);
  flex: 1 1 7rem; font: inherit; font-weight: 700; min-height: 32px; min-width: 0; padding: .15rem .25rem; text-transform: none;
}
.roster-group-name-input:hover { border-color: var(--line-strong); }
.roster-group-name-input:focus-visible { background: var(--bg); border-color: var(--primary); outline: 2px solid var(--focus-ring); outline-offset: 0; }
/* One control for the whole Hell/Dunkel board, above the columns -- a zone is
   not a record, so unlike the Gruppen columns there is exactly one of these
   per page and it can carry a real <label for>. */
.roster-board-toolbar { display: flex; justify-content: flex-end; padding: .5rem 0 .75rem; }
.roster-board-sort-form { align-items: center; display: flex; gap: .5rem; }
.roster-board-sort-form .form-label { margin: 0; }
/* The options are words, not a heading -- the column's uppercase must not
   reach into the select. */
.roster-sort-form select {
  font-size: var(--text-xs);
  max-width: 9.5rem;
  min-height: var(--control-height);
  /* #250: room on the right for the arrow, so "Spielstärke" never runs into it. */
  padding: .25rem 2rem .25rem .5rem;
  text-transform: none;
}
/* flex-wrap is the other half of the same measurement: below ~360px the
   number + name + badges + five controls genuinely do not fit on one line, so
   the controls drop to a second one rather than forcing the whole page wider.
   Verified live at 320/390/430 via page.addStyleTag before it was written
   here, not reasoned about. */
.roster-chip {
  align-items: center;
  flex-wrap: wrap;
  min-width: 0;
  border-radius: var(--radius-sm);
  cursor: grab;
  display: flex;
  font-size: var(--text-base);
  gap: .25rem;
  justify-content: space-between;
  margin: 0 0 .25rem;
  padding: .25rem .5rem;
}
.roster-chip.is-dragging { opacity: .5; }
/* Forgejo #266: a chip is ONE row (number, name, the four badge columns,
   zone menu, trash) wherever its column has room -- the 28rem columns above
   are sized for that (owner 2026-10-26: two rows read badly). Only a column
   narrower than 25rem of content (a phone) falls back to two rows: name and
   controls on top, the badge columns flush under the name. */
.roster-zone { container: roster-column / inline-size; }
/* A very long name wraps onto a second line rather than being cut (measured
   on DEV: "Pochpatchara Suchowaraphan" was cut at 390 and 1024px). Only that
   chip grows; every other one stays a single row. */
.roster-zone > .roster-chip > .roster-chip-name { overflow-wrap: anywhere; text-overflow: clip; white-space: normal; }
@container roster-column (max-width: 25rem) {
  .roster-zone > .roster-chip {
    display: grid; gap: .2rem .25rem; grid-template-areas: "num avatar name trial ctrl" ". . tags tags tags";
    grid-template-columns: auto auto minmax(0, 1fr) auto auto;
  }
  .roster-zone > .roster-chip > .trial-badge { grid-area: trial; }
  .roster-zone > .roster-chip > .roster-chip-number { grid-area: num; }
  /* Forgejo #287: the avatar keeps its place before the name; without one the column is empty. */
  .roster-zone > .roster-chip > .player-avatar { grid-area: avatar; }
  .roster-zone > .roster-chip > .roster-chip-name { grid-area: name; }
  .roster-zone > .roster-chip > .roster-chip-tags { grid-area: tags; }
  .roster-zone > .roster-chip > .roster-chip-controls { grid-area: ctrl; }
}
.roster-chip-unassigned { background: var(--bg); box-shadow: inset 0 0 0 1px var(--line); }
/* Owner 2026-11-03 (mockup: neutral player cards, the colour lives in the panel
   head): a chip is the quiet card and says its colour with a stripe on the left,
   so a column of 25 players no longer reads as a wall of slate. The Team board's
   fit frame (.roster-line-chip[data-fit]) replaces the stripe there. */
.roster-chip-light { background: var(--bg); box-shadow: inset 0 0 0 1px var(--line), inset 4px 0 0 var(--zone-light-mark); color: var(--ink); }
.roster-chip-dark { background: var(--bg); box-shadow: inset 0 0 0 1px var(--line), inset 4px 0 0 var(--zone-dark-bg); color: var(--ink); }
/* The per-chip <details> text menu was replaced by always-visible toggle
   buttons plus a trash button, so those three rules are gone with it. */
.roster-chip-number { flex: 0 0 auto; font-variant-numeric: tabular-nums; min-width: 1.4rem; opacity: .65; text-align: right; }
/* A chip without a number yet (built in JS after a paste, before
   renumberZones) keeps the slot in the markup -- the ERB partial and
   buildChipElement stay structurally identical -- but it takes no space. */
.roster-chip-number:empty { display: none; }
/* The name IS the control (2026-09-27; since #252 a button that opens the
   Spielerdaten dialog, hence the button resets below). It inherits the chip's own colour
   rather than taking a link colour: a chip is one of three backgrounds, and
   .roster-chip-dark is nearly black, where any default link blue is
   unreadable. The affordance is carried by a quiet underline instead, solid on
   hover. The flex sizing stays on this element -- it is still the flex child
   that has to absorb a long name. */
.roster-chip-name {
  background: none;
  border: 0;
  color: inherit;
  cursor: pointer;
  font: inherit;
  padding: 0;
  text-align: left;
  /* A real basis rather than auto: it is what lets the name keep a usable
     width while the row wraps around it. */
  flex: 1 1 6rem;
  min-width: 0;
  overflow: hidden;
  text-decoration: underline;
  text-decoration-style: dotted;
  text-decoration-thickness: 1px;
  text-overflow: ellipsis;
  text-underline-offset: 2px;
  white-space: nowrap;
}
.roster-chip-name:hover { text-decoration-style: solid; }
.roster-chip-name:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
/* margin-left: auto keeps the controls hard right once the row wraps --
   justify-content: space-between on the <li> stops applying per-line. */
.roster-chip-controls { display: flex; flex: 0 0 auto; flex-wrap: wrap; gap: .15rem; justify-content: flex-end; margin-left: auto; }
.roster-zone-button {
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: inherit;
  cursor: pointer;
  font: inherit;
  font-size: var(--text-xs);
  font-weight: 700;
  line-height: 1;
  min-height: 24px;
  min-width: 24px;
  opacity: .6;
  padding: 0 .25rem;
}
.roster-zone-button:hover { opacity: 1; }
/* Forgejo #253: one button with the current colour opens the other choices. */
.roster-zone-menu { position: relative; }
.roster-zone-current { align-items: center; border-color: currentColor; display: inline-flex; justify-content: center; list-style: none; opacity: 1; }
.roster-zone-current::-webkit-details-marker { display: none; }
.roster-zone-menu-panel {
  background: var(--bg); border: 1px solid var(--line-strong); border-radius: var(--radius-md);
  box-shadow: 0 .5rem 1rem var(--shadow-tint); color: var(--ink); display: grid; gap: .1rem;
  margin-top: .2rem; min-width: 8.5rem; padding: .25rem; position: absolute; right: 0; top: 100%; z-index: 30;
}
.roster-zone-option {
  background: none; border: 0; border-radius: var(--radius-sm); color: inherit; cursor: pointer; font: inherit;
  font-size: var(--text-sm); font-weight: 650; min-height: 32px; padding: .2rem .5rem; text-align: left;
}
.roster-zone-option[hidden] { display: none; }
.roster-zone-option.is-minor { color: var(--muted); font-size: var(--text-xs); font-weight: 500; }
.roster-zone-option:hover { background: var(--control-hover); }
.roster-zone-option:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }
.roster-zone-button:focus-visible, .roster-chip-remove:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
/* Set apart from the zone toggles, and only fully opaque on hover: a
   mis-click here during drag-heavy work deletes a roster row. */
/* Forgejo #92: deliberately NOT given the 44px hit-area overlay the other
   dense controls get, and deliberately still under 44px. This is a DESTRUCTIVE
   control sitting a few px from the chip's zone buttons and its name, inside a
   ~30px-tall chip. A centred 44px area would reach ~10px past the button on
   every side and swallow clicks meant for its siblings -- turning a
   hard-to-hit button into an easy-to-hit delete, which is strictly worse than
   the small target it was meant to fix. 30px fits the chip's existing height
   budget without growing it. Same reasoning for `.token-chip button`. */
.roster-chip-remove {
  background: transparent;
  border: 0;
  color: inherit;
  cursor: pointer;
  display: inline-flex;
  margin-left: .25rem;
  min-height: 30px;
  min-width: 30px;
  opacity: .5;
  padding: 0;
  place-items: center;
}
.roster-chip-remove:hover { color: var(--danger); opacity: 1; }
.roster-chip-remove svg { display: block; }
.segment-add-tab {
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: var(--radius-md) var(--radius-md) 0 0;
  color: var(--primary-dark);
  cursor: pointer;
  display: inline-flex;
  flex: 0 0 auto;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 700;
  gap: .25rem;
  height: 40px;
  justify-content: center;
  padding: 0 .75rem;
  transition: background-color 180ms var(--ease-out), border-color 180ms var(--ease-out);
}
.segment-add-tab:not([aria-selected="true"]):hover { background: var(--control-hover); }
.segment-add-tab[aria-selected="true"] { background: var(--bg); box-shadow: var(--shadow-tab-seam); color: var(--ink); }
.segment-workspace { border: 1px solid var(--line); border-top: 0; overflow: clip; }
.segment-workspace[hidden] { display: none; }
.segment-workspace-header {
  align-items: center;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  min-height: 72px;
  padding: .75rem 1rem;
}
.segment-workspace-header h3 { font-size: var(--text-md); margin: 0; }
.segment-workspace-header p { color: var(--muted); font-size: var(--text-sm); margin: .2rem 0 0; }
/* The collapsed segment's coaching intent. `notes` is an uncapped text
   column, so it is clamped rather than allowed to grow the header. */
.segment-intent {
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  display: -webkit-box;
  line-clamp: 2;
  overflow: hidden;
}
.segment-intent[hidden] { display: none; }
/* Collapsed only -- an expanded segment has the same text in its Coaching intent
   field a few lines below, and the two one scroll apart read as a bug. */
.segment-workspace[data-mode="edit"] .segment-intent { display: none; }
.segment-workspace-toggle {
  cursor: pointer;
  transition: background-color 180ms var(--ease-out);
}
.segment-workspace-toggle:hover { background: var(--control-hover); }
.segment-header-toggle { border-radius: var(--radius-sm); outline: none; }
.segment-header-toggle:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 3px;
}
.segment-workspace-actions { align-items: center; display: flex; gap: .5rem; }
.segment-workspace-actions .btn { gap: .5rem; }
/* Icon-only at every width, like the station rows below it: the whole header
   is already the control, this is what makes that legible. */
.segment-expand-button {
  align-items: center;
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  color: var(--ink);
  cursor: pointer;
  display: inline-flex;
  justify-content: center;
  min-height: 40px;
  min-width: 40px;
  padding: .5rem;
}
.segment-expand-button:hover { background: var(--control-hover); }
.segment-expand-button:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 1px; }
.segment-expand-button b { color: var(--muted); transition: transform 180ms var(--ease-out); }
.segment-workspace[data-mode="edit"] .segment-expand-button b { transform: rotate(180deg); }
/* 2026-10-07 (owner request): an expanded segment's settings open at full
   width above its diagram, never as a left column beside it. The two-column
   split (2026-09-22, at >=1180px) cost the canvas a third of its width for
   a short metadata detour; the settings form keeps its own two inner
   columns (.segment-settings). */
.segment-body { display: grid; grid-template-columns: minmax(0, 1fr); }
/* Forgejo #341 (gate 10-07 lifted 2026-11-10, contract CP-LAY-01/02): from
   900 px the rink stands sticky beside the station list, so choosing a station
   never scrolls the rink away. Below, one column. */
.segment-stage { display: grid; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 900px) {
  .segment-stage { align-items: start; grid-template-columns: minmax(0, 11fr) minmax(0, 9fr); }
  .segment-stage > .segment-body { position: sticky; top: calc(var(--workspace-bars, 7.5rem) + .25rem); }
  .segment-stage-list { border-left: 1px solid var(--line); }
  .segment-stage-list > .segment-stations-heading { border-top: 0; }
}
/* Forgejo #341 (CP-LAY-02): an open station's head sticks below the bars while
   its fields scroll past. Below 900 px it also shows a mini rink with the
   station's area; from 900 px the stage's own rink stands beside the list. */
.station.is-expanded > .station-summary { background: var(--bg); position: sticky; top: var(--workspace-bars, 7.5rem); z-index: 2; }
.station-head-map { display: none; }
@media (max-width: 899px) {
  .station.is-expanded .station-head-map { display: block; flex: 0 0 auto; height: 2.25rem; width: 4.5rem; }
}
/* Two columns: the four short attributes stack on the left, Notes fills the
   right and stretches to the same height (align-items: stretch, so the
   textarea grows instead of the column ending short). */
.segment-settings {
  align-items: stretch;
  display: grid;
  gap: 1rem;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
.segment-field-stack { display: grid; gap: .5rem; }
.segment-field-notes { display: flex; flex-direction: column; }
.segment-field-notes textarea.form-control { flex: 1; }
.field-icon { color: var(--muted); flex: 0 0 auto; }
/* Every control in this form is the same height, dropdown or free text --
   .form-control's min-height is only a floor, and a <select>'s intrinsic
   height differs from a number input's. */
.segment-control { height: 44px; min-height: 44px; padding-block: 0; }
.segment-duration-control { display: flex; gap: .5rem; }
.segment-duration-control .segment-control { min-width: 0; }
.segment-duration-preset { flex: 0 0 7.5rem; }
.segment-edit-fields, .segment-new-fields { background: var(--bg); border-bottom: 1px solid var(--line); padding: 1rem; }
.segment-edit-fields[hidden] { display: none; }
.segment-edit-actions { display: flex; grid-column: 1 / -1; justify-content: flex-end; }
.segment-new-workspace .segment-workspace-header { background: var(--bg); }
/* The add-segment form is the same two columns plus its own submit, which
   spans both underneath them. */
.segment-new-fields { border-bottom: 0; }
.segment-new-fields .btn-primary { grid-column: 1 / -1; justify-self: end; }
/* 2026-10-07: the Add station / Add drill buttons sit on the left, right
   after the heading, instead of at the far end of the row. */
.segment-stations-heading {
  align-items: center;
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: .75rem 1.25rem;
  justify-content: flex-start;
  padding: .75rem 1rem;
}
.segment-stations-heading h4 { font-size: var(--text-base); margin: 0; }
.segment-stations-heading p { color: var(--muted); font-size: var(--text-sm); margin: .1rem 0 0; }
.segment-workspace [id$="-ice-context"] { padding: 1rem; }

/* 2026-10-07: the segment diagram reads as its own section -- a card inset
   from the segment's edges with a real heading -- rather than as more of
   the settings above it. */
.segment-workspace .diagram-workspace {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-2xl);
  margin: 1rem;
  padding: 0 0 .75rem;
}
.segment-diagram-heading {
  align-items: center;
  border-bottom: 1px solid var(--line);
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  margin-bottom: .75rem;
  padding: .75rem 1rem .75rem;
}
.segment-diagram-heading h4 { color: var(--ink); font-size: var(--text-lg); font-weight: 800; margin: 0; }
.segment-diagram-heading p { color: var(--muted); font-size: var(--text-sm); margin: .15rem 0 0; }
.segment-workspace .diagram-stations-panel,
.segment-workspace .diagram-layout-panel { padding-inline: .75rem; }
.copy-diagram-control { align-items: center; display: flex; flex: 0 1 auto; gap: .5rem; }
.copy-diagram-control select { flex: 0 1 12rem; min-width: 8rem; width: auto; }
.copy-diagram-control > button { flex: 0 0 auto; }

/* One rink per segment; what is drawn on it comes from the segment's own
   show_station_overlay / show_layout_overlay switches. */
.segment-rinks { margin-top: 1rem; }
/* Forgejo #249: the session page's fold around a segment's rink. */
.segment-diagram { margin-top: .75rem; }
/* Forgejo #256: the Team's "Mitkopieren" ticks. */
.roster-team-snippets { border: 0; display: grid; gap: .5rem; margin: 1rem 0 0; padding: 0; }
.roster-team-snippet { align-items: flex-start; border-radius: var(--radius-lg); justify-content: flex-start; text-align: left; }
.roster-team-snippet span { display: grid; gap: .1rem; }
.roster-team-snippet small { color: var(--muted); font-size: var(--text-xs); }
/* 2026-10-26: the change note and its suggestion; multi-line texts keep their lines. */
.roster-change-note { field-sizing: content; min-height: 3.5rem; width: 100%; }
.roster-change-note-suggestion { align-items: flex-start; display: flex; flex-wrap: wrap; gap: .5rem; justify-content: space-between; }
.roster-change-note-suggestion p, .roster-change-note-text { color: var(--muted); font-size: var(--text-sm); margin: 0; white-space: pre-line; }
.roster-change-note-suggestion p { flex: 1 1 16rem; }
/* Like .metric-protocol-new: a wrapping label let the heading squeeze the
   menu below its button, whose right-anchored panel then hung 36px past a
   1024px window (measured on DEV). */
.snippet-new > summary { white-space: nowrap; }
.snippet-list { display: grid; gap: .75rem; list-style: none; margin: 0; padding: 0; }
.snippet-row { display: grid; gap: .5rem; padding: 1rem; }
.snippet-row-head { align-items: center; display: flex; flex-wrap: wrap; gap: .5rem; }
.snippet-row-head .form-control { flex: 1 1 12rem; min-width: 0; }
.snippet-row-head form { margin-left: auto; }
.segment-diagram-body { padding: 0 1rem 1rem; }
.segment-diagram-body .segment-rinks { margin-top: .5rem; }
.segment-rink-switches {
  align-items: center;
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin: 0 1rem;
  padding: 0 0 .5rem;
}
/* On the read-only session page the same switches sit inside a run-sheet
   article, which has no 1rem gutter to align to. (.no-print itself is
   already handled by the print stylesheet further down.) */
.run-sheet .segment-rink-switches { border-bottom: 0; margin: .75rem 0 0; padding: 0; }
.segment-rink-switches > .form-label { margin: 0 .25rem 0 0; }
.segment-rink-switches > small { color: var(--muted); font-size: var(--text-xs); }
/* 2026-10-07: inside the editor's Edit & arrange row. */
.segment-rink-switches.segment-rink-switches-compact { border-bottom: 0; flex-wrap: nowrap; margin: 0; padding: 0; }
.ice-map { margin: 0; }
.ice-map svg {
  background: var(--ice-surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  display: block;
  max-height: var(--rink-max-height);
  width: 100%;
}
.ice-map-legend { display: flex; flex-wrap: wrap; gap: .25rem; margin-top: .5rem; }
.ice-map-legend > button, .ice-map-legend > span { align-items: center; background: transparent; border: 0; border-radius: var(--radius-sm); color: var(--ink); display: flex; font: inherit; font-size: var(--text-sm); gap: .25rem; min-height: 36px; padding: .25rem .5rem; }
.ice-map-legend button { cursor: pointer; }
.ice-map-legend button:hover { background: var(--surface); }
.ice-map-legend button:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 1px; }
.ice-map-legend i { background: var(--station-color); border-radius: 50%; height: .55rem; width: .55rem; }
.ice-map-allocation[role="button"] { cursor: pointer; outline: none; }
.ice-map-allocation rect { transition: fill-opacity 180ms var(--ease-out), stroke-width 180ms var(--ease-out); }
.ice-map-allocation[role="button"]:hover rect, .ice-map-allocation[role="button"]:focus rect, .ice-map-allocation.is-selected rect { fill-opacity: .42; stroke-width: .75; }
/* Forgejo #97. The other four `outline: none` sites all pair with a real
   replacement (.form-control's 3px ring, .token-control's :focus-within ring,
   .segment-header-toggle's :focus-visible, and the editor canvas, which is
   deliberate and desktop-only). This one did not: a 1px drop-shadow in
   --focus-ring (34% alpha) is not a visible focus indicator on an SVG rect.
   Drive the rect's own stroke instead -- it is already the shape's outline and
   already has a transition, so focus reads the same way selection does. */
.ice-map-allocation:focus-visible rect {
  stroke: var(--primary);
  stroke-width: 1.4;
}

/* One panel visible at a time: the live editable canvas
   (.diagram-layout-panel) on the Layout tab, the combined read-only preview
   (diagram + station allocations, .diagram-stations-panel) on Stations. */
.segment-workspace-actions .btn-primary { gap: .5rem; }
/* Layout/Stations are a tab strip with the same shape as .segment-tabs above,
   but in a cool "ice" hue instead of the warm segment palette -- the level
   change (inside a segment, not between segments) has to be readable at a
   glance. Own class names, never .segment-tab: a page-wide controller test
   asserts exactly one selected button.segment-tab on the builder page. */
/* The editor is open as soon as a segment is. Viewport width is the one gate:
   editing is desktop-only (editor--stage's own 1024px rule), so below it the
   station map stands in for the editor; from 1024px the editor draws the stations itself. */
.diagram-workspace .diagram-layout-panel { display: none; }
@media (min-width: 1024px) {
  .diagram-workspace .diagram-layout-panel { display: block; }
  .diagram-workspace .diagram-stations-panel { display: none; }
}
/* Forgejo #242: the segment's Einordnung, one line under its meta. */
.segment-classification-text, .segment-classification .form-hint { color: var(--muted); font-size: var(--text-xs); margin: .15rem 0 0; overflow-wrap: anywhere; }
.station-map-lines { font-size: var(--text-sm); list-style: none; margin: .5rem 0 0; padding: 0; }
.session-classification { margin-top: 1rem; }
.conflict-warning {
  align-items: baseline;
  background: var(--warning-bg);
  color: var(--warning-ink);
  display: flex;
  font-size: var(--text-sm);
  gap: .5rem;
  margin: .75rem 0;
}
.conflict-warning ul { margin: 0; padding-left: 1rem; }

/* Forgejo #12: segment-level rotation matrix, replacing the old per-station
   "Group rotation" <select>. */
.rotation-matrix-section { margin: 1rem 0; }
.rotation-matrix-header {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  justify-content: space-between;
  margin-bottom: .5rem;
}
.rotation-matrix-header h4 { margin: 0; }
/* Forgejo #270: "verteilen mit" + the button in one row. */
.rotation-generate-form { align-items: center; display: inline-flex; flex-wrap: wrap; gap: .5rem; }
.rotation-generate-form select { width: auto; }
.rotation-matrix-section > summary small { color: var(--muted); font-weight: 400; margin-left: .5rem; }
.rotation-matrix-form { overflow-x: auto; }
.rotation-matrix { border-collapse: collapse; font-size: var(--text-sm); min-width: 100%; }
.rotation-matrix th, .rotation-matrix td { padding: .25rem .5rem; text-align: left; }
.rotation-matrix thead th { border-bottom: 1px solid var(--line); color: var(--muted); font-weight: 600; }
.rotation-matrix tbody th { border-right: 1px solid var(--line); font-weight: 600; white-space: nowrap; }
.rotation-matrix td select { min-width: 8rem; }
.duplicate-banner {
  align-items: center;
  background: var(--surface-strong);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  display: flex;
  flex-wrap: wrap;
  font-size: var(--text-base);
  gap: .75rem;
  justify-content: space-between;
  margin: 0 0 1rem;
  padding: .75rem 1rem;
}

.stations-group + .stations-group { border-top: 1px solid var(--line); }
.stations-group:not(:has(.stations > *)) { display: none; }
/* Forgejo #341: parallel stations and sequential drills stack again -- in the
   stage's list column, two lists side by side (2026-10-07) were too narrow. */
.stations-group-heading {
  color: var(--muted);
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: .02em;
  margin: 0;
  padding: .75rem 1rem 0;
  text-transform: uppercase;
}
.stations { padding: 0 1rem 1rem; }
.station-arrangement-badge {
  align-items: center;
  background: var(--surface-strong);
  border-radius: var(--radius-sm);
  display: inline-flex;
  font-size: var(--text-base);
  height: 1.4rem;
  justify-content: center;
  margin-right: .25rem;
  width: 1.4rem;
}
.station { border-top: 1px solid var(--line); transition: background-color 180ms var(--ease-out); }
/* Clears the sticky app header plus the (now compact) .builder-save-bar, so
   clicking a station on the ice map scrolls its heading into view instead of
   under the save bar. */
.station { scroll-margin-top: calc(var(--workspace-bars, 7.5rem) + .5rem); } /* #340: the measured sticky bars */
.station:last-child { border-bottom: 1px solid var(--line); }
.station.is-selected { background: oklch(0.97 0.02 245); }
.station-summary {
  align-items: center;
  display: flex;
  gap: .25rem;
  list-style: none;
  min-height: 68px;
  padding: .5rem .25rem;
}
.station-number {
  align-items: center;
  background: var(--surface-strong);
  border-radius: var(--radius-sm);
  display: flex;
  font-size: var(--text-sm);
  font-weight: 800;
  height: 2rem;
  justify-content: center;
  margin-right: .75rem;
  width: 2rem;
  cursor: grab;
  touch-action: none;
}
.station-summary strong, .station-summary small { display: block; }
.station-summary small { color: var(--muted); font-size: var(--text-sm); margin-top: .15rem; }
.station-expand-button {
  align-items: center;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  color: var(--ink);
  cursor: pointer;
  display: inline-flex;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 700;
  gap: .25rem;
  justify-content: center;
  min-height: 40px;
  min-width: 40px;
  padding: .5rem;
}
.station-expand-button:hover { background: var(--control-hover); }
.station-expand-button:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 1px; }
.station-expand-button b { color: var(--muted); font-size: var(--text-md); transition: transform 180ms var(--ease-out); }
.station.is-expanded .station-expand-button b { transform: rotate(180deg); }
.station-remove { flex: 0 0 auto; }
.station-form { background: var(--bg); border-top: 1px solid var(--line); padding: 1rem; }
.station-form > * + * { margin-top: 1rem; }
/* Everything the coach writes on the left, the ice it happens on on the
   right. One column below the same 1024px the rest of the builder uses --
   the ice area picker needs real width and is the first thing to give way. */
.station-form-grid { display: grid; gap: 1.25rem; grid-template-columns: minmax(0, 1fr); }
.station-form-main { display: grid; gap: 1rem; grid-template-columns: minmax(0, 1fr); }
.station-form-side { min-width: 0; }
@media (min-width: 1024px) {
  .station-form-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 26rem); align-items: start; }
}
/* Forgejo #342 (found in Chrome): in the stage's list column the two form
   columns squeezed the fields to ~70 px -- one column there. */
.segment-stage-list .station-form-grid { grid-template-columns: minmax(0, 1fr); }
.station-optional { min-width: 0; }
.station-optional-disclosure > summary {
  align-items: center;
  color: var(--primary-dark);
  cursor: pointer;
  display: flex;
  font-size: var(--text-sm);
  font-weight: 700;
  gap: .25rem;
  list-style: none;
  min-height: 36px;
}
.station-optional-disclosure > summary::-webkit-details-marker { display: none; }
.station-optional-disclosure > summary em { color: var(--muted); font-style: normal; font-weight: 500; }
.station-optional-disclosure > summary:hover { text-decoration: underline; }
.station-optional-disclosure > summary:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.station-optional-disclosure .station-optional-body { padding-bottom: .25rem; }
.station-optional-body > * + * { margin-top: .5rem; }
.station-duration-input { max-width: 7rem; }
.station-form-actions { align-items: center; display: flex; flex-wrap: wrap; gap: .75rem; justify-content: flex-end; }
.choice-row { display: flex; flex-wrap: wrap; gap: .5rem; }
.choice-chip {
  align-items: center;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 999px;
  display: inline-flex;
  font-size: var(--text-sm);
  gap: .5rem;
  min-height: 44px;
  padding: .5rem .75rem;
}
.choice-chip i {
  background: var(--chip-color);
  border-radius: 50%;
  height: .55rem;
  width: .55rem;
}

/* Forgejo #9: utilities first, Edit immediately before Run, Run dominant and
   far right (source order == visual order, no CSS `order` needed).
   2026-09-22: every action in this row is icon-only at every width, so the
   row is one line by construction rather than by hoping labels fit. Since
   2026-11-02 (Forgejo #294) the export menu is icon + chevron and Run carries
   its name; up to six items remain (export, calendar, my stations, share,
   edit, run) and must still fit 390px.
   .action-label is still used by drills#show, which keeps its labels.
   NO overflow here, deliberately: this row is the containing block for both
   .action-menu panels, and any overflow value but visible makes it their
   clipping ancestor too -- overflow-y computes to auto the moment overflow-x
   is set, so a 48px row would clip a panel that hangs entirely below it. */
.session-actions { align-items: center; display: flex; flex-wrap: nowrap; gap: .5rem; position: relative; }
.session-action { flex-shrink: 0; gap: .5rem; padding-left: .5rem; padding-right: .5rem; }
.print-options { align-items: center; display: flex; flex-wrap: nowrap; gap: .4rem; }
/* Forgejo #294: Print and Download PDF sit at the top of the export panel, icon and text. */
.session-export-actions { display: grid; gap: .5rem; }
.session-export-actions .btn { gap: .5rem; justify-content: flex-start; width: 100%; }

/* A disclosure that looks like one more button in the row and opens a panel
   under it. Used twice: the Print/PDF content options, and the share-link
   manager that used to be the last section of the page.

   The <details> is deliberately NOT positioned: the panel resolves against
   .session-actions instead, so `right: 0` right-aligns it under the whole
   row. Anchored to the chevron button -- third of eight -- a 20rem panel
   would start off the left edge of a 390px screen. */
.action-menu { display: inline-flex; }
.action-menu > summary { cursor: pointer; display: inline-flex; list-style: none; }
.action-menu > summary::-webkit-details-marker { display: none; }
.action-menu[open] > summary svg { transform: rotate(180deg); }
.action-menu > summary svg { transition: transform 140ms var(--ease-out); }
.action-menu-panel {
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  box-shadow: 0 1rem 2rem var(--shadow-tint);
  display: grid;
  gap: .5rem;
  max-height: min(70vh, 34rem);
  overflow: auto;
  padding: 1rem;
  position: absolute;
  right: 0;
  top: calc(100% + .35rem);
  width: min(20rem, calc(100vw - 2rem));
  z-index: 30;
}
.action-menu-panel-wide { width: min(34rem, calc(100vw - 2rem)); }
.action-menu-panel .choice-chip { border-radius: var(--radius-lg); justify-content: flex-start; width: 100%; }
.action-menu-panel .choice-chip span { display: grid; text-align: left; }
.action-menu-panel .choice-chip small { color: var(--muted); font-size: var(--text-xs); }
.action-menu-title { font-size: var(--text-sm); font-weight: 750; margin: 0; }
.action-menu-hint { color: var(--muted); font-size: var(--text-xs); margin: 0; }
/* The share manager was written as a top-level page section; inside a panel
   its own heading block and outer padding would be a second frame. */
.action-menu-panel .share-manager { border: 0; margin: 0; padding: 0; }
.action-menu-panel .share-manager-heading h2 { font-size: var(--text-md); }

/* One block, one border: what the session IS -- duration, team, location,
   status, objective, focus points. Objective and focus points used to sit in
   a separate section below a rule, which read as a different kind of thing. */
.session-attributes {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
.session-attributes > div { padding: 1rem; }
.session-attributes span { color: var(--muted); display: block; font-size: var(--text-xs); }
.session-attributes strong { display: block; font-size: var(--text-md); margin-top: .2rem; }
.session-attributes > .session-attribute-wide { grid-column: 1 / -1; padding-top: 0; }
/* Forgejo #272: the coaches in the session page's Essentials, colour dot
   first, the head coach leading. */
.session-coach-chips { display: flex; flex-wrap: wrap; gap: .25rem 1rem; list-style: none; margin: .25rem 0 0; padding: 0; }
.session-coach-chips li { align-items: center; display: inline-flex; font-weight: 650; gap: .25rem; }
.session-coach-chips i { background: var(--person-color); border-radius: 50%; flex: none; height: .65rem; width: .65rem; }
.session-coach-chips small { color: var(--muted); font-weight: 400; }
.session-attributes .session-head-coach { align-items: center; display: flex; gap: .25rem; }
.session-head-coach i { background: var(--person-color); border-radius: 50%; flex: none; height: .65rem; width: .65rem; }
/* #275: the session page's Essentials as compact as the builder's. */
.session-essentials .session-attributes > div { padding: .5rem 1rem; }
.session-essentials .session-attributes > .session-attribute-wide { padding-top: 0; }
.session-intent-objective span { color: var(--muted); display: block; font-size: var(--text-xs); }
.session-intent-objective p { margin: .2rem 0 0; max-width: 70ch; }
.focus-point-chips { display: flex; flex-wrap: wrap; gap: .25rem; margin-top: .25rem; }
.focus-point-chip {
  background: var(--surface-strong);
  border-radius: 999px;
  font-size: var(--text-sm);
  font-weight: 650;
  padding: .2rem .5rem;
}
/* The status control is the one thing on this block whose whole job is to be
   recognisable at a glance, so the trigger IS the badge and the options are
   the same badges. Colours come from the same --status-*-bg/ink tokens as
   every other appearance of a status (pinned by status_colour_parity_test);
   nothing here declares a colour of its own. */
.status-menu { display: inline-block; margin-top: .25rem; position: relative; }
.status-menu > summary {
  align-items: center;
  cursor: pointer;
  display: inline-flex;
  gap: .25rem;
  list-style: none;
  min-height: 32px;
  padding: .25rem .5rem .25rem .75rem;
}
.status-menu > summary::-webkit-details-marker { display: none; }
.status-menu > summary:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.status-menu > summary svg { opacity: .7; transition: transform 140ms var(--ease-out); }
.status-menu[open] > summary svg { transform: rotate(180deg); }
.status-menu-panel {
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-lg);
  box-shadow: 0 .75rem 1.5rem var(--shadow-tint);
  display: grid;
  gap: .25rem;
  /* Right-aligned, not left: the status is the last cell of the attribute
     grid at every width, so a left-anchored panel runs at the screen edge
     (measured flush at r=320 on a 320px phone). Anchored right it opens
     inward from the trigger instead. */
  right: 0;
  max-width: calc(100vw - 2rem);
  min-width: max(100%, 9rem);
  padding: .5rem;
  position: absolute;
  top: calc(100% + .3rem);
  z-index: 20;
}
/* 2026-11-03: the drill FORM puts the picker in the left column, so the
   session switcher's right anchor opened it off-screen to the left. */
.status-picker-field .status-menu-panel { left: 0; right: auto; }
.status-menu-form { display: block; }
/* Inside the session index, .status-menu's nearest overflow ancestor is
   .data-table-wrap -- and that wrap's own overflow-x:auto forces its
   overflow-y to compute as auto too (same rule .session-actions is kept
   overflow-free for). The absolute panel popping out below a row near the
   bottom of the table then lands in that forced scroll region instead of
   being visible, so opening it silently required an internal scroll to
   reach -- reported as "der Container expandiert nicht". Static positioning
   makes the row grow instead, which is what a scroll container can show
   without clipping. Scoped to .data-table so the session page's own
   attribute-block status menu (no overflow ancestor) keeps its popover. */
.data-table .status-menu-panel { margin-top: .25rem; min-width: 0; position: static; }
/* Reads its background/colour from the .status-<key> class it also carries --
   this rule only gives it the size a tap target needs, which .status-badge
   (a label, not a control) deliberately does not have. */
.status-option {
  border: 1px solid transparent;
  border-radius: 999px;
  cursor: pointer;
  font-size: var(--text-sm);
  font-weight: 750;
  min-height: 40px;
  padding: .25rem .75rem;
  text-align: left;
  width: 100%;
}
.status-option:hover { border-color: currentColor; }
.status-option.is-current { border-color: currentColor; }
.status-option.is-current::after { content: " ✓"; }

/* Roster and its two halves, between the attributes and the plan. Collapsed
   by default -- open, it is the tallest thing on the page. */
.session-disclosure { border-bottom: 1px solid var(--line); }
.session-disclosure > summary, .session-subdisclosure > summary {
  align-items: center;
  cursor: pointer;
  display: flex;
  gap: .75rem;
  justify-content: space-between;
  list-style: none;
  min-height: 52px;
  padding: .75rem 1rem;
}
.session-disclosure > summary::-webkit-details-marker,
.session-subdisclosure > summary::-webkit-details-marker { display: none; }
.session-disclosure > summary:hover, .session-subdisclosure > summary:hover { background: var(--surface); }
.session-disclosure > summary:focus-visible,
.session-subdisclosure > summary:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: -3px; }
.session-disclosure-title { display: grid; min-width: 0; }
.session-disclosure-title small { overflow-wrap: anywhere; }
.session-disclosure-title strong { font-size: var(--text-md); }
.session-disclosure-title small { color: var(--muted); font-size: var(--text-sm); }
/* The right-hand side of a disclosure's summary: the actions that stay usable
   while it is collapsed, then the chevron. Kopieren lives here so a coach can
   hand out the two team lists without opening the board (2026-09-27). */
.session-disclosure-actions { align-items: center; display: flex; flex-shrink: 0; gap: .5rem; }
/* The summary is the click target for the whole disclosure, so its own cursor
   must not follow into a real control sitting inside it. */
.session-disclosure-actions .btn { cursor: pointer; }
/* Reserves no width when empty -- an "aria-live" span that is blank most of
   the time must not push the chevron around when it fills. */
.roster-copy-status { color: var(--muted); font-size: var(--text-sm); }
.roster-copy-status:empty { display: none; }
.session-disclosure-body { display: grid; gap: .75rem; padding: 1rem 1.25rem 1.25rem; }
.session-subdisclosure {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.session-subdisclosure > div { padding: 0 1rem 1rem; }

.run-sheet > h2 { font-size: var(--text-md); margin: 0 0 .5rem; }
/* "Practice plan" is the page's main section heading, not a label on a
   toolbar -- it was the same 1rem as the four things around it. */
.run-sheet-heading h2 { margin: 0; }
.run-sheet-heading > div > p { color: var(--muted); font-size: var(--text-sm); margin: .15rem 0 0; }
.run-sheet-heading { align-items: baseline; display: flex; flex-wrap: wrap; gap: .5rem 1rem; justify-content: space-between; }
.rink-display-batch { align-items: center; display: flex; flex-wrap: wrap; gap: .4rem; }
.rink-display-batch > .form-label { margin: 0 .15rem 0 0; }
.rink-display-batch form { display: inline-flex; }
.run-sheet { display: grid; gap: 1rem; margin-top: 1.5rem; }
.run-sheet > article { padding: 1.25rem 1.5rem; }
.run-sheet article > header { display: flex; gap: 1rem; }
.run-sheet time { color: var(--primary-dark); font-size: var(--text-base); font-weight: 800; width: 4rem; }
.run-sheet h3 { font-size: var(--text-md); margin: 0; }
.run-sheet header p { color: var(--muted); font-size: var(--text-sm); margin: .2rem 0; }
.run-stations { display: grid; gap: .5rem; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); margin-top: 1rem; }
.run-stations > div { align-items: center; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-lg); display: flex; gap: .6rem; min-height: 64px; padding: .7rem; }
.run-stations > div > span { align-items: center; background: var(--primary); border-radius: 50%; color: white; display: flex; font-size: var(--text-xs); height: 1.75rem; justify-content: center; width: 1.75rem; }
.run-stations p { display: flex; flex: 1; flex-direction: column; font-size: var(--text-sm); margin: 0; }
.run-stations small, .muted-copy { color: var(--muted); }
/* A segment with no stations and no drills renders no rink at all, which on
   its own just looks like something failed to load -- this says what is
   missing and what it costs the plan. */
.segment-empty {
  background: var(--surface);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  margin-top: 1rem;
  padding: 1rem 1rem;
}
.segment-empty p { color: var(--muted); font-size: var(--text-sm); margin: 0 0 .5rem; }
.segment-empty p strong { color: var(--ink); }
.segment-empty p:last-of-type { margin-bottom: .5rem; }
.segment-empty .text-link { font-size: var(--text-sm); }

@media (max-width: 767px) {
  .app-header { padding: 0 .75rem; }
  .app-mark { margin-right: .5rem; }
  .app-header-right { gap: .4rem; }
  .club-switcher { flex: 0 1 auto; }
  .app-main { padding: 1.25rem .75rem 5rem; }
  .page-heading, .builder-header { align-items: stretch; flex-direction: column; }
  /* Full-width primary CTA on mobile -- but NOT one that is a member of the
     session page's icon-only action row. There it is the 7th of 7 equal
     squares, and width:100% made it 366px wide inside a nowrap row: the page
     became 659px on a 390px screen, which pushed the button past the fold and
     made mobile browsers shrink-to-fit the whole document. Measured with
     Chromium in the DEV container, not guessed. */
  .page-heading .btn-primary:not(.session-action) { width: 100%; }
  /* The row has to hold its widest case on the narrowest phone: a coach who
     is assigned to a station sees EIGHT actions (print, pdf, options,
     calendar, my stations, share, edit, run). At .6rem padding that case
     overflows 390px, and the 7-action case still overflows 320px. Height
     stays 44px; only the horizontal padding gives way. */
  .session-actions, .print-options { gap: .3rem; }
  .session-actions .session-action { padding-inline: .25rem; }
  .form-grid { grid-template-columns: 1fr; }
  .session-row { flex-wrap: wrap; }
  .session-row-actions { flex-shrink: 1; }
  .builder-totals { border-top: 1px solid var(--line); padding-top: .75rem; }
  .builder-save-bar { top: 64px; }
  .builder-save-bar small { display: none; }
  .segment-settings { grid-template-columns: 1fr; }
  .segment-settings .btn, .segment-settings .btn-primary { grid-column: 1 / -1; width: 100%; }
  .segment-new-fields .btn-primary { justify-self: stretch; }
  .segment-workspace-header { align-items: flex-start; }
  .segment-workspace-actions .btn { font-size: 0; min-width: 44px; padding-inline: .75rem; }
  .segment-workspace-actions .btn svg { margin: 0; }
  .station-summary { align-items: center; flex-wrap: wrap; }
  .station-summary > .flex-1 { min-width: calc(100% - 3.5rem); }
  .conflict-warning { align-items: flex-start; flex-direction: column; }
  .session-attributes { grid-template-columns: 1fr 1fr; }
  .form-section { border-radius: var(--radius-2xl); padding: 1rem; }
  .form-actions .btn-primary { width: 100%; }
  .segmented-control { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 6.5rem), 1fr)); width: 100%; }
  .segmented-control span { justify-content: center; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    scroll-behavior: auto !important;
    transition-duration: .01ms !important;
  }
}

/* M7 — rink-side run and per-coach views */
.immersive-body { background: var(--immersive-bg); }
.immersive-main { min-height: 100vh; padding: 0; }

.visually-hidden {
  border: 0;
  clip: rect(0 0 0 0);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

.practice-run {
  background: var(--bg);
  color: var(--ink);
  min-height: 100vh;
}

.run-header {
  background: var(--immersive-bg);
  color: white;
  position: sticky;
  top: 0;
  z-index: 20;
}

/* Forgejo #27: a fixed "story"-style segment marker — never grows/animates
   with elapsed time. That is the stopwatch's job, and since 2026-09-22 the
   stopwatch does not even run unless a coach starts it. */
.run-progress {
  display: flex;
  gap: .2rem;
  padding: .35rem .6rem 0;
}

.run-progress > span {
  background: oklch(1 0 0 / .25);
  border-radius: 999px;
  flex: 1;
  height: 3px;
}

.run-progress > span.is-complete { background: oklch(1 0 0 / .65); }
.run-progress > span.is-current { background: white; }

.run-header-bar {
  align-items: center;
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  min-height: 52px;
  padding: .35rem .9rem .6rem;
}
.run-wall-clock { align-items: center; display: flex; gap: .4rem; opacity: .85; }
.run-wall-clock strong { font-size: var(--text-md); font-variant-numeric: tabular-nums; font-weight: 750; }

/* The stopwatch measures THIS segment, not the hour: elapsed, then what is
   left of the segment's own budget. Past that budget the second number keeps
   counting, signed the other way -- clamping it at zero would remove the one
   thing the clock exists to tell you. */
.run-stopwatch {
  align-items: baseline;
  display: flex;
  gap: .5rem;
  font-variant-numeric: tabular-nums;
}
.run-stopwatch > span:first-child { font-size: var(--text-2xl); font-weight: 800; letter-spacing: -.02em; }
.run-stopwatch > span + span { font-size: var(--text-md); font-weight: 750; opacity: .7; }
/* --warning-bg, not --warning-ink: this sits on the dark immersive header,
   where the ink half of that pair is the invisible one. */
.run-stopwatch.is-overtime > span + span { color: var(--warning-bg); opacity: 1; }
.run-stopwatch > small {
  background: oklch(1 0 0 / .15);
  border-radius: 999px;
  font-size: var(--text-2xs);
  font-weight: 750;
  padding: .12rem .5rem;
  text-transform: uppercase;
}
.practice-run.is-running .run-stopwatch > small { background: var(--primary); }

/* The transport bar is fixed, so the last station has to be able to clear it:
   its own height plus the notch inset, which is not a constant. */
.run-main { margin: 0 auto; max-width: 52rem; padding-bottom: calc(5.5rem + env(safe-area-inset-bottom)); }

.run-segment-head { padding: 1.1rem 1rem .5rem; }
.run-segment-head h1 { font-size: var(--text-2xl); letter-spacing: -.025em; line-height: 1.15; margin: 0; }
.run-segment-intent { font-size: var(--text-md); margin: .45rem 0 0; white-space: pre-line; }
.run-segment-meta {
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  gap: .35rem 1.1rem;
  margin: .55rem 0 0;
}
.run-segment-meta > div { align-items: center; display: flex; gap: .35rem; }
.run-segment-meta dt { display: flex; }
.run-segment-meta dd { font-size: var(--text-md); font-weight: 700; margin: 0; }

.run-segment-map > div { padding: 0 0 .5rem; }
.run-map-note { color: var(--muted); font-size: var(--text-base); margin: 0; padding: .75rem 1rem; }

.run-empty {
  align-items: center;
  display: flex;
  flex-direction: column;
  font-size: var(--text-lg);
  gap: .4rem;
  justify-content: center;
  padding: 3rem 2rem;
  text-align: center;
}
.run-empty span { color: var(--muted); }

/* One disclosure per station, stacked. Collapsed reads as a legible list of
   what this segment is; the coach's own station (Forgejo #14) starts open. */
.run-station-list { display: grid; gap: .5rem; padding: .5rem 1rem 1rem; }
.run-station {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  overflow: hidden;
}
.run-station.is-mine { border-color: var(--primary); }
.run-station.is-sequential { border-style: dashed; }
.run-station > summary {
  align-items: center;
  cursor: pointer;
  display: flex;
  gap: .7rem;
  list-style: none;
  min-height: 60px;
  padding: .6rem .9rem;
}
.run-station > summary::-webkit-details-marker { display: none; }
.run-station > summary:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: -3px; }
/* A triangle, per the owner's own description of the control, rather than the
   registry's stroke chevron -- the registry lives in Ruby and this list is
   built in JS, and a second hand-copied icon path is a parity test waiting to
   happen (see roster_chip_drag.js's TRASH_ICON_PATH). */
.run-station > summary::after {
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-top: 7px solid var(--muted);
  content: "";
  flex-shrink: 0;
  margin-left: auto;
  transition: transform 140ms var(--ease-out);
}
.run-station[open] > summary::after { transform: rotate(180deg); }
.run-station-index {
  align-items: center;
  background: var(--ink);
  border-radius: 50%;
  color: white;
  display: flex;
  flex-shrink: 0;
  font-size: var(--text-sm);
  font-weight: 750;
  height: 1.9rem;
  justify-content: center;
  width: 1.9rem;
}
.run-station.is-mine .run-station-index { background: var(--primary); }
.run-station-headline { display: grid; min-width: 0; }
.run-station-headline strong {
  font-size: var(--text-md);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.run-station-headline small {
  color: var(--muted);
  font-size: var(--text-sm);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.run-station-body { background: var(--bg); border-top: 1px solid var(--line); }

.run-phase-status {
  align-items: center;
  display: flex;
  gap: .6rem;
  justify-content: space-between;
  padding: .75rem 1rem;
}
.run-phase-status > strong { font-size: var(--text-base); }
.run-phase-status > div { display: flex; gap: .35rem; }
.run-phase-status button {
  background: var(--surface-strong);
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  height: 12px;
  padding: 0;
  width: 12px;
}
.run-phase-status button.is-current { background: var(--primary); }

.run-diagram {
  background: var(--ice-surface);
  border-bottom: 1px solid var(--line);
  border-top: 1px solid var(--line);
  position: relative;
}
.run-diagram svg {
  display: block;
  max-height: 52vh;
  min-height: 13rem;
  touch-action: none;
  width: 100%;
}
.run-diagram > span {
  background: oklch(0.18 0.035 255 / .9);
  border-radius: var(--radius-sm);
  bottom: .55rem;
  color: white;
  font-size: var(--text-xs);
  left: 50%;
  padding: .3rem .55rem;
  pointer-events: none;
  position: absolute;
  transform: translateX(-50%);
  white-space: nowrap;
}
.run-phase-description {
  color: var(--muted);
  font-size: var(--text-md);
  line-height: 1.45;
  margin: 0;
  padding: .85rem 1rem 0;
}

.run-assignment {
  background: var(--surface);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.run-assignment > div { min-width: 0; padding: .75rem 1rem; }
.run-assignment span {
  color: var(--muted);
  display: block;
  font-size: var(--text-xs);
  font-weight: 700;
}
.run-assignment strong {
  display: block;
  font-size: var(--text-md);
  margin-top: .15rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.run-instructions {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: 1fr 1fr;
  padding: 1.25rem 1rem 1.5rem;
}
.run-instructions h2 {
  font-size: var(--text-md);
  margin: 0 0 .65rem;
}
.run-instructions ul {
  font-size: var(--text-md);
  line-height: 1.45;
  margin: 0;
  padding-left: 1.25rem;
}
.run-instructions li + li { margin-top: .45rem; }
.run-instructions li.is-empty { color: var(--muted); list-style: none; margin-left: -1.25rem; }
.run-instructions > p {
  background: var(--surface);
  border-radius: var(--radius-lg);
  font-size: var(--text-md);
  grid-column: 1 / -1;
  line-height: 1.45;
  margin: 0;
  padding: .85rem 1rem;
}

/* The transport bar. Fixed, because it is the one thing reached for with
   gloves on while the plan above it scrolls. */
.run-transport {
  background: var(--immersive-bg);
  bottom: 0;
  color: white;
  display: flex;
  gap: .4rem;
  justify-content: center;
  left: 0;
  padding: .5rem .75rem calc(.5rem + env(safe-area-inset-bottom));
  position: fixed;
  right: 0;
  z-index: 25;
}
.run-transport-button {
  align-items: center;
  background: oklch(1 0 0 / .1);
  border: 0;
  border-radius: var(--radius-lg);
  color: white;
  cursor: pointer;
  display: flex;
  flex: 1 1 0;
  justify-content: center;
  max-width: 5rem;
  min-height: 48px;
  text-decoration: none;
}
.run-transport-button:disabled { cursor: default; opacity: .35; }
.run-transport-button:not(:disabled):active { background: oklch(1 0 0 / .22); }
.run-transport-button:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.run-transport-start:not(:disabled) { background: var(--primary); }
.run-transport-stop:not(:disabled) { background: var(--danger); }
.run-transport-close { margin-left: .6rem; }

.coach-view {
  margin: -2rem auto -4rem;
  max-width: 52rem;
  min-height: calc(100vh - 56px);
}
.coach-view-header {
  align-items: center;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  display: grid;
  grid-template-columns: 44px minmax(0, 1fr) auto;
  height: 56px;
  position: sticky;
  top: 56px;
  z-index: 15;
}
.coach-view-header > a:first-child {
  align-items: center;
  color: var(--ink);
  display: flex;
  font-size: var(--text-xl);
  height: 44px;
  justify-content: center;
  text-decoration: none;
}
.coach-view-header > div { display: flex; flex-direction: column; min-width: 0; }
.coach-view-header strong, .coach-view-header span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.coach-view-header strong { font-size: var(--text-base); }
.coach-view-header span { color: var(--muted); font-size: var(--text-xs); }
.coach-run-link {
  align-items: center;
  background: var(--primary);
  border-radius: var(--radius-md);
  color: white;
  display: inline-flex;
  font-size: var(--text-sm);
  font-weight: 750;
  min-height: 44px;
  padding: 0 .75rem;
  text-decoration: none;
}

.ice-awareness { border-bottom: 1px solid var(--line); }
.ice-awareness > summary {
  align-items: center;
  cursor: pointer;
  display: flex;
  font-size: var(--text-base);
  font-weight: 700;
  gap: .75rem;
  list-style: none;
  min-height: 48px;
  padding: 1rem 1rem;
}
.ice-awareness > summary span { color: var(--muted); flex: 0 0 auto; }
.ice-awareness > summary strong {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-align: right;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ice-awareness > div { background: var(--bg); padding: .75rem 1rem; }
.ice-awareness p { display: flex; flex-direction: column; font-size: var(--text-base); margin: 0; }
.ice-awareness p + p { margin-top: .75rem; }
.ice-awareness p span { color: var(--muted); }

.coach-timeline { padding: 1rem; }
/* One block per segment: its rink first, then the coach's own assignments in
   it -- the same order Run mode uses, and the reason the assignments start
   closed (the picture above already says what is happening). */
.coach-segment + .coach-segment { border-top: 1px solid var(--line); margin-top: 1.25rem; padding-top: 1.25rem; }
.coach-segment-head { margin-bottom: .5rem; }
.coach-segment-head h2 { font-size: var(--text-lg); letter-spacing: -.02em; margin: 0; }
.coach-segment-head p {
  align-items: center;
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  font-size: var(--text-sm);
  gap: .25rem;
  margin: .2rem 0 0;
}
.coach-segment .segment-rinks { margin-bottom: .5rem; }
.coach-segment-note {
  background: var(--surface);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  color: var(--muted);
  font-size: var(--text-sm);
  margin: 0 0 .5rem;
  padding: .75rem 1rem;
}
.coach-segment-stations > details { position: relative; }
.coach-segment-stations > details::before {
  background: var(--line);
  bottom: 0;
  content: "";
  left: 5.85rem;
  position: absolute;
  top: 0;
  width: 1px;
  z-index: 0;
}
.coach-segment-stations > details > summary {
  align-items: center;
  cursor: pointer;
  display: grid;
  gap: .75rem;
  grid-template-columns: 4.75rem 1rem minmax(0, 1fr) 1rem;
  list-style: none;
  min-height: 74px;
  position: relative;
  z-index: 1;
}
.coach-timeline time {
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  font-weight: 750;
}
.timeline-dot {
  background: var(--coach-color);
  border: 3px solid var(--bg);
  border-radius: 50%;
  height: 1rem;
  width: 1rem;
}
.coach-timeline summary > span:nth-child(3) { min-width: 0; }
.coach-timeline summary strong, .coach-timeline summary small { display: block; }
.coach-timeline summary strong { font-size: var(--text-md); }
.coach-timeline summary small { color: var(--muted); font-size: var(--text-base); margin-top: .2rem; }
.assignment-detail {
  border-bottom: 1px solid var(--line);
  margin-left: 6.5rem;
  padding: .25rem 0 1.5rem;
}
.assignment-detail section { margin-top: 1.25rem; }
.assignment-detail h2 { font-size: var(--text-md); margin: 0 0 .5rem; }
.assignment-detail p, .assignment-detail ul { color: var(--muted); font-size: var(--text-md); line-height: 1.5; margin: 0; }
.assignment-detail li + li { margin-top: .25rem; }
.assignment-awareness {
  background: var(--bg);
  border-radius: var(--radius-lg);
  font-size: var(--text-base);
  padding: .75rem .75rem;
}
.assignment-awareness summary { cursor: pointer; font-weight: 700; }
.assignment-awareness p { font-size: var(--text-base); margin-top: .5rem; }

.coach-phases {
  display: flex;
  gap: .75rem;
  margin: 1.25rem -1rem 0 0;
  overflow-x: auto;
  padding-bottom: .5rem;
  scroll-snap-type: x mandatory;
}
.coach-phases figure {
  flex: 0 0 min(31rem, calc(100vw - 8.5rem));
  margin: 0;
  scroll-snap-align: start;
}
.coach-phases figcaption { font-size: var(--text-base); font-weight: 750; margin-bottom: .25rem; }
.coach-phases svg {
  background: var(--ice-surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  display: block;
  min-height: 13rem;
  touch-action: none;
  width: 100%;
}
.coach-phases figure > p { font-size: var(--text-base); margin-top: .5rem; }
.coach-empty { padding: 4rem 1rem; text-align: center; }
.coach-empty h1 { font-size: var(--text-lg); }
.coach-empty p { color: var(--muted); }

.coach-view-links { margin-top: 1.5rem; }
.coach-view-links h2 { font-size: var(--text-md); margin: 0; }
.coach-view-links > p { color: var(--muted); font-size: var(--text-base); margin: .25rem 0 .75rem; }
.coach-view-links > div { border-top: 1px solid var(--line); }
.coach-view-links a {
  align-items: center;
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  display: flex;
  min-height: 60px;
  padding: .5rem;
  text-decoration: none;
}
.coach-view-links i { background: var(--coach-color); border-radius: 50%; height: .75rem; margin-right: .75rem; width: .75rem; }
.coach-view-links span { display: flex; flex: 1; flex-direction: column; }
.coach-view-links small { color: var(--muted); }

/* 390px is the documented design width (spec §10), but Run mode is the one
   surface held in a glove at the boards -- on a 320px phone the stopwatch and
   the transport bar together were 18px too wide, which is enough to make the
   whole document scroll sideways. Measured, not estimated. */
@media (max-width: 420px) {
  .run-header-bar { gap: .5rem; padding-inline: .6rem; }
  .run-stopwatch { gap: .35rem; }
  .run-stopwatch > span:first-child { font-size: var(--text-lg); }
  .run-stopwatch > small { font-size: var(--text-2xs); padding-inline: .35rem; }
  .run-transport { gap: .25rem; padding-inline: .5rem; }
  .run-transport-close { margin-left: .3rem; }
}

@media (max-width: 767px) {
  .run-instructions { grid-template-columns: 1fr; }
  .run-instructions > p { grid-column: auto; }
  .coach-view { margin: -1.25rem -.75rem -5rem; }
  .coach-view-header { top: 56px; }
  .assignment-detail { margin-left: 0; padding-left: 6.5rem; }
  .coach-phases { margin-right: -1rem; }
}

@media (max-width: 420px) {
  .run-station-heading { min-height: 78px; }
  .run-station-heading h1 { font-size: var(--text-xl); }
  .run-assignment > div { padding: .65rem .75rem; }
  .run-assignment strong { font-size: var(--text-md); }
  .run-instructions { gap: 1.25rem; padding-top: 1rem; }
  .coach-timeline { padding: .75rem; }
  .coach-segment-stations > details::before { left: 4.85rem; }
  .coach-segment-stations > details > summary {
    gap: .5rem;
    grid-template-columns: 4rem .8rem minmax(0, 1fr) .75rem;
  }
  .coach-timeline time { font-size: var(--text-xs); }
  .assignment-detail { padding-left: 5.25rem; }
  .coach-phases figure { flex-basis: calc(100vw - 6.75rem); }
}

/* M8 — share management, public documents, and print surfaces */
.share-manager {
  border-top: 1px solid var(--line);
  margin-top: 2.5rem;
  padding-top: 1.5rem;
}
.share-manager-heading {
  align-items: center;
  display: flex;
  gap: 1rem;
  justify-content: space-between;
}
.share-manager h2, .share-manager h3 { margin: 0; }
.share-manager h2 { font-size: var(--text-md); }
.share-manager h3 { font-size: var(--text-base); }
.share-manager-heading p { color: var(--muted); font-size: var(--text-base); margin: .25rem 0 0; }
.share-create { border-bottom: 1px solid var(--line); margin-top: .75rem; }
.share-create > summary {
  color: var(--primary-dark);
  cursor: pointer;
  font-size: var(--text-base);
  font-weight: 700;
  list-style: none;
  min-height: 44px;
  padding: .75rem 0;
}
.share-form {
  align-items: end;
  display: grid;
  gap: .75rem;
  grid-template-columns: minmax(12rem, 1fr) minmax(12rem, 1fr) auto;
  padding: 0 0 1rem;
}
.coach-share-list, .share-link-list { margin-top: 1.25rem; }
.coach-share-list > div, .share-link-list > div {
  align-items: center;
  border-bottom: 1px solid var(--line);
  display: flex;
  gap: .75rem;
  min-height: 60px;
}
.coach-share-list > div > span {
  background: var(--coach-color);
  border-radius: 50%;
  height: .7rem;
  width: .7rem;
}
.coach-share-list p, .share-link-list p {
  display: flex;
  flex: 1;
  flex-direction: column;
  font-size: var(--text-base);
  margin: 0;
  min-width: 0;
}
.coach-share-list small, .share-link-list small { color: var(--muted); margin-top: .15rem; }
.share-link-list > div.is-revoked { color: var(--muted); min-height: 52px; }
.share-url {
  font-size: var(--text-sm);
  max-width: 20rem;
  width: 40%;
}

/* Forgejo #28 */
.share-password-field { grid-column: 1 / -1; }
.share-password-field > div { margin-top: .5rem; max-width: 16rem; }
.share-url-actions { align-items: center; display: flex; flex: 1; gap: .5rem; min-width: 0; }
.share-url-actions .share-url { flex: 1; max-width: none; width: auto; }

.public-body {
  background: var(--bg);
  color: var(--ink);
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  margin: 0;
  -webkit-font-smoothing: antialiased;
}
.public-main { min-height: 100vh; }
.public-document {
  margin: 0 auto;
  max-width: 68rem;
  padding: 2rem 1.25rem 5rem;
}
.public-document-header {
  align-items: flex-start;
  border-bottom: 1px solid var(--line);
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  padding-bottom: 1.5rem;
}
.public-mark {
  color: var(--primary-dark);
  display: block;
  font-size: var(--text-sm);
  font-weight: 800;
  margin-bottom: .5rem;
}
.public-document-header h1 {
  font-size: var(--text-3xl);
  letter-spacing: -.025em;
  line-height: 1.15;
  margin: 0;
  text-wrap: balance;
}
.public-document-header p { color: var(--muted); margin: .5rem 0 0; }
/* Forgejo #276: logo and Print button share the right edge; on paper only the logo stays. */
.public-document-brand { align-items: center; display: flex; flex: 0 0 auto; gap: .75rem; }
.public-document-logo { display: block; height: 4rem; max-width: 10rem; object-fit: contain; width: auto; }
.public-coach-header .public-document-logo { height: 2.75rem; }
.public-lede {
  font-size: var(--text-lg);
  line-height: 1.55;
  margin: 1.5rem 0;
  max-width: 70ch;
}
.public-facts {
  border-bottom: 1px solid var(--line);
  display: grid;
  gap: 1.5rem 2rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  padding: 1.5rem 0;
}
.public-facts h2, .public-phases > h2, .public-notes h2, .public-plan > h2 {
  font-size: var(--text-md);
  margin: 0 0 .5rem;
}
.public-facts p, .public-facts ul {
  color: var(--muted);
  line-height: 1.5;
  margin: 0;
}
.public-facts ul { padding-left: 1.25rem; }
.public-phases { padding-top: 2rem; }
.public-phases figure { margin: 0; padding: 1rem 0 2rem; }
.public-phases figure + figure { border-top: 1px solid var(--line); }
.public-phases .sequence-player[hidden] + figure { border-top: 0; }
@media print { .public-phases .sequence-player + figure { border-top: 0; } }
.public-phases figcaption {
  align-items: baseline;
  display: flex;
  justify-content: space-between;
  margin-bottom: .5rem;
}
.public-phases figcaption span { color: var(--muted); font-size: var(--text-sm); }
.public-phases svg, .station-diagrams svg {
  background: var(--ice-surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  display: block;
  max-height: 32rem;
  touch-action: none;
  width: 100%;
}
.public-phases figure > p, .station-diagrams figure > p {
  color: var(--muted);
  line-height: 1.5;
  margin: .75rem 0 0;
  white-space: pre-line;
}
.public-notes {
  border-top: 1px solid var(--line);
  max-width: 70ch;
  padding-top: 1.5rem;
}
.public-notes p { color: var(--muted); line-height: 1.5; }

/* Forgejo #277: the printed session's Essentials card and coaching intent. */
.print-essentials {
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  display: grid;
  gap: .55rem 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  margin: 1rem 0 0;
  padding: .7rem 1rem;
}
.print-essentials dt { color: var(--muted); font-size: var(--text-xs); font-weight: 750; letter-spacing: .04em; text-transform: uppercase; }
.print-essentials dd { font-weight: 650; margin: .1rem 0 0; }
.print-intent { margin-top: .9rem; }
.print-intent p { line-height: 1.45; margin: 0 0 .5rem; }
.print-intent strong, .print-chips > strong { font-size: var(--text-sm); margin-right: .4rem; }
.print-chips { align-items: center; display: flex; flex-wrap: wrap; gap: .35rem; }
.print-chips ul { display: flex; flex-wrap: wrap; gap: .35rem; list-style: none; margin: 0; padding: 0; }
.print-chips li { border: 1px solid var(--line-strong); border-radius: 999px; font-size: var(--text-sm); padding: .1rem .6rem; }
.public-plan { margin-top: 1.25rem; }
.plan-overview { list-style: none; margin: 0; padding: 0; }
.plan-overview li {
  align-items: baseline;
  border-top: 1px solid var(--line);
  display: flex;
  gap: 1rem;
  padding: .5rem 0;
}
.plan-overview span { flex: 1; font-weight: 700; }
.plan-overview small { color: var(--muted); font-size: var(--text-sm); }
.public-plan time, .segment-sheet > header time {
  color: var(--primary-dark);
  font-size: var(--text-base);
  font-weight: 800;
  width: 4rem;
}
/* The printed roster: two-view text (zones, then Gruppen), so it is columns of
   short lists rather than anything rink-shaped. */
.print-roster { border-top: 1px solid var(--line); margin-top: 2rem; padding-top: 1.25rem; }
.print-roster h2 { font-size: var(--text-md); margin: 0 0 .75rem; }
.print-roster-groups-heading { font-size: var(--text-md); margin: 1.25rem 0 .5rem; }
/* The zones' own sort line sits ABOVE the columns (there is one mode for all
   three), so it is outside .print-roster-columns and needs the base rule its
   per-group counterpart inherits from there. */
.print-roster-sort { color: var(--muted); font-size: var(--text-xs); margin: 0 0 .3rem; }
.print-roster-columns {
  display: grid;
  gap: 1rem 1.5rem;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}
.print-roster-columns h3, .print-roster-columns h4 { font-size: var(--text-base); margin: 0 0 .35rem; }
.print-roster-columns ol { color: var(--muted); font-size: var(--text-base); line-height: 1.5; margin: 0; padding-left: 1.3rem; }
.print-roster-columns p { color: var(--muted); font-size: var(--text-base); margin: 0; }
/* Two classes, so it outranks the .print-roster-columns p rule above without
   an !important. */
.print-roster-columns .print-roster-sort { font-size: var(--text-xs); margin-bottom: .3rem; }
.print-roster-zone { font-weight: 700; }
.print-roster-tags { font-size: var(--text-sm); }
.print-roster-ungrouped { color: var(--muted); font-size: var(--text-sm); margin: .9rem 0 0; }
/* Forgejo #284: the roster's Team picture, its own page after the cover. */
.print-team-image { border-top: 1px solid var(--line); margin-top: 2rem; padding-top: 1.25rem; }
.print-team-image canvas { display: block; height: auto; margin: 0 auto; max-width: 100%; width: 100%; }
@media print {
  .print-team-image { border-top: 0; break-after: page; break-before: page; margin-top: 0; padding-top: 0; }
  .print-team-image canvas { max-height: 255mm; object-fit: contain; width: auto; }
}
/* Forgejo #209: the Team's line sheet. */
.print-lines { border-top: 1px solid var(--line); margin-top: 2rem; padding-top: 1.25rem; }
.print-lines h2 { font-size: var(--text-md); margin: 0 0 .75rem; }
.print-lines .print-roster-columns { grid-template-columns: minmax(0, 1fr); }
.print-lines-goalies { font-size: var(--text-sm); margin: 0 0 .35rem; }
.print-lines-list { font-size: var(--text-sm); list-style: none; margin: .25rem 0 0; padding: 0; }
.print-lines-list li { border-bottom: 1px solid var(--line); padding: .2rem 0; }

/* Forgejo #277: a segment is a full-width band, its stations and drills are
   indented cards with a stripe -- the two never read alike. */
.segment-sheets { margin-top: 1.5rem; }
.segment-sheet + .segment-sheet { margin-top: 1.5rem; }
.segment-band {
  align-items: baseline;
  background: var(--primary);
  border-radius: var(--radius-md);
  color: white;
  display: flex;
  flex-wrap: wrap;
  gap: .25rem 1rem;
  padding: .5rem 1rem;
  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
}
.segment-sheet > header .segment-band time { color: white; width: auto; }
.segment-sheet > header h2 { font-size: var(--text-xl); margin: 0; }
.segment-sheet > header .segment-band p { color: white; font-size: var(--text-sm); margin: 0 0 0 auto; }
.segment-sheet-notes { padding: 0 1rem; }
.segment-sheet-notes p { color: var(--muted); font-size: var(--text-base); margin: .5rem 0 0; }
.segment-sheet > header .segment-layout-name { color: var(--ink); font-weight: 700; }
.segment-sheet > header .segment-sheet-intent { color: var(--ink); white-space: pre-line; }
/* The segment's own diagram: the overview of the ice, framed and labelled. */
.segment-print-diagram { margin: .75rem 0 0; }
.segment-print-diagram figcaption { color: var(--primary-dark); font-size: var(--text-xs); font-weight: 800; letter-spacing: .06em; margin-bottom: .25rem; text-transform: uppercase; }
.segment-print-diagram .segment-rinks { margin: 0; }
.segment-print-diagram .ice-map svg { border: 2px solid var(--primary); border-radius: var(--radius-md); }
.station-sheet {
  border: 1px solid var(--line-strong);
  border-left: 4px solid var(--primary);
  border-radius: var(--radius-md);
  margin: .75rem 0 0 1rem;
  padding: .75rem 1rem;
}
.station-sheet > header {
  align-items: flex-start;
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  margin-bottom: .5rem;
}
.station-sheet > header span {
  color: var(--primary-dark);
  font-size: var(--text-sm);
  font-weight: 750;
}
.station-sheet > header h3 { font-size: var(--text-lg); margin: .15rem 0; }
.station-sheet > header p, .station-sheet > header > strong {
  color: var(--muted);
  font-size: var(--text-base);
  margin: 0;
}
.station-sheet-body {
  align-items: start;
  display: grid;
  gap: 1rem;
  grid-template-columns: minmax(0, 2fr) minmax(13rem, 1fr);
}
/* Drill phases: the detail, two to a row, plain white ice without the grey
   surround -- unlike the framed segment overview above. */
.station-diagrams {
  display: grid;
  gap: .75rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}
.station-diagrams svg { background: var(--bg); }
.station-diagrams [data-rink-surround] { display: none; }
.station-diagrams figure { margin: 0; }
.station-diagrams figcaption { font-size: var(--text-sm); font-weight: 750; margin-bottom: .25rem; }
.station-sheet aside {
  background: var(--surface);
  border-radius: var(--radius-lg);
  padding: 1rem;
}
.station-sheet aside section + section { border-top: 1px solid var(--line); margin-top: 1rem; padding-top: 1rem; }
.station-sheet h4 { font-size: var(--text-base); margin: 0 0 .5rem; }
/* The station's own objective/focus points. Sits between the header and the
   drill body (station-level, so it is outside that branch) and reads as part
   of the heading block rather than as another aside section. */
.station-intent { margin: .25rem 0 1rem; }
.station-intent p { color: var(--muted); line-height: 1.5; margin: 0 0 .5rem; }
.station-intent p:last-child { margin-bottom: 0; }
.assignment-detail .station-intent h3 { font-size: var(--text-md); margin: 0 0 .5rem; }
.station-sheet aside p, .station-sheet aside ul, .station-custom-notes {
  color: var(--muted);
  font-size: var(--text-base);
  line-height: 1.45;
  margin: 0;
}
.station-sheet aside ul { padding-left: 1rem; }
.station-phase-list { color: var(--muted); font-size: var(--text-base); line-height: 1.45; margin: 0; padding-left: 1.25rem; }
.station-phase-list strong { color: var(--ink); }
.station-phase-list p { margin: .2rem 0 .5rem; white-space: pre-line; }
/* No diagram column exists in list mode, so the notes column takes the row
   rather than leaving an empty 2fr gap where the rink used to be. */
.session-document.is-list .station-sheet-body { grid-template-columns: minmax(0, 1fr); }

.public-coach-view { margin: 0 auto; min-height: 100vh; }
.public-coach-header {
  align-items: center;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  display: grid;
  gap: .75rem;
  grid-template-columns: auto minmax(0, 1fr) auto;
  min-height: 64px;
  padding: .5rem 1rem;
  position: sticky;
  top: 0;
  z-index: 15;
}
.public-coach-header .public-mark { margin: 0; }
.public-coach-header > div { display: flex; flex-direction: column; min-width: 0; }
.public-coach-header > div strong, .public-coach-header > div span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.public-coach-header > div span { color: var(--muted); font-size: var(--text-sm); }

@media (max-width: 767px) {
  .share-form { grid-template-columns: 1fr; }
  .share-form .btn-primary { width: 100%; }
  .share-link-list > div { align-items: flex-start; flex-wrap: wrap; padding: .75rem 0; }
  .share-url { max-width: none; order: 2; width: 100%; }
  .public-document { padding: 1.25rem .75rem 4rem; }
  .public-document-header h1 { font-size: var(--text-2xl); }
  .public-facts, .station-sheet-body, .station-diagrams { grid-template-columns: 1fr; }
  .station-sheet { margin-left: 0; }
  .station-sheet > header { flex-direction: column; }
  .public-coach-header { grid-template-columns: minmax(0, 1fr) auto; }
  .public-coach-header .public-mark { display: none; }
}

@media print {
  @page { margin: 12mm; size: A4 portrait; }

  :root {
    --bg: white;
    --surface: oklch(0.97 0 0);
    --surface-strong: oklch(0.93 0 0);
    --ink: black;
    --muted: oklch(0.36 0 0);
    --line: oklch(0.78 0 0);
  }
  .no-print { display: none !important; }
  .public-document { max-width: none; padding: 0; }
  .public-document-header h1 { font-size: 20pt; }
  /* Forgejo #277 (owner): the print flows -- no fresh page per segment or
     after the cover. A station or drill is never cut, and a segment band
     never stays alone at the foot of a page. */
  .segment-sheets { margin-top: 6mm; }
  .segment-sheet + .segment-sheet { margin-top: 6mm; }
  .segment-sheet > header, .segment-print-diagram figcaption { break-after: avoid; break-inside: avoid; }
  .segment-print-diagram { break-inside: avoid; }
  /* An A4 page is narrower than the phone breakpoint, so the browser's own
     print would pick up the phone rules while Grover's PDF does not: these
     restate the card indent and the phase grid so both look alike. */
  .station-sheet { break-inside: avoid; margin-left: 4mm; }
  /* #277: the aside (coaching points, equipment, notes) is a fixed 45mm so
     the drill gets the rest of the card. */
  .station-sheet-body { gap: 5mm; grid-template-columns: minmax(0, 1fr) 45mm; }
  /* Repeated inside @media print on purpose: the rule above has the same
     specificity and comes later in the file than the screen-block .is-list
     override, so without this a diagram-free PDF -- the main reason the mode
     exists -- would still reserve the empty 2fr diagram track. */
  .session-document.is-list .station-sheet-body { grid-template-columns: minmax(0, 1fr); }
  .station-diagrams { gap: 4mm; grid-template-columns: repeat(auto-fit, minmax(55mm, 1fr)); }
  .station-diagrams figure { break-inside: avoid; }
  /* Measured on DEV (#277): at 70mm a station card took 60% of a page, so
     each one sat alone on its page; at 55mm two fit. The segment overview
     stays the bigger picture. */
  .station-diagrams svg { max-height: 55mm; }
  .segment-print-diagram .ice-map svg { max-height: 80mm; }
  /* A one-phase drill puts its description beside the diagram, not under it:
     measured, the text below added 32mm and kept a station card at 157mm --
     one per page. Side by side it is about 115mm, two per page. */
  .station-diagrams figure:only-child { align-items: start; column-gap: 4mm; display: grid; grid-template-columns: 60mm minmax(0, 1fr); }
  .station-diagrams figure:only-child figcaption { grid-column: 1 / -1; }
  .station-diagrams figure:only-child svg { max-height: 45mm; }
  .station-diagrams figure:only-child > p { margin-top: 0; }
  .public-phases figure { break-inside: avoid; }
  .public-phases svg { max-height: 150mm; }
  .ice-map, .plan-overview li, .station-sheet aside { break-inside: avoid; }
  .print-roster-columns > section { break-inside: avoid; }
  .coach-segment-stations > details { break-inside: avoid; }
  .coach-segment-stations > details > .assignment-detail { display: block; }
  .coach-phases { display: block; overflow: visible; }
  .coach-phases figure { margin-bottom: 5mm; width: 100%; }
}

/* M9 — PDF extraction review */
.status-pending, .status-processing, .status-extracted {
  background: oklch(0.92 0.045 205);
  color: oklch(0.34 0.09 205);
}
.status-needs_review {
  background: var(--warning-bg);
  color: var(--warning-ink);
}
.status-accepted, .status-accepted_text_only {
  background: var(--status-ready-bg);
  color: var(--status-ready-ink);
}
.status-rejected, .status-failed {
  background: oklch(0.94 0.04 25);
  color: oklch(0.38 0.13 25);
}
.import-batch-list, .import-item-list { border-top: 1px solid var(--line); }
.import-batch-list > a, .import-item-list > a {
  align-items: center;
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  display: flex;
  gap: 1rem;
  min-height: 72px;
  padding: .75rem .5rem;
  text-decoration: none;
}
.import-batch-list > a:hover, .import-item-list > a:hover { background: var(--surface); }
.import-batch-list > a > div, .import-item-list > a > div {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-width: 0;
}
.import-batch-list small, .import-item-list small {
  color: var(--muted);
  font-size: var(--text-sm);
  margin-top: .2rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.import-batch-list b { color: var(--muted); font-size: var(--text-sm); }
.import-item-list > a > span:first-child {
  align-items: center;
  background: var(--surface-strong);
  border-radius: var(--radius-sm);
  display: flex;
  font-size: var(--text-sm);
  font-weight: 800;
  height: 2rem;
  justify-content: center;
  width: 2rem;
}

.import-review {
  margin: 0 auto;
  max-width: 96rem;
  padding-bottom: 6rem;
}
.import-review-header {
  align-items: end;
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  margin-bottom: 1.25rem;
}
.import-review-header h1 {
  font-size: var(--text-2xl);
  letter-spacing: -.02em;
  margin: .25rem 0;
}
.import-review-header p { color: var(--muted); margin: 0; }
.import-validation {
  background: var(--warning-bg);
  border-radius: var(--radius-lg);
  color: var(--warning-ink);
  margin-bottom: 1rem;
  padding: .75rem 1rem;
}
.import-validation ul { margin: .5rem 0 0; padding-left: 1.25rem; }
.import-review-workspace {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
.import-source > h2, .import-extraction > h2 {
  font-size: var(--text-base);
  margin: 0 0 .5rem;
}
.import-source > div {
  background: oklch(0.94 0 0);
  border-radius: var(--radius-lg);
  max-height: calc(100vh - 13rem);
  overflow: auto;
}
.import-source img {
  display: block;
  height: auto;
  width: 100%;
}
.import-extraction { min-width: 0; }
.import-diagram-editor {
  border-bottom: 1px solid var(--line);
  margin-bottom: 1rem;
  padding-bottom: 1rem;
}
.import-diagram-editor > svg {
  background: white;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  width: 100%;
}
.import-diagram-editor > aside {
  align-items: start;
  display: flex;
  gap: .75rem;
  justify-content: space-between;
  margin-top: .75rem;
}
.import-diagram-editor > aside ul {
  display: flex;
  flex: 1;
  gap: .5rem;
  list-style: none;
  margin: 0;
  overflow-x: auto;
  padding: 0 0 .5rem;
}
.import-diagram-editor > aside li { flex: 0 0 9rem; }
.import-diagram-editor > aside > span { color: var(--muted); font-size: var(--text-sm); }
.import-viewport-note {
  background: var(--warning-bg);
  border-radius: var(--radius-lg);
  color: var(--warning-ink);
  font-size: var(--text-sm);
  margin-bottom: .75rem;
  padding: .75rem;
}
.import-json-form > * + * { margin-top: .75rem; }
.import-json-form details {
  border-bottom: 1px solid var(--line);
  border-top: 1px solid var(--line);
}
.import-json-form summary {
  cursor: pointer;
  font-size: var(--text-base);
  font-weight: 700;
  min-height: 44px;
  padding: .75rem 0;
}
.import-json {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--text-sm);
  line-height: 1.45;
  min-height: 28rem;
  white-space: pre;
}
.import-review-actions {
  align-items: center;
  background: var(--bg);
  border-top: 1px solid var(--line);
  bottom: 0;
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  left: 0;
  min-height: 68px;
  padding: .75rem max(1rem, calc((100vw - 96rem) / 2));
  position: fixed;
  right: 0;
  z-index: 20;
}
.import-review-actions p { color: var(--muted); font-size: var(--text-sm); margin: 0; }
.import-review-actions > div { display: flex; gap: .5rem; }

@media (max-width: 1023px) {
  .import-review-workspace { grid-template-columns: 1fr; }
  .import-source > div { max-height: 55vh; }
  .import-review-actions { align-items: stretch; flex-direction: column; position: static; }
  .import-review-actions > div { flex-direction: column-reverse; }
  .import-review-actions .btn-primary,
  .import-review-actions .btn,
  .import-review-actions .btn-error { width: 100%; }
  .import-batch-list b { display: none; }
}

/* M11 — season planning */
.season-plan-list { border-top: 1px solid var(--line); }
.season-plan-row {
  align-items: center;
  border-bottom: 1px solid var(--line);
  color: var(--ink);
  display: grid;
  gap: 1rem;
  grid-template-columns: 7rem minmax(0, 1fr) auto;
  min-height: 92px;
  padding: 1rem .5rem;
  text-decoration: none;
}
.season-plan-row:hover { background: var(--surface); }
.season-plan-year { color: var(--primary-dark); font-size: var(--text-md); font-weight: 800; }
.season-plan-row h2 { font-size: var(--text-md); margin: 0; }
.season-plan-row p, .season-plan-row > span { color: var(--muted); font-size: var(--text-sm); margin: .25rem 0 0; }

.season-plan-tabs {
  border-bottom: 1px solid var(--line);
  display: flex;
  gap: 1.25rem;
  margin-bottom: .5rem;
  overflow-x: auto;
}
.season-plan-tab {
  color: var(--muted);
  font-size: var(--text-base);
  font-weight: 650;
  padding: .5rem .1rem;
  text-decoration: none;
  white-space: nowrap;
}
.season-plan-tab.is-active { border-bottom: 2px solid var(--primary); color: var(--ink); }

.season-mobile-note { display: none; }
.season-planner-header {
  align-items: end;
  border-bottom: 1px solid var(--line);
  display: flex;
  gap: 2rem;
  justify-content: space-between;
  padding-bottom: 1.5rem;
}
.season-planner-header h1 { font-size: var(--text-3xl); letter-spacing: -.03em; line-height: 1.1; margin: .5rem 0; }
.season-planner-header > div > p:last-child { color: var(--muted); margin: 0; }
.season-kicker {
  color: var(--primary-dark);
  font-size: var(--text-sm);
  font-weight: 800;
  letter-spacing: .08em;
  margin: 1.5rem 0 0;
  text-transform: uppercase;
}
.season-header-actions { display: flex; gap: .5rem; }
.season-header-actions form { margin: 0; }
.season-brief {
  align-items: start;
  display: grid;
  gap: 2rem;
  grid-template-columns: minmax(0, 1fr) auto;
  margin: 1.5rem 0 2.5rem;
  padding: 1.25rem 1.5rem;
}
.season-brief h2 { font-size: var(--text-sm); margin: 0 0 .25rem; text-transform: uppercase; }
.season-brief p { line-height: 1.5; margin: 0; max-width: 70ch; }
.season-focus-list { align-items: center; display: flex; flex-wrap: wrap; gap: .25rem; }
.season-focus-list span {
  background: var(--chip-bg);
  border-radius: 999px;
  color: var(--accent);
  font-size: var(--text-xs);
  font-weight: 700;
  padding: .25rem .5rem;
}
.season-planner-grid {
  align-items: start;
  display: grid;
  gap: 3rem;
  grid-template-columns: minmax(0, 1fr) 18rem;
}
.season-timeline .section-heading { margin-bottom: 1.5rem; }
.season-block {
  display: grid;
  gap: 1rem;
  grid-template-columns: 2rem minmax(0, 1fr);
  position: relative;
}
.season-block:not(:last-of-type)::before {
  background: var(--line);
  content: "";
  left: .95rem;
  position: absolute;
  top: 2rem;
  bottom: -1rem;
  width: 2px;
}
.season-block-marker {
  align-items: center;
  background: var(--ink);
  border-radius: 50%;
  color: white;
  display: flex;
  font-size: var(--text-xs);
  font-weight: 800;
  height: 2rem;
  justify-content: center;
  position: relative;
  width: 2rem;
  z-index: 1;
}
.season-block-content {
  margin-bottom: 1.25rem;
  min-width: 0;
  padding: 1rem 1.25rem;
}
.season-block-content > header {
  align-items: start;
  display: flex;
  gap: 1rem;
  justify-content: space-between;
}
.season-block-content > header p {
  color: var(--primary-dark);
  font-size: var(--text-xs);
  font-weight: 800;
  margin: 0 0 .25rem;
}
.season-block-content h3 { font-size: var(--text-lg); margin: 0; }
.season-block-notes { color: var(--muted); font-size: var(--text-base); line-height: 1.5; margin: .75rem 0 0; }
.season-session-list { margin-top: 1rem; }
.season-session {
  align-items: start;
  border-top: 1px solid var(--line);
  display: grid;
  gap: .75rem;
  grid-template-columns: 2.75rem minmax(0, 1fr) auto;
  min-height: 82px;
  padding: .75rem 0;
}
.season-session-date { align-items: center; display: flex; flex-direction: column; }
.season-session-date span { color: var(--muted); font-size: var(--text-2xs); font-weight: 800; text-transform: uppercase; }
.season-session-date strong { font-size: var(--text-lg); }
.season-session a { color: var(--ink); font-size: var(--text-base); font-weight: 750; text-decoration: none; }
.season-session a:hover { color: var(--primary-dark); }
.season-session p { color: var(--muted); font-size: var(--text-xs); margin: .15rem 0 .5rem; }
.focus-match { color: var(--warning-ink); display: flex; flex-wrap: wrap; font-size: var(--text-xs); gap: .25rem .75rem; }
.focus-match.is-complete { color: var(--success); }
.focus-match.is-neutral { color: var(--muted); }
.focus-match span { color: var(--muted); }
.season-empty-line { color: var(--muted); font-size: var(--text-sm); margin: .75rem 0; }
/* Forgejo #16: Edit/Delete became peer icon buttons (Delete no longer
   hidden inside Edit's disclosure -- both are one tap away, named per
   block via aria-label). */
.season-block-header-actions { align-items: flex-start; border-top: 1px dashed var(--line); gap: .5rem; padding-top: 1rem; }
.season-block-edit { margin: 0; }
.season-add-block > summary {
  cursor: pointer;
  font-size: var(--text-sm);
  font-weight: 750;
  min-height: 44px;
  padding: .75rem 0;
}
.season-block-form { display: grid; gap: .75rem; padding: .5rem 0 1rem; }
.season-block-form-grid { display: grid; gap: .75rem; grid-template-columns: minmax(0, 1.4fr) repeat(2, minmax(9rem, 1fr)); }
.season-block-form .notice-error { grid-column: 1 / -1; }
.season-block-form .notice-error ul { margin: 0; padding-left: 1rem; }
.season-block-edit > form:last-child { margin-bottom: 1rem; }
.season-add-block {
  background: var(--bg);
  border: 1px dashed var(--line-strong);
  padding: 0 1rem;
}
.season-empty { border-top: 1px solid var(--line); margin-bottom: 1rem; padding: 2rem 0; text-align: center; }
.season-empty h3 { margin: 0; }
.season-empty p { color: var(--muted); margin: .5rem 0 0; }
.season-unassigned {
  position: sticky;
  top: 80px;
}
.season-unassigned > p { color: var(--muted); font-size: var(--text-sm); margin: 0 0 .75rem; }
.season-unassigned-list { border-top: 1px solid var(--line); list-style: none; margin: 0 0 1rem; padding: 0; }

/* Forgejo #16: compact rows (date/title/status + View/Add) replacing the
   old plain-link list, so a practice is viewed or assigned in at most two
   taps without leaving this aside. */
.season-unassigned-row {
  align-items: center;
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: .5rem .75rem;
  justify-content: space-between;
  padding: .75rem 0;
}
.season-unassigned-info { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.season-unassigned-info strong { font-size: var(--text-sm); font-weight: 700; }
.season-unassigned-date { color: var(--muted); font-size: var(--text-xs); }
.season-unassigned-actions { align-items: center; display: flex; flex-wrap: wrap; gap: .5rem; }
.season-add-chooser { position: relative; }
.season-add-chooser > summary { list-style: none; }
.season-add-chooser > summary::-webkit-details-marker { display: none; }
.season-add-chooser ul {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: 0 4px 12px oklch(0 0 0 / 0.08);
  list-style: none;
  margin: .25rem 0 0;
  min-width: 10rem;
  padding: .25rem;
  position: absolute;
  right: 0;
  z-index: 5;
}
.season-add-chooser li + li { margin-top: .1rem; }
.season-add-chooser .text-button { display: block; padding: .5rem; text-align: left; width: 100%; }

.season-assign-status {
  color: var(--primary-dark);
  font-size: var(--text-sm);
  font-weight: 700;
  min-height: 1.2rem;
}

@media (max-width: 1023px) {
  .season-mobile-note {
    background: var(--warning-bg);
    color: var(--warning-ink);
    display: block;
    font-size: var(--text-sm);
    margin: -1.25rem -.75rem 1.25rem;
    padding: .75rem;
  }
  .season-edit-only { display: none !important; }
  .season-planner-grid { grid-template-columns: 1fr; }
  .season-unassigned { position: static; }
}

@media (max-width: 767px) {
  .season-plan-row { grid-template-columns: 1fr auto; }
  .season-plan-year { grid-column: 1 / -1; }
  .season-planner-header h1 { font-size: var(--text-2xl); }
  .season-brief { grid-template-columns: 1fr; }
  .season-block-content > header { flex-direction: column; }
  .season-session { grid-template-columns: 2.5rem minmax(0, 1fr); }
  .season-block-form-grid { grid-template-columns: 1fr; }
}

/* M12 — integration settings */
.integrations-page { max-width: 64rem; }
.integrations-heading { border-bottom: 1px solid var(--line); margin-bottom: 0; padding-bottom: 1.5rem; }
.integration-section {
  display: grid;
  gap: 2.5rem;
  grid-template-columns: minmax(13rem, .7fr) minmax(0, 1.3fr);
  padding: 2rem 0;
}
.integration-section + .integration-section { border-top: 1px solid var(--line); }
.integration-section-intro h2 { font-size: var(--text-md); margin: 0 0 .5rem; }
.integration-section-intro p {
  color: var(--muted);
  font-size: var(--text-sm);
  line-height: 1.5;
  margin: 0;
  max-width: 34ch;
}
.integration-content { min-width: 0; }
.integration-state {
  align-items: center;
  background: var(--bg);
  display: flex;
  gap: .5rem;
  justify-content: flex-end;
  min-height: 72px;
  padding: .75rem 1rem;
}
.integration-state > div { display: flex; flex: 1; flex-direction: column; }
.integration-state strong { font-size: var(--text-base); }
.integration-state span { color: var(--muted); font-size: var(--text-sm); margin-top: .15rem; }
.integration-state form { margin: 0; }
.api-token-reveal {
  background: oklch(0.92 0.04 155);
  color: oklch(0.3 0.08 155);
  padding: 1rem;
}
.api-token-reveal > strong { font-size: var(--text-base); }
.api-token-reveal > p { font-size: var(--text-sm); margin: .25rem 0 .75rem; }
.api-token-value {
  color: var(--ink);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--text-sm);
  min-height: 4.2rem;
  overflow-wrap: anywhere;
}
.integration-reference { border-bottom: 1px solid var(--line); margin-top: .75rem; }
.integration-reference > summary {
  cursor: pointer;
  font-size: var(--text-sm);
  font-weight: 750;
  min-height: 44px;
  padding: .75rem 0;
}
.integration-reference > div { background: var(--bg); padding: .75rem 1rem; }
.integration-reference p, .integration-reference li { color: var(--muted); font-size: var(--text-sm); }
.integration-reference ul { margin: .5rem 0 0; padding-left: 1.25rem; }
.integration-reference code {
  color: var(--ink);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.integration-import-row {
  align-items: start;
  display: grid;
  gap: 1.5rem;
  grid-template-columns: minmax(0, 1fr) minmax(17rem, .9fr);
  padding: 0 0 1.5rem;
}
.integration-import-row + .integration-import-row {
  border-top: 1px solid var(--line);
  padding-top: 1.5rem;
}
.integration-import-row h3 { font-size: var(--text-base); margin: 0; }
.integration-import-row p { color: var(--muted); font-size: var(--text-sm); line-height: 1.45; margin: .25rem 0 .75rem; }
.integration-import-row form { display: grid; gap: .5rem; }
.integration-import-row input[type="file"] { font-size: var(--text-sm); padding: .5rem; }
.integration-import-row input[type="file"]::file-selector-button {
  background: var(--surface);
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--ink);
  cursor: pointer;
  font: inherit;
  font-weight: 700;
  margin-right: .75rem;
  min-height: 32px;
  padding: .25rem .5rem;
}

@media (max-width: 767px) {
  .integration-section { gap: 1rem; grid-template-columns: 1fr; }
  .integration-section-intro p { max-width: 65ch; }
  .integration-state { align-items: stretch; flex-direction: column; }
  .integration-state .btn-primary,
  .integration-state .btn,
  .integration-state .btn-error { width: 100%; }
  .integration-import-row { grid-template-columns: 1fr; }
  .integration-import-row .btn { width: 100%; }
}

/* Forgejo #1 — auth pages (sign in, password reset) and the post-login
   dashboard: the two areas that were still raw generator/prototype markup
   with no other issue in this batch scheduled to rewrite them. */
.auth-page {
  align-items: flex-start;
  display: flex;
  justify-content: center;
  padding: clamp(2rem, 8vh, 5rem) 1rem 3rem;
}
.auth-card {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-2xl);
  box-shadow: 0 16px 50px oklch(0.2 0.03 250 / .07);
  max-width: 26rem;
  padding: clamp(1.5rem, 4vw, 2.25rem);
  width: 100%;
}
.auth-mark { color: var(--primary-dark); display: block; font-size: var(--text-xs); font-weight: 850; letter-spacing: .08em; margin-bottom: 1rem; text-transform: uppercase; }
.auth-card h1 { font-size: var(--text-2xl); margin: 0 0 .25rem; }
.auth-card > p { color: var(--muted); margin: 0 0 1.25rem; }
.auth-card form > * + * { margin-top: 1rem; }
.auth-card .btn-primary { width: 100%; }
.auth-card-footer { font-size: var(--text-base); margin-top: 1.25rem; text-align: center; }

/* Brand lockup subtext. Deliberately only on the surfaces where the product
   introduces itself (sign-in, password reset, a password-gated share link) --
   never in the persistent app header, where chrome should recede. Specificity
   has to beat `.auth-card > p`, which otherwise claims this paragraph. */
.auth-card .brand-tagline,
.public-mark + .brand-tagline {
  color: var(--muted);
  font-size: var(--text-sm);
  letter-spacing: .01em;
  margin: -.75rem 0 1.25rem;
}

/* Forgejo #92. PRODUCT.md commits to a 44px minimum touch target. The core
   controls (.btn-*, .text-button, .app-header nav a) already honour it; these do
   not, and every one of them sits on a surface a coach uses on a phone at the
   rink. They are deliberately dense -- a chip that reads as compact is the point
   -- so the HIT AREA is expanded with a centred pseudo-element rather than by
   growing the visible box. That keeps layout byte-for-byte identical, which
   matters here because there is no browser in this environment to verify a
   reflow with (standing policy, #23).

   Deliberately excluded: every drill-editor and palette control (.palette-*,
   .phase-tab, .clear-menu-list button, .form-control-compact). The editor is
   desktop-gated below 1024px and is mouse-driven by design.

   Residual risk, NOT verified here: on the densest chip rows a 44px area can
   overlap a neighbour's, and the later element wins the overlap. Needs one pass
   on a real device. Still strictly better than a 24px target. */
.filter-chip,
.rink-area-map-chip,
.ice-map-legend > button,
.btn-sm,
.date-quick-actions .btn {
  position: relative;
}

.filter-chip::after,
.rink-area-map-chip::after,
.ice-map-legend > button::after,
.btn-sm::after,
.date-quick-actions .btn::after {
  content: "";
  height: max(100%, 44px);
  left: 50%;
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  width: max(100%, 44px);
}

/* A pointer device does not need the expanded area, and on a chip row it is
   where the overlap risk lives. Coarse pointers keep it. */
@media (hover: hover) and (pointer: fine) {
  .filter-chip::after,
        .rink-area-map-chip::after,
  .ice-map-legend > button::after,
  .btn-sm::after,
  .date-quick-actions .btn::after {
    height: 100%;
    width: 100%;
  }
}

/* Owner 2026-11-03 (Lineup mockup): a colour's panel wears its colour in the
   head, the body stays the quiet surface. The head runs edge to edge
   (negative margin = the panel's padding) and follows the panel's corner. */
.roster-zone[data-zone="light"] > .roster-zone-heading, .roster-team-side-light > .roster-team-heading { background: var(--zone-light-bg); box-shadow: inset 0 -1px 0 var(--zone-light-line); color: var(--zone-light-ink); }
.roster-zone[data-zone="dark"] > .roster-zone-heading, .roster-team-side-dark > .roster-team-heading { background: var(--zone-dark-bg); color: var(--zone-dark-ink); }
.roster-zone[data-zone="light"] > .roster-zone-heading, .roster-zone[data-zone="dark"] > .roster-zone-heading {
  border-radius: calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px) 0 0; margin: -.5rem -.5rem .5rem; padding: .5rem .75rem;
}
.roster-team-side-light > .roster-team-heading, .roster-team-side-dark > .roster-team-heading {
  border-radius: calc(var(--radius-2xl) - 1px) calc(var(--radius-2xl) - 1px) 0 0; margin: -.75rem -.75rem 0; padding: .75rem 1rem;
}
:is(.roster-zone, .roster-team-side)[data-zone="dark"] .roster-sort-label, .roster-zone[data-zone="light"] .roster-sort-label { color: inherit; opacity: .8; }
.roster-team-side-dark > .roster-team-heading small { color: inherit; opacity: .75; }

/* UI design brief 2026-11-03 (§8: touch targets 44 x 44): the same trick for the
   controls a crawl at 390 px found under 44px (help "?", header help, chip
   remove, section and phase tabs, the "x" of a menu), measured by element box.
   Coarse pointers only; a mouse keeps the compact box. Inputs take no
   pseudo-element, so the submit buttons grow instead, and the Sessions-list
   title link (a whole row's target) gets the height of a row. */
@media (pointer: coarse) {
  .field-help-trigger, .app-help, .roster-chip-remove, .section-tab, .phase-view-tab, .segment-add-tab { position: relative; }
  .field-help-trigger::after, .app-help::after, .roster-chip-remove::after, .section-tab::after, .phase-view-tab::after, .segment-add-tab::after {
    content: ""; height: max(100%, 44px); left: 50%; position: absolute; top: 50%; transform: translate(-50%, -50%); width: max(100%, 44px);
  }
  input.btn, input.btn-sm { min-height: 44px; }
  .session-title-link { align-content: center; min-height: 44px; }
}

.toggle-control { align-items: center; cursor: pointer; display: inline-flex; gap: .5rem; min-height: 44px; position: relative; }
.toggle-control input { height: 1px; opacity: 0; position: absolute; width: 1px; }
.toggle-control > span { background: var(--surface-strong); border: 1px solid var(--control-border); border-radius: 999px; height: 1.5rem; position: relative; transition: background-color 150ms ease, border-color 150ms ease; width: 2.7rem; }
.toggle-control > span::after { background: white; border-radius: 50%; box-shadow: 0 1px 3px oklch(0.2 0.03 250 / .25); content: ""; height: 1.1rem; left: .15rem; position: absolute; top: .14rem; transition: transform 150ms var(--ease-out); width: 1.1rem; }
.toggle-control input:checked + span { background: var(--primary); border-color: var(--primary); }
.toggle-control input:checked + span::after { transform: translateX(1.15rem); }
.toggle-control input:focus-visible + span { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.toggle-control strong { font-size: var(--text-sm); }

input[type="file"].form-control { cursor: pointer; padding: .25rem; }
input[type="file"].form-control::file-selector-button {
  background: var(--surface);
  border: 0;
  border-radius: var(--radius-md);
  color: var(--ink);
  cursor: pointer;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 750;
  margin-right: .75rem;
  min-height: 34px;
  padding: .5rem .75rem;
}

.dashboard-page { max-width: 64rem; }
/* Forgejo #305: tiles and the calendar. A tile can be a link (same look, a hover). */
.dashboard-tiles { margin: 1rem 0 0; }
.stat-tile-link { color: inherit; text-decoration: none; }
.stat-tile-link:hover { border-color: var(--control-border-hover); }
.stat-tile-link:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.dashboard-calendar { margin-top: 1.5rem; padding: 1rem 1.25rem 1.25rem; }
.dashboard-calendar-head { align-items: flex-start; display: flex; flex-wrap: wrap; gap: .75rem 1rem; justify-content: space-between; }
.dashboard-calendar-head h2 { font-size: var(--text-lg); margin: 0; }
.dashboard-calendar-head .form-hint { margin: .25rem 0 0; }
.dashboard-calendar-controls { align-items: center; display: flex; flex-wrap: wrap; gap: .5rem; }
.dashboard-calendar-nav { display: flex; gap: .25rem; }
.dashboard-calendar-switch { background: var(--surface-strong); border-radius: var(--radius-xl); display: inline-flex; gap: .25rem; padding: .25rem; }
.dashboard-calendar-switch-option { border-radius: var(--radius-md); color: var(--muted); font-size: var(--text-sm); font-weight: 700; padding: .5rem .75rem; text-decoration: none; }
.dashboard-calendar-switch-option.is-on { background: var(--bg); box-shadow: var(--shadow-switch); color: var(--primary-dark); }
.cal-legend { color: var(--muted); display: flex; flex-wrap: wrap; font-size: var(--text-sm); gap: .25rem 1rem; list-style: none; margin: .75rem 0; padding: 0; }
.cal-mark { font-weight: 800; }
.cal-mark.is-mine { color: var(--primary); }
.cal-mark.is-open { color: var(--danger); }
.cal-week { display: grid; gap: .5rem; grid-template-columns: minmax(0, 1fr); list-style: none; margin: 0; padding: 0; }
@media (min-width: 768px) { .cal-week { grid-template-columns: repeat(7, minmax(0, 1fr)); } }
.cal-day { background: var(--surface-subtle); border: 1px solid var(--line); border-radius: var(--radius-lg); display: grid; gap: .25rem; min-height: 4rem; min-width: 0; padding: .5rem; }
.cal-day.is-today { border-color: var(--primary); box-shadow: inset 0 0 0 1px var(--primary); }
@media (max-width: 767px) { .cal-day.is-empty { min-height: 0; } }
.cal-day-head { color: var(--muted); font-size: var(--text-sm); }
.cal-day-head b { color: var(--ink); }
.cal-session { background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-md); color: var(--ink); display: grid; font-size: var(--text-sm); gap: 0; min-width: 0; padding: .25rem .5rem; text-decoration: none; }
.cal-session:hover { border-color: var(--control-border-hover); }
.cal-session:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 1px; }
.cal-session strong { overflow-wrap: anywhere; }
.cal-session small, .cal-session-time { color: var(--muted); font-size: var(--text-xs); }
.cal-session.is-mine { box-shadow: inset 3px 0 0 var(--primary); }
.cal-session.is-open { border-color: var(--danger); border-style: dashed; }
.cal-month { display: grid; gap: .25rem; }
.cal-month-head, .cal-month-week { display: grid; gap: .25rem; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.cal-month-head span { color: var(--muted); font-size: var(--text-xs); font-weight: 800; text-align: center; }
.cal-month-day { background: var(--surface-subtle); border: 1px solid var(--line); border-radius: var(--radius-md); display: grid; align-content: start; gap: .25rem; min-height: 4.5rem; min-width: 0; padding: .25rem; }
.cal-month-day.is-outside { opacity: .5; }
.cal-month-day.is-today { border-color: var(--primary); }
.cal-month-number { color: var(--muted); font-size: var(--text-xs); font-weight: 700; }
.cal-month-day .cal-session { align-items: center; display: flex; gap: .25rem; overflow: hidden; padding: .1rem .25rem; white-space: nowrap; }
.cal-month-day .cal-session > span:last-child { overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 767px) { .cal-month-day .cal-session > span:last-child { display: none; } .cal-month-day { min-height: 3rem; } }
.dashboard-welcome { align-items: center; display: flex; gap: 1rem; }
.dashboard-welcome h1 { font-size: var(--text-2xl); margin: 0; }
.dashboard-facts { margin-top: .5rem; }
.dashboard-facts span { color: var(--muted); }

/* Forgejo #89. The dashboard's job is to answer "what is next" before anything
   else, so the next practice is the one large, high-contrast element and every
   other block is deliberately quieter. No new colour or shape vocabulary --
   this reuses the existing panel/surface tokens. */
.dashboard-next {
  border: 1px solid var(--line);
  border-radius: var(--radius-2xl);
  margin-top: 2rem;
  padding: 1.25rem;
}

.dashboard-next h2 {
  color: var(--muted);
  font-size: var(--text-sm);
  font-weight: 800;
  letter-spacing: .07em;
  margin: 0 0 .5rem;
  text-transform: uppercase;
}

.dashboard-next-card {
  color: var(--ink);
  display: block;
  text-decoration: none;
}

.dashboard-next-card strong {
  display: block;
  font-size: var(--text-xl);
  letter-spacing: -.02em;
  line-height: 1.2;
}

.dashboard-next-card:hover strong,
.dashboard-next-card:focus-visible strong { color: var(--primary-dark); }

.dashboard-next-when {
  color: var(--primary-dark);
  display: block;
  font-size: var(--text-md);
  font-weight: 700;
  margin-top: .25rem;
}

.dashboard-next-meta {
  color: var(--muted);
  display: block;
  font-size: var(--text-base);
  margin-top: .2rem;
}

.dashboard-next-actions {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-top: 1rem;
}

.dashboard-next-empty h2 {
  color: var(--ink);
  font-size: var(--text-lg);
  letter-spacing: -.01em;
  text-transform: none;
}

.dashboard-next-empty p { color: var(--muted); margin: 0 0 1rem; }

.dashboard-section { margin-top: 2rem; }

.dashboard-section-heading {
  align-items: baseline;
  display: flex;
  gap: 1rem;
  justify-content: space-between;
}

.dashboard-section-heading h2,
.dashboard-section-title {
  font-size: var(--text-md);
  margin: 0 0 .5rem;
}

.dashboard-list { list-style: none; margin: 0; padding: 0; }

.dashboard-list li + li { border-top: 1px solid var(--line); }

.dashboard-list a {
  color: var(--ink);
  display: block;
  min-height: 44px;
  padding: .75rem .25rem;
  text-decoration: none;
}

.dashboard-list a:hover { background: var(--surface); }
.dashboard-list a strong { display: block; font-size: var(--text-md); font-weight: 700; }
.dashboard-list a span { color: var(--muted); font-size: var(--text-sm); }

/* Pairs the colour with a real character and role="status", never colour
   alone -- same rule the season calendar's status signals follow. */
.dashboard-alert {
  align-items: baseline;
  background: var(--warning-bg);
  border-radius: var(--radius-lg);
  color: var(--warning-ink);
  display: flex;
  font-size: var(--text-base);
  gap: .5rem;
  margin: 0;
  padding: .75rem 1rem;
}

.dashboard-alert span[aria-hidden] { font-weight: 800; }

.dashboard-quick { display: flex; flex-wrap: wrap; gap: .5rem; }
.dashboard-actions { margin-top: 1.75rem; }

/* Forgejo #3 — unified drill authoring workspace. The canvas/toolbar/phase
   list markup itself (editor/_toolbar.html.erb, the JS-rendered phaseList
   items) is untouched — #7 owns restyling those; this is just the new
   shared container the metadata panel and phase editor sit inside. */
.phase-editor { margin-top: 1.5rem; }
.viewport-banner { display: none; }
.phase-editor-header {
  align-items: center;
  display: flex;
  gap: 1rem;
  justify-content: flex-end;
  margin-bottom: .75rem;
}
.phase-editor-header span { color: var(--muted); font-size: var(--text-base); font-weight: 700; }
.single-diagram-editor .phase-editor-header { flex-wrap: wrap; }
.diagram-save-status {
  align-items: center;
  color: var(--muted);
  display: flex;
  gap: .5rem;
  margin: 0 auto 0 0;
}
.diagram-save-status strong { font-size: var(--text-sm); }
.diagram-save-status small { font-size: var(--text-xs); }
.diagram-save-indicator { border-radius: 50%; flex: 0 0 auto; height: .5rem; width: .5rem; }
.diagram-save-status[data-save-state="saved"] { color: var(--success); }
.diagram-save-status[data-save-state="saved"] .diagram-save-indicator { background: var(--success); }
.diagram-save-status[data-save-state="dirty"] { color: var(--warning-ink); }
.diagram-save-status[data-save-state="dirty"] .diagram-save-indicator { background: var(--warning-ink); }
.diagram-save-status[data-save-state="saving"] { color: var(--accent); }
.diagram-save-status[data-save-state="saving"] .diagram-save-indicator { background: var(--accent); }
.diagram-save-status[data-save-state="error"] { color: var(--danger); }
.diagram-save-status[data-save-state="error"] .diagram-save-indicator { background: var(--danger); }
/* Forgejo #188: the notes of a phase (the formatting toolbar sits above the textarea in the phase card). */
.phase-notes-field { margin-bottom: 1rem; display: grid; gap: .35rem; grid-template-columns: minmax(0, 1fr); }
.phase-notes-heading { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .5rem; }
.phase-notes-toolbar { display: flex; flex-wrap: wrap; gap: .25rem; }
.phase-notes-field textarea { resize: vertical; min-height: 7rem; }
.phase-notes { margin-top: .75rem; }
.phase-notes :is(p, ul, ol) { margin: 0 0 .5rem; }
.phase-notes :is(ul, ol) { padding-left: 1.25rem; }
.phase-notes ul { list-style: disc; }
.phase-notes ol { list-style: decimal; }
.phase-notes a { color: var(--primary-dark); text-decoration: underline; }
/* Forgejo #193: the help pages (/help and the guides) and the question mark in the header. */
.app-help { align-items: center; flex: none; border-radius: 999px; color: var(--muted); display: inline-flex; height: 2.25rem; justify-content: center; width: 2.25rem; }
.app-help:hover, .app-help.is-current { background: var(--control-hover); color: var(--primary-dark); }
.app-help:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.help-page { display: grid; gap: 1rem; grid-template-columns: minmax(0, 1fr); margin: 0 auto; max-width: 62rem; }
.help-lead { color: var(--muted); margin: .25rem 0 0; max-width: 46rem; }
.help-breadcrumb { color: var(--muted); font-size: var(--text-sm); }
.help-breadcrumb a { color: var(--primary-dark); text-decoration: underline; }
.help-guide-list { display: grid; gap: 1rem; list-style: none; margin: 0; padding: 0; }
.help-guide-card .card-title a { color: inherit; text-decoration: none; }
.help-guide-card .card-title a:hover { text-decoration: underline; }
.help-subtitle { font-size: var(--text-md); font-weight: 750; margin: 1rem 0 .5rem; }
.help-list, .help-marks { margin: .5rem 0; padding-left: 1.25rem; }
.help-list { list-style: disc; }
ol.help-list { list-style: decimal; }
.help-callout { display: grid; gap: .5rem; padding: .75rem 1rem; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); }
.help-callout p { margin: 0; }
.help-copy-bar { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-top: .5rem; }
.help-copy-bar [role="status"] { font-size: var(--text-sm); }
.help-code-scroll { max-height: 22rem; overflow: auto; }
.workshop-columns { display: grid; gap: 1rem; }
@media (min-width: 1024px) { .workshop-columns { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; } }
.workshop-review .help-image { max-width: 100%; height: auto; }
.workshop-review [data-state="error"] { color: var(--danger); }
.help-code { margin: .5rem 0; padding: .75rem 1rem; overflow-x: auto; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--text-sm); line-height: 1.5; white-space: pre; }
.help-marks { list-style: decimal; }
.help-list li, .help-marks li { margin: .25rem 0; }
.help-toc { columns: 2 16rem; margin: .5rem 0; padding-left: 1.25rem; list-style: decimal; }
.help-toc a { color: var(--primary-dark); text-decoration: underline; }
.help-step { scroll-margin-top: 4.5rem; }
.help-step-title { align-items: center; display: flex; font-size: var(--text-lg, 1.2rem); gap: .5rem; margin: 0 0 .5rem; }
.help-step-number { align-items: center; background: var(--primary); border-radius: 999px; color: #fff; display: inline-flex; flex: none; font-size: var(--text-base); font-weight: 750; height: 2rem; justify-content: center; width: 2rem; }
.help-step-text p { margin: .5rem 0; }
.help-figure { margin: 1rem 0 0; }
.help-image { border: 1px solid var(--line-strong); border-radius: var(--radius-md); display: block; height: auto; max-width: 100%; }
.help-figure figcaption { color: var(--muted); font-size: var(--text-sm); margin-top: .5rem; }
.help-page kbd { background: var(--surface-subtle); border: 1px solid var(--line-strong); border-radius: 4px; font-size: var(--text-sm); padding: 0 .25rem; }
.help-dl dt { font-weight: 700; margin-top: .75rem; }
.help-dl dd { margin: .2rem 0 0; }
.help-back { margin: 0; }
/* Forgejo #191: phase cards beside the canvas. From 1024 px (the width the editor itself unlocks at; since 2026-10-23 the tabs
   above the canvas are gone, so the cards are the only way to change phase and must exist wherever the editor does), only while the
   personal width setting (rinkplan:phase-panel:v1: off / standard / third / half, class phase-panel-hidden on the editor for
   "off", data-phase-panel-width for the rest) has not hidden them; never in the board mode or fullscreen, where the canvas gets every
   pixel. */
.phase-diagram-layout { display: grid; gap: 1rem; grid-template-columns: minmax(0, 1fr); }
.phase-panel { display: none; }
@media (min-width: 1024px) {
  .phase-editor:not(.phase-panel-hidden):not(.is-board):not(:fullscreen):not(.is-fullscreen) .phase-diagram-layout { grid-template-columns: minmax(0, 1fr) 13rem; }
  .phase-editor[data-phase-panel-width="third"]:not(.phase-panel-hidden):not(.is-board):not(:fullscreen):not(.is-fullscreen) .phase-diagram-layout { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
  .phase-editor[data-phase-panel-width="half"]:not(.phase-panel-hidden):not(.is-board):not(:fullscreen):not(.is-fullscreen) .phase-diagram-layout { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .phase-editor:not(.phase-panel-hidden):not(.is-board):not(:fullscreen):not(.is-fullscreen) .phase-panel { display: flex; }
}
.phase-panel { align-self: start; flex-direction: column; gap: .5rem; max-height: calc(100vh - 8rem); overflow-y: auto; position: sticky; top: 4.5rem; }
.phase-card-list { display: flex; flex-direction: column; gap: .5rem; list-style: none; margin: 0; padding: 0; }
.phase-card { background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-md); overflow: hidden; position: relative; }
.phase-card[data-active="true"] { border-color: var(--primary); box-shadow: 0 0 0 2px color-mix(in oklab, var(--primary) 25%, transparent); }
/* Name and duration are inputs that look like text until the coach points at them. */
.phase-card-head { align-items: center; display: flex; gap: .35rem; padding: .35rem .4rem 0; }
.phase-card-number { background: var(--surface); border: 1px solid var(--line-strong); border-radius: 999px; flex: none; font-size: var(--text-sm); font-weight: 700; line-height: 1.2; padding: 0 .45rem; }
.phase-card[data-active="true"] .phase-card-number { background: var(--primary); border-color: var(--primary); color: #fff; }
.phase-card :is(.phase-card-name, .phase-card-duration-input, .phase-card-description, .phase-card-notes) { background: transparent; border: 1px solid transparent; border-radius: var(--radius-sm, 6px); color: var(--ink); font: inherit; font-size: var(--text-sm); min-width: 0; padding: .15rem .3rem; text-overflow: ellipsis; }
.phase-card :is(.phase-card-name, .phase-card-duration-input, .phase-card-description, .phase-card-notes):hover:not([readonly]) { border-color: var(--line-strong); }
.phase-card :is(.phase-card-name, .phase-card-duration-input, .phase-card-description, .phase-card-notes):focus { background: var(--bg); border-color: var(--primary); outline: 2px solid var(--focus-ring); outline-offset: 0; }
.phase-card-name { flex: 1 1 auto; font-weight: 700; }
.phase-card-duration { align-items: center; color: var(--muted); display: inline-flex; flex: none; font-size: var(--text-sm); gap: .1rem; }
.phase-card-duration-input { text-align: right; width: 3.4rem; -moz-appearance: textfield; }
.phase-card-duration-input::-webkit-outer-spin-button, .phase-card-duration-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.phase-card-main { background: transparent; border: 0; color: var(--ink); cursor: pointer; display: block; font: inherit; margin-top: .3rem; padding: 0; text-align: left; width: 100%; }
.phase-card-main:focus-visible, .phase-card-add:focus-visible, .phase-card-action:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: -2px; }
.phase-card-preview { background: var(--bg); display: block; line-height: 0; }
.phase-card-svg { display: block; height: auto; width: 100%; aspect-ratio: 2 / 1; }
.phase-card-extras { display: grid; gap: .35rem; padding: .4rem .4rem 0; }
.phase-card-extras :is(.phase-card-description, .phase-card-notes) { border-color: var(--line); width: 100%; }
.phase-card-extras .phase-card-notes { resize: vertical; }
.phase-card-extras .phase-notes-field { margin-bottom: 0; }
.phase-card .phase-notes-toolbar .btn { font-size: var(--text-sm); min-height: 1.8rem; min-width: 1.8rem; padding: 0 .45rem; }
.phase-card-actions { display: flex; flex-wrap: wrap; gap: .25rem; justify-content: flex-end; padding: .35rem; }
.phase-card-action { background: var(--bg); border: 1px solid var(--line-strong); border-radius: var(--radius-sm, 6px); color: var(--ink); cursor: pointer; font: inherit; font-size: var(--text-sm); font-weight: 600; min-height: 2rem; min-width: 2rem; padding: 0 .55rem; }
.phase-card-action:disabled { cursor: default; opacity: .45; }
.phase-card-remove:hover:not(:disabled) { background: var(--danger-bg); color: var(--danger); }
/* The "+" menu is fixed at the button: the panel scrolls, and a positioned child would be clipped by it. */
.phase-card-add-menu { display: inline-flex; margin-right: auto; }
.phase-card-menu { background: var(--bg); border: 1px solid var(--line-strong); border-radius: var(--radius-lg); box-shadow: var(--shadow-popover); display: grid; gap: .15rem; min-width: 10rem; padding: .3rem; position: fixed; z-index: 60; }
.phase-card-menu[hidden] { display: none; }
.phase-card-menu-item { background: transparent; border: 0; border-radius: var(--radius-md); color: var(--ink); cursor: pointer; font: inherit; font-size: var(--text-sm); font-weight: 600; padding: .45rem .6rem; text-align: left; }
.phase-card-menu-item:hover, .phase-card-menu-item:focus-visible { background: var(--control-hover); }
/* The width button of the Edit & arrange row: a click cycles, hovering (or keyboard focus) opens the list. */
.phase-width-menu { position: relative; }
.phase-width-menu > .btn { align-items: center; display: inline-flex; gap: .3rem; }
.phase-width-list { background: var(--bg); border: 1px solid var(--line-strong); border-radius: var(--radius-lg); box-shadow: var(--shadow-popover); display: none; gap: .15rem; left: 0; min-width: 6rem; padding: .3rem; position: absolute; top: 100%; z-index: 40; }
.phase-width-menu:hover .phase-width-list, .phase-width-menu:has(:focus-visible) .phase-width-list { display: grid; }
.phase-width-list button { background: transparent; border: 0; border-radius: var(--radius-md); color: var(--ink); cursor: pointer; font: inherit; font-size: var(--text-sm); font-weight: 600; padding: .4rem .6rem; text-align: left; }
.phase-width-list button:hover, .phase-width-list button:focus-visible { background: var(--control-hover); }
.phase-width-list button[aria-checked="true"] { background: var(--editor-tool-selected-bg); color: var(--primary-dark); }
.drill-edit-menu { position: relative; }
.drill-edit-menu-list { background: var(--bg); border: 1px solid var(--line-strong); border-radius: var(--radius-lg); box-shadow: var(--shadow-popover); display: none; min-width: 12rem; padding: .25rem; position: absolute; right: 0; top: 100%; z-index: 40; }
.drill-edit-menu:hover .drill-edit-menu-list, .drill-edit-menu:has(:focus-visible) .drill-edit-menu-list { display: grid; }
.drill-edit-menu-list a { align-items: center; border-radius: var(--radius-md); color: var(--ink); display: flex; font-size: var(--text-sm); font-weight: 600; gap: .5rem; padding: .5rem .5rem; text-decoration: none; white-space: nowrap; }
.drill-edit-menu-list a:hover, .drill-edit-menu-list a:focus-visible { background: var(--control-hover); }
.phase-card-add { background: transparent; border: 2px dashed var(--line-strong); border-radius: var(--radius-md); color: var(--muted); cursor: pointer; font: inherit; font-weight: 700; min-height: 3rem; padding: .5rem; }
.phase-card-add:hover { background: var(--control-hover); color: var(--ink); }
.phase-editor-layout { display: flex; flex-direction: column; gap: 1.5rem; }
.phase-editor-canvas { flex: 1; min-width: 0; position: relative; }
/* Forgejo #163: playback subtitle, a lower third over the canvas / a reader diagram. */
.playback-caption { position: absolute; left: 50%; bottom: 0.75rem; transform: translateX(-50%); max-width: calc(100% - 1.5rem);
  display: flex; flex-direction: column; gap: 0.1rem; padding: 0.4rem 0.75rem; border-radius: 0.375rem; pointer-events: none;
  background: rgb(0 0 0 / 0.7); color: #fff; text-align: center; font-size: var(--text-base); line-height: 1.3; }
.playback-caption[hidden] { display: none; }
.playback-caption strong { font-weight: 700; }
.reader-caption-host { position: relative; }
@media print { .playback-caption { display: none !important; } }
.phase-editor-sidebar h2 { font-size: var(--text-md); margin: 0 0 .6rem; }
.phase-list { list-style: none; margin: 0 0 .75rem; padding: 0; }
.phase-editor-duplicate { width: 100%; }

/* Forgejo #31 — contextual element inspector */
.element-inspector {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-top: .75rem;
  padding: .75rem 1rem;
}
.element-inspector-title {
  color: var(--muted);
  flex-basis: 100%;
  font-size: var(--text-xs);
  font-weight: 700;
  margin: 0;
  text-transform: uppercase;
}
.inspector-field-group { display: flex; flex-wrap: wrap; gap: 1rem; }
.inspector-field { min-width: 8rem; }
.inspector-field .form-control { min-height: 2.75rem; }
.inspector-field-checkbox { display: flex; min-width: auto; }
.inspector-path-attachment { display: flex; align-items: center; gap: .5rem; min-width: auto; }

/* Forgejo #39 — unsafe view-change dialog and the persistent post-crop notice */
.view-change-dialog { margin-top: .75rem; }
.view-change-dialog p { margin: 0 0 .75rem; }

/* Forgejo #38 — station preset add/replace/cancel confirmation */
.station-preset-dialog { margin-top: .75rem; }
.station-preset-dialog p { margin: 0 0 .75rem; }
.view-outside-notice { align-items: center; display: flex; flex-wrap: wrap; gap: .75rem; justify-content: space-between; margin-top: .75rem; }

/* Forgejo #41 — local draft/crash recovery banner */
.draft-recovery-banner {
  align-items: center;
  background: var(--warning-bg);
  border-radius: var(--radius-lg);
  color: var(--warning-ink);
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  justify-content: space-between;
  margin-bottom: .75rem;
  padding: .75rem;
}

/* Forgejo #37 — inline text/label editor: a plain HTML input positioned
   (via JS, left/top in px) directly over the click/element point on the
   stage, not an SVG element — see stage_controller.js's openInlineEditor(). */
.inline-text-editor {
  background: var(--bg);
  border: 2px solid var(--primary);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-size: var(--text-md);
  min-width: 6rem;
  padding: .15rem .5rem;
  position: fixed;
  text-align: center;
  transform: translate(-50%, -50%);
  z-index: 40;
}
textarea.inline-text-editor { min-height: 1.6rem; resize: both; }

/* Forgejo #42: the Alt-click overlap chooser -- a plain HTML popup
   positioned (via JS, left/top in px) over the click point, same
   "outside the SVG" approach as .inline-text-editor above. */
/* Forgejo #45: the editor's export options panel -- a <details> popover,
   same visual language as .rink-view-popover, holding the content/
   background/scale radios. The Export button itself stays a sibling, always
   visible (the "Diagram only" one-click path never requires opening this). */
.export-panel { align-items: center; display: flex; gap: .5rem; position: relative; }
.export-panel-options { position: static; }
/* Forgejo #176: the floating bar over the selection; absolute inside .phase-editor-canvas (which has no overflow). */
.element-context-bar { position: absolute; z-index: 5; display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem; max-width: calc(100% - 0.5rem);
  padding: 0.25rem 0.375rem; background: #fff; border: 1px solid var(--line-strong); border-radius: var(--radius-md); box-shadow: 0 2px 8px rgb(0 0 0 / 0.18); }
.element-context-bar[hidden], .element-context-bar [hidden] { display: none; }
.element-context-bar .context-field { display: inline-flex; align-items: center; gap: 0.25rem; margin: 0; }
.element-context-bar .context-field select { width: auto; max-width: 9rem; }
.element-context-bar .context-check { font-size: var(--text-base); }
@media (max-width: 1023px) { .element-context-bar { display: none; } }
.position-palette { display: grid; gap: 0.25rem; }
.position-palette[hidden] { display: none; }
.position-palette-row { display: flex; flex-wrap: wrap; gap: 0.25rem; }
.position-palette .btn[aria-pressed="true"] { background: var(--primary); color: #fff; border-color: var(--primary); }
.video-export-status { color: var(--muted); font-size: var(--text-sm); }
.video-export-status:empty { display: none; }
.export-panel-options summary { cursor: pointer; list-style: none; }
.export-panel-options summary::-webkit-details-marker { display: none; }
.export-panel-options summary::marker { content: ""; }
.export-panel-menu {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  box-shadow: 0 8px 24px var(--shadow-tint);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  max-width: calc(100vw - 2rem);
  padding: .25rem;
  position: absolute;
  right: 0;
  top: calc(100% + .25rem);
  width: 30rem;
  z-index: 20;
}
.export-panel-options fieldset {
  border: 0;
  margin: 0;
  min-width: 0;
  padding: .5rem .75rem;
  white-space: normal;
}
.export-panel-options fieldset + fieldset { border-left: 1px solid var(--line); }
.export-panel-options legend { color: var(--muted); font-size: var(--text-sm); }
.export-panel-options label { display: block; font-size: var(--text-base); padding: .15rem 0; }

@media (max-width: 767px) {
  .export-panel-menu { grid-template-columns: 1fr; width: min(18rem, calc(100vw - 2rem)); }
  .export-panel-options fieldset + fieldset { border-left: 0; border-top: 1px solid var(--line); }
}

.overlap-chooser {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-overlap-chooser);
  display: flex;
  flex-direction: column;
  min-width: 9rem;
  overflow: hidden;
  position: fixed;
  transform: translate(-50%, -50%);
  z-index: 40;
}

.overlap-chooser-option {
  background: none;
  border: none;
  color: var(--ink);
  cursor: pointer;
  font-size: var(--text-base);
  padding: .5rem .75rem;
  text-align: left;
}

.overlap-chooser-option:hover, .overlap-chooser-option:focus-visible {
  background: var(--surface);
}

/* Forgejo #37: the stage <svg> carries tabindex="-1" purely so
   closeInlineEditor() can programmatically restore focus to it after a
   commit/cancel — it is never part of the tab order, so the browser's
   default focus ring (meant for keyboard navigation) would be misleading
   noise here rather than a useful cue. */
svg[data-editor--stage-target="svg"]:focus { outline: none; }
/* Forgejo #174: a finger drag in the path tool draws instead of scrolling the page. */
svg[data-editor--stage-target="svg"][data-tool="path"] { touch-action: none; }

@media (min-width: 1024px) {
  .phase-editor-layout { flex-direction: row; }
  .phase-editor-sidebar { flex: 0 0 16rem; }
}

/* Shared split-editor chrome: the rink owns the full row, with one compact
   command bar above and the tool bar below. Context panels follow the tools
   instead of permanently taking width from the drawing surface. */
.editor-command-bar {
  align-items: center;
  background: var(--surface-subtle);
  border-radius: var(--radius-2xl);
  flex-wrap: wrap;
  gap: .5rem;
  margin-bottom: .75rem;
  padding: .5rem .65rem;
}
.editor-command-bar-divider { align-self: stretch; border-left: 1px solid var(--line); }
/* 2026-10-23: Rink & surface, the arrange actions, the view icons and the exports are ONE row (desktop only, from 1024 px), so the
   buttons are a little smaller than the 44 px touch size; the drawing board of a tablet has its own big buttons. */
.editor-command-bar { column-gap: .35rem; }
.editor-command-bar .icon-button { --size: 2.2rem; height: 2.2rem; min-height: 2.2rem; min-width: 2.2rem; width: 2.2rem; }
.editor-command-bar .icon-button.icon-button-label { width: auto; padding: 0 .4rem; }
.editor-command-bar .clear-menu > summary { align-items: center; display: inline-flex; }
.editor-rink-menu { position: relative; }
/* The export buttons, pushed to the far right of the Edit & arrange row (the Rink & surface menu opens the row). */
.editor-command-bar-end { align-items: center; display: flex; flex-wrap: wrap; gap: .5rem; margin-left: auto; }
.editor-rink-menu-panel .editor-view-group-wide { grid-column: 1 / -1; }
.editor-rink-menu-panel .copy-diagram-control + .copy-diagram-control { margin-top: .4rem; }
.icon-button[data-export-state="failed"] { color: var(--danger); }
.editor-rink-menu > summary { align-items: center; display: inline-flex; gap: .4rem; list-style: none; }
.editor-rink-menu > summary::-webkit-details-marker { display: none; }
.editor-rink-menu > summary svg { color: var(--muted); transition: transform 150ms var(--ease-out); }
.editor-rink-menu[open] > summary svg { transform: rotate(180deg); }
.editor-rink-menu-panel {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-popover);
  display: grid;
  gap: 1rem;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  padding: .8rem;
  left: 0;
  position: absolute;
  top: calc(100% + .65rem);
  width: min(34rem, calc(100vw - 2rem));
  z-index: 35;
}
.editor-rink-menu-panel .rink-view-popover-grid { left: 0; top: calc(100% + .35rem); width: min(22rem, calc(100vw - 3.6rem)); }
.editor-context-panels { margin-top: .75rem; }

/* 2026-10-07: the "1:1" reset-scale control in the Edit & arrange row --
   an icon-button whose face is text, since no icon says "100%" better. */
.icon-button.icon-button-label { font: inherit; font-size: var(--text-xs); font-weight: 800; }

@media (max-width: 1023px) {
  .viewport-banner {
    background: var(--warning-bg);
    border-radius: var(--radius-lg);
    color: var(--warning-ink);
    display: block;
    font-size: var(--text-sm);
    margin-bottom: 1rem;
    padding: .75rem;
  }
}

/* Forgejo #7 — icon-first tool palette */
.palette-field { display: flex; flex-direction: column; gap: .3rem; }
.palette-label { color: var(--muted); font-size: var(--text-xs); font-weight: 700; text-transform: uppercase; }
.palette-preview-svg { height: 100%; width: 100%; }

/* Forgejo #17 — Configuration hub */
.configuration-grid {
  display: grid;
  gap: 1.25rem;
  grid-template-columns: 1fr;
}
/* Forgejo #290 (N4): the cards sit under headings, a quiet label above each group. */
.configuration-group + .configuration-group { margin-top: 2rem; }
.configuration-group-title { color: var(--muted); font-size: var(--text-sm); font-weight: 750; letter-spacing: .02em; margin: 0 0 .75rem; text-transform: uppercase; }
.configuration-card-header {
  align-items: center;
  display: flex;
  justify-content: space-between;
  margin-bottom: .5rem;
}
.configuration-card-header h2 { font-size: var(--text-md); margin: 0; }
.configuration-card p { color: var(--muted); margin: 0 0 1rem; }

@media (min-width: 768px) {
  .configuration-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Forgejo #43 — active-tool cursor, status line, disabled undo/redo, and the
   keyboard-shortcut reference dialog. Cursor is driven entirely by the
   stage's own data-tool attribute (set in syncToolFields()) rather than a
   class list — one rule per tool value, nothing to toggle on/off by hand. */
svg[data-tool] { cursor: default; }
svg[data-tool="player"], svg[data-tool="object"], svg[data-tool="overlay"],
svg[data-tool="text"], svg[data-tool="number"] { cursor: crosshair; }
svg[data-tool="pan"] { cursor: grab; }
svg.stage-panning-active { cursor: grabbing; }

.tool-status { color: var(--muted); font-size: var(--text-base); margin: 0 0 .5rem; min-height: 1.2em; }

button[data-editor--stage-target="undoButton"]:disabled,
button[data-editor--stage-target="redoButton"]:disabled { cursor: not-allowed; opacity: .5; }

/* 2026 form/editor overhaul — diagram-first workspace (Forgejo #56). */
.drill-workspace > .page-heading { margin-bottom: 1rem; }
.phase-editor { margin-top: 0; }
.phase-editor-header { border-bottom: 1px solid var(--line); margin: 0 0 .75rem; padding: 0 0 .65rem; }
/* Phases are the level below variations, so they take the diagram tabs' cool
   "ice" hue rather than the warm variation/segment palette -- same reason the
   segment diagram's Layout/Stations strip does. */
.phase-tabs-shell { border-bottom: 1px solid var(--line); margin-bottom: .75rem; }
/* Forgejo #312: the drill page's export row sits at the right end of the phase tabs. */
.phase-tabs-shell:has(.phase-tabs-export) { align-items: flex-end; display: flex; gap: .5rem; }
.phase-tabs-shell:has(.phase-tabs-export) .phase-tabs-scroll { flex: 1 1 auto; min-width: 0; }
.phase-tabs-export { flex: none; padding-bottom: .25rem; }
@media (max-width: 639px) {
  /* Phones: the tabs keep the full width, the export row takes its own line above them. */
  .phase-tabs-shell:has(.phase-tabs-export) { flex-wrap: wrap; }
  .phase-tabs-shell:has(.phase-tabs-export) .phase-tabs-scroll { flex-basis: 100%; }
  .phase-tabs-export { margin-left: auto; order: -1; }
}
.phase-tabs-scroll { align-items: flex-end; display: flex; gap: .35rem; overflow-x: auto; padding: .2rem .1rem 0; scrollbar-width: thin; }
/* Same look as the segment tabs (owner, 2026-10-08): neutral inactive tabs,
   the active one joins the page. The number comes from a CSS counter so it
   follows the DOM order the server AND the editor's own re-render produce -
   no second numbering to keep in step. */
.phase-list { counter-reset: phase; display: flex; flex: 0 0 auto; gap: .35rem; list-style: none; margin: 0; padding: 0; }
.phase-tab-item { align-items: center; counter-increment: phase; display: inline-flex; position: relative; }
.phase-tab { background: var(--surface); border: 1px solid var(--line); border-bottom: 0; border-radius: var(--radius-md) var(--radius-md) 0 0; color: var(--muted); cursor: pointer; font: inherit; font-size: var(--text-sm); font-weight: 700; min-height: 40px; padding: .45rem 1.6rem .4rem .75rem; white-space: nowrap; transition: background-color 180ms var(--ease-out); }
.phase-tab::before { content: counter(phase) ". "; }
.phase-tab:hover { background: var(--control-hover); }
.phase-tab[aria-selected="true"] { background: var(--bg); box-shadow: var(--shadow-tab-seam); color: var(--ink); }
.phase-tab:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: -2px; }
/* Drill variations use the segment tab language, in their own class family
   (a builder-page test counts selected button.segment-tab page-wide). Each
   tab is a real link: switching variation rebuilds the whole editor. */
.variation-tabs { display: flex; flex: 0 0 auto; gap: .25rem; list-style: none; margin: 0; padding: 0; }
.variation-tab-item {
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: var(--radius-md) var(--radius-md) 0 0;
  display: inline-flex;
  height: 40px;
  overflow: hidden;
  position: relative;
  transition: background-color 180ms var(--ease-out);
}
.variation-tab-item.is-active { background: var(--bg); box-shadow: var(--shadow-tab-seam); }
.variation-tab-item:not(.is-active):hover { background: var(--control-hover); }
.variation-tab {
  align-items: center;
  color: var(--muted);
  display: inline-flex;
  font-size: var(--text-sm);
  font-weight: 700;
  gap: .25rem;
  height: 100%;
  padding: .5rem .75rem;
  text-decoration: none;
  white-space: nowrap;
}
.variation-tab[aria-selected="true"] { color: var(--ink); }
.variation-tab:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: -2px; }
/* Marks a variation that has no diagrams of its own and is following the
   base drill's. */
.variation-tab em { color: var(--muted); font-size: var(--text-base); font-style: normal; }
.variation-tab-item .segment-tab-handle { padding-left: .25rem; }
.variation-add-form { display: inline-flex; }
.drill-variation-workspace { border-radius: 0 0 var(--radius-md) var(--radius-md); padding: 1rem; }
.drill-variation-workspace .phase-editor { margin-top: 0; }
.variation-attributes { border-top: 1px solid var(--line); margin-top: 1.25rem; padding-top: 1rem; }
.variation-attributes .section-heading h3 { font-size: var(--text-md); margin: 0; }
.variation-attributes .section-heading p { color: var(--muted); font-size: var(--text-sm); margin: .2rem 0 0; }
.variation-fallback-notice {
  align-items: center;
  background: var(--surface);
  border: 1px dashed var(--line);
  border-radius: var(--radius-lg);
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: space-between;
  padding: 1rem;
}
.variation-fallback-notice p { color: var(--muted); font-size: var(--text-sm); margin: .25rem 0 0; max-width: 46rem; }
.variation-fallback-preview { margin-top: 1.25rem; }
.drill-summary { color: var(--muted); margin: 0 0 1rem; max-width: 52rem; }
.drill-attributes { display: grid; gap: .75rem 1.5rem; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); margin: 0; }
.drill-attributes > div { min-width: 0; }
.drill-attributes dt { color: var(--muted); font-size: var(--text-xs); font-weight: 800; letter-spacing: .05em; text-transform: uppercase; }
.drill-attributes dd { font-size: var(--text-base); margin: .2rem 0 0; }

/* min(16rem, 100%), not a bare 16rem: this section sits inside
   .builder-section, which is overflow: clip -- invisible to
   mobile_layout_probe's clipped() skip, so a too-wide minimum here would
   silently clip rather than show up as a measured page overflow. At 320px
   the section's own content box is roughly 254px after its padding, just
   under a bare 16rem (256px). */
.classification-body { display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr)); }
.classification-ladder { grid-column: 1 / -1; min-width: 0; }
.classification-axis-texts { font-size: var(--text-sm); list-style: none; margin: .5rem 0 0; padding: 0; }
.classification-sub-skills-label { margin-top: 1.25rem; }
.classification-sub-skills, .classification-primary-skills { display: flex; flex-wrap: wrap; gap: .5rem .5rem; font-size: var(--text-sm); list-style: none; margin: .5rem 0 0; padding: 0; }
.classification-sub-skills small, .classification-primary-skills small { color: var(--muted); }
/* Forgejo #131: Taktik reads like the Sub-Skills list above it - roles as
   chips, game problems as the same inline list. */
.classification-tactics-label { margin-top: 1.25rem; }
.classification-roles { margin: .5rem 0 0; }
.classification-game-problems { display: flex; flex-wrap: wrap; font-size: var(--text-sm); gap: .5rem .5rem; list-style: none; margin: .5rem 0 0; padding: 0; }
.classification-game-problems small { color: var(--muted); }
.drill-show-diagram {
  background: var(--ice-surface-alt);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  display: block;
  height: var(--rink-max-height);
  margin-inline: auto;
  max-width: 100%;
  width: auto;
}
.drill-show-diagram.is-empty { aspect-ratio: 2 / 1; align-items: center; color: var(--muted); display: flex; font-size: var(--text-base); justify-content: center; }
/* The drill show page's per-phase carousel (2026-09-27): the rink diagram plus
   that phase's images and videos, swiped like a multi-image Instagram post.

   The track is a NATIVE scroll-snap container. The swipe, the momentum and the
   snap are the browser's, which is the only version that can coexist with
   rink-pan-zoom: its pointer handlers sit on the diagram inside slide one, and
   a transform carousel would have had to take that gesture away from it. */
.phase-carousel { position: relative; }
.carousel-track {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  /* The dots say where you are; a scrollbar under a rink says nothing. */
  scrollbar-width: none;
}
.carousel-track::-webkit-scrollbar { display: none; }
.carousel-track:focus-visible { border-radius: var(--radius-lg); outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.carousel-slide {
  align-items: center;
  display: flex;
  flex: 0 0 100%;
  justify-content: center;
  min-width: 0;
  scroll-snap-align: center;
}
/* pan-x pan-y, and the pan-x half is the load-bearing one: it is what lets the
   BROWSER scroll the track horizontally over the diagram. pan-y alone was
   wrong in exactly the opposite direction -- it forbids horizontal scrolling,
   so the swipe never reached the track and panned the rink instead (measured
   on the deployment at 390px, scrollLeft stayed 0). Omitting pinch-zoom from
   the list is deliberate: a two-finger gesture is then not the browser's, so
   it still reaches the SVG's own pinch handler.

   This is only half the fix; see data-pan-zoom-defer-touch in
   rink_pan_zoom_controller.js for why touch-action alone still left the rink
   dragged out of place. */
.carousel-slide-diagram { touch-action: pan-x pan-y; flex-direction: column; }
/* Forgejo #158: the play bar sits UNDER the diagram, full width (the slide is a flex row otherwise). */
.carousel-slide-diagram .reader-playback-bar { align-self: stretch; }
.carousel-media {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  display: block;
  max-height: var(--rink-max-height);
  max-width: 100%;
  object-fit: contain;
}
.carousel-embed { max-width: 100%; width: 100%; }
.carousel-embed iframe {
  aspect-ratio: 16 / 9;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  display: block;
  width: 100%;
}
.carousel-embed-fallback { font-size: var(--text-sm); margin: .5rem 0 0; text-align: center; }
/* Hidden below 768px on purpose: a phone swipes, and two 40px targets over a
   rink would cover the diagram they are meant to reveal. 768 is one of the
   breakpoints token_discipline_test already pins -- no new boundary. */
.carousel-arrow { display: none; }
@media (min-width: 768px) {
  .carousel-arrow {
    align-items: center;
    background: var(--bg);
    border: 1px solid var(--line-strong);
    border-radius: 50%;
    color: var(--ink);
    cursor: pointer;
    display: flex;
    height: 40px;
    justify-content: center;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 40px;
    z-index: 2;
  }
  .carousel-arrow:hover { background: var(--control-hover); }
  .carousel-arrow:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
  .carousel-arrow[hidden] { display: none; }
  .carousel-arrow-previous { left: .5rem; }
  .carousel-arrow-next { right: .5rem; }
}
.carousel-dots {
  align-items: center;
  display: flex;
  gap: .5rem;
  justify-content: center;
  margin-top: .5rem;
}
.carousel-dot {
  align-items: center;
  background: transparent;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  color: var(--muted);
  cursor: pointer;
  display: flex;
  justify-content: center;
  min-height: 32px;
  min-width: 32px;
  padding: 0;
}
.carousel-dot:hover { background: var(--control-hover); }
/* white, not a token: --on-primary does not exist, and .btn-primary sets its
   own text colour the same way. The club theme only ever moves --primary
   within the contrast envelope club_theme_contrast_test pins across all 360
   hues, so white on it stays legible for every club. */
.carousel-dot.is-active { background: var(--primary); border-color: var(--primary); color: white; }
.carousel-dot:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.carousel-dot svg { display: block; }

/* The drill editor's per-phase media manager, outside the canvas. */
.phase-media-panel { border-top: 1px solid var(--line); margin-top: 1rem; padding-top: 1rem; }
.phase-media-list { display: grid; gap: .4rem; list-style: none; margin: 0 0 .75rem; padding: 0; }
.phase-media-item {
  align-items: center;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  display: flex;
  gap: .5rem;
  padding: .35rem .5rem;
}
.phase-media-icon { color: var(--muted); display: flex; flex: 0 0 auto; }
.phase-media-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.phase-media-forms { display: grid; gap: 1rem; }
@media (min-width: 768px) {
  .phase-media-forms { grid-template-columns: repeat(3, 1fr); }
}
.phase-media-form { display: grid; gap: .4rem; }
.phase-media-form .form-hint { margin: 0; }
.phase-media-error:empty { display: none; }

.variation-fallback-preview h4 { color: var(--muted); font-size: var(--text-xs); font-weight: 800; letter-spacing: .05em; margin: 0 0 .5rem; text-transform: uppercase; }

/* Height is fixed and the WIDTH follows the svg's own viewBox ratio -- no
   `aspect-ratio` here on purpose. A hardcoded 2:1 only matches the quarter_*
   views; the presets run from 0.25 (youth_cross_ice) to 2.0, so on half rink
   or an end zone the rink was drawn small and centred inside a 2:1 box,
   leaving wide dead strips. Those strips are not inert: screenToRinkPoint
   maps them to real coordinates outside the viewBox and has no clamp, so a
   click there placed an element nobody could see. With width:auto the box IS
   the rink, at every preset, so every pixel of it is live. */
/* The rink grows across the editor row, stopping only when its height would
   pass --editor-rink-max-height. width = min(available, cap x ratio) with the live aspect-ratio (both
   set by stage_controller#applyEditorViewBox) means the box is ALWAYS exactly
   the rink's own shape -- no max-height, because a max-height that engaged
   would letterbox and bring back the clickable dead strips 1fad23e removed.
   A 22.5x30 portrait crop (the 1v1 drill) was previously drawn 22rem tall and
   therefore only ~16rem wide, which is the "too small" complaint. */
.editor-rink { background: var(--ice-surface-alt); border: 0; border-radius: var(--radius-xl); box-shadow: inset 0 0 0 1px var(--line); display: block; height: auto; margin-inline: auto; width: min(100%, calc(var(--editor-rink-max-height) * var(--rink-aspect, 2))); }
.editor-toolbar { background: var(--surface-subtle); border-radius: var(--radius-2xl); margin-top: .65rem; }
.editor-toolbar-section { padding: .7rem .8rem; }
.editor-toolbar-section + .editor-toolbar-section { border-top: 1px solid var(--line); }
.editor-toolbar-heading { align-items: center; display: flex; gap: 1rem; justify-content: space-between; margin-bottom: .55rem; }
.editor-toolbar-heading > span, .editor-toolbar-label { color: var(--muted); display: block; font-size: var(--text-xs); font-weight: 800; letter-spacing: .06em; margin-bottom: .5rem; text-transform: uppercase; }
.editor-toolbar-heading .tool-status { margin: 0; text-align: right; }
.palette-field > select:not(.form-control) { position: absolute; height: 1px; opacity: 0; width: 1px; }
.palette-tool-button { align-items: center; background: var(--bg); border: 1px solid var(--line-strong); border-radius: var(--radius-lg); color: var(--ink); cursor: pointer; display: flex; font-size: var(--text-xs); font-weight: 700; gap: .4rem; justify-content: flex-start; min-height: 44px; padding: .35rem .45rem; }
.palette-tool-button svg { flex: 0 0 1.7rem; height: 1.7rem; width: 1.7rem; }
.palette-tool-button:hover { background: var(--control-hover); }
.palette-tool-button.is-selected { background: var(--editor-tool-selected-bg); border-color: var(--primary); color: var(--primary-dark); }
.palette-tool-button:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 1px; }
/* View & reset: four named groups with a one-line explanation each, rather
   than one flat row where "Full rink" sat next to "Clear…". */
.editor-toolbar-view { display: grid; gap: .9rem; }
.editor-view-group { display: grid; gap: .4rem; }
.editor-view-group + .editor-view-group { border-top: 1px solid var(--line); padding-top: .8rem; }
.editor-view-group-label { color: var(--ink); font-size: var(--text-sm); font-weight: 750; }
.editor-view-hint { color: var(--muted); font-size: var(--text-xs); line-height: 1.35; margin: 0; }
.editor-view-group .palette-field + .palette-field { margin-top: .2rem; }
.editor-view-group .clear-menu > summary, .editor-view-group > button { width: 100%; }
.editor-option-group { align-items: end; display: flex; gap: .65rem; }

.form-control-compact { min-height: var(--control-height); padding: .5rem .5rem; }
.editor-inline-control, .editor-action-row { align-items: center; display: flex; flex-wrap: wrap; gap: .35rem; }
.editor-number-option .form-control { width: 4.5rem; }
.editor-text-option .form-control { width: 10rem; }
.editor-icon-actions { gap: .3rem; }
.editor-icon-actions .icon-button:disabled { cursor: not-allowed; opacity: .5; }
.clear-menu { position: relative; }
.clear-menu > summary { list-style: none; }
.clear-menu > summary::-webkit-details-marker { display: none; }
.editor-icon-actions .clear-menu-list { left: 0; max-width: calc(100vw - 2rem); right: auto; }
.clear-menu-list { background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-xl); box-shadow: var(--shadow-popover); display: grid; min-width: 13rem; padding: .35rem; position: absolute; right: 0; top: calc(100% + .35rem); z-index: 30; }
.clear-menu-list button { background: transparent; border: 0; border-radius: var(--radius-md); color: var(--ink); cursor: pointer; font: inherit; font-size: var(--text-sm); min-height: 38px; padding: .4rem .55rem; text-align: left; }
.clear-menu-list button:hover { background: var(--danger-bg); color: var(--danger); }
.clear-menu-list button:last-child { border-top: 1px solid var(--line); color: var(--danger); margin-top: .2rem; }
.drill-metadata-section { margin-top: 1.5rem; }

.drill-variations-section { margin-top: 1.5rem; }
.drill-variations { display: flex; flex-direction: column; gap: .75rem; }
.drill-variation { background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: .75rem 1rem; }
.drill-variation.is-sorting { opacity: .6; }
.drill-variation-toolbar { align-items: center; display: flex; gap: .5rem; margin-bottom: .5rem; }
.drill-variation-handle { color: var(--muted); cursor: grab; padding: .5rem .25rem; touch-action: none; }
.drill-variation-move { display: flex; flex: 0 0 auto; }
.drill-variation-move button { background: transparent; border: 0; color: var(--muted); cursor: pointer; height: 40px; width: 28px; }
.drill-variation-fields { display: grid; gap: .75rem; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); margin-top: .75rem; }
.drill-variation-fields > .field-full { grid-column: 1 / -1; }
.drill-variation-form-actions { align-items: center; display: flex; gap: .75rem; justify-content: flex-end; margin-top: .75rem; }
.drill-variation-add { margin-top: .75rem; }

.shortcut-dialog {
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  box-shadow: 0 1rem 3rem var(--shadow-tint-strong);
  max-height: 80vh;
  max-width: 40rem;
  overflow-y: auto;
  padding: 1.5rem;
  width: 90vw;
}
.shortcut-dialog::backdrop { background: var(--scrim); }
.shortcut-dialog-header { align-items: center; display: flex; justify-content: space-between; margin-bottom: 1rem; }
.shortcut-dialog-header h2 { font-size: var(--text-lg); margin: 0; }
.shortcut-dialog h3 { color: var(--muted); font-size: var(--text-sm); margin: 1rem 0 .25rem; text-transform: uppercase; }
.shortcut-list { display: grid; gap: .25rem .5rem; grid-template-columns: auto 1fr; margin: 0; }
.shortcut-list dt { font-weight: 700; }
.shortcut-list dd { color: var(--muted); margin: 0; }

/* Forgejo #52 — DEB RTK knowledge review surface */
.status-raw, .status-reviewed { background: var(--surface-strong); color: var(--muted); }
.status-approved { background: var(--status-ready-bg); color: var(--status-ready-ink); }
.status-superseded { background: var(--surface-strong); color: var(--muted); }

.deb-review-filter-form { align-items: end; display: flex; flex-wrap: wrap; gap: .75rem; margin-bottom: 1rem; }
.deb-review-status-counts { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 1.5rem; }
.deb-review-list { display: flex; flex-direction: column; gap: .5rem; }
.deb-review-row {
  align-items: center;
  color: var(--ink);
  display: flex;
  gap: 1rem;
  text-decoration: none;
}
.deb-review-row div { min-width: 0; }
.deb-review-row strong { display: block; }
.deb-review-row small { color: var(--muted); }

.deb-review-detail { display: flex; flex-direction: column; gap: 1.25rem; margin: 0 auto; max-width: 60rem; padding-bottom: 4rem; }
.deb-provenance-record + .deb-provenance-record { border-top: 1px solid var(--line); margin-top: .75rem; padding-top: .75rem; }
.deb-provenance-record p { margin: .2rem 0; }
.deb-review-conflict-card {
  align-items: center;
  border-top: 1px solid var(--line);
  display: flex;
  gap: 1rem;
  justify-content: space-between;
  padding: .75rem 0;
}
.deb-review-actions { display: flex; flex-direction: column; gap: .75rem; }
.deb-media + .deb-media { border-top: 1px solid var(--line); margin-top: 1.25rem; padding-top: 1.25rem; }
.deb-media-frame { aspect-ratio: 16 / 9; background: #000; border-radius: var(--radius-lg); overflow: hidden; width: 100%; }
.deb-media-frame iframe { border: 0; display: block; height: 100%; width: 100%; }
.deb-media figcaption { color: var(--muted); font-size: var(--text-base); margin-top: .5rem; }
.deb-empty-content { color: var(--muted); }
.deb-confidence-line { border-top: 1px solid var(--line); color: var(--muted); font-size: var(--text-base); margin-top: 1rem; padding-top: .75rem; }
.deb-value-list { margin: 0; padding-left: 1rem; }
.deb-value-list li + li { margin-top: .25rem; }
.deb-value-nested { margin: 0; }
.deb-value-nested dt { color: var(--muted); font-size: var(--text-base); }

/* Forgejo #78 (C2): a plain <ul> of recurring slots -- no dedicated
   styling existed yet, so it fell back to raw browser bullet-list
   defaults. */
.season-recurring-slot-list { list-style: none; margin: 0 0 1rem; padding: 0; }
.season-recurring-slot-list li {
  align-items: center;
  border-bottom: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  padding: .5rem 0;
}
.season-recurring-slot-list li:last-child { border-bottom: none; }

/* Forgejo #81 (D2): Monday-first month grid. Each .month-week is its own
   CSS grid so a block bar's grid-column can be positioned against the
   same 7 columns the day cells below it use, independent of any other
   week's row. No visual browser pass was available in this environment --
   see CLAUDE.md's own #81 entry. */
.season-calendar-header {
  align-items: end;
  display: flex;
  gap: 1.5rem;
  justify-content: space-between;
  margin-bottom: 1.5rem;
}
.season-calendar-header h1 { font-size: var(--text-2xl); letter-spacing: -.02em; margin: .25rem 0 0; }

.calendar-grid-scroll {
  max-width: 100%;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scrollbar-width: thin;
}
.calendar-grid-scroll:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }

.month-grid { background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-2xl); overflow: hidden; }
.month-grid-weekday-header {
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  display: grid;
  font-size: var(--text-xs);
  font-weight: 700;
  grid-template-columns: repeat(7, 1fr);
  padding: .5rem 0;
  text-align: center;
  text-transform: uppercase;
}
.month-week { border-bottom: 1px solid var(--line); }
.month-week:last-child { border-bottom: none; }
.month-week-blocks {
  display: grid;
  gap: .2rem;
  grid-auto-rows: 1.4rem;
  grid-template-columns: repeat(7, 1fr);
  padding: .25rem .2rem 0;
}
/* A slim bar in the accent tint. It used to be min-height: 44px inside a
   1.4rem grid row and covered the day numbers. */
.month-block-bar {
  align-items: center;
  background: var(--chip-bg);
  border: 0;
  border-left: 3px solid var(--primary);
  border-radius: var(--radius-sm);
  color: var(--primary-dark);
  cursor: pointer;
  display: flex;
  font-size: var(--text-2xs);
  font-weight: 700;
  height: 100%;
  overflow: hidden;
  padding: 0 .5rem;
  text-align: left;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* A finger needs the 44px the bar itself no longer has: on a coarse pointer the
   bar rows grow instead (the bar clips its text, so an ::after hit area would
   be clipped with it). */
@media (pointer: coarse) { .month-week-blocks, .week-blocks { grid-auto-rows: 44px; } }
.month-week-days { display: grid; grid-template-columns: repeat(7, 1fr); min-height: 6rem; }
.month-day { border-left: 1px solid var(--line); padding: .25rem; }
.month-day:first-child { border-left: none; }
.month-day.is-outside-month { background: var(--surface); color: var(--muted); }
.month-day.is-today .month-day-number {
  background: var(--primary);
  border-radius: 50%;
  color: white;
  display: inline-flex;
  height: 1.5rem;
  justify-content: center;
  align-items: center;
  width: 1.5rem;
}
.month-day-number { font-size: var(--text-sm); font-weight: 700; }
.month-day-sessions { display: flex; flex-direction: column; gap: .25rem; margin-top: .25rem; }
.month-session-chip {
  align-items: center;
  border-radius: var(--radius-sm);
  display: flex;
  font-size: var(--text-2xs);
  gap: .25rem;
  min-height: 44px;
  padding: .25rem .5rem;
  text-decoration: none;
}
.month-session-chip-title { color: inherit; flex: 1; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.month-session-chip-status { color: inherit; font-size: var(--text-2xs); opacity: .8; text-transform: uppercase; }
.month-session-chip.status-planned { background: var(--status-planned-bg); color: var(--status-planned-ink); }
.month-session-chip.status-draft { background: var(--status-draft-bg); color: var(--status-draft-ink); }
.month-session-chip.status-published { background: var(--status-published-bg); color: var(--status-published-ink); }
.month-session-chip.status-completed { background: var(--status-completed-bg); color: var(--status-completed-ink); }
.month-session-chip.is-faulty { background: var(--danger-bg); color: var(--danger); }
.month-session-chip.is-past:not(.is-faulty) { opacity: .65; }
.month-session-chip-projected {
  background: transparent;
  border: 1px dashed var(--line);
  color: var(--muted);
  justify-content: center;
}
.month-session-chip-projected:hover { background: var(--surface); }

.calendar-block-dialog {
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  box-shadow: 0 1rem 3rem var(--shadow-tint-strong);
  /* The reset zeroes the UA's margin: auto, which is what centres a modal
     dialog - without it the popup opens in the top-left corner. */
  margin: auto;
  max-width: 28rem;
  padding: 1.5rem;
  width: 90vw;
}
.calendar-block-dialog::backdrop { background: var(--scrim); }
/* The detail popup (sessions and blocks, month and week): heading + X, the
   entry's facts as a two-column list, then its actions. */
.calendar-detail-header { align-items: start; display: flex; gap: .75rem; justify-content: space-between; margin-bottom: .75rem; }
.calendar-detail-header h2 { font-size: var(--text-lg); line-height: 1.25; overflow-wrap: anywhere; padding-top: .5rem; }
.calendar-detail-list { display: grid; gap: .5rem; margin: 0; }
.calendar-detail-list > div { display: grid; gap: .75rem; grid-template-columns: 6.5rem minmax(0, 1fr); }
.calendar-detail-list dt { color: var(--muted); font-size: var(--text-sm); }
.calendar-detail-list dd { margin: 0; overflow-wrap: anywhere; }
.calendar-detail-warning { align-items: center; color: var(--danger); display: inline-flex; gap: .25rem; font-weight: 700; }
.calendar-detail-actions { display: flex; flex-wrap: wrap; gap: .5rem; justify-content: flex-end; margin-top: 1.25rem; }
/* A block that runs on from the previous / into the next week: the name is
   always shown, the squared edge says it continues. */
.month-block-bar.continues-before { border-bottom-left-radius: 0; border-left-style: dashed; border-top-left-radius: 0; }
.month-block-bar.continues-after { border-bottom-right-radius: 0; border-top-right-radius: 0; }

/* Forgejo #82 (D3): week view. --hour-row-height sets the total scrollable
   height of the 06:00-23:00 axis; every session's top/height (inline
   style, from WeekGrid#position_for) is a percentage of this same height,
   so the two always agree. No visual browser pass was available in this
   environment -- see CLAUDE.md's own #82 entry. */
.week-grid { --hour-row-height: 60rem; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-2xl); overflow: hidden; }
.week-hour-axis-spacer { width: 4rem; }
.week-grid-header { border-bottom: 1px solid var(--line); display: grid; grid-template-columns: 4rem repeat(7, 1fr); }
.week-day-header { border-left: 1px solid var(--line); padding: .5rem; text-align: center; }
.week-day-header span { color: var(--muted); display: block; font-size: var(--text-2xs); text-transform: uppercase; }
.week-day-header strong { font-size: var(--text-md); }
.week-day-header.is-today strong { color: var(--primary-dark); }
.week-blocks-row { border-bottom: 1px solid var(--line); display: grid; grid-template-columns: 4rem 1fr; padding: .25rem 0; }
.week-blocks { display: grid; gap: .2rem; grid-auto-rows: 1.4rem; grid-template-columns: repeat(7, 1fr); }
.week-all-day-row { border-bottom: 1px solid var(--line); display: grid; grid-template-columns: 4rem 1fr; padding: .25rem 0; }
.week-all-day-row .week-hour-axis-spacer { color: var(--muted); font-size: var(--text-2xs); padding-top: .25rem; text-align: right; padding-right: .5rem; }
.week-all-day-cells { display: grid; gap: .2rem; grid-template-columns: repeat(7, 1fr); }
.week-all-day-cell { display: flex; flex-direction: column; gap: .2rem; padding: 0 .2rem; }
.week-time-body { display: grid; grid-template-columns: 4rem 1fr; }
.week-hour-axis { height: var(--hour-row-height); position: relative; }
.week-hour-label {
  color: var(--muted);
  font-size: var(--text-2xs);
  padding-right: .5rem;
  position: absolute;
  right: 0;
  text-align: right;
  transform: translateY(-50%);
}
.week-day-columns { display: grid; grid-template-columns: repeat(7, 1fr); }
.week-day-column {
  background-image: repeating-linear-gradient(to bottom, var(--line) 0, var(--line) 1px, transparent 1px, transparent calc(var(--hour-row-height) / 18));
  border-left: 1px solid var(--line);
  height: var(--hour-row-height);
  position: relative;
}
.week-session-block {
  border-radius: var(--radius-xs);
  font-size: var(--text-2xs);
  left: 2px;
  overflow: hidden;
  padding: .15rem .25rem;
  position: absolute;
  right: 2px;
  text-decoration: none;
}
.week-session-block .month-session-chip-title { display: block; }
.week-session-block.month-session-chip-projected {
  align-items: center;
  display: flex;
  gap: .2rem;
  justify-content: center;
}

/* Forgejo #83 (D4): year view. Each mini-month is one big <a> (no
   individual session click targets at this zoom level, per the project
   owner's own instruction) -- day cells are plain <span>s, never nested
   interactive elements. No visual browser pass was available in this
   environment -- see CLAUDE.md's own #83 entry. */
.year-grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); }
.year-mini-month {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  color: inherit;
  display: block;
  padding: .75rem;
  text-decoration: none;
}
.year-mini-month:hover { background: var(--surface); }
.year-mini-month h2 { font-size: var(--text-base); margin: 0 0 .5rem; }
.year-mini-month-weekday-header {
  color: var(--muted);
  display: grid;
  font-size: var(--text-2xs);
  grid-template-columns: repeat(7, 1fr);
  text-align: center;
  text-transform: uppercase;
}
.year-mini-month-blocks { display: grid; gap: 1px; grid-template-columns: repeat(7, 1fr); height: 2px; margin-top: 1px; }
.year-mini-month-block-underline { background: var(--accent); border-radius: 1px; }
.year-mini-month-week { display: grid; grid-template-columns: repeat(7, 1fr); }
.year-mini-month-day {
  align-items: center;
  display: flex;
  flex-direction: column;
  font-size: var(--text-2xs);
  gap: 1px;
  padding: .1rem 0;
}
.year-mini-month-day.is-outside-month { visibility: hidden; }
.year-mini-month-day.is-today { color: var(--primary-dark); font-weight: 700; }
.year-mini-month-dot { border-radius: 50%; height: 4px; width: 4px; }
.year-mini-month-dot.is-session { background: var(--primary); }
.year-mini-month-dot.is-projected { background: var(--line); }

/* Forgejo #84 (D5): view switcher + prev/next/today -- plain links only,
   no client-side router. */
.season-calendar-nav { align-items: center; display: flex; flex-wrap: wrap; gap: .75rem; justify-content: space-between; margin-bottom: 1.5rem; }
.season-calendar-view-switch, .season-calendar-date-nav { display: flex; gap: .5rem; }
.season-calendar-view-switch a.is-current, .season-calendar-task.is-current { background: var(--primary); border-color: var(--primary); color: white; }
.season-calendar-views { align-items: center; display: flex; flex-wrap: wrap; gap: 1.25rem; }

@media (max-width: 767px) {
  .season-calendar-header { align-items: stretch; flex-direction: column; gap: 1rem; }
  .season-calendar-header .btn { width: 100%; }
  .season-calendar-nav { align-items: stretch; flex-direction: column; }
  .season-calendar-view-switch, .season-calendar-date-nav {
    overflow-x: auto;
    padding-bottom: .2rem;
    scrollbar-width: thin;
  }
  .season-calendar-view-switch > *, .season-calendar-date-nav > * { flex: 0 0 auto; }
  .month-grid { min-width: 52rem; }
  .week-grid { min-width: 56rem; }
}


/* In split editors the tools use the same compact horizontal language as the command bar. The only
   floating layer here is the menu of a tool group (Forgejo #177): it opens upward over the rink. Its
   containers carry no overflow (a clipping ancestor would cut the panel off, see .session-actions). */
.editor-tools-bar { margin-top: .75rem; overflow: visible; position: relative; }
.editor-tools-bar-row { align-items: center; display: flex; gap: .5rem; }
.editor-tools-bar-row > .editor-toolbar-section { flex: 1 1 0; min-width: 0; }
.editor-tools-bar-row > .toolbar-layout-menu { flex: 0 0 auto; margin: 0 .5rem 0 0; }
.editor-tools-bar-row > .editor-toolbar-section {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: .65rem;
  padding: .5rem .65rem;
}
.editor-tools-bar .editor-toolbar-heading { flex: 0 0 auto; margin: 0; }
.editor-tools-bar .editor-toolbar-heading > span { margin: 0; }
.editor-tools-bar .palette-tools { flex: 1 1 0; min-width: 0; } /* Forgejo #178: leaves room for the layout gear on the same line */
.editor-tools-bar .palette-tool-button { flex: 1 1 0; justify-content: center; min-width: 0; white-space: nowrap; }
.editor-tools-bar .palette-tool-button svg { flex-basis: 1.5rem; height: 1.5rem; width: 1.5rem; }

/* Forgejo #189: the whole editor goes fullscreen. The canvas keeps its usual cap logic with the window height
   minus the bars; everything else keeps its place (tabs on top, command bar, canvas, playback, tools). */
.phase-editor:fullscreen, .phase-editor.is-fullscreen { --editor-rink-max-height: calc(100vh - 19rem); background: var(--page-bg); overflow: auto; padding: .75rem 1rem; }
/* The heading and the Diagram/Media switch give their room to the canvas
   (leaving fullscreen brings them back). The command, playback and tool bars stay. */
.phase-editor:is(:fullscreen, .is-fullscreen) :is(.phase-editor-header, .phase-view-tabs, .viewport-banner) { display: none; }
.phase-editor:fullscreen::backdrop { background: var(--page-bg); }

/* Forgejo #179: board mode. A full-surface drawing board for tablets: fullscreen when the browser allows it, otherwise the
   editor simply fixes itself over the page. Every normal bar steps aside; the board bar (buttons of at least 48 px) is the
   only chrome. A finger draws, so the canvas takes no browser gestures. */
.board-bar { display: none; }
.phase-editor.is-board { background: var(--page-bg); display: flex; flex-direction: column; gap: .5rem; inset: 0; overflow: auto; padding: .5rem .75rem; position: fixed; z-index: 90; --editor-rink-max-height: calc(100vh - 8.5rem); }
.phase-editor.is-board:fullscreen { inset: auto; position: relative; }
.phase-editor.is-board :is(.phase-editor-header, .phase-view-tabs, .viewport-banner, .draft-recovery-banner,
  .editor-command-bar, .playback-bar, .timing-panel, .animation-check, .editor-tools-bar, .editor-context-panels, .element-context-bar, .phase-media-tab) { display: none; }
.phase-editor.is-board .phase-diagram-panel, .phase-editor.is-board .phase-diagram-layout { display: contents; }
.phase-editor.is-board .editor-workbench, .phase-editor.is-board .editor-stage-column { display: contents; }
.phase-editor.is-board .phase-editor-canvas { flex: 1 1 auto; min-height: 0; }
.phase-editor.is-board svg[data-editor--stage-target="svg"] { touch-action: none; }
.phase-editor.is-board .board-bar { align-items: center; background: #fff; border: 1px solid var(--line-strong); border-radius: var(--radius-lg); display: flex; flex: 0 0 auto; flex-wrap: wrap; gap: .5rem .9rem; padding: .4rem .6rem; position: relative; }
.board-group { display: flex; flex-wrap: wrap; gap: .25rem; }
.board-button { align-items: center; background: var(--bg); border: 1px solid var(--line-strong); border-radius: var(--radius-md); color: var(--ink); cursor: pointer; display: inline-flex; font: inherit; font-size: var(--text-sm); font-weight: 700; gap: .5rem; justify-content: center; min-height: 48px; min-width: 48px; padding: .25rem .5rem; }
.board-button svg { height: 1.6rem; width: 1.6rem; }
.board-button.is-selected { background: var(--editor-tool-selected-bg); border-color: var(--primary); color: var(--primary-dark); }
.board-swatch { background: var(--swatch); padding: 0; }
.board-done { background: var(--primary); border-color: var(--primary); color: #fff; margin-left: auto; }
.board-status { color: var(--muted); font-size: var(--text-xs); position: absolute; right: .6rem; top: -1.25rem; }

/* Forgejo #178: the workbench is a grid of the stage (canvas + playback bar) and the tool bar; the dock
   class picks the areas. Vertical docks stack the groups and open their menus sideways. */
.editor-workbench { display: grid; gap: .75rem; grid-template-columns: minmax(0, 1fr); }
.editor-stage-column { grid-area: stage; min-width: 0; }
.editor-workbench > .editor-tools-bar { grid-area: bar; margin-top: 0; min-width: 0; }
.editor-workbench.toolbar-dock-bottom { grid-template-areas: "stage" "bar"; }
.editor-workbench.toolbar-dock-top { grid-template-areas: "bar" "stage"; }
.editor-workbench.toolbar-dock-left { grid-template-areas: "bar stage"; grid-template-columns: auto minmax(0, 1fr); }
.editor-workbench.toolbar-dock-right { grid-template-areas: "stage bar"; grid-template-columns: minmax(0, 1fr) auto; }
.toolbar-layout-menu { position: relative; }
.toolbar-layout-menu > summary { list-style: none; }
.toolbar-layout-menu > summary::-webkit-details-marker { display: none; }
.toolbar-layout-panel { bottom: calc(100% + .35rem); top: auto; display: grid; gap: .5rem; min-width: 9rem; padding: .5rem; right: 0; width: max-content; z-index: 36; }
.toolbar-layout-panel fieldset { border: 0; display: grid; gap: .15rem; margin: 0; padding: 0; }
.toolbar-layout-panel legend { color: var(--muted); font-size: var(--text-xs); font-weight: 800; letter-spacing: .06em; padding: 0; text-transform: uppercase; }
.toolbar-layout-panel label { align-items: center; display: flex; font-size: var(--text-sm); gap: .5rem; }
.toolbar-dock-top .tool-menu-panel, .toolbar-dock-top .toolbar-layout-panel { bottom: auto; top: calc(100% + .35rem); transform-origin: top left; }
.toolbar-dock-left .tool-menu-panel, .toolbar-dock-left .toolbar-layout-panel { bottom: auto; left: calc(100% + .35rem); right: auto; top: 0; transform-origin: top left; }
.toolbar-dock-right .tool-menu-panel, .toolbar-dock-right .toolbar-layout-panel { bottom: auto; left: auto; right: calc(100% + .35rem); top: 0; transform-origin: top right; }
.toolbar-dock-left .editor-tools-bar-row, .toolbar-dock-right .editor-tools-bar-row { align-items: stretch; flex-direction: column; }
.toolbar-dock-left .editor-tools-bar-row > .editor-toolbar-section, .toolbar-dock-right .editor-tools-bar-row > .editor-toolbar-section { align-items: stretch; flex: 0 0 auto; flex-direction: column; }
.toolbar-dock-left .editor-tools-bar .palette-tools, .toolbar-dock-right .editor-tools-bar .palette-tools { flex: 0 0 auto; }
.toolbar-dock-left .palette-tools.tool-groups, .toolbar-dock-right .palette-tools.tool-groups { flex-direction: column; flex-wrap: nowrap; width: 9rem; }
.toolbar-dock-left .tool-group, .toolbar-dock-right .tool-group { flex: 0 0 auto; min-width: 0; }
.toolbar-dock-left .tool-menu-panel, .toolbar-dock-right .tool-menu-panel { min-width: 15rem; }
.toolbar-dock-left .toolbar-layout-menu, .toolbar-dock-right .toolbar-layout-menu { margin: .25rem 0 0; }
/* Compact: icons only. The names stay in title and aria-label (tool_menu_controller keeps them current). */
.toolbar-mode-compact .editor-tools-bar .palette-tool-button { padding: .35rem; }
.toolbar-mode-compact .editor-tools-bar .palette-tool-button > span { block-size: 1px; inline-size: 1px; margin: -1px; overflow: hidden; position: absolute; }
.toolbar-mode-compact .editor-tools-bar .editor-toolbar-heading { display: none; }
.toolbar-mode-compact.toolbar-dock-left .palette-tools.tool-groups, .toolbar-mode-compact.toolbar-dock-right .palette-tools.tool-groups { width: 3.5rem; }
.toolbar-mode-compact .tool-group { min-width: 3.25rem; }

/* Forgejo #177: the tool groups of the palette and the menu of each. */
.palette-tools.tool-groups { align-items: stretch; display: flex; flex-direction: row; flex-wrap: wrap; gap: .35rem; }
.tool-groups > .visually-hidden { position: absolute; }
.tool-group { flex: 1 1 0; min-width: 6.5rem; position: relative; }
.tool-group > .palette-tool-button { width: 100%; }
.tool-group > select.visually-hidden { height: 1px; opacity: 0; position: absolute; width: 1px; }
.tool-menu-panel { background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-xl); bottom: calc(100% + .35rem); box-shadow: var(--shadow-popover);
  display: grid; gap: .5rem; left: 0; max-height: min(34rem, calc(100vh - 8rem)); max-width: calc(100vw - 2rem); min-width: 100%; overflow-y: auto;
  padding: .25rem; position: absolute; transform-origin: bottom left; width: 16rem; z-index: 35; }
.tool-menu-panel[hidden] { display: none; }
.tool-menu-head { border-bottom: 1px solid var(--line); padding: .25rem .25rem .5rem; }
.tool-menu-variants { display: grid; gap: .15rem; }
.tool-menu-item { align-items: center; background: transparent; border: 0; border-radius: var(--radius-md); color: var(--ink); cursor: pointer; display: flex; font: inherit;
  font-size: var(--text-sm); font-weight: 700; gap: .75rem; min-height: 44px; padding: .25rem .5rem; text-align: left; width: 100%; }
.tool-menu-item svg { flex: 0 0 1.8rem; height: 1.8rem; width: 1.8rem; }
.tool-menu-item:hover, .tool-menu-item.is-selected { background: var(--surface); }
.tool-menu-item.is-selected { box-shadow: inset 2px 0 var(--primary); }
.tool-menu-item:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: -2px; }
.tool-menu-extras { border-top: 1px solid var(--line); display: grid; gap: .5rem; padding: .5rem .25rem .25rem; }
.tool-menu-extras .editor-option-group { align-items: stretch; display: grid; gap: .5rem; }
.tool-menu-extras .form-control { min-width: 0; width: 100%; }
@media (hover: none) { .tool-menu-panel { max-height: min(30rem, calc(100vh - 10rem)); } }
@media (prefers-reduced-motion: no-preference) {
  .tool-menu-panel { transition: opacity 120ms var(--ease-out), transform 120ms var(--ease-out); }
  @starting-style { .tool-menu-panel { opacity: 0; transform: scale(.97) translateY(3px); } }
}

/* ---------------------------------------------------------------------------
   Shared filter bar + data list (2026-09-14)

   One component for every "All" listing -- Spieler, sessions, and the drill
   library's own panel reuse these classes so the three read as one thing
   rather than three separately-invented bars.

   Chips are real <input type=checkbox> styled through :has() rather than a
   hand-rolled toggle: the native control keeps keyboard and screen-reader
   behaviour for free, and ApplicationController's `allow_browser versions:
   :modern` already requires :has support, so there is no fallback to carry.
   --------------------------------------------------------------------------- */
/* .filter-bar itself is gone: every listing now renders its groups inside
   shared/_search_filters' collapsible menu (.library-filter-body) rather
   than a standalone bar. The group/label/chip parts below are still the
   shared vocabulary those menus are built from. */
.filter-bar-group { border: 0; display: flex; flex-direction: column; gap: .25rem; margin: 0; min-width: 0; padding: 0; }
.filter-bar-label {
  color: var(--muted);
  display: block;
  font-size: var(--text-2xs);
  font-weight: 800;
  letter-spacing: .07em;
  padding: 0;
  text-transform: uppercase;
}
.filter-bar-options { display: flex; flex-wrap: wrap; gap: .25rem; }
.filter-bar-actions { align-items: center; display: flex; gap: .5rem; justify-content: flex-end; }
.filter-bar-summary { color: var(--muted); font-size: var(--text-sm); margin: -.5rem 0 .75rem; }

.filter-chip {
  align-items: center;
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  color: var(--muted);
  cursor: pointer;
  display: inline-flex;
  font-size: var(--text-sm);
  font-weight: 600;
  gap: .25rem;
  line-height: 1;
  min-height: 32px;
  padding: 0 .75rem;
  position: relative;
  /* Named properties, never `all`: only these three ever change. */
  transition: background-color 140ms var(--ease-out), border-color 140ms var(--ease-out), color 140ms var(--ease-out);
  user-select: none;
}
/* The native checkbox stays in the DOM and focusable, just not painted -- the
   chip itself is the visual control. */
.filter-chip input { height: 100%; inset: 0; margin: 0; opacity: 0; position: absolute; width: 100%; }
.filter-chip:has(input:checked) {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--bg);
}
.filter-chip:has(input:focus-visible) { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.filter-chip:active { transform: scale(.97); }
@media (hover: hover) and (pointer: fine) {
  .filter-chip:hover { border-color: var(--primary); color: var(--ink); }
  .filter-chip:has(input:checked):hover { background: var(--primary-dark); color: var(--bg); }
}

/* A dense row list in the same language as .session-row, for listings whose
   rows carry editable fields rather than a single whole-row link. */
.data-list { display: flex; flex-direction: column; gap: .5rem; }
.data-row {
  align-items: end;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-xl);
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1rem;
  padding: .75rem .75rem;
  transition: border-color 140ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .data-row:hover { border-color: var(--surface-strong); }
}
.data-row-main { flex: 1 1 11rem; min-width: 0; }
.data-row-title { font-size: var(--text-md); font-weight: 700; margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.data-row-meta { color: var(--muted); font-size: var(--text-xs); margin: .1rem 0 0; }
.data-row-field { display: flex; flex: 0 1 9rem; flex-direction: column; gap: .2rem; min-width: 0; }
.data-row-field-wide { flex: 1 1 14rem; }
.data-row-field .form-label { font-size: var(--text-2xs); letter-spacing: .05em; }
.data-row-actions { align-items: center; display: flex; flex: 0 0 auto; gap: .5rem; margin-left: auto; }

/* Row-level save feedback. It replaces a submit button, so it has to be
   legible on its own: a colour plus real text, never colour alone. Opacity
   only -- no movement, which is also what keeps it safe under reduced
   motion. */
.inline-save-status {
  color: var(--muted);
  font-size: var(--text-xs);
  font-weight: 700;
  min-width: 5.5rem;
  opacity: 0;
  text-align: right;
  transition: opacity 180ms var(--ease-out);
  white-space: nowrap;
}
.inline-save-status[data-state] { opacity: 1; }
.inline-save-status[data-state="saved"] { color: var(--success-bright); }
.inline-save-status[data-state="error"] { color: var(--danger); }

@media (prefers-reduced-motion: reduce) {
  .filter-chip, .data-row, .inline-save-status { transition-duration: 1ms; }
  .filter-chip:active { transform: none; }
}

/* Roster chip badges: Spielstärke, Erwachsen/Junior, Position, Flex, so a
   coach can see what auto-assign was balancing. Since #254 each is a filled
   pill in its attribute's colour (tokens above), legible on all three chip
   backgrounds without a per-zone rule; the full words are the tooltip. */
/* Forgejo #266: fixed columns -- Stärke, Alter, Position, Flex -- so the
   badges of a column of chips line up like a table; a missing value keeps its
   place as a faint dash. */
.roster-chip-tags { align-items: center; display: grid; flex: 0 0 auto; gap: .2rem; grid-template-columns: 1.9rem 1.3rem 3.3rem 2.3rem; justify-items: start; }
.roster-chip-tag-empty { color: currentColor; font-size: var(--text-xs); opacity: .3; padding-left: .25rem; }
.roster-chip-tag {
  align-items: center;
  border: 1px solid transparent;
  border-radius: var(--radius-xs);
  display: inline-flex;
  font-size: var(--text-2xs);
  font-weight: 800;
  letter-spacing: .02em;
  line-height: 1;
  min-height: 1.15rem;
  padding: .1rem .25rem;
}
.roster-chip-tag-skill { background: var(--tag-skill-bg); padding: .15rem .25rem; }
/* Forgejo #258: three bars, the filled ones are the level (A 1, F1 2, F2 3);
   the empty ones stay faintly visible so the count reads at a glance. */
.strength-bars { align-items: flex-end; display: inline-flex; gap: 2px; height: 16px; }
.strength-bars i { background: currentColor; border-radius: 1px; display: block; opacity: .22; width: 5px; }
.strength-bars i:nth-child(1) { height: 6px; }
.strength-bars i:nth-child(2) { height: 11px; }
.strength-bars i:nth-child(3) { height: 16px; }
.strength-bars i.is-on { opacity: 1; }
.roster-chip-tag-skill[data-value="beginner"] { color: var(--tag-skill-1); }
.roster-chip-tag-skill[data-value="advanced_1"] { color: var(--tag-skill-2); }
.roster-chip-tag-skill[data-value="advanced_2"] { color: var(--tag-skill-3); }
.roster-chip-tag-status[data-value="adult"] { background: var(--tag-adult-bg); color: var(--tag-adult-ink); }
.roster-chip-tag-status[data-value="junior"] { background: var(--tag-junior-bg); color: var(--tag-junior-ink); }
.roster-chip-tag-position { background: var(--tag-position-bg); border-color: var(--tag-position-ink); color: var(--tag-position-ink); }
.roster-chip-tag-flex { background: var(--tag-flex-bg); color: var(--tag-flex-ink); font-weight: 650; }

/* Club-wide paste import (Spieler). The preview is the whole point of this
   flow -- it has to make "wird hinzugefügt" vs "schon im Club" readable at a
   glance, so each state pairs a colour with real words, never colour alone. */
.player-import { margin-bottom: 1rem; }
.player-import-form { display: flex; flex-direction: column; gap: .5rem; }
.import-preview { margin: .75rem 0; }
.import-preview-summary { font-size: var(--text-base); margin: 0 0 .5rem; }
.import-preview-list { display: flex; flex-direction: column; gap: .25rem; list-style: none; margin: 0; max-height: 18rem; overflow-y: auto; padding: 0; }
.import-preview-item {
  align-items: center;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  display: flex;
  font-size: var(--text-sm);
  gap: .75rem;
  justify-content: space-between;
  padding: .25rem .5rem;
}
.import-preview-name { font-weight: 650; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.import-preview-state { align-items: center; display: flex; flex: 0 0 auto; font-size: var(--text-xs); font-weight: 700; gap: .25rem; }
.import-preview-state svg { display: block; }
/* Forgejo #289: this name's picture will be taken over. */
.import-preview-photo { align-items: center; color: var(--muted); display: flex; flex: 0 0 auto; font-size: var(--text-xs); font-weight: 700; gap: .25rem; margin-left: auto; }
.import-preview-new .import-preview-state { color: var(--success-bright); }
.import-preview-new { border-color: oklch(0.82 0.09 155); }
.import-preview-existing .import-preview-state,
.import-preview-duplicate .import-preview-state { color: var(--muted); }
.import-preview-existing .import-preview-name,
.import-preview-duplicate .import-preview-name { color: var(--muted); text-decoration: line-through; }

/* Rink Layouts library rows. The preview is the reason this page exists as a
   list rather than a set of links, so it gets real space -- but it stays a
   fixed box and lets the SVG letterbox inside it, which is fine here in a way
   it is NOT in the editor: nothing maps a click back to rink coordinates on
   this page, so dead strips are inert rather than a trap. */
.rink-layout-preview {
  align-items: center;
  background: var(--ice-surface-alt);
  border-radius: var(--radius-md);
  box-shadow: inset 0 0 0 1px var(--line);
  display: flex;
  flex: 0 0 auto;
  height: 4.5rem;
  justify-content: center;
  overflow: hidden;
  width: 9rem;
}
.rink-layout-preview svg { display: block; height: 100%; width: 100%; }
.rink-layout-preview-empty { color: var(--muted); font-size: var(--text-xs); font-weight: 700; }

@media (max-width: 767px) {
  .rink-layout-preview { height: 3.5rem; width: 7rem; }
}

/* 2026-10-07: rink/wheel_zoom.js. A plain wheel over a diagram scrolls the
   page; this says once, briefly, that Ctrl/Cmd + scroll zooms. Fixed on
   <body> over the diagram's centre, never inside the SVG (PNG export) or a
   host that could clip it, and never in the way of a click. */
.zoom-hint {
  background: var(--ink);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-popover);
  color: var(--bg);
  font-size: var(--text-sm);
  font-weight: 700;
  padding: .5rem .75rem;
  pointer-events: none;
  position: fixed;
  transform: translate(-50%, -50%);
  white-space: nowrap;
  z-index: 60;
}
.zoom-hint[hidden] { display: none; }

/* Drill library: heading actions. */
.library-import > summary { align-items: center; min-height: 44px; }
.library-import .action-menu-panel { width: min(22rem, calc(100vw - 2rem)); }
@media (max-width: 767px) {
  .library-import > summary { justify-content: center; width: 100%; }
}

/* Forgejo #111: the compact chart inside a coach assignment / public drill. */

/* Forgejo #116: .action-menu-panel is absolutely positioned against the
   nearest positioned ancestor, so the rows that host one need to be it.
   .action-group itself stays untouched (used elsewhere). */
.drill-actions { position: relative; }
.drill-export-form { display: grid; gap: .5rem; }

/* Forgejo #115: the Atlas view of the drill library. */
/* .section-tabs is inline-flex and never shrinks below its content: 13 family
   tabs made the document ~2100px wide on a phone (measured, Forgejo #112). */
.atlas-families { display: flex; flex-wrap: nowrap; max-width: 100%; overflow-x: auto; scrollbar-width: thin; }
.atlas-families .section-tab { flex-shrink: 0; white-space: nowrap; }
.atlas-ladder { display: grid; gap: .5rem; margin-top: 1rem; min-width: 0; }
.atlas-ladder h2 { margin: 0; }
.atlas-info { display: grid; gap: .75rem; margin-top: 1rem; }

/* Forgejo #242: the Skill-Leiter. The table scrolls inside its wrap at phone
   width (the wrap is overflow-x: auto); the page never widens. */
.skill-ladder-caption { font-size: var(--text-xs); margin: .25rem 0 0; }
.skill-ladder tbody th { border-bottom: 1px solid var(--line); font-size: var(--text-sm); font-weight: 650; min-width: 9rem; padding: .5rem .5rem; text-align: left; }
.skill-ladder thead th.skill-ladder-context { text-align: center; white-space: nowrap; }
.skill-ladder-context small { color: var(--muted); display: block; font-size: var(--text-2xs); font-weight: 650; }
.skill-ladder tbody td.skill-ladder-cell { padding: .5rem .5rem; text-align: center; }
.skill-ladder-cell.is-empty { color: var(--muted); }
.skill-ladder-count { font-weight: 800; }
a.skill-ladder-count { display: inline-block; min-width: 2rem; padding: .15rem .5rem; }
.skill-ladder-labels { font-size: var(--text-xs); list-style: none; margin: 0; padding: 0; }
.skill-ladder-labels li + li { margin-top: .15rem; }

/* Drill edit page (owner, 2026-10-08): details first, in the shape of the
   session setup. Left two thirds identity and filing, right third the
   Einordnung; below, three full-width sections on a 4-column grid. */
.drill-metadata-section .session-setup-content .builder-section-body { padding: 1rem; }
.drill-form-layout { display: grid; gap: 1rem; grid-template-columns: 1fr; }
@media (min-width: 1024px) { .drill-form-layout { grid-template-columns: 2fr 1fr; } }
.drill-form-main { align-content: start; display: grid; gap: .5rem; min-width: 0; }
/* The column is stretched to the Atlas' height; align-content: start keeps the
   rows at their natural size and leaves the rest free below (measured: without
   it the leftover was spread over every row, 103px instead of 74px). */
.drill-form-row-objective textarea { min-height: var(--control-height); }
.drill-form-row { display: grid; gap: .5rem; grid-template-columns: 1fr; min-width: 0; }
.drill-form-row > * { min-width: 0; }
@media (min-width: 768px) {
  .drill-form-row-catalog { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .drill-form-row-catalog > :only-child { grid-column: 1; }
  .drill-form-row-numbers { grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr) minmax(0, .8fr); }
  .drill-form-row-filing { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.drill-form-side { background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-lg); display: grid; align-content: start; gap: .5rem; min-width: 0; padding: 1rem; }
.drill-form-side h3, .drill-form-section h3 { font-size: var(--text-base); font-weight: 750; margin: 0; }
.drill-form-sections { display: grid; gap: .5rem; margin-top: 1rem; }
.drill-form-section { border-top: 1px solid var(--line); padding-top: .5rem; }
.drill-form-cols { display: grid; gap: .5rem; grid-template-columns: 1fr; margin-top: .5rem; }
@media (min-width: 768px) { .drill-form-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); } .drill-form-cols > .span-2 { grid-column: span 2; } }
@media (min-width: 1024px) { .drill-form-cols { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.status-picker-field .status-menu { margin-top: 0; }
.status-picker-field .status-option { cursor: pointer; }
.sub-skill-picker { position: relative; }
.sub-skill-picker > summary { align-items: center; cursor: pointer; display: flex; justify-content: space-between; list-style: none; }
.sub-skill-picker > summary::-webkit-details-marker { display: none; }
.sub-skill-picker-panel { background: var(--bg); border: 1px solid var(--line-strong); border-radius: var(--radius-lg); box-shadow: 0 1rem 2rem var(--shadow-tint); left: 0; margin-top: .25rem; max-height: 18rem; overflow-y: auto; padding: .5rem .75rem; position: absolute; right: 0; z-index: 30; }
.sub-skill-group { border: 0; margin: 0 0 .5rem; padding: 0; }
.sub-skill-group[hidden], .sub-skill-picker-panel [hidden] { display: none; }
.sub-skill-group legend { color: var(--muted); font-size: var(--text-xs); font-weight: 800; letter-spacing: .05em; margin-bottom: .2rem; text-transform: uppercase; }
.sub-skill-option { align-items: center; cursor: pointer; display: flex; flex-wrap: wrap; gap: .5rem; min-height: 32px; }
.phase-editor-header.section-heading { align-items: baseline; display: flex; justify-content: space-between; }
.phase-editor-header h2 { font-size: var(--text-md); margin: 0; }
.phase-view-tabs { border-bottom: 1px solid var(--line); display: flex; gap: .35rem; margin-bottom: .75rem; }
.phase-view-tab { background: var(--surface); border: 1px solid var(--line); border-bottom: 0; border-radius: var(--radius-md) var(--radius-md) 0 0; color: var(--muted); cursor: pointer; font: inherit; font-size: var(--text-sm); font-weight: 700; min-height: 38px; padding: .4rem .9rem; }
.phase-view-tab:hover { background: var(--control-hover); }
.phase-view-tab[aria-selected="true"] { background: var(--bg); box-shadow: var(--shadow-tab-seam); color: var(--ink); }
.phase-view-tab:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: -2px; }
.phase-diagram-panel[hidden], .phase-media-tab[hidden] { display: none; }

.drill-metadata-section .field-label-row { margin-bottom: .2rem; }

.axes-panel { display: contents; }

/* Drill library filters, integrated (2026-10-08). The always-open panel is gone:
   a thin toolbar above the table (active chips + "Weitere Filter"), and every
   other filter in its own column header. */
.filter-toolbar { align-items: center; display: flex; flex-wrap: wrap; gap: .5rem .75rem; margin-bottom: .5rem; }
.more-filters { position: relative; }
.more-filters > summary { gap: .25rem; }
.more-filters > summary.is-active { border-color: var(--primary); color: var(--primary-dark); }
.column-filter-panel-start { left: 0; right: auto; }
.filter-chips { align-items: center; display: flex; flex-wrap: wrap; gap: .25rem; list-style: none; margin: 0; padding: 0; }
.filter-chip-active { align-items: center; background: var(--chip-bg); border: 1px solid var(--chip-border); border-radius: 999px; color: var(--primary-dark); display: inline-flex; font-size: var(--text-sm); font-weight: 650; gap: .25rem; min-height: 32px; padding: .15rem .5rem .15rem .75rem; text-decoration: none; }
.filter-chip-active:hover { background: oklch(0.9 0.045 245); }
.filter-chip-active:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.filter-reset { font-size: var(--text-sm); }
.column-filter-extra { display: grid; gap: .25rem; text-transform: none; }
.column-filter-form .filter-bar-label { margin-top: .25rem; }
/* The popover lives inside the table's scroll wrapper, which clips it: while one
   is open the wrapper keeps room for it, so a short result never cuts it off. */
.data-table-wrap:has(.column-filter[open]) { min-height: 24rem; }
.data-table-classification { white-space: nowrap; }

/* Header popovers: .column-filter-panel's own width never won against the later
   .action-menu-panel rule (same specificity), so they were all 20rem. 16rem fits
   every one of them (the five 1-5 chips included); column_filter_position keeps
   the panel inside the table's visible area on a phone. */
.data-table .column-filter-panel { width: min(16rem, calc(100vw - 2rem)); }

/* Drill library, second pass (2026-10-08): hero search, the Ansicht/Actions strip,
   the Development|Context digits and the status symbol. */
.library-heading { margin-bottom: .25rem; }
.search-hero { margin: .5rem auto 1.25rem; max-width: 44rem; }
.search-hero-field { align-items: center; background: var(--bg); border: 1px solid var(--line-strong); border-radius: 999px; box-shadow: var(--shadow-control); display: flex; gap: .5rem; padding: .25rem .5rem .25rem 1rem; transition: box-shadow 140ms ease, border-color 140ms ease; }
.search-hero-field:focus-within { border-color: var(--primary); box-shadow: 0 0 0 3px var(--focus-ring); }
.search-hero-field svg { color: var(--muted); flex: 0 0 auto; }
.search-hero-field input[type=search] { background: transparent; border: 0; box-shadow: none; flex: 1; font: inherit; font-size: var(--text-md); min-height: 44px; min-width: 0; outline: none; padding: 0; }
.search-hero-field .btn-primary { border-radius: 999px; flex: 0 0 auto; }
.library-strip { align-items: flex-end; display: flex; flex-wrap: wrap; gap: .75rem 2rem; justify-content: space-between; margin-bottom: 1rem; }
.library-strip-group { display: grid; gap: .25rem; }
.library-strip-label { color: var(--muted); font-size: var(--text-2xs); font-weight: 800; letter-spacing: .08em; text-transform: uppercase; }
.library-strip-row { align-items: center; display: flex; flex-wrap: wrap; gap: .5rem; position: relative; }
.view-button { align-items: center; background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--radius-lg); color: var(--ink); cursor: pointer; display: inline-flex; font-size: var(--text-sm); font-weight: 700; gap: .25rem; list-style: none; min-height: 44px; padding: 0 1rem; text-decoration: none; }
.view-button::-webkit-details-marker { display: none; }
.view-button:hover { background: var(--control-hover); }
.view-button:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }
.view-button.is-on { background: var(--primary); border-color: var(--primary); color: var(--bg); }
.view-button.is-on:hover { background: var(--primary-dark); }
.view-button.is-filtering { border-color: var(--primary); color: var(--primary-dark); }
.view-count { background: var(--primary); border-radius: 999px; color: var(--bg); font-size: var(--text-xs); padding: 0 .5rem; }
.catalog-filter { position: relative; }
@media (max-width: 767px) {
  /* Anchor to the full-width row, not to the trigger sitting mid-row. */
  .catalog-filter { position: static; }
  .catalog-filter .column-filter-panel { max-width: calc(100vw - 1.5rem); }
}
.library-strip-actions .library-strip-row { position: relative; }
.dc-value { font-size: var(--text-base); font-variant-numeric: tabular-nums; font-weight: 800; letter-spacing: .02em; white-space: nowrap; }
/* The status column is fixed: the symbol never changes width, and the dropdown
   is lifted out of the flow so opening it moves no row. */
.drill-table .data-table-status { text-align: center; width: 4.25rem; }
.drill-table .status-badge.status-dot { align-items: center; background: transparent; border: 1px solid transparent; border-radius: 999px; display: inline-flex; font-size: var(--text-lg); gap: .15rem; justify-content: center; min-height: 44px; min-width: 52px; padding: 0 .5rem; }
.drill-table .status-badge.status-dot:hover { background: var(--surface-strong); }
/* Owner 2026-11-03: the status as a quiet drawn mark, not an emoji. Full, half and
   empty circle, so the shape says it even without the colour. */
.status-mark { --mark: var(--line-strong); border: 2px solid var(--mark); border-radius: 50%; box-sizing: border-box; display: inline-block; flex: none; height: .9rem; vertical-align: -.1em; width: .9rem; }
.status-mark-published { --mark: var(--status-mark-published); background: var(--mark); }
.status-mark-draft { --mark: var(--status-mark-draft); background: linear-gradient(90deg, var(--mark) 50%, transparent 50%); }
.status-mark-archived { --mark: var(--status-mark-archived); }
.drill-table .status-menu { margin-top: 0; }
.drill-table .status-menu-panel { left: 50%; margin-top: 0; min-width: 9rem; position: absolute; transform: translateX(-50%); top: 100%; z-index: 30; }
.data-table-wrap:has(.status-menu[open]) { min-height: 14rem; }
.drill-table .data-table-actions { width: 3rem; }
.data-table thead th .data-table-sort, .data-table thead th .data-table-label { white-space: nowrap; }

/* Action bar, third pass (2026-10-08): hero search below the strip, one action
   dropdown with an apply icon, "Weitere Filter" at the right edge, and the row's
   delete as a bare icon at the end. */
.library-strip { margin-bottom: .5rem; }
.drill-bulk-bar .bulk-action-select { flex: 1 1 10rem; max-width: 16rem; width: auto; }
.drill-bulk-bar .bulk-bar-actions { flex-wrap: wrap; }
.drill-bulk-bar .bulk-count { white-space: nowrap; }
.drill-bulk-bar .bulk-apply { min-height: 44px; min-width: 44px; }
.drill-bulk-bar .bulk-form { display: none; }
.bulk-bar-trailing { margin-left: auto; position: relative; }
.row-icon-button { align-items: center; background: none; border: 0; border-radius: var(--radius-md); color: var(--muted); cursor: pointer; display: inline-flex; justify-content: center; min-height: 44px; min-width: 44px; padding: 0; }
.row-icon-button:hover { background: var(--danger-bg); color: var(--danger); }
.row-icon-button:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: -2px; }
.bulk-dialog { margin: auto; border: 1px solid var(--line-strong); border-radius: var(--radius-2xl); box-shadow: 0 1rem 3rem var(--shadow-tint); max-width: min(26rem, calc(100vw - 2rem)); padding: 1.25rem; width: 100%; }
.bulk-dialog::backdrop, .workspace-drawer::backdrop { background: oklch(0.2 0.03 255 / .45); }
.bulk-dialog h2 { font-size: var(--text-md); margin: 0 0 .25rem; }
.bulk-dialog form { display: grid; gap: .5rem; }
.bulk-dialog-mode { border: 0; display: grid; gap: .25rem; margin: 0; padding: 0; }
.bulk-dialog-mode label { align-items: center; cursor: pointer; display: flex; gap: .5rem; min-height: 32px; }
.bulk-dialog .form-actions { display: flex; gap: .5rem; justify-content: flex-end; }
/* Forgejo #337: the leave question names each unsaved area; its three answers stand as one even column at every width. */
.leave-guard-list { list-style: disc; margin: .5rem 0; padding-left: 1.25rem; }
.leave-guard-dialog .form-actions { display: grid; gap: .5rem; }
/* Forgejo #279: the block-date dialog -- one fieldset per affected session. */
.reconcile-dialog { max-width: min(32rem, calc(100vw - 2rem)); }
.reconcile-dialog p { margin: 0; }
.reconcile-dialog-dates { color: var(--muted); font-variant-numeric: tabular-nums; }
.reconcile-dialog-warning { align-items: center; background: var(--warning-bg); border-radius: var(--radius-lg); color: var(--warning-ink); display: flex; gap: .5rem; padding: .5rem .5rem; }
.reconcile-dialog-list { display: grid; gap: .5rem; list-style: none; margin: 0; max-height: 50vh; overflow-y: auto; padding: 0; }
.reconcile-dialog-list fieldset { border: 1px solid var(--line); border-radius: var(--radius-lg); display: grid; gap: .2rem; margin: 0; min-width: 0; padding: .5rem .75rem; }
.reconcile-dialog-list legend { align-items: center; display: flex; flex-wrap: wrap; font-weight: 600; gap: .25rem; padding: 0 .25rem; }
.reconcile-dialog-choice { align-items: center; cursor: pointer; display: flex; gap: .5rem; min-height: 32px; }
/* Forgejo #283: the block-change hint under the essentials' block field. */
.block-date-hint { background: var(--warning-bg); border-radius: var(--radius-lg); color: var(--warning-ink); display: grid; gap: .5rem; margin-top: .5rem; padding: .5rem .75rem; }
.block-date-hint[hidden] { display: none; }
.block-date-hint p { margin: 0; }
.block-date-hint-actions { display: flex; flex-wrap: wrap; gap: .5rem; }
/* A long block name wraps inside its button instead of leaving the narrow column. */
.block-date-hint .btn { height: auto; max-width: 100%; min-height: 2rem; padding-block: .25rem; text-align: left; white-space: normal; }
.data-table-sub-skills { max-width: 16rem; }
.drill-table th:nth-child(2), .drill-table td:nth-child(2) { min-width: 14rem; }

/* Focus & Coaching: the single term view. */
.term-manage { display: grid; gap: .5rem; margin-bottom: 1.25rem; }
.term-manage .category-name-form { align-items: end; display: flex; flex-wrap: wrap; gap: .5rem; }
.term-manage .category-name-form .form-control { flex: 1 1 18rem; }
.term-usage h2 { font-size: var(--text-md); margin: 0 0 .5rem; }
.term-usage h3 { font-size: var(--text-base); margin: 1rem 0 .25rem; }
.term-usage-list { display: grid; gap: .25rem; list-style: none; margin: 0; padding: 0; }
.term-usage-list li { align-items: baseline; border-bottom: 1px solid var(--line); display: flex; flex-wrap: wrap; gap: .25rem .75rem; padding: .25rem 0; }
.term-usage-list small { color: var(--muted); }
.row-actions { align-items: center; display: flex; gap: .15rem; justify-content: flex-end; }
.term-new, .term-usage-cell { white-space: nowrap; }

/* A segment whose sequential drills run past its own time. */
.segment-time-warning {
  align-items: center;
  background: var(--warning-bg);
  color: var(--warning-ink);
  display: flex;
  flex-wrap: wrap;
  font-size: var(--text-sm);
  gap: .5rem 1rem;
  justify-content: space-between;
  padding: .5rem 1rem;
}
.segment-time-warning[hidden] { display: none; }
/* Forgejo #130: the priority select sits at the right end of a ticked row and
   is not drawn at all while the row is unticked (it is disabled then and
   submits nothing) - 163 greyed-out selects were pure noise. */
.sub-skill-option .sub-skill-priority { flex: 0 0 auto; margin-left: auto; min-height: 32px; padding-block: .2rem; width: auto; }
.sub-skill-option .sub-skill-priority:disabled { display: none; }
.classification-sub-skill-group { margin: .5rem 0 .2rem; }
.run-measure { margin-top: .75rem; }
.run-measure-panel { display: grid; gap: .6rem; margin-top: .5rem; padding: .75rem; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--surface); }
.run-measure-keys { display: grid; gap: .5rem; grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); }
.run-measure-key { min-height: 44px; }
.run-measure-counter { font-size: var(--text-lg); font-weight: 700; margin: 0; }
.run-measure-message { margin: 0; }
/* Forgejo #204: the Spielerdaten dialog (opened by the chip's name since #252). */
.roster-player-data-grid { display: grid; gap: .5rem; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: .75rem 0; }
.roster-player-data-grid label { display: grid; gap: .25rem; }
.roster-player-data-grid .player-flexible-toggle { display: inline-flex; grid-column: 1 / -1; }
.roster-player-data-dialog .form-actions { align-items: center; }
/* Forgejo #206: the group distribute buttons are button_to forms in a flex row. */
.roster-group-actions .inline-form { display: contents; }
/* Forgejo #268: a roster tab's distribute choice. The controls ("Was soll
   passieren?", the size, the button) on the left, the box saying what the
   checked mode does on the right; stacked on a phone. The switch is CSS
   alone: every pane that is not the checked radio's is hidden -- sentence,
   schema, button text, size hint (test/design/roster_distribute_modes_test). */
/* Owner 2026-11-03 (mockup "settings" card): ONE panel per tab -- the choice on top
   as a neutral switch under its overline, then the parameters and the single
   action on the left with what it does right next to them, in the same quiet
   box. Same markup and same look on Hell/Dunkel, Gruppen and 5 v 5 Teams, so
   a coach learns it once. */
.roster-distribute { background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-2xl); box-shadow: var(--shadow-card); display: grid; gap: 1rem 1.5rem; grid-template-columns: minmax(0, 1fr); padding: 1rem 1.25rem 1.25rem; }
@media (min-width: 768px) {
  .roster-distribute { align-items: start; grid-template-areas: "choice choice" "controls box"; grid-template-columns: minmax(14rem, 18rem) minmax(0, 1fr); }
  .roster-distribute-choice { grid-area: choice; }
  .roster-distribute-controls { grid-area: controls; }
  .roster-distribute-box { grid-area: box; }
}
.roster-distribute-controls { align-content: start; display: grid; gap: .75rem; min-width: 0; order: 3; }
.roster-distribute-controls .form-control, .roster-distribute-controls select { width: 100%; }
.roster-distribute-controls .btn { justify-content: center; width: 100%; }
/* confirm_step_controller: the armed button turns to the danger colour and the
   line under it says what the second click will replace. Colours only, so the
   button does not move under the pointer. */
.roster-distribute-controls .btn.is-armed { --btn-bg: var(--danger); --btn-border: var(--danger); background: var(--danger); border-color: var(--danger); }
.roster-distribute-confirm { color: var(--danger); font-size: var(--text-sm); font-weight: 650; margin: 0; }
.roster-distribute-choice { border: 0; margin: 0; min-width: 0; order: 1; padding: 0; }
.roster-distribute-overline { color: var(--muted); font-size: var(--text-xs); font-weight: 800; letter-spacing: .1em; margin: 0 0 .5rem; padding: 0; text-transform: uppercase; }
.roster-distribute-field .form-label small { color: var(--muted); font-size: var(--text-xs); font-weight: 400; }
.roster-distribute-box { background: var(--surface-subtle); border-radius: var(--radius-xl); display: grid; gap: .5rem; min-width: 0; order: 2; padding: 1rem 1rem; }
.roster-distribute-pane { display: grid; gap: .5rem; }
.roster-distribute-pane > .roster-distribute-text { order: 1; }
.roster-distribute-pane > .roster-schema { order: 2; }
.roster-distribute-pane > .roster-schema-legend { order: 3; }
.roster-distribute-text { font-size: var(--text-sm); line-height: 1.5; margin: 0; max-width: 64ch; }
.roster-distribute-facts { border-top: 1px solid var(--line); color: var(--muted); font-size: var(--text-sm); margin: 0; padding-top: .5rem; }
.roster-distribute-facts b { color: var(--ink); }
/* Before, arrow, after on one row at every width -- stacked, the arrow points nowhere. */
.roster-schema { align-items: start; display: grid; gap: 1rem; grid-template-columns: minmax(0, auto) auto minmax(0, auto); justify-content: start; }
.roster-schema-side { display: grid; gap: .25rem; }
.roster-schema-side > small { color: var(--muted); font-size: var(--text-xs); overflow-wrap: anywhere; }
.roster-schema-row { align-items: center; display: flex; gap: .2rem; }
.roster-schema-label { color: var(--muted); font-size: var(--text-xs); min-width: 2.7rem; }
.roster-schema-arrow { align-self: center; color: var(--muted); font-size: var(--text-lg); }
.roster-schema-dot { border: 2px solid transparent; border-radius: 50%; display: inline-block; flex: none; height: .7rem; width: .7rem; }
.roster-schema-dot.is-stay { background: var(--line-strong); }
.roster-schema-dot.is-new { background: var(--primary); }
.roster-schema-dot.is-moved { background: var(--fit-flex); }
.roster-schema-dot.is-free { border: 2px dashed var(--line-strong); }
.roster-schema-dot.is-dropped { background: var(--line-strong); opacity: .3; }
.roster-schema-legend { color: var(--muted); display: flex; flex-wrap: wrap; font-size: var(--text-xs); gap: .25rem 1rem; margin: 0; }
.roster-schema-legend > span { align-items: center; display: inline-flex; gap: .25rem; }
.roster-distribute:has([data-mode-choice="1"]:checked) [data-mode-pane]:not([data-mode-pane~="1"]),
.roster-distribute:has([data-mode-choice="2"]:checked) [data-mode-pane]:not([data-mode-pane~="2"]),
.roster-distribute:has([data-mode-choice="3"]:checked) [data-mode-pane]:not([data-mode-pane~="3"]) { display: none; }
@media (max-width: 767px) {
  .roster-distribute .segmented-control { display: grid; grid-template-columns: repeat(var(--modes, 3), minmax(0, 1fr)); width: 100%; }
  .roster-distribute .segmented-control-2 { --modes: 2; }
}
/* Forgejo #207/#260: the Team board. Two colour columns from 1024px, one
   below; each line is a list (LW, C, RW, a light divider, LD, RD) with the
   position on the left and the player filling the rest of the row, so the
   name is not cut. */
.roster-team-sides { display: grid; gap: 1rem; grid-template-columns: minmax(0, 1fr); margin: .75rem 0; }
@media (min-width: 1024px) { .roster-team-sides { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.roster-team-side { border: 1px solid var(--line); border-radius: var(--radius-2xl); container: roster-side / inline-size; display: grid; gap: .75rem; min-width: 0; padding: .75rem; }
.roster-team-heading { font-size: var(--text-md); margin: 0; }
.roster-team-heading small { color: var(--muted); font-weight: 400; }
.roster-team-row, .roster-line { display: grid; gap: .25rem; min-width: 0; }
.roster-team-chips { display: flex; flex-wrap: wrap; gap: .25rem; min-width: 0; }
.roster-line-label { color: var(--muted); font-size: var(--text-xs); font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
.roster-line-slots { display: grid; gap: .25rem; grid-template-columns: minmax(0, 1fr); list-style: none; margin: 0; padding: 0; }
.roster-line-slot {
  align-items: center; border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--surface-subtle); display: grid;
  gap: .5rem; grid-template-columns: 2rem minmax(0, 1fr); min-height: 2.6rem; min-width: 0; padding: .2rem .25rem;
}
/* UI design brief 2026-11-03: "+ Besetzen" -- the free seat is the way in for tap
   and keyboard (roster_line_board#pick), the legend sits beside what it explains,
   and the players without a colour are listed on the result, not only counted. */
.roster-line-free.roster-line-free-button { background: transparent; border: 0; border-radius: var(--radius-md); color: var(--primary-dark); cursor: pointer; font: inherit; font-size: var(--text-sm); font-style: normal; font-weight: 700; min-height: 2rem; padding: .2rem .5rem; text-align: left; }
.roster-line-free-button:hover { background: var(--control-hover); }
/* Forgejo #298: the swap button on a seated chip, the tap way to change a seat. */
.roster-chip-swap { align-items: center; background: transparent; border: 1px solid var(--line); border-radius: var(--radius-sm); color: var(--muted); cursor: pointer; display: inline-flex; flex: 0 0 auto; font: inherit; font-size: var(--text-sm); height: 1.6rem; justify-content: center; margin-left: auto; position: relative; width: 1.6rem; }
.roster-chip-swap:hover { background: var(--control-hover); color: var(--primary-dark); }
.roster-chip-swap:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 1px; }
@media (pointer: coarse) { .roster-chip-swap::after { content: ""; height: 44px; left: 50%; position: absolute; top: 50%; transform: translate(-50%, -50%); width: 44px; } }
.roster-line-free-button:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 1px; }
.fit-legend { display: flex; flex-wrap: wrap; gap: .5rem; list-style: none; margin: .75rem 0 0; padding: 0; }
.fit-legend > li { background: var(--bg); border: 1px solid var(--line); border-radius: 999px; padding: .15rem .5rem; }
.unassigned-section { background: var(--surface-subtle); border: 1px dashed var(--line-strong); border-radius: var(--radius-xl); display: grid; gap: .5rem; margin: .75rem 0 0; padding: .5rem .75rem; }
.unassigned-head { align-items: center; display: flex; gap: .5rem; justify-content: space-between; }
.unassigned-section .form-hint { margin: 0; }
.unassigned-list { display: flex; flex-wrap: wrap; gap: .25rem; list-style: none; margin: 0; padding: 0; }
.unassigned-chip { background: var(--bg); border: 1px solid var(--line); border-radius: 999px; font-size: var(--text-sm); padding: .15rem .5rem; }
.count-tag:empty { display: none; }
.count-tag { background: var(--surface-strong); border-radius: var(--radius-sm); color: var(--muted); font-size: var(--text-xs); font-weight: 800; padding: .15rem .5rem; }
.slot-picker { border: 1px solid var(--line); margin: auto; border-radius: var(--radius-2xl); box-shadow: var(--shadow-popover); max-height: 90dvh; max-width: min(32.5rem, calc(100vw - 1.5rem)); overflow: auto; padding: 1rem; width: 100%; }
.slot-picker::backdrop { background: var(--scrim); }
.slot-picker-head { align-items: center; display: flex; justify-content: space-between; }
.slot-picker-head h3 { font-size: var(--text-lg); margin: 0; }
.slot-picker-list { display: grid; gap: .25rem; list-style: none; margin: .5rem 0 0; padding: 0; }
.slot-picker-choice { align-items: center; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-lg); cursor: pointer; display: grid; gap: .1rem .5rem; grid-template-columns: minmax(0, 1fr) auto; min-height: 2.75rem; padding: .5rem .75rem; text-align: left; width: 100%; }
.slot-picker-choice:hover { background: var(--control-hover); border-color: var(--control-border-hover); }
.slot-picker-choice:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 1px; }
.slot-picker-choice strong { overflow-wrap: anywhere; }
.slot-picker-choice small { color: var(--muted); font-size: var(--text-sm); grid-column: 1; }
.slot-picker-choice .roster-fit-mark { grid-column: 2; grid-row: 1 / span 2; }
/* A free seat stays dashed: the one place the eye should go to. */
.roster-line-slot:not(:has(.roster-line-chip)) { border-style: dashed; border-color: var(--line-strong); }
/* The light divider between forwards and defence (owner: "leicht angedeutet"). */
.roster-line-slot.is-defense-start { margin-top: .5rem; position: relative; }
.roster-line-slot.is-defense-start::before { border-top: 1px solid var(--line); content: ""; left: 2.4rem; position: absolute; right: .3rem; top: -.4rem; }
.roster-line-slot-name { color: var(--muted); font-size: var(--text-xs); font-weight: 800; text-align: center; }
.roster-line-free { color: var(--muted); font-size: var(--text-sm); font-style: italic; }
.roster-line-chip { cursor: default; gap: .5rem; margin: 0; min-height: 2.1rem; min-width: 0; padding: .25rem .5rem; }
.roster-line-chip .roster-chip-name { flex: 1 1 auto; min-width: 0; text-overflow: ellipsis; white-space: nowrap; }
.roster-line-chip .roster-chip-tags { flex: 0 0 auto; }
/* Forgejo #262: the goalie slot holds several, stacked like the line rows. */
.roster-goalie-chips { display: grid; gap: .25rem; min-width: 0; }
/* Forgejo #208: drag on the Team board. */
.roster-line-chip[draggable="true"] { cursor: grab; }
/* 2026-10-26 (owner): a line's weight, with a light bar behind it for the
   share of the line's best (every seat F2). */
.roster-line-head {
  align-items: baseline; background: linear-gradient(90deg, color-mix(in oklch, var(--primary) 16%, transparent) var(--line-strength), transparent var(--line-strength));
  border-radius: var(--radius-sm); display: flex; flex-wrap: wrap; gap: .2rem .5rem; padding: .15rem .5rem;
}
.roster-line-strength { color: var(--muted); font-size: var(--text-xs); }
.roster-line-strength strong { color: var(--ink); }
.roster-line-strength-warning { color: var(--warning-ink); font-weight: 700; }
/* UI design brief 2026-11-03: the Team's numbers right at the result. A tile
   is as tall as its text needs (no fixed ratio); the state shows as a border
   and, in the board's warning text, as words -- never colour alone. */
.stat-strip { display: grid; gap: .5rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 6.5rem), 1fr)); margin: .75rem 0 0; }
.stat-tile { background: var(--surface-subtle); border: 1px solid var(--line); border-radius: var(--radius-xl); display: grid; gap: .1rem; min-height: 3.5rem; min-width: 0; padding: .5rem .75rem; }
.stat-tile strong { font-size: var(--text-xl); font-variant-numeric: tabular-nums; line-height: 1.1; }
.stat-tile span { color: var(--muted); font-size: var(--text-sm); overflow-wrap: anywhere; }
.stat-tile.is-caution { background: var(--warning-bg); border-color: var(--fit-alternate); }
.stat-tile.is-caution strong, .stat-tile.is-caution span { color: var(--warning-ink); }
.stat-tile.is-danger { background: var(--danger-bg); border-color: var(--danger); }
.stat-tile.is-danger strong { color: var(--danger); }
.stat-tile .stat-tile-detail { color: inherit; font-size: var(--text-xs); line-height: 1.4; margin-top: .2rem; overflow-wrap: anywhere; }
/* The fit's glyph and word (position_fit_mark): the glyph always, the word once the
   side panel is wide enough (see the roster-side container below). */
.roster-fit-mark { align-items: center; display: inline-flex; flex: 0 0 auto; font-size: var(--text-xs); font-weight: 700; gap: .25rem; }
.roster-fit-mark[data-fit="preferred"] > :first-child { color: var(--fit-preferred); }
.roster-fit-mark[data-fit="off"] > :first-child { color: var(--fit-off); }
.roster-fit-word { border: 0; clip-path: inset(50%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px; }
/* Forgejo #299 (brief §5.1: a misfit's label never disappears): in the narrow list
   layout the word stays visible for a deviation and an alternative; a wish
   seat says enough with its tick. */
.roster-fit-mark:is([data-fit="off"], [data-fit="alternate"]) > .roster-fit-word { clip-path: none; height: auto; overflow: visible; position: static; width: auto; }
/* Forgejo #268: the form holds the distribute block now, the same width on every tab. */
.roster-build-form { display: block; }
.roster-build-form select { width: auto; }
.roster-team-pool { border: 1px dashed var(--line-strong); border-radius: var(--radius-lg); min-height: 2.25rem; padding: .25rem; }
/* Forgejo #259: every seated chip shows how its seat suits the player -- a
   frame and a left stripe in the fit colour, the words in its tooltip. The
   drag hint (#257) is the SLOT's border, so both stay visible during a drag;
   its rules come after so a hint is never overridden. Signals, never a block. */
.roster-line-chip[data-fit="preferred"] { box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--fit-preferred) 65%, transparent), inset 4px 0 0 var(--fit-preferred); }
.roster-line-chip[data-fit="alternate"] { box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--fit-alternate) 65%, transparent), inset 4px 0 0 var(--fit-alternate); }
.roster-line-chip[data-fit="flex"] { box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--fit-flex) 65%, transparent), inset 4px 0 0 var(--fit-flex); }
.roster-line-chip[data-fit="off"] { box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--fit-off) 65%, transparent), inset 4px 0 0 var(--fit-off); }
.roster-line-chip[data-fit] { padding-left: .75rem; }
/* Forgejo #267: the hover/focus card of a Team chip -- attributes in words and
   why its seat has that colour. Never while dragging. */
.roster-line-chip { position: relative; }
.roster-chip-card {
  background: var(--bg); border: 1px solid var(--line-strong); border-radius: var(--radius-md); box-shadow: 0 .5rem 1.25rem var(--shadow-tint);
  color: var(--ink); display: none; font-size: var(--text-sm); font-weight: 400; gap: .5rem; left: 0; padding: .5rem .75rem;
  pointer-events: none; position: absolute; top: calc(100% + .35rem); white-space: normal; width: min(19rem, 100%); z-index: 45;
}
.roster-line-chip:hover .roster-chip-card, .roster-line-chip:focus-within .roster-chip-card { display: grid; }
.roster-line-chip.is-dragging .roster-chip-card { display: none; }
.roster-chip-card dl { display: grid; gap: .15rem; margin: 0; }
.roster-chip-card dl > div { display: grid; gap: .5rem; grid-template-columns: 8.5rem minmax(0, 1fr); }
.roster-chip-card dt { color: var(--muted); }
.roster-chip-card dd { margin: 0; }
.roster-chip-card-fit { border-left: 4px solid var(--line-strong); display: grid; gap: .1rem; margin: 0; padding-left: .5rem; }
.roster-chip-card-fit[data-fit="preferred"] { border-color: var(--fit-preferred); }
.roster-chip-card-fit[data-fit="alternate"] { border-color: var(--fit-alternate); }
.roster-chip-card-fit[data-fit="flex"] { border-color: var(--fit-flex); }
.roster-chip-card-fit[data-fit="off"] { border-color: var(--fit-off); }
/* The 3px look is the 1px border plus a 2px ring: a wider border grew every
   row of the line list during dragstart, moved the held chip under the
   pointer, and Chrome ended the drag without a drop (2026-10-26,
   test/design/drag_state_layout_test.rb). */
.roster-line-slot[data-fit-hint] { border-style: solid; box-shadow: 0 0 0 2px var(--fit-hint-colour); }
.roster-line-slot[data-fit-hint="preferred"] { --fit-hint-colour: var(--fit-preferred); background: color-mix(in oklch, var(--fit-preferred) 14%, transparent); border-color: var(--fit-preferred); }
.roster-line-slot[data-fit-hint="alternate"] { --fit-hint-colour: var(--fit-alternate); background: color-mix(in oklch, var(--fit-alternate) 14%, transparent); border-color: var(--fit-alternate); }
.roster-line-slot[data-fit-hint="flex"] { --fit-hint-colour: var(--fit-flex); border-color: var(--fit-flex); }
.roster-line-slot[data-fit-hint="off"] { --fit-hint-colour: var(--fit-off); border-color: var(--fit-off); }
.roster-line-slot.is-drop-target, .roster-team-pool.is-drop-target { background: var(--control-hover); border-color: var(--primary); }
/* UI design brief 2026-11-03 (owner: "3+2-Formation ab ~900 px, Liste darunter"):
   once a colour's panel is 30rem wide, a line is drawn as it stands on the ice --
   three forwards in a row, the two defenders centred below at two thirds of the
   width. The size is the panel's own (container), not the viewport's, so two
   panels side by side and one stacked panel decide alike. Slots keep a minimum
   height and grow with their text; there is no fixed ratio. Below 30rem the
   list of #260 stays. */
@container roster-side (min-width: 30rem) {
  .roster-line-slots { gap: .5rem; grid-template-columns: repeat(6, minmax(0, 1fr)); row-gap: .5rem; }
  .roster-line-slot { align-content: start; grid-column: span 2; grid-template-columns: minmax(0, 1fr); min-height: 6.5rem; padding: .5rem; }
  .roster-line-slots-4 .roster-line-slot, .roster-line-slots-2 .roster-line-slot { grid-column: span 3; }
  .roster-line-slots-5 .roster-line-slot.is-defense-start { grid-column: 2 / span 2; }
  .roster-line-slots-5 .roster-line-slot.is-defense-start + .roster-line-slot { grid-column: 4 / span 2; }
  .roster-line-slot.is-defense-start { margin-top: 0; }
  .roster-goalie-slot { max-width: 20rem; min-height: 4.5rem; }
  .roster-line-slot.is-defense-start::before { display: none; }
  .roster-line-slot-name { text-align: left; }
  .roster-line-chip { align-content: start; flex-wrap: wrap; }
  .roster-line-chip .roster-chip-name { flex: 1 1 4.5rem; overflow-wrap: anywhere; text-overflow: clip; white-space: normal; }
  .roster-fit-word { clip-path: none; height: auto; overflow: visible; position: static; width: auto; }
}
/* Drill suggestions in the station form (Forgejo #141): a quiet disclosure
   under the drill field, flat rows inside - title and score, reasons as one
   line, the button on the right. */
.drill-suggestions { margin-top: .5rem; }
.drill-suggestions > summary { align-items: center; color: var(--primary-dark); cursor: pointer; display: inline-flex; font-size: var(--text-sm); font-weight: 700; gap: .25rem; list-style: none; min-height: 36px; }
.drill-suggestions > summary::-webkit-details-marker { display: none; }
.drill-suggestions > summary::after { border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; content: ""; height: .4rem; margin-left: .15rem; transform: rotate(45deg) translateY(-2px); transition: transform 140ms var(--ease-out); width: .4rem; }
.drill-suggestions[open] > summary::after { transform: rotate(225deg) translateY(-2px); }
.drill-suggestions-head { align-items: center; display: flex; flex-wrap: wrap; gap: .25rem .75rem; margin-bottom: .25rem; }
.drill-suggestions-head .form-hint { margin: 0; }
.drill-suggestions-goal { align-items: center; display: flex; flex: 1 1 14rem; gap: .5rem; max-width: 22rem; }
.drill-suggestions-goal .form-label { margin: 0; }
.drill-suggestion-list { display: grid; list-style: none; margin: 0; padding: 0; }
/* Flex that wraps, not a two-column grid: the station column is ~230px on a
   320px phone, and a button beside the text squeezed the title to ~100px
   (seen in a screenshot on homelab-dev). Narrow, the button drops below. */
.drill-suggestion { align-items: center; border-top: 1px solid var(--line); display: flex; flex-wrap: wrap; gap: .25rem .75rem; padding: .5rem 0; }
.drill-suggestion-main { flex: 1 1 13rem; min-width: 0; }
.drill-suggestion > button { flex: 0 0 auto; margin-left: auto; }
.drill-suggestion-title strong { font-size: var(--text-sm); }
.drill-suggestion-title { align-items: baseline; display: flex; flex-wrap: wrap; gap: .1rem .5rem; margin: 0; overflow-wrap: anywhere; }
.drill-suggestion-score { background: var(--chip-bg); color: var(--primary-dark); font-variant-numeric: tabular-nums; }
.drill-suggestion-reasons { display: flex; flex-wrap: wrap; font-size: var(--text-xs); gap: 0 .25rem; list-style: none; margin: .1rem 0 0; padding: 0; }
.drill-suggestion-reasons li + li::before { color: var(--muted); content: "· "; }
.drill-suggestion-reasons .is-gap { color: var(--muted); }

/* A compact record row (measurements, goals, player development): the main
   text left, a pencil right that opens its form across the whole row. */
.row-icon-button.row-icon-edit:hover { background: var(--surface); color: var(--ink); }
.record-list { display: grid; list-style: none; margin: 0; padding: 0; }
.record-row { align-items: start; border-top: 1px solid var(--line); display: grid; gap: .25rem .75rem; grid-template-columns: minmax(0, 1fr) auto; padding: .5rem 0; }
.record-row:last-child { border-bottom: 1px solid var(--line); }
.record-row-main { display: grid; gap: .1rem; grid-template-columns: minmax(0, 1fr); min-width: 0; padding-top: .25rem; }
.record-row-main p { margin: 0; overflow-wrap: anywhere; }
.record-row-headline { align-items: baseline; display: flex; flex-wrap: wrap; gap: .1rem .5rem; }
.record-row-headline strong { font-size: var(--text-md); font-variant-numeric: tabular-nums; }
.record-row-aside { color: var(--muted); font-size: var(--text-sm); }
.record-row-edit > summary { list-style: none; }
.record-row-edit > summary::-webkit-details-marker { display: none; }
.record-row-edit[open] { grid-column: 1 / -1; }
.record-row-edit[open] > summary { margin-left: auto; }

/* Messungen on the session page (Forgejo #135/#138): a session-disclosure like
   the roster above it. */
/* minmax(0, 1fr), not the implicit auto track: an auto track grows to the
   min-content of a select's longest option and made the page 30px too wide at
   320 (measured on homelab-dev). The box runs edge to edge like the roster's
   sub-boxes; the text inside lines up with the summary's 1rem. */
.observation-body { gap: .75rem; grid-template-columns: minmax(0, 1fr); padding: 1rem 1.25rem 1.25rem; }
.observation-subheading { color: var(--muted); font-size: var(--text-xs); font-weight: 800; letter-spacing: .05em; margin: .25rem 0 0; text-transform: uppercase; }
.observation-individual { box-shadow: inset 3px 0 0 var(--line-strong); padding-left: .75rem; }
.observation-player { font-weight: 750; }
.observation-history > summary { cursor: pointer; width: fit-content; }
.observation-capture { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); display: grid; gap: .75rem; grid-template-columns: minmax(0, 1fr); padding: .75rem 1rem 1rem; }
.observation-capture-switch { flex-wrap: wrap; justify-self: start; max-width: 100%; }
.observation-capture-switch span { white-space: nowrap; }
/* The switch only shows the chosen form where :has() exists; elsewhere all
   three stay visible, which is the safe direction. */
.observation-capture:has(.observation-capture-switch) .observation-capture-panel { display: none; }
.observation-capture:has(input[value="group"]:checked) [data-capture-panel="group"],
.observation-capture:has(input[value="player"]:checked) [data-capture-panel="player"],
.observation-capture:has(input[value="assessment"]:checked) [data-capture-panel="assessment"] { display: block; }

/* The measurement, assessment and goal forms: two columns on a phone, four
   once the form itself is wide enough (a container query, so the same grid
   fits the session page and the narrower planner block card). */
.compact-form { container-type: inline-size; gap: .75rem; padding: 0; }
.compact-form .form-actions { margin-top: 0; }
.compact-form-grid { align-items: start; display: grid; gap: .75rem .75rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.compact-form-grid .field-span-2, .compact-form-grid .field-full { grid-column: 1 / -1; }
.compact-form-grid .form-hint { margin: 0; }
.compact-form-grid .form-hint:empty { display: none; }
.compact-form-pair { display: grid; gap: .75rem .75rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.compact-form-pair[hidden] { display: none; }
@container (min-width: 40rem) {
  .compact-form-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .compact-form-grid .field-span-2 { grid-column: span 2; }
}
.compact-form-grid > div > .form-hint { margin-top: .25rem; }

/* Goals on a season block (Forgejo #133/#137): record rows under a small
   heading, the review as one line under the meta line. Text carries every
   state; colour only repeats it. */
.learning-goals { margin-top: 1rem; }
.learning-goals-heading { color: var(--muted); font-size: var(--text-xs); font-weight: 800; letter-spacing: .05em; margin: 0 0 .2rem; text-transform: uppercase; }
.learning-goal-main { background: var(--chip-bg); color: var(--primary-dark); }
.learning-goal-progress { font-size: var(--text-sm); font-variant-numeric: tabular-nums; margin-top: .1rem; }
.learning-goal-progress.progress-not-measured { color: var(--muted); }
.learning-goal-progress.progress-not-comparable { color: var(--warning-ink); }
.learning-goal-progress.progress-reached strong { color: var(--success); }
.learning-goal-warnings { color: var(--warning-ink); display: flex; flex-wrap: wrap; font-size: var(--text-xs); gap: 0 .75rem; list-style: none; margin: .1rem 0 0; padding: 0; }
.learning-goal-progress details > summary { cursor: pointer; width: fit-content; }
.learning-goal-progress details ul { color: var(--muted); font-size: var(--text-xs); margin: .2rem 0 0; padding-left: 1rem; }
.learning-goal-progress details .form-hint { font-size: var(--text-xs); }
.learning-goal-edit { align-items: end; display: grid; gap: .5rem; grid-template-columns: minmax(0, 1fr) auto; padding-bottom: .5rem; }
.learning-goal-add > summary { align-items: center; cursor: pointer; display: inline-flex; font-size: var(--text-sm); font-weight: 750; gap: .25rem; list-style: none; min-height: 44px; }
.learning-goal-add > summary::-webkit-details-marker { display: none; }

/* Messprotokolle (Forgejo #132): one table, the counting rule as a small line
   under the name, editing in flow inside the name cell. */
.compact-form textarea.form-control { min-height: 3.5rem; }
.metric-protocol-new .action-menu-panel { width: min(30rem, calc(100vw - 2rem)); }
.metric-protocol-table td:first-child { min-width: 16rem; }
.metric-protocol-name { margin: 0; }
.metric-protocol-name small { color: var(--muted); }
.metric-protocol-definition { color: var(--muted); display: grid; font-size: var(--text-xs); gap: .05rem .5rem; grid-template-columns: max-content minmax(0, 1fr); margin: .2rem 0 0; max-width: 48rem; }
.metric-protocol-definition dt { font-weight: 750; }
.metric-protocol-definition dt::after { content: ":"; }
.metric-protocol-definition dd { margin: 0; }
.metric-protocol-new > summary { white-space: nowrap; }
.metric-protocol-table tr.is-hidden td { color: var(--muted); }
.metric-protocol-edit > summary { cursor: pointer; font-size: var(--text-sm); margin-top: .25rem; width: fit-content; }
/* 40rem is the compact grid's 4-column breakpoint: an open edit form is three
   short rows on a desktop instead of one 650px column; the other cells stay at
   the top of such a tall row. */
.metric-protocol-edit .compact-form { margin-top: .5rem; min-width: min(40rem, 75vw); }
.metric-protocol-table tbody td { vertical-align: top; }
@media (max-width: 767px) {
  .metric-protocol-definition { grid-template-columns: minmax(0, 1fr); }
  .metric-protocol-definition dd { margin-bottom: .15rem; }
}

/* A player's development (Forgejo #139): one collapsible row per sub-skill in
   the page's own data-list shape; entries are record rows. */
.ds-collapse.player-development-skill > summary { align-items: start; display: grid; gap: .1rem; justify-content: stretch; }
.player-development-skill h3 { font-size: var(--text-base); font-weight: 750; margin: 0; }
.player-development-counts, .player-development-trend { color: var(--muted); font-size: var(--text-sm); }
.player-development-trend { font-variant-numeric: tabular-nums; }
.player-development-skill > .record-list { padding: .1rem 1rem .5rem; }
.player-development-skill > .record-list > .record-row:first-child { border-top: 0; }
.player-development-skill > .record-list > .record-row:last-child { border-bottom: 0; }

/* The Academy page (onboarding, 2026-10-18). Every chapter is a card; the
   contents list is a disclosure below 1024px and a sticky column from there. */
.academy-layout { display: grid; gap: 1.25rem; grid-template-columns: minmax(0, 1fr); }
.academy-toc-mobile { margin-bottom: 1.25rem; }
.academy-toc-body { padding: .5rem 1rem 1rem; }
.academy-toc { display: none; }
.academy-toc nav { position: sticky; top: 4.5rem; }
.academy-toc-list { display: grid; gap: .1rem; list-style: none; margin: 0; padding: 0; }
.academy-toc-list a { border-radius: var(--radius-sm); color: var(--ink); display: flex; font-size: var(--text-sm); gap: .5rem; padding: .25rem .5rem; text-decoration: none; }
.academy-toc-list a:hover { background: var(--surface-subtle); color: var(--primary-dark); }
.academy-toc-num { color: var(--muted); font-variant-numeric: tabular-nums; font-weight: 700; }
.academy-chapters { display: grid; gap: 1.25rem; grid-template-columns: minmax(0, 1fr); min-width: 0; }
.academy-chapter { scroll-margin-top: 4.5rem; } /* below the 56px sticky .app-header */
.academy-chapter > .card-body { display: grid; gap: 1rem; grid-template-columns: minmax(0, 1fr); }
.academy-chapter h2 { font-size: var(--text-2xl); letter-spacing: -.03em; line-height: 1.15; }
.academy-chapter h3 { margin-top: .25rem; }
.academy-chapter ul:not([class]), .academy-chapter ol:not([class]) { display: grid; gap: .25rem; list-style: disc; margin: 0; padding-left: 1.25rem; }
.academy-hero h1 { font-size: var(--text-3xl); letter-spacing: -.035em; line-height: 1.08; }
.academy-hero h1 em { color: var(--primary); font-style: normal; }
.academy-lead { color: var(--ink); font-size: var(--text-md); max-width: 48rem; }
.academy-micro, .academy-sources { color: var(--muted); font-size: var(--text-sm); }
.academy-pills { display: flex; flex-wrap: wrap; gap: .5rem; list-style: none; margin: 0; padding: 0; }
.academy-pills li, .academy-tag { background: var(--chip-bg); border: 1px solid var(--chip-border); border-radius: 999px; color: var(--primary-dark); display: inline-block; font-size: var(--text-sm); font-weight: 700; padding: .25rem .75rem; }
.academy-actions { display: flex; flex-wrap: wrap; gap: .5rem; }
.academy-statement { background: var(--accent); border-radius: var(--radius-lg); color: var(--bg); font-size: var(--text-md); font-weight: 700; line-height: 1.5; padding: 1rem 1.25rem; }
.academy-statement span { color: var(--warning-bg); }
.academy-grid-2, .academy-grid-3, .academy-grid-4, .academy-families, .academy-sources-grid, .academy-flow, .academy-split {
  display: grid; gap: 1rem; grid-template-columns: minmax(0, 1fr); list-style: none; margin: 0; padding: 0;
}
.academy-split { align-items: start; gap: 1.5rem; }
.academy-split > div { align-content: start; display: grid; gap: .5rem; grid-template-columns: minmax(0, 1fr); }
/* #246: the one-row Skill-Leiter example. minmax(0, 1fr) so the table scrolls
   in its wrap instead of widening the panel; the row header drops the
   ladder's 9rem minimum so Context 1-3 fit at phone width. */
.academy-ladder-example { grid-template-columns: minmax(0, 1fr); }
.academy-ladder-example .skill-ladder tbody th { min-width: 0; }
.academy-tile, .academy-panel, .academy-role, .academy-flow li { background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-lg); display: grid; gap: .25rem; align-content: start; padding: 1rem; }
.academy-panel { background: var(--surface-subtle); }
.academy-tile small, .academy-flow li small { color: var(--muted); font-size: var(--text-sm); }
.academy-num { color: var(--primary); font-size: var(--text-xs); font-variant-numeric: tabular-nums; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; }
.academy-tile .academy-count { color: var(--primary-dark); font-weight: 700; }
.academy-role { border-top: 4px solid var(--role-color); }
.academy-role-1 { --role-color: var(--role-offense-with-puck); }
.academy-role-2 { --role-color: var(--role-offense-without-puck); }
.academy-role-3 { --role-color: var(--role-defense-on-puck); }
.academy-role-4 { --role-color: var(--role-defense-off-puck); }
.academy-question { font-weight: 700; }
.academy-flow li.is-highlight { background: var(--chip-bg); border-color: var(--chip-border); }
.academy-levels, .academy-timeline, .academy-stations, .academy-sequence { display: grid; gap: .5rem; list-style: none; margin: 0 0 .75rem; padding: 0; }
.academy-levels li, .academy-sequence li { align-items: baseline; display: flex; gap: .75rem; }
.academy-level-num { background: var(--chip-bg); border-radius: 999px; color: var(--primary-dark); flex: none; font-weight: 800; height: 1.75rem; line-height: 1.75rem; text-align: center; width: 1.75rem; }
.academy-time { background: var(--chip-bg); border-radius: var(--radius-sm); color: var(--primary-dark); flex: none; font-size: var(--text-sm); font-variant-numeric: tabular-nums; font-weight: 800; padding: .1rem .5rem; }
.academy-timeline li { border-left: 3px solid var(--chip-border); display: grid; gap: .1rem; padding-left: .75rem; }
.academy-timeline small { color: var(--muted); font-size: var(--text-sm); }
.academy-ice-map { display: block; height: auto; max-height: var(--rink-max-height); width: 100%; }
.academy-formula { background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius-sm); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--text-sm); overflow-wrap: anywhere; padding: .5rem .5rem; }
.academy-callout { background: var(--surface-subtle); border-left: 4px solid var(--primary); border-radius: var(--radius-sm); padding: 1rem 1rem; }
.academy-callout-safe { background: var(--success-bg); border-left-color: var(--success); }
.academy-callout-warn { background: var(--warning-bg); border-left-color: var(--warning-ink); color: var(--warning-ink); }
.academy-review { border-top: 1px solid var(--line); display: grid; gap: .25rem; padding-top: .75rem; }
.academy-table th[scope="row"] { font-weight: 700; white-space: normal; }
.academy-where { color: var(--muted); font-size: var(--text-xs); }
.academy-faq { display: grid; gap: .5rem; }
.academy-protocols ul { display: grid; gap: .25rem; list-style: none; margin: 0; }
.academy-faq .ds-collapse-sub > summary + div p { margin: 0; }
/* Forgejo #311: the search over the page and the "Eishockeys ABC" chapter. */
.academy-search { align-items: center; display: flex; flex-wrap: wrap; gap: .25rem .75rem; margin-bottom: 1.25rem; }
.academy-search input { flex: 1 1 16rem; max-width: 28rem; }
.academy-search small { color: var(--muted); font-size: var(--text-sm); }
.academy .is-search-hidden { display: none; }
.academy-abc-age { align-items: center; display: flex; flex-wrap: wrap; font-size: var(--text-sm); font-weight: 700; gap: .5rem; }
.academy-abc-group, .academy-abc-pages { display: grid; gap: .5rem; grid-template-columns: minmax(0, 1fr); }
/* While the search hides every page of a group, its heading goes too. */
.academy-abc-group:not(:has(.academy-abc-page:not(.is-search-hidden))) { display: none; }
.academy-abc-page > summary small { margin-left: auto; padding-left: .75rem; white-space: nowrap; }
.academy-abc-page > summary + div { display: grid; gap: 1rem; grid-template-columns: minmax(0, 1fr); }
.academy-abc-copy { display: grid; gap: .5rem; max-width: 48rem; }
.academy-abc-copy h4, .academy-abc-matrix h4, .academy-abc-steps h4 { font-size: var(--text-md); margin-top: .5rem; }
.academy-abc-copy p, .academy-abc-copy li { hyphens: auto; }
.academy-abc-copy blockquote { background: var(--surface-subtle); border-left: 4px solid var(--primary); border-radius: var(--radius-sm); font-style: italic; margin: 0; padding: .75rem 1rem; }
.academy-abc-matrix { display: grid; gap: .5rem; }
.academy-abc-legend { color: var(--muted); display: flex; flex-wrap: wrap; font-size: var(--text-sm); gap: .25rem 1rem; }
.academy-abc-matrix .data-table th[scope="row"], .academy-abc-category th { text-align: left; }
.academy-abc-matrix .data-table th[scope="row"] { font-weight: 600; white-space: normal; }
.academy-abc-matrix .data-table td { white-space: nowrap; }
.academy-abc-category th { background: var(--surface-subtle); color: var(--primary-dark); font-size: var(--text-xs); letter-spacing: .06em; text-transform: uppercase; }
.academy-abc .is-main { color: var(--success); }
.academy-abc .is-minor { color: var(--warning-ink); }
.academy-abc .is-empty { color: var(--muted); }
.academy-abc[data-abc-age="0"] [data-age]:not([data-age="0"]),
.academy-abc[data-abc-age="1"] [data-age]:not([data-age="1"]),
.academy-abc[data-abc-age="2"] [data-age]:not([data-age="2"]),
.academy-abc[data-abc-age="3"] [data-age]:not([data-age="3"]) { display: none; }
.academy-abc-steps { display: grid; gap: .5rem; }
.academy-abc-steps .academy-tile p { font-size: var(--text-sm); margin: 0; }
.academy-abc-step-1 { border-top: 4px solid var(--danger); }
.academy-abc-step-2 { border-top: 4px solid var(--warning-ink); }
.academy-abc-step-3 { border-top: 4px solid var(--primary); }
.academy-abc-figgrid { display: flex; flex-wrap: wrap; gap: .75rem; }
.academy-abc-figure { align-items: center; background: var(--surface-subtle); border: 1px solid var(--line); border-radius: var(--radius-lg); display: flex; flex: 0 1 14rem; flex-direction: column; gap: .5rem; justify-content: center; margin: 0; min-width: 0; padding: .75rem; }
.academy-abc-figure.is-wide { flex-basis: 100%; }
.academy-abc-figure img { height: auto; max-height: 22rem; max-width: 100%; object-fit: contain; }
.academy-abc-figure figcaption { color: var(--muted); font-size: var(--text-sm); text-align: center; }
@media (min-width: 768px) {
  .academy-grid-2, .academy-grid-3, .academy-grid-4, .academy-families, .academy-sources-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .academy-flow { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .academy-timeline { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .academy-layout { grid-template-columns: 15rem minmax(0, 1fr); }
  .academy-toc-mobile { display: none; }
  .academy-toc { display: block; }
  .academy-split { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .academy-grid-3, .academy-sources-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1180px) {
  .academy-grid-4, .academy-families { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .academy-flow { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

/* Account form: one checkbox per team. Owner/Admin see every team, so the
   list gives way to a one-line note while one of those roles is chosen
   (without :has() both show, which is harmless). */
.account-teams { border: 0; margin: 1rem 0 0; padding: 0; }
.account-teams-all { display: none; }
.account-access:has(select option[value="owner"]:checked, select option[value="admin"]:checked) .account-team-list { display: none; }
.account-access:has(select option[value="owner"]:checked, select option[value="admin"]:checked) .account-teams-all { display: block; }
.account-team { align-items: center; display: flex; flex-wrap: wrap; gap: .25rem .5rem; min-height: 44px; }
.account-team input { height: 1.1rem; width: 1.1rem; }
.account-team small { color: var(--muted); }
.credentials-value { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; min-width: 0; flex: 1 1 12rem; }
.account-password { margin-top: 1rem; }

/* Forgejo #152: playback bar of the phase editor. While playing, the rink ignores the pointer. */
.editor-rink.is-playing { pointer-events: none; }
.playback-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; padding: 0.5rem 0; }
.playback-transport, .playback-views { display: inline-flex; align-items: center; gap: 0.25rem; }
.playback-views { margin-left: auto; }
.playback-bar input[type="range"] { flex: 1 1 12rem; min-width: 8rem; }
.playback-time { font-variant-numeric: tabular-nums; min-width: 6.5rem; color: var(--muted); }
.playback-hint { flex-basis: 100%; margin: 0; font-size: var(--text-base); color: var(--muted); }
.playback-hint[hidden] { display: none; }
/* A pressed view toggle reads as "on", like the accent-filled toggles of the editor bars. */
.playback-bar .icon-button[aria-pressed="true"], .playback-options[open] > summary { background: var(--primary); border-color: var(--primary); color: #fff; }
.playback-bar [data-state="playing"] .icon-play, .playback-bar [data-state="paused"] .icon-pause { display: none; }
.playback-options { position: relative; }
.playback-options-panel { bottom: calc(100% + 0.35rem); top: auto; }
.playback-field { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; font-size: var(--text-base); }
.playback-field .form-control { width: 7rem; }
.playback-check { justify-content: flex-start; }
.playback-check input { width: auto; }
.playback-options-panel .eyebrow { margin: 0; }

/* Forgejo #158: the compact play bar of the read-only views. */
.timing-head { align-items: center; display: flex; gap: 0.5rem; margin-bottom: .5rem; font-size: var(--text-base); }
.timing-summary { color: var(--muted); flex: 1; }
.timing-close { --size: 2rem; min-height: 2rem; min-width: 2rem; height: 2rem; width: 2rem; padding: 0; }
.timing-panel { border: 1px solid var(--line); border-radius: var(--radius-md); padding: 0.5rem; margin-bottom: 0.5rem; background: var(--surface); }
.timing-panel[hidden], .timing-empty[hidden] { display: none; }
.timing-empty { margin: 0; color: var(--muted); font-size: var(--text-base); }
.timing-row { display: grid; grid-template-columns: 8rem minmax(0, 1fr) 7rem; align-items: center; gap: 0.5rem; padding: 0.125rem 0; }
.timing-name { background: none; border: 0; padding: 0; text-align: left; cursor: pointer; font-size: var(--text-base); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.timing-row[data-selected] .timing-name { font-weight: 700; color: var(--primary); }
.timing-lane { position: relative; height: 1.75rem; background: var(--control-hover); border-radius: var(--radius-sm); touch-action: none; cursor: pointer; }
.timing-ruler { height: 1.25rem; font-size: var(--text-xs); color: var(--muted); }
.timing-tick { position: absolute; top: 0; bottom: 0; border-left: 1px solid var(--line-strong); padding-left: 2px; pointer-events: none; }
.timing-bar { position: absolute; top: 2px; bottom: 2px; background: var(--primary); border-radius: var(--radius-sm); cursor: grab; touch-action: none; min-width: 6px; }
.timing-bar:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 1px; }
.timing-bar--coupled { background: repeating-linear-gradient(135deg, var(--primary) 0 5px, color-mix(in srgb, var(--primary) 45%, var(--surface)) 5px 9px); cursor: not-allowed; display: flex; align-items: center; justify-content: center; }
.timing-chain { width: 1rem; height: 1rem; fill: none; stroke: #fff; stroke-width: 1.6; stroke-linecap: round; pointer-events: none; filter: drop-shadow(0 0 1px rgb(0 0 0 / 0.6)); }
.timing-handle { position: absolute; top: 0; bottom: 0; width: 8px; cursor: ew-resize; }
.timing-handle[data-edge="start"] { left: 0; }
.timing-handle[data-edge="end"] { right: 0; }
.timing-playhead { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--danger); pointer-events: none; }
.timing-ease { font-size: var(--text-base); }
.animation-check-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.25rem; }
.animation-check-list button { background: none; border: 0; padding: 0.125rem 0; text-align: left; cursor: pointer; font-size: var(--text-base); }
.animation-check-list button:hover { color: var(--primary); }
.animation-check .timing-head { display: flex; align-items: center; gap: 0.5rem; }
@media (max-width: 1023px) { .timing-toggle, .timing-panel, .animation-check-toggle { display: none; } }
.reader-playback-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; padding: .5rem 0; }
.reader-playback-bar[hidden] { display: none; }
.reader-playback-bar input[type="range"] { flex: 1 1 8rem; min-width: 6rem; }
.reader-playback-bar select { font-size: var(--text-base); }
@media print { .reader-playback-bar { display: none !important; } }
/* Forgejo #226: one mark per phase under the playback range; active = underline + bold, never colour alone. Owner 2026-10-25: a short phase keeps a mark wide enough to hit (min-width); a later mark may cover the empty end of an earlier one, never its number. */
.chapter-marks { position: relative; flex: 1 1 100%; height: 1.5rem; }
.chapter-marks[hidden] { display: none; }
.chapter-mark { position: absolute; top: 0; bottom: 0; min-width: 1.75rem; overflow: hidden; display: flex; align-items: center; gap: 0.25rem; padding: 0 0.25rem; border: 1px solid var(--line); border-bottom-width: 2px; border-radius: var(--radius-sm, 0.25rem); background: var(--surface); font-size: var(--text-sm, 0.75rem); cursor: pointer; white-space: nowrap; }
.chapter-mark-name { overflow: hidden; text-overflow: ellipsis; }
.chapter-mark.is-active { z-index: 1; font-weight: 700; border-color: var(--primary); border-bottom-width: 4px; }
@media print { .chapter-marks { display: none !important; } }
/* Forgejo #227: on the drill page one player per mode; the sequence bar only in the drill scope. */
.is-drill-scope .reader-playback-bar:not(.is-sequence) { display: none; }
.drill-variation-workspace:not(.is-drill-scope) .reader-playback-bar.is-sequence { display: none; }
.playback-scope { display: inline-flex; gap: 0.25rem; }
.playback-scope button[aria-pressed="true"] { font-weight: 700; border-color: var(--primary); border-bottom-width: 3px; }
.drill-catalog-dialog { background: var(--bg); margin: auto; border: 1px solid var(--line-strong); border-radius: var(--radius-lg); box-shadow: var(--shadow-popover); color: var(--ink); max-width: 28rem; padding: 1.25rem; }
.drill-catalog-dialog::backdrop { background: rgb(0 0 0 / .35); }
.drill-catalog-dialog h2 { font-size: var(--text-md); margin: 0 0 .5rem; }
.drill-catalog-dialog p { margin: 0 0 1rem; }

/* 2026-11-03: up to 420 px the header has no room for the area name; the
   page heading below it says the same. Keeps the club field readable. */
@media (max-width: 420px) {
  .app-current-area { display: none !important; }
}

/* Forgejo #308 (owner 2026-11-03): a variation's basic fields. Each one is a
   small card with its switch; the switch alone decides what is shown -- the
   base drill's value (Wie Basis), the input (Eigener Text / Ergänzen /
   Ersetzen) or the note that the field is empty here. */
.variation-overrides { display: grid; gap: .75rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); margin-top: 1rem; }
.variation-field { background: var(--surface-subtle); border: 1px solid var(--line); border-radius: var(--radius-xl); display: grid; gap: .5rem; margin: 0; min-width: 0; padding: .75rem 1rem; }
.variation-field > legend { margin: 0; padding: 0; }
.variation-field { align-content: start; }
.variation-field .segmented-control { align-self: start; justify-self: start; }
.variation-field-base { color: var(--muted); font-size: var(--text-sm); margin: 0; overflow-wrap: anywhere; white-space: pre-line; }
.variation-field-base > span, .variation-field-result > span { font-weight: 700; }
.variation-field-result { font-size: var(--text-sm); margin: 0; overflow-wrap: anywhere; }
.variation-field:has([data-mode="inherit"]:checked) [data-pane]:not([data-pane~="inherit"]),
.variation-field:has([data-mode="own"]:checked) [data-pane]:not([data-pane~="own"]),
.variation-field:has([data-mode="add"]:checked) [data-pane]:not([data-pane~="add"]),
.variation-field:has([data-mode="replace"]:checked) [data-pane]:not([data-pane~="replace"]),
.variation-field:has([data-mode="clear"]:checked) [data-pane]:not([data-pane~="clear"]) { display: none; }
.variation-details-card > summary strong { overflow-wrap: anywhere; }
.variation-add { display: inline-flex; }
.variation-new-form { display: grid; gap: .75rem; }
.variation-new-source { border: 0; display: grid; gap: .5rem; margin: 0; padding: 0; }
.variation-new-source .choice-chip { align-items: flex-start; border-radius: var(--radius-lg); justify-content: flex-start; text-align: left; }
.variation-new-source .choice-chip span { display: grid; gap: .1rem; }
.variation-new-source .choice-chip small { color: var(--muted); font-size: var(--text-sm); }
.variation-new-actions { display: flex; gap: .5rem; justify-content: flex-end; }

/* Forgejo #330: paging under a listing, the workshop's status words and the Drills sub-menu. */
.pagination { align-items: center; display: flex; flex-wrap: wrap; gap: .75rem 1.5rem; justify-content: space-between; margin: 1rem 0; }
.pagination-count { color: var(--muted); font-size: var(--text-sm); margin: 0; }
.pagination-pages, .pagination-per { align-items: center; display: flex; flex-wrap: wrap; gap: .25rem; }
.pagination-per-label { color: var(--muted); font-size: var(--text-sm); margin-right: .25rem; }
.pagination-gap { color: var(--muted); padding: 0 .25rem; }
.pagination .is-disabled { opacity: .45; pointer-events: none; }
.workshop-status { cursor: help; text-decoration: underline dotted; text-underline-offset: .2em; }
.workshop-progress { display: grid; gap: .75rem; }
.workshop-heading-actions { justify-content: flex-end; }
.section-tabs { display: flex; flex-wrap: wrap; gap: .5rem; margin: 0 auto 1.25rem; max-width: 80rem; width: 100%; }
