/* ─── Design tokens ─────────────────────────────────────────────────── */
:root {
  --brand: #0a4d3a;
  --brand-tint: #e6efe9;
  --bg: #fafaf7;
  --card: #ffffff;
  --alt-cell: #fcfcf9;
  --ink: #181816;
  --ink2: #45453f;
  --ink3: #6f6f66;
  --line: #ebebe5;
  --warn: #c2410c;
  --now: #dc2626;
  --reserve: #b45309;
  --reserve-tint: #fbe9c8;
  --free: #1d5fa8;
  --free-tint: #e8f1fb;
}

/* ─── Reset & base ──────────────────────────────────────────────────── */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html,
body {
  height: 100%;
  font-family: 'Inter', -apple-system, system-ui, sans-serif;
  background: var(--bg);
  color: var(--ink);
  -webkit-text-size-adjust: 100%;
  overflow-x: hidden;
  /* prevent page-level horizontal scroll on iOS */
}

body {
  display: flex;
  flex-direction: column;
}

button {
  font-family: inherit;
  cursor: pointer;
}

/* ─── App shell ─────────────────────────────────────────────────────── */
#app {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  overflow: hidden;
}

/* Planering/Todo mode — a soft green wash behind the whole page (overriding
   --bg, which .grid-wrapper/.grid-root/.plan-col all show through with no
   background of their own) plus a matching header tint, so it's obvious at
   a glance you're on the editable side, not the read-only Schema mirror.
   Built on --brand (the same green as the rest of the app's accent) rather
   than a separate hue, so "you're editing" reads as an intensified version
   of the app's own identity instead of a clashing warning color — a
   deliberately more muted step than the old amber/yellow wash it replaced.
   See render() in app.js for where the class is toggled. */
#app.mode-planning {
  --bg: #eef5f0;
}

#app.mode-planning .header {
  background: #dcece0;
  border-bottom-color: #c3ddc9;
}

/* ─── Login screen ──────────────────────────────────────────────────── */
.login-screen {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100dvh;
  padding: 24px;
  background: var(--bg);
}

.login-card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 32px 28px;
  width: 100%;
  max-width: 360px;
}

.login-logo {
  width: 48px;
  height: 48px;
  border-radius: 12px;
  background: var(--brand);
  color: #fff;
  font-weight: 800;
  font-size: 16px;
  letter-spacing: 0.5px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 20px;
}

.login-title {
  font-size: 20px;
  font-weight: 700;
  text-align: center;
  margin-bottom: 4px;
}

.login-sub {
  font-size: 13px;
  color: var(--ink2);
  text-align: center;
  margin-bottom: 24px;
}

.field+.field {
  margin-top: 12px;
}

.field label {
  display: block;
  font-size: 11px;
  font-weight: 700;
  color: var(--ink3);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 5px;
}

.field input {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  font-size: 14px;
  font-family: inherit;
  background: var(--bg);
  color: var(--ink);
  outline: none;
  transition: border-color 120ms;
}

.field input:focus {
  border-color: var(--brand);
}

.btn-primary {
  width: 100%;
  margin-top: 20px;
  padding: 12px;
  border: none;
  border-radius: 10px;
  background: var(--brand);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
}

.btn-primary:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.login-error {
  color: var(--warn);
  font-size: 13px;
  text-align: center;
  margin-top: 10px;
}

/* ─── Header ────────────────────────────────────────────────────────── */
.header {
  background: var(--card);
  border-bottom: 1px solid var(--line);
  padding: 8px 16px 12px;
  flex-shrink: 0;
}

.header-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
}

.header-brand {
  display: flex;
  align-items: center;
  gap: 10px;
}

.brand-logo {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: var(--brand);
  color: #fff;
  font-weight: 700;
  font-size: 12px;
  letter-spacing: 0.5px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

/* An uploaded tenant logo (see brandLogoHTML) reuses .brand-logo's box but
   as a real image — object-fit keeps a non-square upload from stretching,
   and the background/color text rules above are harmless no-ops on an img. */
img.brand-logo {
  object-fit: cover;
  background: #fff;
  border: 1px solid var(--line);
}

.header-title {
  font-size: 15px;
  font-weight: 700;
  line-height: 1.1;
}

.header-date {
  font-size: 11px;
  color: var(--ink2);
  margin-top: 2px;
}

/* Schema/Planering toggle — replaces the old static .header-title, see
   renderHeader() in app.js. Wraps onto a second line instead of squeezing
   or overflowing once every tab (Schema/Planering/Todo/Önska tider/
   Tidbokning) is shown — a tab never moves into the hamburger menu just
   because the header got narrow. */
.view-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  row-gap: 2px;
}

/* An underline, not a background pill (which .view-tab.active used to be) —
   a flat tint here collided with .mode-planning's own green-tinted header
   background, making the active tab nearly invisible against it. An
   underline reads regardless of what color sits behind it, on Schema's
   plain white header or Planering's green one alike. rgba (not a flat
   --brand-tint) on hover for the same reason: it darkens whatever's
   underneath instead of assuming a light background. */
.view-tab {
  padding: 2px 8px 4px;
  border: none;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--ink2);
  font-size: 14px;
  font-weight: 700;
  line-height: 1.3;
}

.view-tab:hover {
  background: rgba(10, 77, 58, .07);
  border-radius: 6px 6px 0 0;
}

.view-tab.active {
  border-bottom-color: var(--brand);
  color: var(--brand);
}

.view-tab-badge {
  display: inline-block;
  min-width: 15px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--warn);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 15px;
  text-align: center;
}

/* Planering's "Senast synkad: ..." line, see renderSyncStatusLine() in app.js. */
.sync-status-line {
  font-size: 11px;
  color: var(--ink2);
  padding: 2px 0 6px;
}

.sync-status-line.error {
  color: var(--warn);
}

.header-brand {
  min-width: 0;
}

/* allow text to truncate rather than push header wider */
.header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
  position: relative;
}

.btn-add {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  border: none;
  background: var(--brand);
  color: #fff;
  font-size: 22px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-bottom: 2px;
}

.btn-icon {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--card);
  font-size: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.btn-menu {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--ink2);
  font-size: 17px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

.header-menu {
  position: absolute;
  top: 44px;
  right: 0;
  z-index: 6;
  min-width: 156px;
  padding: 6px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.header-menu-item {
  width: 100%;
  padding: 8px 10px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--ink);
  text-align: left;
  font-size: 13px;
  font-weight: 600;
}

.header-menu-item:hover {
  background: var(--bg);
}

.header-menu-item.danger {
  color: var(--warn);
}

/* "Inloggad som: ..." — a plain info line at the top of the hamburger
   menu (renderHeader/renderAdminHeader's menuItems), not a button, with a
   divider under it to separate it from the actionable items below. */
.header-menu-info {
  padding: 6px 10px 10px;
  margin-bottom: 2px;
  border-bottom: 1px solid var(--line);
  font-size: 12px;
  color: var(--ink3);
  overflow-wrap: break-word;
}

/* "Hantera versioner"/"+ Lägg till" render twice — as header-actions
   buttons (desktop) and again inside the hamburger menu (mobile), see
   renderHeader's planningMenuItems — these two rules keep exactly one copy
   visible at a time instead of both, mobile-first like the rest of this
   file (base = mobile, the min-width query below = desktop). */
.header-action-desktop-only {
  display: none;
}

.menu-item-mobile-only {
  display: block;
}

@media (min-width: 640px) {
  .header-action-desktop-only {
    display: inline-block;
  }

  .menu-item-mobile-only {
    display: none;
  }
}

/* A flex item of .week-nav-center itself, sitting snug between the
   prev/next arrows — it used to sit in its own row above them, back when
   the arrows flanked the date-strip instead (see .week-grid's own
   comment). Sized as a real heading, not a small muted caption, since it's
   the one thing telling you which week you're looking at. */
.week-number-label {
  font-size: 16px;
  font-weight: 800;
  color: var(--ink);
}

/* .week-grid is ONE CSS grid holding both visual rows: "Idag" + the
   arrows/week-number group on row 1, the 7 day-pills on row 2 (.date-strip
   itself is display:contents below, so it contributes its children — the
   day-pills — as direct items of THIS grid rather than a nested box with
   its own, separately-computed column tracks). One grid computing every
   column width once is what actually guarantees "Idag" lines up with
   Monday and "Vecka N" centers over the pills — two separate-but-matching
   grids (an earlier version of this) can each resolve their fr-tracks
   slightly differently and drift apart pixel-by-pixel; a version before
   that reserved "Idag" its own extra column the day-pills never used,
   which pushed Monday one column over and guaranteed a mismatch outright.
   "Idag" and Monday's pill share column 1 (different rows, so no
   collision); the week-number group spans ALL 7 columns (not "columns
   2-8") and centers within that span, matching the pills' own full extent
   — not offset by whatever column "Idag" happens to occupy.
   overflow-x:auto — on a phone too narrow for 7 comfortably-sized day
   columns, the whole grid scrolls as one instead of spilling out of
   .header (which has no overflow handling of its own) and breaking the
   layout; both rows necessarily stay aligned even while scrolled, since
   they're the same element. */
.week-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(36px, 1fr));
  align-items: center;
  gap: 10px 6px;
  margin-bottom: 10px;
  overflow-x: auto;
  scrollbar-width: none;
}

.week-grid::-webkit-scrollbar {
  display: none;
}

/* "Visa även tomma planer" checkbox (renderHeader/renderGrid) — sits right
   under the day-pills, deliberately its own row outside .week-grid rather
   than another item inside that grid (see .week-grid's own doc comment in
   app.js: adding a column there would throw its shared width computation
   off). No horizontal padding here either — .header already supplies the
   16px side inset every sibling row relies on; see .period-bar's comment
   for the doubled-inset bug that pattern caused once already. */
.empty-plans-toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: -4px 0 10px;
  font-size: 12px;
  color: var(--ink2);
  cursor: pointer;
}

.empty-plans-toggle input {
  cursor: pointer;
}

.week-nav-center {
  grid-row: 1;
  grid-column: 1 / -1;
  justify-self: center;
  display: flex;
  align-items: center;
  gap: 10px;
}

.btn-today {
  grid-row: 1;
  grid-column: 1;
  padding: 5px 10px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--card);
  font-size: 12px;
  font-weight: 600;
  color: var(--ink2);
}

.btn-today:hover {
  background: var(--brand-tint);
  border-color: var(--brand);
  color: var(--brand);
}

.btn-week-nav {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--card);
  font-size: 20px;
  line-height: 1;
  color: var(--ink2);
  display: flex;
  align-items: center;
  justify-content: center;
}

.btn-week-nav:hover {
  background: var(--brand-tint);
  border-color: var(--brand);
  color: var(--brand);
}

/* Contributes its children (the day-pills) directly as .week-grid's row-2
   items — see .week-grid's own comment for why. */
.date-strip {
  display: contents;
}

.day-pill {
  grid-row: 2;
  padding: 6px 0;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--card);
  color: var(--ink);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
}

.day-pill.active {
  background: var(--brand);
  color: #fff;
  border-color: var(--brand);
}

.day-pill .dow {
  font-size: 9px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  opacity: 0.7;
}

.day-pill .dn {
  font-size: 15px;
  font-weight: 700;
}

.day-dot {
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: var(--brand);
  margin-top: 2px;
}

.day-pill.active .day-dot {
  background: #fff;
}

/* ─── Time grid ─────────────────────────────────────────────────────── */

/*
 * Grid scroll architecture:
 *   .grid-wrapper  → single scroll container (overflow: auto, both axes)
 *   .grid-root     → shared width for headers and cells (width: max-content; min-width: 100%)
 *   .col-header    → sticky top:0 (plan column headers row)
 *   .col-header-spacer → sticky top:0 AND left:0 (frozen corner cell)
 *   .time-axis     → sticky left:0 (time labels column)
 * All content scrolls together — no separate inner scroll containers.
 */

.grid-wrapper {
  flex: 1;
  overflow: auto;
  min-width: 0;
  /* prevent flex child from expanding page width on iOS */
  -webkit-overflow-scrolling: touch;
}

.grid-root {
  display: flex;
  flex-direction: column;
  min-height: 100%;
  width: max-content;
  min-width: 100%;
  /* expand to fit all plan columns, fill full width on desktop */
}

/* "Todo" tab — see renderTodosPage() in app.js. Reuses #grid-wrapper as a
   plain scrolling list instead of the day-grid. */
.todos-page {
  padding: 12px;
  max-width: 640px;
  margin: 0 auto;
}

.todo-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  margin-bottom: 8px;
  border-radius: 10px;
  background: var(--brand-tint);
  border: 1px solid var(--line);
}

.todo-row-content {
  flex: 1;
  min-width: 0;
}

.todo-row-label {
  font-weight: 700;
  font-size: 13px;
  margin-bottom: 2px;
}

.todo-row-body {
  font-size: 12px;
  color: var(--ink2);
}

.todo-row-plan {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink);
  margin-top: 4px;
}

/* Plan didn't change — worth saying explicitly (so "nothing here" doesn't
   read as "forgot to check"), but shouldn't draw the eye like an actual
   from->to change does. */
.todo-row-plan-unchanged {
  font-weight: 400;
  color: var(--ink2);
}

/* Sits at the far right of the row, not stacked below the text. Column, not
   row, so "Visa" and "Återställ"/"Ta bort"/"Ångra" stack vertically instead
   of forcing the row wider on a narrow (phone-width) screen. */
.todo-row-actions {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: none;
}

/* .btn-secondary normally assumes a flex row it should stretch to fill (e.g.
   .form-actions), which made these grow far too wide otherwise. Compound
   selector (not just .todo-row-view/.todo-row-revert) so it beats
   .btn-secondary's flex:1 regardless of which rule comes first in the file. */
.todo-row-view.btn-secondary,
.todo-row-revert.btn-secondary {
  flex: none;
  width: auto;
  white-space: nowrap;
}

.todos-empty {
  padding: 40px 20px;
  text-align: center;
  color: var(--ink2);
  font-size: 13px;
}

/* Sticky column-header row (sticks to top of grid-wrapper) */
.col-header {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--card);
  border-bottom: 1px solid var(--line);
  display: flex;
}

/* Frozen corner cell — sticky in both directions */
.col-header-spacer {
  position: sticky;
  left: 0;
  z-index: 4;
  /* above time-axis */
  width: 56px;
  flex-shrink: 0;
  background: var(--card);
  border-right: 1px solid var(--line);
}

/* Plan column headers — no overflow constraint, scroll with grid-root */
.col-header-plans {
  display: flex;
  flex: 1;
  min-width: 0;
}

.plan-col-header {
  flex: 1 0 78px;
  min-width: 78px;
  padding: 8px 6px;
  border-right: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

.plan-site-name {
  max-width: 100%;
  overflow-wrap: normal;
  word-break: keep-all;
  font-size: 10px;
  font-weight: 700;
  text-align: center;
  line-height: 1.2;
  color: var(--ink);
}

.plan-name {
  max-width: 100%;
  overflow-wrap: anywhere;
  font-size: 9px;
  color: var(--ink2);
  text-align: center;
  line-height: 1.2;
}

.plan-sizes {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  max-width: 100%;
  gap: 2px;
  margin-top: 2px;
}

.size-badge {
  font-size: 8px;
  font-weight: 700;
  padding: 1px 5px;
  border-radius: 999px;
  background: #f3f3ee;
  color: var(--ink2);
}

/* Grid body row */
.grid-body {
  flex: 1;
  display: flex;
}

/* Time axis — sticky left so it stays visible during horizontal scroll */
.time-axis {
  position: sticky;
  left: 0;
  z-index: 2;
  width: 56px;
  flex-shrink: 0;
  background: var(--card);
  border-right: 1px solid var(--line);
}

.time-label {
  position: absolute;
  left: 0;
  right: 6px;
  font-family: ui-monospace, 'JetBrains Mono', 'SF Mono', monospace;
  font-size: 10px;
  font-weight: 600;
  color: var(--ink3);
  text-align: right;
}

/* Dusk marker — sunset time for the day being viewed (see sunsetTime() /
   buildGridBody() in app.js), only drawn when it falls within the visible
   hour range. .sunset-label sits in .time-axis next to the hour labels;
   .sunset-line is the matching dashed line across every plan column. */
.sunset-label {
  position: absolute;
  left: 0;
  right: 6px;
  font-size: 10px;
  font-weight: 700;
  color: var(--warn);
  text-align: right;
  white-space: nowrap;
}

.sunset-line {
  position: absolute;
  left: 0;
  right: 0;
  border-top: 1px dashed var(--warn);
  opacity: .55;
  pointer-events: none;
}

/* "Nu"-linje — current time of day (see buildGridBody()/updateNowLine() in
   app.js), only drawn when the viewed day is today and the time falls
   within the visible hour range. Solid and a different color from the
   dashed amber dusk marker above so the two are never mistaken for one
   another when both show at once. */
.now-label {
  position: absolute;
  left: 0;
  right: 6px;
  font-size: 10px;
  font-weight: 700;
  color: var(--now);
  text-align: right;
  white-space: nowrap;
}

.now-line {
  position: absolute;
  left: 0;
  right: 0;
  border-top: 1.5px solid var(--now);
  pointer-events: none;
}

.plan-columns {
  position: relative; /* anchors .sunset-line, which spans the full row */
  display: flex;
  flex: 1;
  min-width: 0;
}

.plan-col {
  flex: 1 0 78px;
  min-width: 78px;
  border-right: 1px solid var(--line);
  position: relative;
}

.plan-col:nth-child(even) {
  background: var(--alt-cell);
}

/* Drag-and-drop of match-blocks (admin only, see wireDragAndDrop() in app.js) */
.plan-col.drop-target {
  background: rgba(30, 107, 80, .12);
}

.match-block[draggable="true"] {
  cursor: grab;
}

.match-block.dragging {
  opacity: .5;
}

/* Follows the cursor during a drag, showing the time it would drop at right
   now (see showDragTooltip/snappedDropTime in app.js). position:fixed so it
   isn't clipped by .grid-wrapper's own scroll/overflow. */
.drag-tooltip {
  position: fixed;
  z-index: 50;
  pointer-events: none;
  transform: translate(-50%, -100%);
  background: var(--ink);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  padding: 4px 8px;
  border-radius: 6px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, .25);
}

/* A fully custom, fully opaque drag visual — replaces the browser's own
   native drag image entirely (see wireDragAndDrop's dragstart, which
   suppresses that native image with a 1x1 transparent gif). The native one
   is rendered with a fixed, browser-imposed translucency no CSS or
   source-image opacity can override, which is why two earlier attempts at
   a "more opaque" custom drag image (via setDragImage) made no visible
   difference at all — the only way to get something fully opaque is to
   hide the native ghost completely and drive this element by hand instead
   (showDragGhost/moveDragGhost/hideDragGhost), tracking the cursor via the
   'drag' event same as .drag-tooltip already does. Repeats .match-block's
   core visual (border-radius/padding/color/box-shadow/flex layout) since
   its own child elements' classes (.match-block-top, .match-time, ...)
   aren't scoped under .match-block itself and render correctly regardless. */
.drag-ghost {
  position: fixed;
  z-index: 51;
  pointer-events: none;
  border-radius: 8px;
  padding: 4px 5px;
  text-align: left;
  color: var(--ink);
  border: 1px solid transparent;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 2px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, .35);
  opacity: 1;
}

.hour-line {
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--line);
}

.half-line {
  position: absolute;
  left: 15%;
  right: 15%;
  height: 1px;
  background: #f3f3ee;
}

/* Match blocks — the same recipe as Planbokningen's own booking blocks
   (.free-block): a light tint fill with a solid-color border, dark text,
   no shadow, set as background/border-color inline (see buildMatchBlock in
   app.js — PLAN_COLOR/PLAN_BG, MOVED_COLOR/MOVED_BG,
   REMOVED_UPSTREAM_COLOR/REMOVED_UPSTREAM_BG), so a booking reads as the
   same app regardless of which tab it's on. Which pair is used IS the
   state signal now (plain/overridden/removed-upstream) — no outline layered
   on top the way this used to work. */
.match-block {
  position: absolute;
  left: 3px;
  right: 3px;
  border-radius: 8px;
  /* Tightened from 4px 5px/gap:2px — with 3 rows now common (time, team,
     Träning/Event's own row) a plain 60-min booking (41px tall, ROW_H's own
     math) didn't have room for all three at the old spacing: every row's
     automatic min-height protection (see .match-block-top's comment) still
     forced text to its full line-height, so the whole block just clipped —
     visible as mangled half-cropped glyphs ("P18" reading as "P1R") rather
     than a clean shrink. This spacing is calculated to fit 3 rows in a
     41px/60-min block with a little room to spare; a genuinely shorter
     booking can still clip, same as any fixed-height box would. */
  padding: 3px 5px;
  text-align: left;
  color: var(--ink);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 1px;
  border: 1px solid transparent;
}

/* Todo's "Visa" button (goToTodoBooking/highlightBooking) — a few rings
   pulsing outward, distinct enough to spot regardless of the block's own
   color/outline (plain, .overridden amber, or .removed-upstream red).
   z-index lifts it above whatever it might otherwise overlap mid-pulse. */
@keyframes match-block-highlight-pulse {
  0% { box-shadow: 0 0 0 0 rgba(37, 99, 235, .7); }
  70% { box-shadow: 0 0 0 10px rgba(37, 99, 235, 0); }
  100% { box-shadow: 0 0 0 0 rgba(37, 99, 235, 0); }
}
.match-block-highlight {
  animation: match-block-highlight-pulse 0.8s ease-out 3;
  z-index: 5;
}

/* Planplanering's own version, on .free-block — see highlightFreeBooking.
   Reuses the same pulse animation as Todo's "Visa" (match-block-highlight). */
.free-block-highlight {
  animation: match-block-highlight-pulse 0.8s ease-out 3;
  z-index: 5;
}

/* Non-match bookings (training/event) get a hatched overlay so they read as
   "plan occupied" rather than a fixture — dark, not white, now that the
   block itself is a light tint rather than a solid fill. */
.match-block.booking {
  background-image: repeating-linear-gradient(
    45deg,
    rgba(10, 77, 58, .08) 0,
    rgba(10, 77, 58, .08) 6px,
    transparent 6px,
    transparent 12px
  );
}

/* min-height:0 opts this out of flexbox's "automatic minimum size" rule —
   without it, a flex item with no overflow of its own refuses to shrink
   below its own content height, no matter how little room .match-block (a
   column flex box) actually has. .match-home already gets this for free
   from its own overflow:hidden (needed for text truncation); without the
   same opt-out here too, a short booking's flex-shrink deficit landed
   entirely on .match-home — a real bug: a 60-min training's team name row
   shrank to literally 0px and vanished while the time above and the
   Träning/Event label below it (same issue, see .match-kind-label) stayed
   fully visible, even though the team name was right there in the DOM. */
.match-block-top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  min-height: 0;
}

.match-time {
  font-family: ui-monospace, monospace;
  font-size: 10px;
  font-weight: 700;
  line-height: 1.1;
}

/* Träning/Event's own label (see buildMatchBlock's kindLabel — matches
   don't get one, their spelform tag stays in .match-block-top instead).
   margin-top:auto pins it to the very bottom of the block regardless of how
   much empty space a longer booking's height leaves above it. */
.match-kind-label {
  margin-top: auto;
  text-align: right;
  font-size: 9px;
  font-style: italic;
  opacity: .85;
  line-height: 1.1;
  min-height: 0; /* see .match-block-top's own comment */
}

/* Same outlined-pill recipe as Planbokningen's own spelform ring
   (.free-block-format) — a translucent white fill only worked back when the
   block itself was a solid dark color. */
.match-size {
  font-size: 8px;
  font-weight: 700;
  padding: 0 3px;
  border-radius: 999px;
  border: 1px solid currentColor;
}

.match-home {
  font-size: 10px;
  font-weight: 600;
  line-height: 1.05;
  overflow: hidden; /* also this row's min-height:0 — see .match-block-top's comment */
}

.match-away {
  font-size: 8px;
  opacity: .85;
  line-height: 1.1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.grid-empty {
  padding: 40px 16px;
  text-align: center;
  color: var(--ink3);
  font-size: 12px;
  width: 100%;
}

/* ─── Bottom sheet ──────────────────────────────────────────────────── */
.sheet-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .4);
  z-index: 20;
  display: flex;
  align-items: flex-end;
}

.sheet {
  position: relative;
  width: 100%;
  background: var(--card);
  border-radius: 18px 18px 0 0;
  padding: 8px 0 32px;
  transform: translateY(100%);
  transition: transform 240ms cubic-bezier(.2, .7, .3, 1);
}

.sheet.open {
  transform: translateY(0);
}

.sheet-handle {
  width: 36px;
  height: 4px;
  background: #d4d4d0;
  border-radius: 2px;
  margin: 6px auto 14px;
}

/* Explicit close button — the handle above is easy to miss (and hidden
   entirely on desktop, see the .sheet-handle{display:none} media rule
   below), so this is the one close affordance guaranteed to always work. */
.sheet-close {
  position: absolute;
  top: 6px;
  right: 10px;
  line-height: 1;
  padding: 4px 8px;
}

.sheet-body {
  padding: 0 20px;
}

.sheet-edit {
  margin-top: 16px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.sheet-edit-title {
  font-weight: 700;
  font-size: 13px;
}

/* .form-actions normally supplies its own 20px side padding (for the old
   match-form modal, whose body had none) — here it sits inside
   .sheet-body, which already has that padding, so it would double up. */
.sheet-edit .form-actions {
  padding: 0;
}

.sheet-plan-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}

.sheet-plan-dot {
  width: 8px;
  height: 8px;
  border-radius: 2px;
}

.sheet-plan-label {
  font-size: 11px;
  color: var(--ink2);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.sheet-home {
  font-size: 22px;
  font-weight: 700;
  line-height: 1.2;
}

.sheet-away {
  font-size: 14px;
  color: var(--ink2);
  margin-top: 2px;
}

.detail-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin: 16px 0;
}

.detail-cell {
  padding: 10px 12px;
  background: var(--bg);
  border-radius: 10px;
  border: 1px solid var(--line);
}

.detail-label {
  font-size: 10px;
  font-weight: 700;
  color: var(--ink3);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.detail-value {
  font-size: 14px;
  font-weight: 600;
  margin-top: 4px;
}

.detail-value.mono {
  font-family: ui-monospace, monospace;
}

.detail-value.warn {
  color: var(--warn);
}

.btn-secondary {
  flex: 1;
  padding: 12px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--card);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}

/* Planering's active-version indicator — see renderHeader's periodBar.
   Sits between the sync-status line and the week strip, only when viewing
   Planering. Read-only (switching which Period is active happens in the
   "Hantera versioner" modal instead — see openManagePeriodsModal), so this
   is just centered text, not an interactive control. */
.period-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  /* No horizontal padding here — .header (the parent) already supplies the
     16px side inset every sibling row (sync-status-line, week-grid, ...)
     relies on; adding it again here doubled it, throwing this row's left
     edge out of alignment with everything above/below it. */
  padding: 8px 0 10px;
}

.period-bar-label {
  flex-shrink: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink2);
}

.period-bar-active {
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
}

/* ─── Match form (modal) ────────────────────────────────────────────── */
.form-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .5);
  z-index: 30;
  display: flex;
  align-items: flex-end;
}

.form-sheet {
  width: 100%;
  background: var(--card);
  border-radius: 18px 18px 0 0;
  padding: 0 0 32px;
  max-height: 92dvh;
  overflow-y: auto;
  transform: translateY(100%);
  transition: transform 240ms cubic-bezier(.2, .7, .3, 1);
}

.form-sheet.open {
  transform: translateY(0);
}

.form-header {
  position: sticky;
  top: 0;
  background: var(--card);
  padding: 16px 20px 12px;
  border-bottom: 1px solid var(--line);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.form-title {
  font-size: 17px;
  font-weight: 700;
}

.btn-close {
  font-size: 22px;
  color: var(--ink2);
  background: none;
  border: none;
}

.form-body {
  padding: 16px 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.form-field label {
  display: block;
  font-size: 11px;
  font-weight: 700;
  color: var(--ink3);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 5px;
}

.form-field label .form-optional {
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink3);
  opacity: 0.7;
}

.form-field input,
.form-field select,
.form-field textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  font-size: 14px;
  font-family: inherit;
  background: var(--bg);
  color: var(--ink);
  outline: none;
}

.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus {
  border-color: var(--brand);
}

.form-field textarea {
  resize: vertical;
  min-height: 64px;
}

.form-date-row {
  display: flex;
  align-items: center;
  gap: 8px;
  position: relative;
  /* Clips the real date input's hit area to the row — iOS Safari renders a
     native date input at its own intrinsic touch-target size regardless of
     the inset:0 box it's positioned into (see .form-date-picker-overlay),
     which can otherwise bleed past the row's edges. */
  overflow: hidden;
}

.form-date-row #f-date {
  flex: 1;
}

.form-date-btn {
  flex-shrink: 0;
}

/* The real <input type="date"> sits invisibly on top of the whole row (text
   field + calendar icon), covering it edge to edge, and IS the click
   target — pointer-events stays at its default (auto) so every tap lands on
   this real input, guaranteeing the browser's native picker opens. It used
   to be a 1px-tall decoy elsewhere in the row with pointer-events:none, with
   a separate visible button/field forwarding clicks to it via
   showPicker()/click() — some browsers silently refuse that for an input
   that was never itself actually clicked. */
.form-date-picker-overlay {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
  border: none;
  padding: 0;
  margin: 0;
}

/* Unified 24h time picker — two selects with shared border */
.time-input {
  display: flex;
  align-items: center;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--bg);
  overflow: hidden;
}

.time-input select {
  flex: 1;
  border: none;
  background: transparent;
  padding: 10px 8px;
  font-size: 14px;
  font-family: inherit;
  color: var(--ink);
  outline: none;
  -webkit-appearance: none;
  appearance: none;
  text-align: center;
  cursor: pointer;
}

.time-input select:focus {
  background: var(--brand-tint);
}

.time-input span {
  font-size: 16px;
  font-weight: 700;
  color: var(--ink2);
  padding: 0 2px;
  user-select: none;
}

/* Wraps the header's calendar icon so the real, invisible date input (below)
   can be sized and positioned exactly over it. overflow:hidden clips the
   input's actual hit area to the icon's box — iOS Safari renders a native
   date input at its own intrinsic touch-target size regardless of the
   inset:0 box it's positioned into, which otherwise bleeds into whatever
   sits next to the icon (e.g. the burger menu button) and steals its taps.
   Explicit width/height (matching .btn-icon) rather than relying on
   inline-flex content sizing, since the clip needs a fixed box to clip to. */
.date-picker-wrap {
  position: relative;
  display: inline-flex;
  width: 36px;
  height: 36px;
  overflow: hidden;
}

/* The real <input type="date"> sits invisibly on top of the calendar icon
   and IS the click target — pointer-events stays at its default (auto) so
   every tap lands on this real input, guaranteeing the browser's native
   picker opens. It used to be a 1px-tall decoy elsewhere in the header with
   pointer-events:none, with the visible icon button forwarding clicks to it
   via showPicker()/click() — some browsers silently refuse that for an
   input that was never itself actually clicked. */
.date-picker-overlay {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
  border: none;
  padding: 0;
  margin: 0;
}

.conflict-warning {
  background: #fff7ed;
  border: 1px solid #fed7aa;
  border-radius: 8px;
  padding: 10px 12px;
  font-size: 13px;
  color: var(--warn);
}

.conflict-warning strong {
  display: block;
  margin-bottom: 4px;
}

/* A stronger, red variant of .conflict-warning — for a Planering booking
   whose Fogis/Svenska Lag reference has been removed entirely (see
   REMOVED_UPSTREAM_COLOR/removedUpstreamPlannedIds in app.js), not just
   diverged and waiting to catch up. Overrides .conflict-warning's amber
   colors when both classes are applied together. */
.conflict-warning-danger {
  background: #fef2f2;
  border-color: #fecaca;
  color: #b91c1c;
}

/* A small blue informational callout — e.g. the "lagen fylls på automatiskt
   från Fogis" note under Administration → Lag. Same box shape as
   .conflict-warning but blue instead of amber, since this isn't warning of
   a problem, just explaining how the list behind it gets populated. */
.info-note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 10px;
  padding: 10px 12px;
  background: #eff6ff;
  border: 1px solid #bfdbfe;
  border-radius: 8px;
  font-size: 12px;
  color: #1d4ed8;
}

.info-note-icon {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #2563eb;
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  font-style: italic;
  display: flex;
  align-items: center;
  justify-content: center;
}

.form-actions {
  display: flex;
  gap: 8px;
  padding: 0 20px;
}

.form-actions .btn-secondary {
  flex: 1;
}

.form-actions .btn-primary {
  flex: 2;
  width: auto;
  margin-top: 0;
}

/* ─── Login button (unauthenticated) ────────────────────────────────── */
.btn-login {
  padding: 7px 14px;
  border-radius: 8px;
  border: 1px solid var(--brand);
  color: var(--brand);
  background: var(--brand-tint);
  font-size: 13px;
  font-weight: 600;
}

.btn-logout {
  padding: 7px 12px;
  border-radius: 8px;
  border: 1px solid var(--line);
  color: var(--ink2);
  background: var(--card);
  font-size: 13px;
  font-weight: 600;
}

.btn-logout:hover {
  border-color: #fca5a5;
  color: var(--warn);
}

/* ─── Login modal ────────────────────────────────────────────────────── */
.login-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .45);
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.login-modal {
  width: 100%;
  max-width: 360px;
  background: var(--card);
  border-radius: 16px;
  overflow: hidden;
}

.login-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px 12px;
  border-bottom: 1px solid var(--line);
}

.login-modal-title {
  font-size: 17px;
  font-weight: 700;
}

.login-modal-body {
  padding: 16px 20px 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Prevent iOS Safari focus-zoom in modal/forms. */
@media (max-width: 820px) {

  .form-field input,
  .form-field select,
  .form-field textarea,
  .login-screen .field input {
    font-size: 16px;
  }
}

/* ─── Admin page (full-page view replacing schedule grid) ────────────── */
.btn-back {
  padding: 6px 12px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: var(--card);
  color: var(--ink2);
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
}

.admin-page-title {
  font-size: 16px;
  font-weight: 700;
  flex: 1;
  text-align: center;
}

/* Section nav lives in the header, below the top row. The header's own
   border-bottom is suppressed so this row becomes the bottom edge. */
.admin-section-nav {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  padding: 10px 16px 0;
  margin: 0 -16px -12px;
  /* bleed to header edges */
}

.admin-snav-btn {
  padding: 8px 14px;
  border-radius: 8px 8px 0 0;
  border: 1px solid transparent;
  border-bottom: none;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink2);
  background: none;
  white-space: nowrap;
  flex-shrink: 0;
}

.admin-snav-btn.active {
  background: var(--bg);
  border-color: var(--line);
  color: var(--brand);
  font-weight: 700;
}

.admin-page {
  flex: 1;
  overflow-y: auto;
  background: var(--bg);
}

.admin-body {
  padding: 16px 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 640px;
}

.admin-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 4px;
}

.admin-section-title {
  font-size: 15px;
  font-weight: 700;
}

.btn-add-small {
  padding: 6px 12px;
  border-radius: 8px;
  border: none;
  background: var(--brand);
  color: #fff;
  font-size: 13px;
  font-weight: 600;
}

.admin-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg);
}

/* Ej matchade platser: the text block gets a full line on narrow screens
   and the action buttons wrap below it, instead of squeezing it to a sliver. */
.unmatched-row { flex-wrap: wrap; }
.unmatched-row .admin-row-info { flex: 1 1 220px; overflow-wrap: anywhere; }
/* The suggestion takes its own full-width line under the text + buttons. */
.unmatched-suggest {
  flex: 1 0 100%;
  padding: 6px 10px;
  font-size: 12px;
  text-align: left;
  white-space: normal;
}

.admin-row-dot {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  flex-shrink: 0;
}

.admin-row-info {
  flex: 1;
  min-width: 0;
}

.admin-row-name {
  font-size: 14px;
  font-weight: 600;
}

.admin-row-sub {
  font-size: 12px;
  color: var(--ink3);
  margin-top: 1px;
}

/* Hjälp-sidan (renderSupportPage) reuses .admin-body/.admin-row-sub for its
   headings/paragraphs, but also needs actual bullet lists — the page-wide
   reset above zeroes every element's margin/padding, so without this a
   plain <ul> would render with no indentation and bullets flush against
   (or overlapping) the text. */
.support-body .admin-row-sub {
  line-height: 1.5;
}

.support-list {
  list-style: disc;
  padding-left: 20px;
  font-size: 12px;
  color: var(--ink3);
  line-height: 1.5;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.support-list strong {
  color: var(--ink);
}

/* Spelformer (formats.js): a wide table that scrolls sideways inside its own
   wrapper on narrow screens instead of widening the page. */
.formats-body { max-width: 900px; }
.formats-table-wrap { overflow-x: auto; }
.formats-table {
  border-collapse: collapse;
  width: 100%;
  font-size: 13px;
}
.formats-table th,
.formats-table td {
  padding: 10px 12px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line);
}
.formats-table thead th {
  font-size: 12px;
  color: var(--ink3);
  white-space: nowrap;
}
.formats-table tbody th { font-weight: 700; white-space: nowrap; }

/* Narrow screens: each age group becomes a card, one "label: value" line per
   column, instead of a table that has to scroll sideways. */
@media (max-width: 720px) {
  .formats-table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
  }
  .formats-table,
  .formats-table tbody,
  .formats-table tr,
  .formats-table th,
  .formats-table td { display: block; }
  .formats-table tr {
    border: 1px solid var(--line);
    border-radius: 10px;
    margin-bottom: 12px;
    padding: 4px 0;
  }
  .formats-table tbody th {
    font-size: 15px;
    border-bottom: 1px solid var(--line);
  }
  .formats-table td {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    padding: 8px 12px;
    border-bottom: none;
    text-align: right;
  }
  .formats-table td::before {
    content: attr(data-label);
    flex-shrink: 0;
    text-align: left;
    color: var(--ink3);
    font-size: 12px;
  }
}

/* The "Hantera versioner" list (renderPeriodsList) needs its own row shape
   instead of reusing .admin-row directly: a Period's name/dates plus three
   possible action buttons (Kopiera/Redigera/Gör aktiv) is more content than
   any other admin list's single-row-with-one-"Redigera"-button layout —
   forcing it all into one flex line (as .admin-row does) squeezed the name
   and dates down to almost nothing on a narrow modal ("Period / 1" wrapping
   across two lines, dates truncated to "20…"). Info stacks on top, actions
   get their own full-width row underneath and can themselves wrap onto a
   second line if needed without dragging the text down with them. */
.period-row {
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg);
}

.period-row-dates {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.period-row-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
}

/* Groups aliases by plan on the Platsalias page (e.g. all of Cloettavallen's
   aliases together) instead of one flat list. */
.admin-alias-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 14px;
}

.admin-alias-group-title {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--ink3);
  padding-left: 2px;
}

/* Administration → Planer's ▲/▼ column-order controls (see movePlan in
   app.js) — stacked tightly next to the plan's color dot, small enough not
   to compete with the row's actual content. */
.admin-row-reorder {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex-shrink: 0;
}

.btn-reorder {
  width: 22px;
  height: 18px;
  padding: 0;
  border-radius: 4px;
  border: 1px solid var(--line);
  background: var(--card);
  font-size: 9px;
  line-height: 1;
  color: var(--ink2);
  display: flex;
  align-items: center;
  justify-content: center;
}

.btn-reorder:disabled {
  opacity: .3;
}

.btn-edit-row {
  padding: 5px 10px;
  border-radius: 7px;
  border: 1px solid var(--line);
  background: var(--card);
  font-size: 12px;
  font-weight: 600;
  color: var(--ink2);
  flex-shrink: 0;
}

/* ─── Admin edit modal ───────────────────────────────────────────────── */
.edit-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: rgba(0, 0, 0, .45);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 16px;
}

.edit-modal {
  background: var(--card);
  border-radius: 16px;
  width: 100%;
  max-width: 420px;
  max-height: 90dvh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 24px 48px rgba(0, 0, 0, .2);
}

.edit-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px 12px;
  border-bottom: 1px solid var(--line);
  flex-shrink: 0;
}

.edit-modal-title {
  font-size: 16px;
  font-weight: 700;
}

.edit-modal-body {
  padding: 16px 20px;
  overflow-y: auto;
  flex: 1;
}

.edit-modal-footer {
  display: flex;
  gap: 8px;
  padding: 12px 20px 16px;
  border-top: 1px solid var(--line);
  flex-shrink: 0;
}

.edit-modal-footer .btn-primary {
  flex: 2;
  /* .btn-primary's base rule sets width:100%/margin-top:20px for its usual
     standalone stacked-button context (e.g. the login form) — in this
     flex footer row that just pushed Spara down 20px below Avbryt/Ta
     bort, making the row look uneven/mismatched (same fix
     .form-actions .btn-primary already applies for its own footer row). */
  width: auto;
  margin-top: 0;
}

.edit-modal-footer .btn-secondary {
  flex: 1;
}

.edit-modal-footer .btn-danger {
  flex: 1;
}

.admin-form-title {
  font-size: 15px;
  font-weight: 700;
  margin-bottom: 8px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}

.admin-form-actions {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}

.admin-form-actions .btn-primary {
  flex: 2;
}

.admin-form-actions .btn-secondary {
  flex: 1;
}

.field-hint {
  font-size: 11px;
  color: var(--ink3);
  margin-top: 3px;
}

/* Administration → Import: each source's settings, "extra" sub-features
   (Lagalias for Fogis, Uteslutna händelser for Svenska Lag) and
   Förhandsgranska/Importera buttons live together in one bordered card,
   instead of the source's settings and its buttons being split across two
   disconnected parts of the page — see renderAdminImport. --import-accent
   is set per card (Fogis vs. Svenska Lag) so the two are visually
   distinguishable at a glance without relying on reading the heading text.
*/
.import-card {
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 14px 16px 16px;
  margin-bottom: 20px;
  background: var(--card);
  border-top: 3px solid var(--import-accent, var(--line));
}

.import-card-title {
  font-size: 15px;
  font-weight: 700;
  margin-bottom: 10px;
  color: var(--import-accent, var(--ink));
}

.import-card-sub {
  margin: 16px 0 10px;
  padding-top: 12px;
  border-top: 1px dashed var(--line);
}

.import-card-sub-title {
  font-size: 13px;
  font-weight: 700;
  margin-bottom: 6px;
}

/* A finished Förhandsgranska/Importera report — see renderImportReport in
   app.js — can run to dozens of rows, so it's wrapped in a native
   <details>/<summary> instead of a plain block: the summary line alone
   (mode + row count) always stays visible and clickable, collapsing
   everything else back down so whatever's below the card (the other
   source, Uteslutna händelser, ...) isn't pushed far off-screen. */
.import-report {
  margin-top: 8px;
}

.import-report-summary {
  list-style: none;
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--alt-cell);
  user-select: none;
}

.import-report-summary::-webkit-details-marker {
  display: none;
}

/* A bordered/filled bar rather than plain text, a chevron that rotates on
   open/close, and an explicit "Fäll ihop/ut" label — this is meant to read
   unmistakably as a clickable, collapsible panel, not just a status line. */
.import-report-summary::before {
  content: '▸';
  display: inline-block;
  color: var(--brand);
  font-size: 11px;
  transition: transform 0.15s ease;
}

.import-report[open] > .import-report-summary::before {
  transform: rotate(90deg);
}

.import-report-summary::after {
  content: 'Fäll ihop';
  margin-left: auto;
  font-weight: 500;
  font-size: 12px;
  color: var(--ink3);
}

.import-report:not([open]) > .import-report-summary::after {
  content: 'Fäll ut';
}

.import-report-summary:hover {
  background: var(--brand-tint);
  border-color: var(--brand);
}

.import-report-body {
  margin-top: 6px;
}

.size-checks {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  padding: 8px 0;
}

.check-label {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 14px;
  cursor: pointer;
}

.btn-danger {
  flex: 1;
  padding: 12px;
  border-radius: 10px;
  border: 1px solid #fca5a5;
  background: #fff;
  color: var(--warn);
  font-size: 14px;
  font-weight: 600;
}

/* .btn-small shrinks a full-size .btn-secondary/.btn-danger/.btn-primary
   button down for a row of side-by-side actions (e.g. Administration →
   Perioder's Kopiera/Redigera/Radera). It must be declared AFTER those
   base classes here — same selector specificity (single class each), so
   with equal specificity the LAST rule in the stylesheet wins for a
   shared property regardless of the order classes appear in the
   element's class attribute. Declaring .btn-small earlier meant
   .btn-danger's own padding/font-size (defined after it, above) silently
   overrode .btn-small's on "btn-danger btn-small" buttons — the "Radera
   knappen är större" bug — while .btn-secondary (defined further up)
   never had that problem. Keep this block below every full-size button
   class it's meant to shrink. */
.btn-small {
  flex: none;
  padding: 6px 12px;
  border-radius: 8px;
  font-size: 13px;
  white-space: nowrap;
}

/* ─── API key result box ────────────────────────────────────────────── */
.apikey-result {
  margin-top: 12px;
  padding: 12px;
  border-radius: 10px;
  background: #f0fdf4;
  border: 1px solid #86efac;
}

.apikey-result-label {
  font-size: 12px;
  font-weight: 600;
  color: #166534;
  margin-bottom: 6px;
}

.apikey-result-key {
  font-family: monospace;
  font-size: 12px;
  word-break: break-all;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 8px;
  color: var(--ink);
}

/* ─── PC: bottom sheets as centered dialogs ──────────────────────────── */
@media (min-width: 640px) {
  .form-overlay {
    align-items: center;
    justify-content: center;
    padding: 24px;
  }

  .form-sheet {
    max-width: 480px;
    border-radius: 16px;
    /* replace slide-up with a subtle scale-in */
    transform: scale(0.95);
    opacity: 0;
    transition: transform 200ms cubic-bezier(.2, .7, .3, 1), opacity 200ms ease;
  }

  .form-sheet.open {
    transform: scale(1);
    opacity: 1;
  }

  /* Same treatment for the booking-details sheet (opened by tapping a
     match/event block) — without this it still slides up as a full-width
     bar pinned to the bottom edge of a wide PC window, the mobile "bottom
     sheet" pattern looking out of place next to the already-centered form. */
  .sheet-overlay {
    align-items: center;
    justify-content: center;
    padding: 24px;
  }

  .sheet {
    max-width: 400px;
    border-radius: 16px;
    padding: 20px 0 24px; /* the mobile 8px top padding assumes the (now hidden) handle */
    transform: scale(0.95);
    opacity: 0;
    transition: transform 200ms cubic-bezier(.2, .7, .3, 1), opacity 200ms ease;
  }

  .sheet.open {
    transform: scale(1);
    opacity: 1;
  }

  /* The grip handle is a mobile swipe-to-dismiss affordance; on a
     mouse-driven centered dialog (closed via its own buttons or a backdrop
     click) it just reads as a stray bar. */
  .sheet-handle {
    display: none;
  }

  /* .week-grid's flexible columns otherwise stretch to fill a wide PC
     window, spreading "Idag"/the arrows/the day-pills out edge to edge.
     Cap and center it on desktop instead. */
  .week-grid {
    max-width: 480px;
    margin-left: auto;
    margin-right: auto;
  }

  /* Booking block text — sized tight (8-10px, see the base rules) for a
     phone's narrow plan columns; a mouse-driven PC window has the room to
     read it more comfortably, so bump it up a bit here. */
  .match-time {
    font-size: 11px;
  }

  .match-size {
    font-size: 9px;
  }

  .match-home {
    font-size: 11px;
  }

  .match-away {
    font-size: 10px;
  }

  .match-kind-label {
    font-size: 10px;
  }
}

/* ─── Tidbokning ────────────────────────────────────────────────────── */
/* Träningstider (Tidbokning/SPEC.md) — admin's weekly-template builder and
   the leader's önska/rangordna grid. Reuses .admin-row/.admin-row-sub/
   .form-field/.btn-* wherever the shape already matches (see
   renderBookingAdmin/renderBookingLeader in app.js); these classes only
   cover what's genuinely new — the format/team checkbox groups in the slot
   editor, the seat-rule sub-panels, and the leader grid itself. Colors
   reuse the app's existing tokens (--brand/--warn/--ink*) rather than the
   design mockups' own separate palette, per Tidbokning/SPEC.md's "Anpassa
   till befintlig style.css där den har egna tokens". */

.format-checks {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
}

.format-check {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
}

.booking-seat-rule {
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 10px;
  margin-bottom: 10px;
  background: var(--bg);
}

.booking-seat-rule-title {
  font-size: 12px;
  font-weight: 700;
  color: var(--ink2);
  margin-bottom: 6px;
}

/* Ledarvy (renderBookingLeader) — modeled on Tidbokning/mockup-ledare.dc.html,
   reusing Steg 1's .tb-header/.tb-pill/.tb-card/.tb-stepper-btn/.tb-quota-*
   tokens rather than introducing a second visual language. See
   renderBookingGrid/bookingCellHTML/bookingDotsHTML in app.js. */

.tb-ledare-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 14px 0 18px;
}
/* Empty (a published schedule with a single team): no leftover margins. */
.tb-ledare-tabs:empty { display: none; }

.tb-ledare-tab {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 10px 16px;
  border-radius: 12px;
  border: 1px solid var(--line);
  background: var(--card);
  font-size: 14px;
}

.tb-ledare-tab-name {
  font-weight: 700;
}

.tb-ledare-tab-format {
  font-size: 12px;
  color: var(--ink3);
}

.tb-ledare-tab-active {
  border-color: var(--brand);
  background: var(--brand-tint);
}

.tb-ledare-tab-active .tb-ledare-tab-format {
  color: var(--brand);
}

.tb-ledare-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 20px;
  align-items: start;
}

.tb-ledare-main {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-width: 0;
}

.tb-ledare-intro {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  flex-wrap: wrap;
}

.tb-ledare-legend {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  justify-content: flex-end;
  font-size: 12px;
  color: var(--ink2);
}

.tb-ledare-legend-item {
  display: flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}

.tb-dot {
  width: 11px;
  height: 11px;
  border-radius: 999px;
  display: inline-block;
  flex-shrink: 0;
}

.tb-dot-free {
  border: 2px solid var(--ink3);
  box-sizing: border-box;
}

.tb-dot-other {
  background: var(--ink3);
}

.tb-dot-own {
  background: var(--brand);
  box-shadow: 0 0 0 2px var(--card), 0 0 0 3px var(--brand);
}

.tb-dot-over {
  background: var(--warn);
}

.tb-ledare-grid {
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 16px;
  /* No padding on the left/top: the sticky time column and day header carry
     it themselves, so scrolled content can't show in a gap beside them. */
  padding: 0 18px 18px 0;
  /* Scrolls in both directions so the day header (sticky top) and the time
     column (sticky left) stay in view while moving around the grid. */
  overflow: auto;
  max-height: 75vh;
  /* label + 5 day columns + gaps */
  --tb-row-min: 694px;
}

/* Every row is at least this wide (not sized by its own content, which would
   make columns drift between rows), so the sticky time
   column has the full scroll width to stay inside. */
.tb-ledare-grid > *,
.tb-ledare-plan-group > * {
  min-width: var(--tb-row-min);
}

.tb-ledare-row {
  display: grid;
  grid-template-columns: 114px repeat(var(--days, 5), minmax(108px, 1fr));
  gap: 8px;
  align-items: stretch;
}

.tb-ledare-row-head {
  font-size: 12px;
  font-weight: 700;
  color: var(--ink3);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--card);
  padding: 18px 0 8px;
}

.tb-ledare-row-label {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  padding: 4px 0 4px 18px;
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--card);
}

.tb-ledare-row-head .tb-ledare-row-label {
  z-index: 4;
}

.tb-ledare-row-time {
  font-size: 15px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* One plan's own group of rows in Önska tider (mirrors Mall's own per-plan
   sections — see .tb-plan-header) — the plan name now sits once, above its
   rows, instead of repeated on every row's own label. */
.tb-ledare-plan-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.tb-ledare-plan-group-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  margin-top: 10px;
  border-radius: 8px;
  background: var(--brand-tint);
  /* Stays at the left edge while the grid is scrolled sideways. */
  position: sticky;
  left: 18px;
  margin-left: 18px;
  box-shadow: -18px 0 0 var(--card);
  /* Narrower than the visible grid and wrapping, so there is room left in the
     row for it to slide along; a head as wide as the row cannot move. */
  max-width: min(420px, calc(100vw - 100px));
  flex-wrap: wrap;
  align-self: flex-start;
  min-width: 0;
}

.tb-ledare-plan-group:first-child .tb-ledare-plan-group-head {
  margin-top: 0;
}

.tb-ledare-plan-group-name {
  font-weight: 800;
  font-size: 14px;
  color: var(--brand);
}

.tb-ledare-plan-group-site {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink2);
  padding-left: 8px;
  border-left: 1px solid var(--brand);
}

.tb-ledare-cell {
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg);
  padding: 8px 10px;
  font: inherit;
  text-align: left;
  cursor: pointer;
  min-height: 78px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
}

.tb-ledare-cell-empty {
  cursor: default;
  color: var(--ink3);
  background: transparent;
  border-style: dashed;
  align-items: center;
  justify-content: center;
  font-size: 13px;
}

.tb-ledare-cell-locked {
  cursor: default;
  border-style: dashed;
  background: repeating-linear-gradient(135deg, var(--bg) 0 6px, var(--line) 6px 12px);
  color: var(--ink3);
  align-items: center;
  justify-content: center;
  text-align: center;
  font-size: 12px;
  gap: 2px;
}

.tb-ledare-cell-locked-title {
  font-weight: 700;
}

.tb-ledare-cell-selected {
  border-color: var(--brand);
  background: var(--brand-tint);
}

/* A cell with room left, not yet chosen, used to look identical to a fully
   booked one — same plain border/background, only the status text said
   otherwise. Now it's coloured distinctly from "vald" (brand green) so an
   open slot reads as open at a glance, not just on close reading. */
.tb-ledare-cell-open {
  border-color: var(--free);
  background: var(--free-tint);
}
.tb-ledare-cell-open .tb-ledare-cell-status {
  color: var(--free);
  font-weight: 700;
}

/* A selected cell past the wanted-count (rank > wanted) is a reserve, not a
   real pick — marked with its own colour + a "Reserv" tag so it's never
   mistaken for one of the team's actual training times. */
.tb-ledare-cell-reserve {
  border-color: var(--reserve);
  background: var(--reserve-tint);
}

.tb-ledare-cell-reserve .tb-ledare-cell-rank {
  background: var(--reserve);
}

.tb-ledare-cell-reserve-tag {
  font-size: 10px;
  font-weight: 700;
  color: var(--reserve);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.tb-ledare-cell-cotraining {
  outline: 2px solid #1d3f57;
  outline-offset: -1px;
}

.tb-ledare-cell-conflict {
  outline: 2px dashed var(--warn);
  outline-offset: -1px;
}

.tb-ledare-cell-top {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
}

.tb-ledare-cell-rank {
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--brand);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}

.tb-ledare-cell-dots {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
}

.tb-ledare-cell-count {
  font-size: 11px;
  font-weight: 700;
  margin-left: 2px;
  color: var(--ink2);
}

.tb-ledare-cell-status {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink2);
}

.tb-ledare-side {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.tb-ledare-wish-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 6px;
}

.tb-ledare-wish-count {
  font-size: 12px;
  color: var(--ink3);
}

.tb-ledare-notes-field {
  margin: 10px 0;
}
.tb-ledare-notes-field textarea {
  font-size: 13px;
}

.tb-ledare-wanted-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 10px;
  background: var(--bg);
  font-size: 13px;
  margin-bottom: 8px;
}

#booking-wishlist-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.tb-ledare-wish {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border-radius: 10px;
  background: var(--bg);
  border: 1px solid var(--line);
}

.tb-ledare-wish-badge {
  min-width: 22px;
  height: 22px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--brand);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.tb-ledare-wish-badge-reserve {
  background: var(--ink3);
}

.tb-ledare-wish-info {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
  gap: 1px;
}

.tb-ledare-wish-label {
  font-size: 14px;
  font-weight: 600;
}

.tb-ledare-wish-sub {
  font-size: 11px;
  color: var(--ink3);
}

.tb-ledare-wish-remove {
  color: #8a2e12;
}

.tb-ledare-check {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 13px;
  padding: 4px 0;
}

.tb-ledare-check-icon {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 1px;
}

.tb-ledare-check-ok {
  background: var(--brand-tint);
  color: var(--brand);
}

.tb-ledare-check-stop {
  background: #fdecea;
  color: var(--warn);
}

.tb-ledare-check-info {
  background: var(--line);
  color: var(--ink2);
}

@media (max-width: 900px) {
  .tb-ledare-layout {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 640px) {
  .tb-ledare-row {
    grid-template-columns: 98px repeat(var(--days, 5), minmax(90px, 1fr));
  }

  .tb-ledare-grid {
    --tb-row-min: 588px;
  }

  .tb-ledare-row-time {
    font-size: 13px;
  }
}

/* Admin builder (Steg 1) — visually modeled on Tidbokning/mockup-admin.dc.html
   (header eyebrow/title, season pills, step strip, per-plan week-grid with
   draggable pass blocks, right-hand info/quota cards). See
   renderBookingAdmin/buildTBPlanSection/wireTBDragAndDrop in app.js. */

.tb-body {
  max-width: none;
  width: 100%;
}

.tb-header {
  margin-bottom: 4px;
}

.tb-eyebrow {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--ink3);
}

.tb-title-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 2px;
}

.tb-title {
  font-size: 30px;
  font-weight: 800;
  letter-spacing: -0.5px;
}

.tb-season-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.tb-pill {
  padding: 8px 16px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--card);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink2);
}

.tb-pill-active {
  background: var(--ink);
  border-color: var(--ink);
  color: #fff;
}

.tb-pill-add {
  border-style: dashed;
  color: var(--ink3);
}

/* Anteckningar (season-note.js): the note button sits last in the pill row; a
   small dot marks a season that has a note. */
.tb-note-btn { position: relative; padding: 8px 12px; }
.tb-note-dot {
  position: absolute;
  top: 4px;
  right: 4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--brand);
}
.season-note-modal { max-width: 560px; }
/* Prefixed to outweigh .form-field textarea's own min-height further up. */
.season-note-modal .season-note-text { min-height: 60dvh; resize: vertical; line-height: 1.5; }
.season-note-meta {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 6px;
  font-size: 12px;
  color: var(--ink3);
}
.season-note-conflict-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
#sn-conflict { margin: 10px 0 0; }

.tb-steps {
  display: flex;
  gap: 24px;
  margin: 18px 0 14px;
  padding-bottom: 11px; /* matches .tb-step's margin-bottom so nothing overflows vertically */
  border-bottom: 1px solid var(--line);
  overflow-x: auto;
  overflow-y: hidden;
}

.tb-step {
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding-bottom: 10px;
  margin-bottom: -11px;
  font-size: 11px;
  font-weight: 700;
  color: var(--ink3);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  white-space: nowrap;
}

.tb-step span {
  font-size: 14px;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink3);
}

.tb-step-active {
  color: var(--brand);
  border-bottom-color: var(--brand);
}

.tb-step-active span {
  color: var(--ink);
}

.tb-step-disabled {
  opacity: 0.45;
  cursor: default;
}

.tb-notice {
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--card);
  font-size: 13px;
  margin-bottom: 14px;
}

.tb-toolbar {
  display: flex;
  align-items: center;
  /* Was space-between, which on a wide screen stranded "Stäng för
     önskemål" way over on the right edge, far from the status/dates it
     acts on. Grouped together instead, with just enough gap to separate
     the two clusters. */
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: 10px 24px;
  margin-bottom: 16px;
}

.tb-toolbar-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  color: var(--ink3);
}

.tb-status-badge {
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--line);
  color: var(--ink2);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.3px;
}

.tb-status-open {
  background: var(--brand-tint);
  color: var(--brand);
}

.tb-toolbar-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  /* Stretches to fill the rest of .tb-toolbar's row, so .tb-toolbar-edit's
     own margin-left:auto below has the full width to push against and
     actually lands at the toolbar's right edge, not just its own content's. */
  flex: 1;
}
/* .btn-primary defaults to width:100%/margin-top:20px (built for a single
   full-width form submit button) — .btn-small resets its flex/padding but
   not those two, so "Publicera" stretched across the row and sat lower
   than "Redigera säsong"/"Stäng för önskemål" next to it. Same fix as
   .free-actions below already needed for the same three button classes. */
.tb-toolbar-actions .btn-secondary,
.tb-toolbar-actions .btn-primary,
.tb-toolbar-actions .btn-danger {
  flex: none;
  width: auto;
  margin-top: 0;
}
/* "Redigera säsong" is used far less often than Stäng/Öppna för önskemål or
   Publicera — pushed to the far right so it's never the button a thumb
   lands on by habit reaching for those two. */
.tb-toolbar-edit {
  margin-left: auto;
}

.tb-layout {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: 18px;
  align-items: start;
}

.tb-main {
  display: flex;
  flex-direction: column;
  gap: 18px;
  min-width: 0;
}

.tb-side {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.tb-card {
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--card);
  padding: 16px;
}

.tb-card-title {
  font-size: 15px;
  font-weight: 800;
  margin-bottom: 8px;
}

.tb-card-body {
  font-size: 13px;
  color: var(--ink2);
  line-height: 1.5;
}

.tb-wish-help-list {
  margin: 6px 0 0;
  padding-left: 18px;
}
.tb-wish-help-list li + li {
  margin-top: 4px;
}

.tb-plan-section {
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--card);
  padding: 18px;
  overflow-x: auto;
}

.tb-plan-header {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-bottom: 14px;
  cursor: pointer;
  user-select: none;
}

.tb-plan-toggle {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  margin-top: 1px;
  border: none;
  background: none;
  color: var(--ink3);
  font-size: 12px;
  transition: transform 0.15s ease;
}

/* Collapsed is the toggle's resting/default look (▶); expanded rotates it
   to point down (▼) — set on the button itself via [aria-expanded], kept in
   sync by wireTBPlanToggles/buildTBPlanSection rather than relying on the
   .tb-plan-collapsed ancestor class, so the arrow's direction never depends
   on selector order. */
.tb-plan-toggle[aria-expanded="true"] {
  transform: rotate(90deg);
}

.tb-plan-header-text {
  flex: 1;
  min-width: 0;
}

.tb-plan-name-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.tb-plan-name {
  font-size: 19px;
  font-weight: 800;
}

.tb-plan-site-badge {
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--brand-tint);
  color: var(--brand);
  font-size: 11px;
  font-weight: 700;
}

.tb-plan-meta {
  font-size: 12px;
  color: var(--ink3);
  margin-top: 2px;
}

.tb-plan-stats {
  flex-shrink: 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink3);
  white-space: nowrap;
  margin-top: 2px;
}

.tb-plan-collapsed .tb-plan-header {
  margin-bottom: 0;
}

.tb-plan-collapsed .tb-week {
  display: none;
}

.tb-week {
  min-width: 560px;
}

.tb-plan-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.tb-plan-toolbar input[type="text"],
.tb-plan-toolbar select {
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card);
  font-size: 13px;
}

.tb-plan-toolbar input[type="text"] {
  min-width: 160px;
}

.tb-toolbar-spacer {
  flex: 1;
}

@media (max-width: 640px) {
  .tb-toolbar-spacer {
    flex-basis: 100%;
    height: 0;
  }
}

.tb-week-headrow {
  display: grid;
  grid-template-columns: 52px repeat(var(--days, 5), 1fr);
  gap: 6px;
}

.tb-axis-spacer {
  /* empty — lines up with .tb-axis below */
}

.tb-daycol-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  font-size: 13px;
  font-weight: 700;
  padding: 0 2px 6px;
}

.tb-add-pass {
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px dashed var(--line);
  background: none;
  color: var(--ink3);
  font-size: 11px;
}

.tb-week-body {
  position: relative;
  display: grid;
  grid-template-columns: 52px repeat(var(--days, 5), 1fr);
  gap: 6px;
}

.tb-axis {
  position: relative;
}

.tb-time-label {
  position: absolute;
  right: 6px;
  transform: translateY(-50%);
  font-size: 11px;
  color: var(--ink3);
}

.tb-daycol {
  position: relative;
  border-left: 1px solid var(--line);
  touch-action: none;
}

.tb-hour-line {
  position: absolute;
  left: 0;
  right: 0;
  border-top: 1px solid var(--line);
}

.tb-pass {
  /* left/width come from the inline style (see buildTBPassBlock): two
     overlapping passes are split side-by-side instead of both being
     full-width, which used to just let the later one hide the earlier
     one's tail with no visible sign they overlapped at all. */
  position: absolute;
  border-radius: 8px;
  background: var(--brand-tint);
  border: 1px solid var(--brand);
  padding: 4px 8px;
  font-size: 11px;
  overflow: hidden;
  cursor: grab;
  touch-action: none;
  user-select: none;
}

.tb-pass-dragging {
  cursor: grabbing;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
  z-index: 5;
}

.tb-pass-time {
  font-weight: 700;
}

.tb-pass-sub {
  color: var(--ink2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tb-pass-handle {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 8px;
  cursor: ns-resize;
}

/* Each team is its own bordered card so neighbours don't run together. */
.tb-quota-row {
  padding: 10px 12px;
  margin-top: 8px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--brand);
  border-radius: 10px;
  background: var(--bg);
}

.tb-quota-row:first-child {
  margin-top: 0;
}

.tb-quota-row + .filter-chips,
.filter-chips + .tb-quota-row {
  margin-top: 0;
}

.tb-quota-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.tb-quota-name {
  font-size: 13px;
  font-weight: 600;
}

.tb-quota-sub {
  font-size: 11px;
  color: var(--ink3);
  margin-top: 2px;
}

.tb-quota-stepper {
  display: flex;
  align-items: center;
  gap: 8px;
}

.tb-stepper-btn {
  width: 24px;
  height: 24px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: var(--bg);
  font-size: 14px;
  line-height: 1;
}

.tb-quota-value {
  font-size: 12px;
  font-weight: 700;
  min-width: 44px;
  text-align: center;
}

/* Träningstid (see FreeBookingService.Seed) sits right under Maxkvot's own
   row, in the same card — same stepper shape, just a quieter label and a
   little breathing room above it. */
.tb-quota-name-sub {
  font-weight: 500;
  color: var(--ink3);
}
.tb-quota-top-minutes {
  margin-top: 8px;
}
.tb-quota-top-minutes .tb-quota-value {
  min-width: 84px;
}

@media (max-width: 900px) {
  .tb-layout {
    grid-template-columns: 1fr;
  }
}

/* ─── Misc ──────────────────────────────────────────────────────────── */
.hidden {
  display: none !important;
}

@media (prefers-color-scheme: dark) {
  /* Dark mode overrides can be added here in a future iteration. */
}
.tb-dot-unavailable { background: transparent; border: none; border-radius: 0; position: relative; }
.tb-dot-unavailable::after { content: ''; position: absolute; left: 0; right: 0; top: 50%; border-top: 2px solid currentColor; opacity: .45; }

/* Tidbokning, step 2: wishes overview. */
.tb-overview { padding: 20px; }
.tb-overview-header { display: flex; align-items: start; justify-content: space-between; gap: 16px; }
.tb-overview h2 { margin: 0 0 6px; font-size: 23px; }
.tb-overview-header p, .tb-overview-meta { color: var(--ink3); font-size: 13px; }
.tb-overview-filters { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0 18px; }
.tb-overview-row { display: grid; grid-template-columns: minmax(130px, 1fr) minmax(130px, 1fr) minmax(190px, 2fr) minmax(160px, 1fr); gap: 16px; align-items: start; padding: 16px; margin-top: 10px; border: 1px solid #ddd9cc; border-radius: 12px; background: #f7f5ef; }
.tb-overview-row h3 { font-size: 15px; margin: 0 0 5px; }
.tb-overview-band { display: inline-block; padding: 6px 10px; border-radius: 8px; font-size: 13px; font-weight: 600; }
.tb-overview-band-calm { background: #e3eee6; color: #1e4a33; }
.tb-overview-band-full { background: #f4ddb5; color: #5e3c0c; }
.tb-overview-band-over { background: #e2a68b; color: #4a1808; }
.tb-overview-warning { color: #8a2e12; font-size: 13px; margin-top: 6px; }
.tb-overview-teams { padding: 0; margin: 0; list-style: none; display: flex; flex-direction: column; gap: 8px; }
.tb-overview-teams li { font-size: 14px; overflow-wrap: anywhere; }
.tb-overview-leader { display: block; font-size: 12px; color: var(--ink3); }
.tb-overview-reserves { margin-top: 12px; font-size: 13px; }
.tb-overview-reserves summary { cursor: pointer; margin-bottom: 6px; }
.tb-overview-contact { display: flex; flex-direction: column; gap: 8px; }

/* Admin-only "vill samträna" marker on a row with 2+ ordinarie lag — see
   domain.BookingCoTraining. Not shown to teams. */
.tb-overview-cotraining { display: flex; flex-direction: column; gap: 4px; }
.tb-overview-cotraining-on .btn-secondary {
  border-color: var(--brand);
  background: var(--brand-tint);
  color: var(--brand);
}
/* Three-or-more-team rows: which teams to mark, picked via checkboxes
   instead of one "everyone" toggle (see bookingCoTrainingHTML). */
.tb-overview-cotraining-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
}
.tb-contact-recipients { white-space: pre-wrap; overflow-wrap: anywhere; font: inherit; font-size: 13px; }
#tb-contact-message { width: 100%; box-sizing: border-box; font: inherit; resize: vertical; }
#tb-contact-feedback { margin-top: 10px; font-size: 13px; }
@media (max-width: 1000px) { .tb-overview-row { grid-template-columns: 1fr 1fr; } }
@media (max-width: 600px) { .tb-overview { padding: 12px; } .tb-overview-row { grid-template-columns: 1fr; } .tb-overview-header { flex-direction: column; } }
.ta-panel { display:flex; flex-direction:column; gap:16px; }
.ta-actions { display:flex; flex-wrap:wrap; align-items:center; gap:10px; }
.ta-actions h2 { flex:1; }
.ta-published-row { display:flex; flex-direction:column; gap:6px; padding:12px; border:1px solid var(--line); border-radius:8px; }
/* The plan (facility · plan name, already combined server-side) needs to
   read clearly as "this is where", not just trail along as plain text
   after the time. */
.ta-published-plan {
  display: inline-flex;
  align-self: flex-start;
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--brand-tint);
  color: var(--brand);
  font-size: 12px;
  font-weight: 700;
}
.ta-published-sharing { font-size: 13px; color: var(--muted, #5b5b52); }
#ta-error { color:var(--warn); }
@media(max-width:600px) {  .ta-actions select { max-width:100%; } }.tb-responses { margin-bottom: 16px; }
.tb-response-list { padding: 0; margin: 12px 0 0; list-style: none; display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 8px; }
.tb-response { display: flex; flex-direction: column; gap: 2px; padding: 10px 12px; border: 1px solid #ddd9cc; border-radius: 10px; background: #f7f5ef; font-size: 14px; overflow-wrap: anywhere; }
.tb-response-status { font-size: 12px; font-weight: 600; }
.tb-response-submitted .tb-response-status { color: #1e4a33; }
.tb-response-draft .tb-response-status { color: #5e3c0c; }
.tb-response-none { border-color: var(--warn); }
.tb-response-none .tb-response-status { color: var(--warn); }
.tb-response-title { font-size: 14px; margin: 16px 0 0; }
/* A team's free-text "andra önskemål" (see BookingGridView.Notes), shown to
   admin here — the one place every team is listed exactly once. The whole
   card is tinted (.tb-response-noted) and a badge sits right by the name, so
   a team that wrote something is obvious at a glance, not just findable by
   reading every card's small print. */
.tb-response-noted {
  border-color: var(--reserve);
  background: var(--reserve-tint);
}
.tb-response-notes-badge {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--reserve);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  white-space: nowrap;
  vertical-align: middle;
}
.tb-response-notes {
  display: block;
  font-size: 12px;
  color: var(--ink2);
  white-space: pre-wrap;
  border-top: 1px dashed var(--line);
  margin-top: 4px;
  padding-top: 4px;
}
.tb-responses-summary { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; cursor: pointer; }
.tb-responses-summary h2 { display: inline; margin: 0; }
.btn-link { background: none; border: none; padding: 0; color: var(--ink2); font: inherit; font-size: 13px; text-decoration: underline; cursor: pointer; }
.btn-link:disabled { color: var(--ink3); text-decoration: none; cursor: default; }
.login-hint { margin: 0 0 12px; font-size: 13px; color: var(--ink2); overflow-wrap: anywhere; }
.login-links { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px; margin-top: 12px; }
#lc { letter-spacing: 6px; font-size: 20px; text-align: center; }

/* Checkboxes inside .form-field must not inherit the text-input styling
   (full width, big padding) or the small-caps field-label styling. */
.form-field input[type="checkbox"],
.form-field input[type="radio"] {
  width: 16px;
  height: 16px;
  padding: 0;
  margin: 0;
  flex: 0 0 auto;
  accent-color: var(--brand);
}
.form-field .format-check {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  text-transform: none;
  letter-spacing: 0;
  cursor: pointer;
}

/* Team/plan picker: a tidy multi-column list, one checkbox + name per cell.
   max-height + overflow-y makes it scroll once there are more rows than fit
   — this used to be split across two separate rules for the same class (one
   further up, flex-based; this one, added later, grid-based), so whichever
   properties the later rule didn't redeclare quietly kept their value from
   the earlier one. Harmless as long as both agreed, until a min-width-170px
   grid column count made scrolling relevant sooner (e.g. "Planer som
   används" as a club adds facilities) than the wrap-based flex layout the
   first rule was written for — then it could look, at a glance, like the
   list just stopped after however many rows fit, since nothing here made
   the fact that it scrolls obvious. */
.booking-team-checks {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  gap: 2px 12px;
  max-height: 220px;
  overflow-y: auto;
  padding: 6px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--bg);
}
.booking-team-checks .format-check {
  padding: 6px 8px;
  border-radius: 6px;
  min-width: 0;
  overflow-wrap: anywhere;
}
.booking-team-checks .format-check:hover { background: var(--brand-tint); }

/* Date picker as the mirror of "Idag": same row, last column (over Söndag).
   The real, invisible <input type="date"> still sits on top and is the click
   target (see .date-picker-overlay); the wrapper just gets the button's box. */
.week-grid .week-date-picker {
  grid-row: 1;
  grid-column: 7;
  justify-self: end;
  width: 36px;
  height: 28px;
  border-radius: 8px;
}
.week-date-picker .btn-cal {
  width: 100%;
  height: 100%;
  padding: 0;
  font-size: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.week-date-picker:hover .btn-cal {
  background: var(--brand-tint);
  border-color: var(--brand);
}

/* Önska tider: is the wish round open or closed? Shown first on the page. */
.tb-status-banner {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 4px 0 16px;
  padding: 12px 14px;
  border-radius: 12px;
  border: 1px solid var(--line);
}
.tb-status-banner > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tb-status-banner strong { font-size: 15px; }
.tb-status-banner span:not(.tb-status-banner-dot) { font-size: 13px; }
.tb-status-banner small { font-size: 12px; color: var(--ink3); overflow-wrap: anywhere; }
.tb-status-banner-dot { flex: 0 0 auto; width: 12px; height: 12px; margin-top: 4px; border-radius: 50%; }
.tb-status-banner-open { background: var(--brand-tint); border-color: var(--brand); }
.tb-status-banner-open strong { color: var(--brand); }
.tb-status-banner-open .tb-status-banner-dot { background: var(--brand); }
.tb-status-banner-closed { background: #f4ddb5; border-color: #d9b26a; color: #5e3c0c; }
.tb-status-banner-closed small { color: #7a5a2b; }
.tb-status-banner-closed .tb-status-banner-dot { background: var(--warn); }

/* Closed for wishes: the picks stay visible, but the grid is inert. */
.tb-ledare-locked { opacity: 0.75; }
.tb-ledare-locked .tb-ledare-cell,
.tb-ledare-locked button { cursor: default; }

/* User form: room between each field and the label of the next one. */
.user-form .form-field { margin-bottom: 16px; }
.user-form .form-field > label { margin-bottom: 6px; }
.user-form .form-field > .admin-row-sub { margin-top: 6px; }

/* Önska tider, step 1: how many sessions a week — the choice everything else
   hangs on, so it is big and sits above the schedule. */
.tb-ledare-wanted {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 14px 20px;
  margin: 0 0 18px;
  padding: 16px 18px;
  border: 2px solid var(--brand);
  border-radius: 14px;
  background: var(--brand-tint);
}
.tb-ledare-wanted-missing { border-color: var(--warn); }
.tb-ledare-wanted-text { display: flex; flex-direction: column; gap: 3px; flex: 1 1 240px; min-width: 0; }
.tb-ledare-wanted-text strong { font-size: 17px; }
.tb-ledare-wanted-text > span:not(.tb-ledare-wanted-step) { font-size: 13px; color: var(--ink2); }
.tb-ledare-wanted-step {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--brand);
}
/* The old black "Spelform/Max pass per vecka" strip is gone — max pass/vecka
   now lives here instead, as a small badge under the +/- stepper (spelform
   is already shown on the team's own card, so it didn't need to move
   anywhere) — next to where the number is actually picked, not off on the
   left with the explanatory text, so it reads as "this is the ceiling on
   the number you're about to choose" rather than a separate fact. */
.tb-ledare-wanted-stepper-wrap { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.tb-ledare-wanted-max {
  display: inline-flex;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--brand-tint);
  color: var(--brand);
  font-size: 11px;
  font-weight: 700;
}
.tb-ledare-wanted-stepper { display: flex; align-items: center; gap: 12px; }
.tb-ledare-wanted-btn {
  width: 48px;
  height: 48px;
  border-radius: 12px;
  border: 1px solid var(--brand);
  background: var(--card);
  color: var(--brand);
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
}
.tb-ledare-wanted-btn:hover:not(:disabled) { background: var(--brand); color: #fff; }
.tb-ledare-wanted-btn:disabled { opacity: 0.4; cursor: default; }
.tb-ledare-wanted-value { display: flex; flex-direction: column; align-items: center; min-width: 84px; line-height: 1.1; }
.tb-ledare-wanted-value b { font-size: 34px; font-weight: 800; color: var(--brand); }
.tb-ledare-wanted-value small { font-size: 12px; color: var(--ink2); }

/* Login dialog: give the fixed overlay its own compositing layer and size it to
   the visible viewport, so mobile browsers repaint it reliably when the
   keyboard or a password-manager sheet opens and closes. */
.login-overlay {
  transform: translateZ(0);
  min-height: 100vh;
  min-height: 100dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* Önska tider: at the wish limit the remaining free cells are shut instead of
   letting a click fail with an error. */
.tb-ledare-cell-limit {
  opacity: 0.5;
  cursor: not-allowed;
  background: repeating-linear-gradient(135deg, var(--bg), var(--bg) 8px, #f0f0ea 8px, #f0f0ea 16px);
  align-items: center;
  text-align: center;
  font-size: 12px;
  color: var(--ink3);
}
.tb-ledare-cell-limit-title { font-weight: 700; }

.tb-inline-note {
  margin: 10px 0 0;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid #d9b26a;
  background: #f4ddb5;
  color: #5e3c0c;
  font-size: 13px;
}
.tb-inline-note-error { border-color: var(--warn); background: #fde8dc; color: #7a2a0c; }

/* Önska tider with 0 sessions chosen: the schedule is dimmed, not needed. */
.tb-ledare-idle { opacity: 0.45; }
.tb-ledare-idle .tb-ledare-cell { cursor: default; }

/* A pass reserved ("tingat") for certain teams: amber, with the team names on
   their own wrapping line so they are never cut off like the summary line. */
.tb-pass-reserved {
  background: #f4ddb5;
  border-color: #c98a1a;
}
.tb-pass-reserved-line {
  margin-top: 2px;
  color: #5e3c0c;
  font-size: 11px;
  line-height: 1.25;
  overflow-wrap: anywhere;
}
.tb-pass-reserved-badge {
  display: inline-block;
  padding: 0 5px;
  border-radius: 999px;
  background: #c98a1a;
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.3px;
}

/* "+ Pass": a clear, solid button — it used to be a grey dashed pill that was
   easy to miss. */
.tb-add-pass {
  padding: 5px 12px;
  border: 1px solid var(--brand);
  background: var(--brand-tint);
  color: var(--brand);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  min-height: 28px;
  /* Same size in every day column: never wrap or get squeezed by the day name. */
  white-space: nowrap;
  flex: 0 0 auto;
  line-height: 1.2;
}
.tb-add-pass:hover,
.tb-add-pass:focus-visible {
  background: var(--brand);
  color: #fff;
}

/* Fördelning (Steg 3): locks, capacity, selection. */

/* Planplanering — two views toggled via .free-view-toggle (see
   renderFreePlaneringContent): 'week' (default) is one section per plan,
   Mån–Fre day columns, same shape as Mall's own per-plan sections
   (.tb-plan-section/.tb-week-headrow); 'day' is the older layout, every
   plan as its own column for one chosen weekday (.free-day-picker) at a
   time. Both built on Mall's block/drag visual language (.tb-pass' sibling
   here is .free-block) since several teams can share a plan at once.
   Storage is still per calendar date underneath (see FreeBookingService's
   own doc comment), but every week in the period looks the same, so
   neither view has real date/week navigation — a weekday is picked, not a
   date. */
.free-panel { gap: 10px; }
/* Vecka/Dag toggle, right next to the published badge in the title row. */
.free-view-toggle {
  display: flex;
  gap: 6px;
  margin-left: auto;
}
/* 'day' view's Mån–Sön picker (see buildFreeDayViewHTML) — same .tb-pill
   row pattern as the wish-overview's own filter pills. */
.free-day-picker {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px;
}
/* 'day' view's body: every plan as its own flex column (variable count, so
   flex not grid, unlike 'week' view's fixed 5-day .free-week-body), sticky
   time axis at the left, scrolls horizontally once there are many plans. */
.free-day-body {
  display: flex;
  align-items: flex-start;
  gap: 0;
  overflow-x: auto;
  position: relative;
  padding-bottom: 8px;
}
.free-day-body .tb-axis {
  position: sticky;
  left: 0;
  z-index: 2;
  width: 52px;
  flex-shrink: 0;
  background: var(--card);
  border-right: 1px solid var(--line);
}
.free-day-plan-col {
  flex: 1 0 200px;
  min-width: 200px;
  border-right: 1px solid var(--line);
}
.free-day-plan-col .free-plan-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  padding: 0 8px 6px;
  font-size: 13px;
  font-weight: 700;
}
.free-day-plan-col .free-daycol {
  margin: 0 6px;
}
/* .btn-secondary/.btn-primary/.btn-danger default to flex:1/width:100%
   (built for a single full-width form button), which stretches "Fyll på
   från önskemål", "Publicera"/"Avpublicera" and "Rensa allt och fyll om"
   across the whole row here — sized to content instead, left-aligned like
   an ordinary button pair. */
.free-actions {
  gap: 10px;
}
.free-actions .btn-secondary,
.free-actions .btn-primary,
.free-actions .btn-danger {
  flex: none;
  width: auto;
  margin-top: 0;
  padding: 8px 16px;
}
/* "Rensa allt och fyll om" is destructive and rare — pushed to the far
   right so it's never the button a thumb lands on by habit. */
.free-actions .free-reset {
  margin-left: auto;
}
.free-published-badge {
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  background: var(--bg);
  color: var(--ink3);
  border: 1px solid var(--line);
}
.free-published-badge-on {
  background: var(--brand-tint);
  color: var(--brand);
  border-color: var(--brand);
}
.free-plan-sections {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
/* One plan's Mån–Fre week — grid-based, same column template as Mall's own
   .tb-week-headrow/.tb-week-body (52px axis + 5 equal day columns), so the
   day headers above line up exactly with the day columns below. */
.free-week-body {
  position: relative;
  display: grid;
  grid-template-columns: 52px repeat(var(--days, 5), 1fr);
  gap: 6px;
}
.free-week-body .tb-axis {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--card);
}
.free-daycol {
  position: relative;
  touch-action: none;
}
.free-block {
  position: absolute;
  border-radius: 8px;
  background: var(--brand-tint);
  border: 1px solid var(--brand);
  padding: 3px 6px;
  font-size: 11px;
  overflow: hidden;
  cursor: grab;
  touch-action: none;
  user-select: none;
}
/* Flex, not a single truncated line: a long team name used to push the
   spelform ring (.free-block-format) past the ellipsis and out of sight
   entirely. Only the name itself truncates now — the ring never shrinks,
   so it's always visible regardless of how long the name is. */
.free-block-team {
  display: flex;
  align-items: center;
  gap: 3px;
  font-weight: 700;
  overflow: hidden;
}
.free-block-team-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
/* Admin-only "vill samträna" marking, carried over from Önskemål (see
   domain.BookingCoTraining) — a dashed brand-coloured outline plus a small
   handshake tag, so two co-located teams that agreed to train together
   stand out without needing to open each block. */
.free-block-cotraining {
  outline: 2px dashed var(--brand);
  outline-offset: -1px;
}
.free-block-cotraining-tag {
  flex-shrink: 0;
  font-size: 10px;
}
/* A booking dragged away from where "Fyll på från önskemål" first put it
   (see freeOriginInfoHTML's own amber .conflict-warning callout, same
   colour) — a left-edge stripe plus a small icon, so it reads at a glance
   in the grid without opening the block. A left border, not outline, so it
   never fights .free-block-cotraining's own dashed outline when both apply. */
.free-block-moved {
  border-left: 3px solid var(--warn);
  background: color-mix(in srgb, var(--warn) 22%, var(--card));
  border-color: var(--warn);
}
/* "Visa reservtider": a team's unused reserve wish drawn at its place in the
   grid — dashed and see-through, never clickable (pointer-events:none), so
   it can't get in the way of dragging the bookings underneath. Green when the
   pattern has room (ReserveOption.Available, same --free as the corner dot
   above), grey when it is full. */
.free-reserve-ghost {
  position: absolute;
  box-sizing: border-box;
  border: 2px dashed var(--ink3);
  border-radius: 8px;
  background: color-mix(in srgb, var(--ink3) 8%, transparent);
  color: var(--ink3);
  font-size: 11px;
  padding: 3px 6px;
  overflow: hidden;
  pointer-events: none;
}
/* Text at the top left, same spot and size as a booking's team name. */
.free-reserve-ghost-label {
  display: block;
  font-weight: 700;
  overflow-wrap: anywhere;
}
.free-reserve-ghost-open {
  border-color: var(--free);
  background: color-mix(in srgb, var(--free) 14%, transparent);
  color: var(--free);
}
.free-reserve-toggle {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
}
/* Same look as the other toolbar selects (.tb-plan-toolbar select): rounded,
   thin border, own chevron instead of the browser's boxy native control. */
.free-reserve-toggle select {
  appearance: none;
  -webkit-appearance: none;
  padding: 7px 30px 7px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background-color: var(--card);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23888' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  color: var(--ink);
  font-size: 13px;
  font-family: inherit;
  cursor: pointer;
  outline: none;
}
.free-reserve-toggle select:focus {
  border-color: var(--brand);
}
.free-block-moved-tag {
  flex-shrink: 0;
  font-size: 10px;
  color: var(--warn);
}
/* More teams booked on this start time than Mall's matching pass has seats
   for (see freeOverbookedStartTimes) — admin isn't told what to do about
   it, just that something needs a look. box-shadow, not outline/border, so
   it never fights .free-block-cotraining's outline or .free-block-moved's
   left border when more than one applies at once. */
.free-block-overbooked {
  box-shadow: inset 0 0 0 2px var(--warn);
}
.free-block-overbooked-tag {
  flex-shrink: 0;
  font-size: 10px;
  color: var(--warn);
}
/* Overbooked AND the team has an unused reserve wish that currently fits
   elsewhere (see FreeBookingService.ReserveOption.Available) — every team in
   a crowded slot already gets .free-block-overbooked's ring, so this can't
   rely on a ring or icon of its own to stand out (icons get lost once
   several overbooked blocks sit side by side, narrow, at 10px). Instead a
   solid corner dot, in --free (the same colour "lediga pass" uses
   elsewhere), unmissable at any block width — the one clearly different
   thing among otherwise-identical crowded blocks. A white ring keeps it
   visible over the tinted background. Opening the block surfaces the move
   (see freeReserveOptionsHTML's "Flytta hit"). */
.free-block-has-reserve::after {
  content: '';
  position: absolute;
  top: 3px;
  right: 3px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--free);
  box-shadow: 0 0 0 2px #fff;
}
.free-block-reserve-tag {
  flex-shrink: 0;
  font-size: 10px;
  color: var(--free);
}
/* The spelform number (7/9/11) after the team name — ringed so it reads as
   its own tag, not part of the name. flex-shrink:0 (it sits in the flex row
   .free-block-team) keeps it from ever being squeezed by a long name. */
.free-block-format {
  flex-shrink: 0;
  display: inline-block;
  min-width: 16px;
  padding: 0 4px;
  border: 1px solid currentColor;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  line-height: 15px;
  text-align: center;
  color: var(--ink2);
}
.free-block-time {
  color: var(--ink2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.free-block-handle {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 8px;
  cursor: ns-resize;
}

/* Drag ghost: a dimmed, non-interactive outline left behind at the booking's
   original spot while it's being dragged, so where it came from stays
   visible even after the real block has moved (possibly to another plan
   column) and is showing its live new time instead of the old one. */
.free-block-ghost {
  opacity: 0.35;
  border-style: dashed;
  pointer-events: none;
  z-index: 1;
}

/* Origin info shown when editing a booking: where it came from, and (once
   moved) where it started out — see freeOriginInfoHTML. */
.free-origin-info {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 14px;
  padding: 10px 12px;
  border-radius: 10px;
  background: var(--bg);
  border: 1px solid var(--line);
  font-size: 13px;
}
.free-origin-label {
  display: inline-block;
  min-width: 130px;
  font-weight: 700;
  color: var(--ink3);
}
/* Action row under a value (e.g. "Bryt samträning"), lined up with the values. */
.free-origin-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin-left: 130px;
}
.free-origin-actions .btn-small {
  flex: none;
  width: auto;
  margin: 0;
  padding: 5px 12px;
  font-weight: 600;
}
.free-origin-hint {
  font-size: 12px;
  color: var(--ink3);
}
/* The same spelform ring as on the booking block itself (.free-block-format),
   just a size up to match this row's own 13px text. */
.free-origin-format {
  font-size: 12px;
  color: var(--ink);
}

/* A team's own unused reserve wishes in the booking modal (see
   freeReserveOptionsHTML) — one row per option, "Använd istället" flush
   right. */
.free-reserve-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  font-size: 13px;
}
.free-reserve-row + .free-reserve-row {
  margin-top: 4px;
}
/* "Ledig plats"/"Fullbokad" tag on each reserve row (see
   ReserveOption.Available) — --free (open) mirrors .free-block-has-reserve's
   grid badge; --ink3 (full) stays quiet since there's no action to take on
   that row beyond the plain "Använd istället" fill. */
.free-reserve-status {
  margin-left: 8px;
  font-size: 11px;
  font-weight: 600;
}
.free-reserve-status-open {
  color: var(--free);
}
.free-reserve-status-full {
  color: var(--ink3);
}
/* Reuses .conflict-warning's amber box — same meaning as there: this has
   diverged from where it started and hasn't caught up. */
.free-origin-moved {
  margin-bottom: 14px;
}
.free-origin-moved .btn-small {
  display: block;
  margin-top: 8px;
}


/* Önska tider, steps 2 and 3 get the same treatment as step 1 (brand border,
   tinted header, step label, 17px title) so the three read as one sequence. */
.tb-ledare-stepbox {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 16px 18px;
  border: 2px solid var(--brand);
  border-radius: 14px;
  background: var(--brand-tint);
}
.tb-ledare-stepbox > strong { font-size: 17px; }
.tb-ledare-stepbox .tb-wish-help-list { font-size: 13px; color: var(--ink2); line-height: 1.5; }
.tb-ledare-stepbox .tb-ledare-legend { flex-wrap: wrap; margin-top: 6px; }
.tb-ledare-side .tb-card { border: 2px solid var(--brand); padding: 0 16px 16px; overflow: hidden; }
.tb-ledare-side .tb-ledare-wish-header {
  margin: 0 -16px 12px;
  padding: 14px 16px;
  background: var(--brand-tint);
  border-bottom: 2px solid var(--brand);
  align-items: center;
}
.tb-ledare-side .tb-card-title { font-size: 17px; margin-bottom: 0; }
.tb-ledare-side .tb-ledare-wish-count { font-size: 14px; font-weight: 800; color: var(--brand); }

/* Önska tider: step 3 sits under the schedule on every screen size;
   the card spans the width like the other steps, but the submit button keeps
   its own width. */
.tb-ledare-side #bl-submit { width: auto; min-width: 240px; }

/* Önska tider: the schedule frame, its title and the day header are made
   plainly visible so it reads as a weekly calendar to tap in. */
.tb-ledare-grid-title {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 4px 0 -6px;
}
.tb-ledare-grid-title strong { font-size: 17px; }
.tb-ledare-grid-title span { font-size: 13px; color: var(--ink2); }
.tb-ledare-grid {
  border: 2px solid var(--brand);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
}
.tb-ledare-row-head {
  font-size: 13px;
  color: var(--ink);
  padding: 14px 0 6px;
  border-bottom: 2px solid var(--line);
}
/* The day row is the grid's first child, so the first plan head needs its own
   "no top margin" rule (the :first-child one above never matches). */
.tb-ledare-row-head + .tb-ledare-plan-group .tb-ledare-plan-group-head { margin-top: 0; }

/* Names of the other teams that wished for a pass (when the season shows them). */
.tb-ledare-cell-teams { font-size: 11px; color: var(--ink2); line-height: 1.3; overflow-wrap: anywhere; }

/* Logg (log.js) */
.log-body { max-width: 980px; }
.log-filters { display: flex; flex-wrap: wrap; gap: 10px 12px; align-items: flex-end; }
.log-filters .form-field { margin: 0; min-width: 140px; flex: 1 1 140px; }
.log-filter-actions { display: flex; gap: 8px; }
.log-list { display: flex; flex-direction: column; gap: 6px; }
.log-row {
  display: grid;
  grid-template-columns: 150px minmax(0, 190px) minmax(0, 1fr) 120px;
  gap: 4px 12px;
  align-items: start;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--card);
  font-size: 13px;
}
.log-row small { display: block; color: var(--ink3); font-size: 11px; overflow-wrap: anywhere; }
/* Long names/e-mail addresses wrap inside their own column instead of running
   under the next one (a grid item defaults to min-width:auto and won't shrink). */
.log-row > * { min-width: 0; }
.log-who strong, .log-what { overflow-wrap: anywhere; }
.log-time { font-variant-numeric: tabular-nums; color: var(--ink2); }
.log-badge { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 11px; font-weight: 700; }
.log-ok { background: var(--brand-tint); color: var(--brand); }
.log-fail { background: #fde8e0; color: var(--warn); }
.log-pager { display: flex; justify-content: space-between; }
@media (max-width: 700px) {
  .log-row { grid-template-columns: 1fr 1fr; }
  .log-what { grid-column: 1 / -1; }
}
.log-detail { display: block; margin-top: 3px; color: var(--ink2); font-size: 12px; line-height: 1.4; overflow-wrap: anywhere; }
/* Sök / Rensa in Logg: same size and height as the filter fields. The base
   .btn-primary adds a top margin and full width that .btn-small doesn't undo. */
.log-filter-actions .btn-primary,
.log-filter-actions .btn-secondary {
  flex: none;
  width: auto;
  margin: 0;
  padding: 11px 20px;
  font-size: 14px;
  font-weight: 600;
  border-radius: 10px;
}

/* Önska tider: notice shown while submitted wishes are locked / being changed. */
.tb-submitted-note {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px 16px;
  padding: 14px 18px;
  border: 2px solid var(--brand);
  border-radius: 14px;
  background: var(--brand-tint);
}
.tb-submitted-note > div { display: flex; flex-direction: column; gap: 2px; flex: 1 1 260px; }
.tb-submitted-note strong { font-size: 16px; }
.tb-submitted-note span { font-size: 13px; color: var(--ink2); }
.tb-submitted-note .btn-secondary { flex: none; }
.tb-submitted-note { min-height: 84px; }
.tb-submitted-note-submitted { border-color: var(--brand); background: var(--brand-tint); }
.tb-submitted-note-draft { border-color: var(--reserve); background: var(--reserve-tint); }
.tb-submitted-note-editing { border-color: var(--free); background: var(--free-tint); }
.tb-submitted-note-closed { border-color: var(--line); background: var(--bg); }
.tb-submitted-note .btn-secondary { cursor: pointer; }
.tb-submitted-note .btn-secondary:hover { background: var(--brand); border-color: var(--brand); color: #fff; }

/* The 5/7/9/11 badges under a plan are tappable and explain themselves. */
button.plan-sizes {
  border: none;
  background: none;
  padding: 3px 4px;
  margin-top: 0;
  align-items: center;
  cursor: pointer;
  font: inherit;
  border-radius: 999px;
}
button.plan-sizes:hover { background: var(--brand-tint); }
.size-help-hint {
  font-size: 9px;
  font-weight: 800;
  width: 13px;
  height: 13px;
  line-height: 13px;
  text-align: center;
  border-radius: 50%;
  background: var(--brand);
  color: #fff;
}
.size-help-popover {
  position: fixed;
  z-index: 1000;
  max-width: 260px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--card);
  color: var(--ink);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18);
  font-size: 13px;
  line-height: 1.45;
}
.size-help-popover strong { font-size: 14px; }
.size-help-popover p { margin: 4px 0; color: var(--ink2); }
.size-help-popover ul { margin: 4px 0 4px 18px; }

/* Phone held sideways (landscape, short screen): the header would otherwise
   take well over half the height. Everything is squeezed into as few rows as
   possible: no logo, date line or sync line, and the week controls on one row
   (Idag, week arrows, the seven day-pills, calendar) instead of two. */
@media (orientation: landscape) and (max-height: 500px) {
  .header {
    padding: 4px max(12px, env(safe-area-inset-right)) 4px max(12px, env(safe-area-inset-left));
  }
  .header-top { margin-bottom: 4px; }
  .header-brand .brand-logo,
  .header-date,
  .sync-status-line { display: none; }

  .week-grid {
    grid-template-columns: auto auto repeat(7, minmax(34px, 1fr)) auto;
    gap: 4px;
    margin-bottom: 4px;
  }
  .btn-today { grid-row: 1; grid-column: 1; }
  .week-nav-center { grid-row: 1; grid-column: 2; justify-self: start; gap: 6px; }
  .week-number-label { font-size: 14px; white-space: nowrap; }
  .week-grid .week-date-picker { grid-row: 1; grid-column: 10; justify-self: end; }
  .day-pill {
    grid-row: 1;
    gap: 0;
    padding: 2px 0;
    line-height: 1.1;
    min-width: 0;
    border-radius: 8px;
  }
  .day-pill .dow { font-size: 9px; }
  .day-pill .dn { font-size: 13px; }
  .day-dot { display: none; }
  .empty-plans-toggle { margin: 0 0 2px; }
  .period-bar { padding: 2px 0 4px; }
  /* The 5/7/9/11 badges under each plan column header are hidden sideways to save height. */
  button.plan-sizes { display: none; }
}
/* In Steg 3, once submitted, the submit button becomes "Ändra inskickade önskemål". */
.tb-change-submitted-btn { width: 100%; margin-top: 20px; padding: 12px; cursor: pointer; }
.tb-change-submitted-btn:hover { background: var(--brand); border-color: var(--brand); color: #fff; }

/* Användare: one checkbox per role. */
.uf-role-option { display: flex; gap: 10px; align-items: center; padding: 6px 0; cursor: pointer; }

/* Info badge: a small "i" that opens the explanation instead of showing it as text. */
.info-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  margin-left: 6px;
  padding: 0;
  border: 1px solid var(--brand);
  border-radius: 50%;
  background: var(--brand-tint);
  color: var(--brand);
  font: italic 700 12px/1 Georgia, serif;
  cursor: pointer;
  vertical-align: middle;
  flex: none;
}
.info-badge:hover { background: var(--brand); color: #fff; }
.info-popover { z-index: 3000; max-width: 280px; }
.uf-role-row { display: flex; align-items: center; }
.uf-role-row .uf-role-option { flex: 0 1 auto; }

/* Schema → Lag (team-week.js): the Dag | Lag switch, the team picker and one team's week. */
.schedule-mode-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 12px;
  margin: -2px 0 8px;
}
.schedule-mode-bar .empty-plans-toggle { margin: 0; }
.seg-toggle { display: inline-flex; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; background: var(--card); }
.seg-btn { padding: 5px 16px; border: none; background: transparent; font: inherit; font-size: 13px; font-weight: 600; color: var(--ink2); cursor: pointer; }
.seg-btn + .seg-btn { border-left: 1px solid var(--line); }
.seg-btn.active { background: var(--brand); color: #fff; }
#team-view-select, #plan-view-select, #fs-team, #fs-plan { flex: 1 1 200px; min-width: 0; max-width: 360px; padding: 6px 8px; border: 1px solid var(--line); border-radius: 8px; background: var(--card); font: inherit; font-size: 14px; }
/* One team's week has no day to pick, so the day pills go. */
.header.team-mode .day-pill { display: none; }
.header.team-mode .week-grid { margin-bottom: 6px; }
.team-week-empty, .team-week-note { padding: 24px 16px; color: var(--ink2); text-align: center; }
.team-week-note { padding: 10px 16px; font-size: 13px; background: var(--brand-tint); }
.team-week .plan-col, .team-week .plan-col-header { flex: 1 0 96px; min-width: 96px; }
.team-day-today .plan-name { color: var(--brand); }
.team-day-today { background: var(--brand-tint); }
.team-block {
  position: absolute;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 4px 6px;
  overflow: hidden;
  text-align: left;
  font: inherit;
  font-size: 11px;
  line-height: 1.25;
  color: var(--ink);
  background: var(--brand-tint);
  border: 1px solid var(--brand);
  border-radius: 8px;
  cursor: pointer;
}
.team-block-match { background: var(--free-tint); border-color: var(--free); }
.team-block-time { font-weight: 800; font-size: 12px; }
.team-block-where { font-weight: 600; }
.team-block-team { font-weight: 700; }
.team-block-kind { color: var(--ink2); }

/* Träningsgrupp: picking the teams that belong to a group (aliases). */
.alias-pick { display: flex; flex-direction: column; gap: 6px; }
.alias-pick-title { margin-top: 6px; font-size: 12px; font-weight: 700; color: var(--ink2); text-transform: uppercase; letter-spacing: 0.4px; }
.alias-pick-list { max-height: 150px; overflow-y: auto; padding: 6px 8px; border: 1px solid var(--line); border-radius: 8px; background: var(--card); }
.alias-pick-item { display: flex; gap: 8px; align-items: center; padding: 3px 0; font-size: 13px; cursor: pointer; }

/* Träningsgrupper: the linked team names are only shown on a wide screen. */
.tg-aliases { display: none; }
@media (min-width: 900px) {
  .tg-aliases { display: block; overflow-wrap: anywhere; }
}
.alias-search { width: 100%; padding: 8px 10px; border: 1px solid var(--line); border-radius: 8px; font: inherit; font-size: 14px; }

/* Whether a training group is tied to teams (Schema → Lag can find its bookings). */
.link-badge { display: inline-block; margin-left: 6px; padding: 1px 8px; border-radius: 999px; font-size: 11px; font-weight: 700; vertical-align: middle; }
.link-badge-on { background: var(--brand-tint); color: var(--brand); border: 1px solid var(--brand); }
.link-badge-off { background: var(--reserve-tint); color: var(--reserve); border: 1px solid var(--reserve); }
/* Small toggle chips above a list (e.g. Träningsgrupper: Utan ledare / Utan lag). */
.filter-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.filter-chip { padding: 4px 12px; border: 1px solid var(--line); border-radius: 999px; background: var(--card); color: var(--ink2); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; }
.filter-chip-count { font-weight: 700; opacity: .75; }
.filter-chip.warn { border-color: var(--reserve); color: var(--reserve); }
.filter-chip.active { background: var(--brand); border-color: var(--brand); color: #fff; }
/* In the picker a ticked team stands out from the rest. */
.alias-pick-item:has(input:checked) { background: var(--brand-tint); font-weight: 700; border-radius: 6px; padding-left: 4px; }
.alias-pick-count { font-size: 13px; font-weight: 700; }
.alias-pick-count-on { color: var(--brand); }
.alias-pick-count-off { color: var(--reserve); }
.link-breakdown { margin-left: 6px; font-size: 11px; color: var(--ink2); font-weight: 600; }
.alias-search-row { display: flex; gap: 8px; align-items: center; }
.alias-search-row .alias-search { flex: 1 1 auto; min-width: 0; }
.alias-search-row .btn-secondary { flex: none; white-space: nowrap; }

/* "Åtkomst nekad": shown when a view is out of reach, instead of silently landing elsewhere. */
.access-notice {
  position: fixed;
  top: 12px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 4000;
  display: flex;
  flex-direction: column;
  gap: 2px;
  max-width: min(420px, calc(100vw - 24px));
  padding: 12px 18px;
  border: 2px solid var(--warn);
  border-radius: 12px;
  background: #fde8e0;
  color: var(--ink);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.2);
  cursor: pointer;
}
.access-notice strong { color: var(--warn); font-size: 15px; }
.access-notice span { font-size: 13px; }

/* Todo: one selectable mail list per team. */
.todo-team { margin-bottom: 24px; min-width: 0; }
.todo-team h2 { margin: 0 0 10px; font-size: 18px; }
.todo-team-count { color: var(--ink2); font-size: 14px; }
.todo-mail-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; border: 0; padding: 0; margin: 0 0 12px; min-width: 0; }
.todo-mail-controls label { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; }
.todo-recipient { max-width: 100%; min-width: 0; padding: 6px; font: inherit; background: var(--card); color: var(--ink); border: 1px solid var(--line); border-radius: 6px; }
.todo-mail-check { flex-shrink: 0; width: 18px; height: 18px; margin: 3px 8px 0 0; }
.todo-mail-status { margin-bottom: 8px; font-size: 13px; overflow-wrap: anywhere; }
.todo-send:disabled { opacity: .5; cursor: default; }
#todo-refresh { margin-bottom: 16px; }

/* Swedish calendar: fixed Monday-first weeks, independent of browser locale. */
.calendar-date-trigger { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 10px 12px; border: 1px solid var(--line); border-radius: 8px; background: var(--card); color: var(--ink); font: inherit; cursor: pointer; }
.calendar-dialog { width: min(360px, calc(100vw - 24px)); max-height: calc(100dvh - 24px); overflow: auto; padding: 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--card); color: var(--ink); box-shadow: 0 12px 40px #0003; }
.calendar-dialog::backdrop { background: #0006; }
.calendar-heading, .calendar-footer { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.calendar-heading strong { text-transform: capitalize; }
.calendar-dialog button { min-height: 40px; border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--ink); font: inherit; cursor: pointer; }
.calendar-heading button, .calendar-footer button { padding: 6px 12px; }
.calendar-table { width: 100%; table-layout: fixed; border-collapse: collapse; margin: 8px 0; text-align: center; }
.calendar-table th { font-size: 11px; font-weight: 600; }
.calendar-table td { padding: 1px; }
.calendar-table .calendar-week { color: var(--ink2); font-weight: 400; }
.calendar-day { width: 100%; padding: 0; }
.calendar-dialog .calendar-day-outside { color: var(--ink2); }
.calendar-dialog button:hover { background: var(--brand-tint); }
.calendar-dialog .calendar-day[aria-current="date"] { border-color: var(--brand); }
.calendar-dialog .calendar-day[aria-pressed="true"] { background: var(--brand); color: white; }
.calendar-dialog button:focus-visible, .calendar-date-trigger:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
/* Anchor the shared calendar to the opening button instead of centering it. */
.calendar-dialog { position: fixed; inset: auto; margin: 0; }

/* Compact "+" sits on the same row as the game-size badges, so it adds no
   header height. */
.plan-sizes-row { position: relative; display: flex; justify-content: center; align-items: center; align-self: stretch; }
.plan-col-header .planning-add-pass {
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 20px;
  height: 20px;
  min-height: 0;
  padding: 0;
  border-radius: 50%;
  font-size: 14px;
  line-height: 1;
}

/* Phones: columns are too narrow to overlay the "+" on the badges, so it
   flows inline after them (wrapping to its own line if it must). */
@media (max-width: 640px) {
  .plan-sizes-row { flex-wrap: wrap; gap: 2px; }
  .plan-col-header .planning-add-pass {
    position: static;
    transform: none;
    flex: none;
  }
}

/* Plan name under the site name in Planplanering headers, as in the other views. */
.tb-plan-name-row { flex-direction: column; align-items: flex-start; gap: 0; }
.tb-plan-subname { font-size: 13px; font-weight: 600; color: var(--ink2); }

.todo-mailed { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.todo-mailed-badge { display: inline-flex; align-items: center; gap: 4px; padding: 3px 10px; border-radius: 999px; border: 1px solid var(--brand); background: var(--brand-tint); color: var(--brand); font-size: 13px; font-weight: 600; }

/* Bottom-edge grip for shortening a training in Planering. */
.match-block .resize-handle {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 10px;
  cursor: ns-resize;
  touch-action: none;
}
.match-block .resize-handle::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 2px;
  width: 22px;
  height: 3px;
  margin-left: -11px;
  border-radius: 2px;
  background: currentColor;
  opacity: 0.35;
}
.match-block.resizing { user-select: none; z-index: 5; }

.sheet-original { margin-top: 6px; font-size: 13px; }

/* Planplanering schedules scroll like Önska tider' grid: each schedule is its
   own scroll container (both directions) with the day header stuck to the top
   and the time axis stuck to the left. */
.tb-plan-section { overflow-x: visible; }
/* No overscroll-behavior: contain on the scrolling calendars below — it stopped the mouse wheel from scrolling the page whenever the pointer was over one. */
.tb-week {
  min-width: 0;
  overflow: auto;
  max-height: 75vh;
  --tb-week-min: 560px;
}
.tb-week-headrow,
.tb-week-body,
.free-week-body {
  min-width: var(--tb-week-min);
}
.tb-week-headrow {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--card);
  padding-top: 2px;
}
.tb-week-headrow .tb-axis-spacer {
  position: sticky;
  left: 0;
  z-index: 4;
  background: var(--card);
}
.tb-week-body .tb-axis,
.free-week-body .tb-axis {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--card);
}
/* Dag view: every plan a column — scrolls both ways, plan heads stick to the top. */
.free-day-body {
  overflow: auto;
  max-height: 75vh;
}
.free-day-plan-col .free-plan-head {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--card);
}

/* Empty parts of a column pan the schedule by touch (the bookings themselves
   stay touch-action:none so they can still be dragged) — otherwise a swipe
   that starts on a column never scrolls sideways. */
.tb-week .tb-daycol,
.tb-week .free-daycol,
.free-day-body .free-daycol {
  touch-action: pan-x pan-y;
}
/* Önska tider's top banner: season name first, then its period, then status. */
.tb-banner-season { font-size: 18px !important; font-weight: 800; }
.tb-banner-dates { font-size: 14px !important; }
.tb-status-banner b { font-weight: 700; }
/* Mall (Steg 1) in read-only mode (locked after the draw / publishing): the
   weekly grid and quotas stay visible but offer nothing to drag or click. */
.tb-readonly .tb-pass { cursor: default; }
.tb-readonly .tb-pass-handle,
.tb-readonly .tb-stepper-btn { display: none; }
.tb-readonly-note { margin-bottom: 12px; }
/* "Hela planeringen" (full-schedule.js): read-only blocks, own team marked. */
.fs-block { cursor: default; touch-action: pan-x pan-y; }
.fs-partial { border-style: dashed; }
.fs-note { font-size: 12px; color: var(--ink3); margin: 8px 0 0; }
/* Administration → Statistik: visit totals per day (counts only). */
.stats-periods { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.stats-totals { display: flex; flex-wrap: wrap; gap: 12px; }
.stats-total { display: flex; flex-direction: column; min-width: 120px; padding: 10px 14px; border: 1px solid var(--line); border-radius: 10px; background: var(--card); }
.stats-total strong { font-size: 22px; }
.stats-total span { font-size: 12px; color: var(--ink3); }
.stats-legend { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--ink2); margin: 8px 0 4px; }
.stats-dot { display: inline-block; width: 10px; height: 10px; border-radius: 2px; }
.stats-dot-in { background: var(--brand); }
.stats-dot-anon { background: #b9c4bd; margin-left: 8px; }
.stats-chart { display: flex; align-items: flex-end; gap: 2px; height: 120px; padding: 6px 0; border-bottom: 1px solid var(--line); }
.stats-col { flex: 1 1 0; min-width: 2px; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; }
.stats-bar-in { background: var(--brand); }
.stats-bar-anon { background: #b9c4bd; }
.stats-table { width: 100%; margin-top: 14px; border-collapse: collapse; font-size: 13px; }
.stats-table th, .stats-table td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--line); }
.user-teams { margin-top: 2px; }

.stats-clear { margin-left: auto; flex: none; width: auto; padding: 5px 12px; }

/* A blocked button must look blocked: the filled/danger buttons otherwise keep
   their colour when disabled and just seem to ignore the click. */
.btn-danger:disabled,
.btn-secondary:disabled,
.btn-add-small:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  filter: grayscale(0.6);
}
.free-reset-note { align-self: center; }
