/* Colours live in "Round 5 and 6" below (one block per theme). Type, space, radius, shadow and
   motion tokens are in :root here; "Design foundation" at the end of the file restyles the controls, cards, dialogs and tables. */
:root {
  /* Type scale: every font size in this file is one of these. */
  --fs-xs: 12px;   /* labels, table heads, hints */
  --fs-sm: 13px;   /* secondary text, dense tables */
  --fs-md: 14px;   /* buttons, body in cards */
  --fs-base: 15px; /* inputs, body, small titles */
  --fs-lg: 18px;   /* card titles */
  --fs-xl: 24px;   /* page and brand titles */
  --fs-kpi: 36px;      /* key numbers */
  --fs-kpi-lg: 56px;   /* the first row of key numbers */
  /* Tennis, physical and mental in Insights: the charts' area colours (--chart-*, per theme below). */
  --area-tennis: var(--chart-tennis); --area-physical: var(--chart-physical); --area-mental: var(--chart-mental);
  --card-pad: 20px;    /* the one inner padding of every card (14px on phones, below) */
  /* The always-dark header: the hero art under a scrim (--bar-scrim, per theme below; at least 62% dark, so the
     lightest part of the art, its court lines, still gives white text 4.5:1), and the main tabs on their own
     --bar-1 bar under it. The fills, lines and text on both: */
  --on-bar-fill: rgba(255, 255, 255, 0.1);
  --on-bar-line: rgba(255, 255, 255, 0.26);
  --on-bar-text: #ffffff;   /* text and icons on the bar */
  --on-bar-strong: #ffffff; /* their hover and focus */
  --radius: 18px;       /* cards and tiles */
  --radius-sm: 12px;    /* controls, strips, list rows, empty states */
  --radius-lg: 20px;    /* dialogs */
  --radius-pill: 999px; /* chips, tabs */
  --radius-note: 4px;   /* the Identity tab's notes: nearly square corners, like paper */
  --shadow-btn: 0 1px 2px rgba(60, 25, 10, 0.2); /* under a primary button */
  --shadow-note: 0 1px 2px rgba(0, 0, 0, 0.06), 0 8px 16px -10px rgba(0, 0, 0, 0.28);
  /* Spacing scale: one rhythm for gaps, card edges and page gutters. */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px;
  --tap: 44px;          /* the smallest target of a main control (WCAG 2.5.8 asks 24, Apple 44) */
  --tap-sm: 36px;       /* a secondary control: the identity board's marks, its "Add part" line */
  /* Motion: used everywhere; the prefers-reduced-motion: reduce block at the end of the file zeroes every transition and animation. */
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
  --dur-1: 120ms;       /* hover and press */
  --dur-2: 220ms;       /* panels and dialogs */
  --font-ui: "SF Pro Text", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: "SF Pro Display", "Segoe UI Variable Display", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", Arial, sans-serif;
}
@media (max-width: 640px) { :root { --card-pad: 14px; --fs-kpi: 30px; --fs-kpi-lg: 44px; --fs-xl: 22px; } }
* { box-sizing: border-box; }

body {
  margin: 0;
  font-size: var(--fs-base); /* was the browser default of 16px */
  font-family: var(--font-ui);
  font-feature-settings: "cv11", "ss01";
  -webkit-font-smoothing: antialiased;
  line-height: 1.5;
  background: var(--bg);
  color: var(--text);
}

/* ---- Top bar: two parts. The hero (a band of the tournament art, Roland-Garros in light and the US Open in dark,
   under a scrim) holds the name, Log today, the theme button and Account. The main tabs are on their own bar under it
   (--bar-1), as folder tabs: the open one is the colour of the page, so it runs into it. On a phone the hero is the
   brand and buttons only and the tabs move to the bottom bar, below. --hero, --bar-1 and --bar-scrim are set per
   theme in "Round 5 and 6" below. ---- */
.topbar { background: var(--bar-1); color: var(--on-bar-strong); box-shadow: var(--shadow); }
.topbar-hero {
  display: flex; flex-wrap: wrap; align-items: center; column-gap: var(--s5); row-gap: var(--s3);
  min-height: 150px; padding: 22px 28px; /* airy, as before the redesign (Lucas, 2026-10-21) */
  background: var(--bar-scrim), var(--hero) center center / cover no-repeat, var(--bar-1);
}
.brand { display: flex; align-items: center; gap: var(--s3); flex: 1 1 0; min-width: 0; }
.brand h1 { margin: 0; font-size: var(--fs-xl); font-weight: 700; letter-spacing: -0.02em; overflow-wrap: anywhere; }
.brand > div { min-width: 0; } /* a long player name (the header, app.js showOwnerName) wraps */
.tabs { display: flex; gap: var(--s1); flex-wrap: wrap; }
.tab-btn {
  position: relative; background: transparent; border: none; color: var(--text-muted); padding: 0 var(--s4); min-height: var(--tap);
  font-size: var(--fs-md); font-weight: 500; cursor: pointer; border-radius: var(--radius-pill);
  transition: background var(--dur-1), color var(--dur-1);
}
.tab-btn:hover { background: var(--row-hover); color: var(--text); }
.tab-btn.active { background: var(--card-bg); color: var(--text); font-weight: 650; }
/* The main tabs: folder tabs on the --bar-1 bar; the open one is the page's colour with a bar of the accent over it. */
#main-nav { align-items: flex-end; flex-wrap: nowrap; padding: var(--s2) var(--s5) 0; box-shadow: inset 0 1px 0 var(--on-bar-line); }
#main-nav .tab-btn { display: inline-flex; flex: 0 0 auto; align-items: center; color: var(--on-bar-text); border-radius: var(--radius-sm) var(--radius-sm) 0 0; }
#main-nav .tab-btn:hover { background: var(--on-bar-fill); }
#main-nav .tab-btn.active { background: var(--bg); color: var(--text); box-shadow: inset 0 3px 0 var(--accent); }
/* The heading as the player picker (app.js renderSourceSelect): "Sam's boards ▾", the heading's own look. */
.board-pick {
  min-height: 0; /* no max-width: a % of a parent sized by the picker itself came out too narrow; fitPicker caps it */ margin: 0 0 0 -10px; padding: 2px 34px 2px 10px; border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent no-repeat right 10px center;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  color: inherit; font: inherit; letter-spacing: inherit; text-shadow: inherit; cursor: pointer; appearance: none; -webkit-appearance: none;
  text-overflow: ellipsis; /* as wide as the name chosen, with the arrow right after it: app.js fitPicker sets the width */
  font-size: var(--fs-lg); /* a step below the page title: "Victor Ryden's boards ▾" read too big (Erik, 2026-10-22) */
}
/* Someone else's boards without a picker: the same step down, so a long name fits. */
body.readonly .brand h1 { font-size: var(--fs-lg); }
.board-pick:hover { border-color: var(--on-bar-line); background-color: var(--on-bar-fill); }
.board-pick:focus-visible { outline: 2px solid var(--on-bar-strong); outline-offset: 2px; }
.board-pick option { color: #111; font-size: var(--fs-md); font-weight: 400; text-shadow: none; }

main { padding: var(--s5) var(--s4) var(--s6); max-width: 1400px; margin: 0 auto; }

.tab-panel { display: none; }
.tab-panel.active { display: block; }

/* Cards and tiles: quiet, with a soft lift that does not shout. */
.card, .kpi-card {
  background: var(--card-bg);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  border: 1px solid var(--border);
}
.card { padding: var(--card-pad); margin-bottom: var(--s4); }

.filter-bar {
  display: flex;
  gap: 20px;
  align-items: flex-end;
  flex-wrap: wrap;
}
.filter-group { display: flex; flex-direction: column; gap: 6px; min-width: 140px; }
.filter-group label { font-size: var(--fs-xs); color: var(--text-muted); font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; }

select, input[type=text], input[type=number], input[type=file], textarea {
  padding: 9px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  font-size: var(--fs-md);
  font-family: inherit;
  background: #fff;
  color: var(--text);
}

.btn-primary, .btn-ghost, .btn-danger {
  border: none;
  border-radius: var(--radius-sm);
  padding: 0 18px;
  min-height: var(--tap);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  font-size: var(--fs-md);
  font-weight: 600;
  letter-spacing: 0.005em;
  cursor: pointer;
  transition: transform var(--dur-1) var(--ease), background-color var(--dur-1), border-color var(--dur-1), box-shadow var(--dur-1), filter var(--dur-1);
}
.btn-primary { background: var(--accent); color: var(--on-accent); box-shadow: var(--shadow-btn); }
.btn-primary:hover { background: var(--accent-dark); filter: none; box-shadow: var(--shadow); }
.btn-ghost { border: 1px solid var(--control-border); background: var(--card-bg); color: var(--text); }
.btn-ghost:hover { background: var(--accent-soft); border-color: var(--clay); }
.btn-danger { background: var(--loss); color: #fff; }
.btn-danger:hover { filter: brightness(1.08); }
.btn-primary:active, .btn-ghost:active, .btn-danger:active { transform: scale(0.98); }

.kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 16px;
  margin: 20px 0;
}
/* Key numbers: big tabular figures, the first row dominant, an accent line at the foot.
   (Border, background and shadow come from the shared card rule in "Visual refresh".) */
.kpi-card {
  position: relative; overflow: hidden;
  padding: var(--s4) var(--s5) var(--s5);
}
.kpi-card::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: linear-gradient(90deg, var(--accent), transparent 75%); opacity: 0.85;
}
.kpi-card .kpi-label { font-size: var(--fs-xs); color: var(--text-muted); font-weight: 650; text-transform: uppercase; letter-spacing: 0.08em; }
.kpi-card .kpi-value {
  margin-top: var(--s2); font-size: var(--fs-kpi); font-weight: 750; line-height: 1.05; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums; color: var(--text);
}
.kpi-card .kpi-sub { font-size: var(--fs-xs); color: var(--text-muted); margin-top: var(--s2); }
.kpi-grid > .kpi-card:nth-child(-n+3) { padding-top: var(--s5); padding-bottom: var(--s5); }
.kpi-grid > .kpi-card:nth-child(-n+3) .kpi-value { font-size: var(--fs-kpi-lg); }
.kpi-grid > .kpi-card:nth-child(-n+3)::after { height: 4px; }

.charts-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(440px, 1fr));
  gap: 18px;
}
.chart-card h3 { margin: 0 0 14px 0; font-size: var(--fs-base); color: var(--navy); }
.hint { font-weight: 400; color: var(--text-muted); font-size: var(--fs-xs); }
.chart-wrap { position: relative; height: 280px; }
/* The charts' legend (chartTheme.js ttChips): small chips under the chart; a click hides or shows a series. */
.chart-note { margin: var(--s2) 0 0; }
.chart-legend { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--s1) var(--s2); list-style: none; margin: var(--s3) 0 0; padding: 0; }
.chart-legend button {
  display: inline-flex; align-items: center; gap: 6px; max-width: 240px; min-height: 28px; padding: 2px 10px;
  border: 1px solid var(--border); border-radius: 999px; background: var(--surface); color: var(--text);
  font: inherit; font-size: var(--fs-xs); font-weight: 600; cursor: pointer; transition: opacity 0.15s, background 0.15s;
}
.chart-legend button:hover { background: var(--row-hover); }
.chart-legend button[aria-pressed="false"] { opacity: 0.45; }
.chart-legend button[aria-pressed="false"] .chart-chip-text { text-decoration: line-through; }
.chart-chip-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; }
.chart-chip-dot.is-striped { width: 10px; height: 10px; box-sizing: border-box; border-radius: 3px; border: 1.5px solid currentColor; background: repeating-linear-gradient(45deg, currentColor 0 2px, transparent 2px 4px); } /* a loss: striped, as its bars are */
.chart-chip-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (prefers-reduced-motion: reduce) { .chart-legend button { transition: none; } }

.card h2 { margin-top: 0; color: var(--navy); }
.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 14px 16px;
}
.field { display: flex; flex-direction: column; gap: 6px; }
.field label { font-size: var(--fs-sm); font-weight: 600; color: var(--text); }
.field .opt, label .opt { font-weight: 400; color: var(--text-muted); font-size: var(--fs-xs); }
/* A required field in a dialog is marked once, after its label, in the accent (the optional ones say so in .opt).
   data-required: one the script checks instead of the browser (Result: the sets can fill it in, matches.js). */
.entry-dialog .field:has(> :is(input, select, textarea):is(:required, [data-required])) > label::after { content: " *"; font-weight: 700; color: var(--clay-text); }
.field-wide { grid-column: 1 / -1; }
.section-label {
  margin: var(--s3) 0 calc(-1 * var(--s1));
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--clay-text);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  border-bottom: 1px solid var(--border);
  padding-bottom: var(--s2);
}
.section-label .hint { margin-left: var(--s2); white-space: nowrap; text-transform: none; letter-spacing: 0; }

.form-actions { margin-top: 20px; display: flex; align-items: center; gap: 14px; }

.table-toolbar {
  display: flex; align-items: center; gap: 14px; margin-bottom: 14px; flex-wrap: wrap;
}
.count-badge { font-size: var(--fs-xs); color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* The entries table scrolls inside its card, so the header row and the first column stay in view. */
.table-wrap { overflow: auto; max-height: min(72vh, 760px); padding-bottom: var(--s2); border-radius: var(--radius-sm); }
table { width: 100%; border-collapse: collapse; font-size: var(--fs-md); font-variant-numeric: tabular-nums; }
thead th {
  text-align: left; padding: var(--s3); border-bottom: 1px solid var(--control-border);
  color: var(--text-muted); font-size: var(--fs-xs); font-weight: 650; text-transform: uppercase; letter-spacing: 0.08em;
  white-space: nowrap;
}
.table-wrap thead th { position: sticky; top: 0; z-index: 2; background: var(--card-bg); }
/* 44px rows, hairline dividers, no zebra (a hovered row is tinted); a number column (tableCols.js isNumeric) is right-aligned under its header. */
tbody td { height: var(--tap); padding: var(--s2) var(--s3); border-bottom: 1px solid var(--border); white-space: nowrap; }
tbody td.row-actions { padding-block: var(--s1); }
@media (min-width: 768px) {
  :is(td, th).num { text-align: right; }
  th.num .th-inner, th.num .th-sort { justify-content: flex-end; text-align: right; }
}
/* Pinned: the first column (the date) on the left, the Open column (.row-actions, tableCols.js) on the right.
   The group row's first cell spans several columns, so it is not pinned. */
.table-wrap tbody td:first-child:not(.empty-cell), .table-wrap thead tr:not(.group-row) th:first-child { position: sticky; left: 0; background: var(--card-bg); }
@media (min-width: 768px) { /* a column scrolled behind the pinned Date column stops at its edge (tableTools.js scrollCue sets scroll-padding-left), no sliver of it shows */
  .table-wrap { scroll-snap-type: x proximity; }
  .table-wrap thead tr:not(.group-row) th:not(:first-child) { scroll-snap-align: start; }
}
.table-wrap .row-actions { position: sticky; right: 0; background: var(--card-bg); box-shadow: inset 1px 0 0 var(--border); }
/* More columns behind the pinned ones (tableTools.js scrollCue): a shadow on the edge they are behind, so a cut-off column reads as scrollable. */
.table-wrap.more-right .row-actions::before, .table-wrap.more-left :is(tbody td:first-child:not(.empty-cell), thead tr:not(.group-row) th:first-child)::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 14px; pointer-events: none; /* an outer box-shadow of a table cell is not painted */
}
.table-wrap.more-right .row-actions::before { right: 100%; background: linear-gradient(90deg, transparent, var(--scroll-shadow)); }
.table-wrap.more-left :is(tbody td:first-child:not(.empty-cell), thead tr:not(.group-row) th:first-child)::after { right: 0; /* inside: the cell clips what is outside it */ background: linear-gradient(270deg, transparent, var(--scroll-shadow)); }
.table-wrap thead tr:not(.group-row) th:first-child, .table-wrap thead th.row-actions { z-index: 3; }
.table-wrap tbody td:first-child:not(.empty-cell), .table-wrap tbody td.row-actions { z-index: 1; }
.table-wrap tbody tr:hover td:is(:first-child, .row-actions):not(.empty-cell) { background: var(--row-hover); }
tbody td.comments-cell {
  white-space: normal;
  max-width: 280px;
  min-width: 180px;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.4;
}
tbody tr:hover { background: var(--row-hover); }
/* Activity and result cells: a coloured chip (the span is added in tableCols.js drawTable). */
.badge-training { color: var(--court-green); }
.badge-match { color: var(--clay-text); }
.badge-dayoff { color: var(--text-muted); }
.badge-win { color: var(--win); }
.badge-loss { color: var(--loss); }
.cell-chip {
  display: inline-block; padding: var(--s1) var(--s3); border-radius: var(--radius-pill);
  font-size: var(--fs-xs); font-weight: 650; letter-spacing: 0.02em; line-height: 1.5;
  /* Opaque fill and a soft outline: the text keeps its contrast on striped and hovered rows. */
  background: var(--card-bg); border: 1px solid color-mix(in srgb, currentColor 40%, transparent);
}
.row-actions button {
  background: none; border: none; cursor: pointer; font-size: var(--fs-md); padding: 2px 6px; border-radius: 6px;
}
.row-actions button:hover { background: var(--row-hover); }
.muted { color: var(--text-muted); font-size: var(--fs-md); line-height: 1.5; }
/* Nothing to show yet (ui.js emptyState): a picture in the accent tint, what appears here, why it is empty, and the next step. */
.empty-state { display: flex; flex-direction: column; align-items: center; gap: var(--s2); padding: var(--s5) var(--s4); text-align: center; color: var(--text-muted); font-size: var(--fs-md); }
.empty-state h3 { margin: 0; font-size: var(--fs-lg); color: var(--text); }
.empty-state p { margin: 0; max-width: 46ch; line-height: 1.5; }
.empty-state > :is(button, .form-actions) { margin-top: var(--s2); }
.empty-art { width: 88px; height: 56px; margin-bottom: var(--s2); }
.empty-art .tile { fill: var(--accent-soft); }
.empty-art .lines { fill: none; stroke: var(--clay); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; opacity: 0.55; }
.empty-art .ball { fill: var(--clay); }
.empty-art .seam { fill: none; stroke: var(--accent-soft); stroke-width: 1.5; stroke-linecap: round; }
/* The cell spans every column of a wide table: pin it to the left so the message shows without scrolling. */
td.empty-cell { position: sticky; left: 0; padding: 0; white-space: normal; }
td.empty-cell .empty-state { align-items: flex-start; text-align: left; padding: var(--s5) var(--s4); }

/* Sized tables (tableCols.js drawTable): each column has the width of its <col>, resizable from its
   header's right edge (tableTools.js). Headers wrap between words; a cell that does not fit ends in
   "…" and shows whole on hover (its title). */
table.sized { table-layout: fixed; }
/* Scrolling to a column (a new one) keeps it clear of the pinned Date and Open columns; drawTable sets
   their widths (tableLayout.js). */
.table-wrap { scroll-padding-inline: var(--pin-left, 0px) var(--pin-right, 0px); }
table.sized thead tr:not(.group-row) th { white-space: normal; vertical-align: bottom; overflow: visible; }
table.sized thead tr:not(.group-row) th .th-label { display: block; overflow: hidden; text-overflow: ellipsis; }
table.sized tbody td:not(.empty-cell) { overflow: hidden; text-overflow: ellipsis; }
table.sized tbody td.row-actions { overflow: visible; } /* its scroll cue sits outside it */
table.sized .cell-chip { max-width: 100%; overflow: hidden; text-overflow: ellipsis; vertical-align: middle; }

/* The one table's parts (tableTools.js createTable, tableCols.js drawTable). */
/* A header: its name is the sort button (↑ / ↓ when sorted); an extra field's ⋯ renames or removes it. */
.th-inner { display: flex; align-items: flex-end; gap: var(--s1); min-width: 0; }
.th-sort {
  display: flex; align-items: flex-end; gap: var(--s1); min-width: 0; padding: 0; border: 0; background: none; cursor: pointer;
  color: inherit; font: inherit; letter-spacing: inherit; text-transform: inherit; text-align: left;
}
.th-sort:hover, th[aria-sort] .th-sort { color: var(--text); }
.th-sort:focus-visible, .th-menu:focus-visible, .add-col-btn:focus-visible, .tour-link:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; border-radius: 4px; }
/* The arrow sits in the header's right padding, so a sorted name keeps its width. */
.th-arrow { position: absolute; right: 2px; bottom: var(--s2); color: var(--clay-text); pointer-events: none; }
.th-menu { flex: none; min-width: 24px; min-height: 24px; margin-left: auto; margin-right: 12px; position: relative; z-index: 2; padding: 0 var(--s1); border: 0; border-radius: 4px; background: none; color: var(--text-muted); font: inherit; cursor: pointer; line-height: 1.2; }
.th-menu:hover { background: var(--surface); color: var(--text); }
/* The "+" column before Open: adds a column. */
table.sized th.add-col { padding: var(--s2) var(--s1); text-align: center; vertical-align: bottom; }
.add-col-btn {
  width: 32px; height: 32px; border: 1px dashed var(--control-border); border-radius: 999px; background: var(--card-bg);
  color: var(--text-muted); font-size: var(--fs-lg); line-height: 1; cursor: pointer;
}
.add-col-btn:hover { border-color: var(--clay); color: var(--clay-text); }
/* The first row adds an entry; its buttons stay in view however far the table is scrolled sideways. */
.table-wrap tbody tr.add-row td.add-cell { position: static; padding: var(--s2) var(--s3); background: var(--card-bg); overflow: visible; }
.add-row-inner { position: sticky; left: var(--s3); display: flex; flex-wrap: wrap; gap: var(--s2); width: fit-content; }
.add-row-btn { min-height: 36px; border-style: dashed; }
tbody tr.add-row:hover { background: transparent; }
/* A tournament's name in a cell: a click filters the table to it. */
.tour-link {
  max-width: 100%; min-height: 24px; padding: 0; border: 0; background: none; cursor: pointer; color: var(--clay-text); font: inherit;
  text-decoration: underline; text-decoration-color: color-mix(in srgb, currentColor 40%, transparent); text-underline-offset: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: baseline;
}
.tour-link:hover { text-decoration-color: currentColor; }
/* The toolbar: the filters, the count, then search, Tournaments and ⋯ at the right. */
.table-filters { flex: 1 1 360px; min-width: 0; }
.table-filters .filter-bar { margin: 0; align-items: stretch; } /* the chips start at the left */
.icon-btn {
  display: grid; place-items: center; width: var(--tap); height: var(--tap); padding: 0; border: 0; border-radius: 999px;
  background: none; color: var(--text-muted); cursor: pointer;
}
.icon-btn:hover { background: var(--surface); color: var(--text); }
.icon-btn:focus-visible, .search-x:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.search-field:not([hidden]) { display: inline-flex; align-items: center; gap: var(--s1); }
.search-field input { width: min(220px, 52vw); }
.search-x { min-width: 36px; min-height: 36px; border: 0; border-radius: 999px; background: none; color: var(--text-muted); font-size: var(--fs-lg); cursor: pointer; }
.search-x:hover { background: var(--surface); color: var(--text); }
/* With the Tournament filter on: the tournament's dates, record, days and hours. */
.tour-summary { margin: 0 0 var(--s3); padding: var(--s2) var(--s3); border-left: 3px solid var(--clay); border-radius: var(--radius-sm); background: var(--surface); font-size: var(--fs-sm); }
.tour-list { list-style: none; margin: 0 0 var(--s3); padding: 0; display: flex; flex-direction: column; gap: var(--s1); }
.tour-list-btn {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px; width: 100%; min-height: 48px; padding: var(--s2) var(--s3);
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--card-bg); color: var(--text); font: inherit; text-align: left; cursor: pointer;
}
.tour-list-btn:hover { border-color: var(--clay); }
.tour-list-btn:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
/* An entry's own Notes text, first in its Notes section (tableCols.js showDetail). */
.entry-own-note { margin: var(--s2) 0; padding: var(--s2) var(--s3); border-radius: var(--radius-sm); background: var(--surface); }
.entry-own-note p { margin: var(--s1) 0 0; white-space: pre-wrap; overflow-wrap: anywhere; }

/* A row card's words ("Tennis", "h") and additions (a result's sets): only on a phone, below. */
.card-word, .card-extra { display: none; }

/* Phones (< 768px, as the + Log button): every row is a two-line card, the date and the first key field,
   then the other key fields (tableCols.js .card-key); a tap opens it. The header, the "+" column and Open
   are not shown; the page scrolls, and the space under main keeps the last card clear of + Log. */
@media (max-width: 767px) {
  .table-wrap { max-height: none; overflow: visible; }
  .table-wrap table.sized { width: 100% !important; table-layout: auto; }
  .table-wrap table.sized colgroup, .table-wrap table.sized thead { display: none; }
  .table-wrap table.sized tbody { display: block; }
  .table-wrap table.sized tbody tr { display: block; }
  .table-wrap table.sized tbody tr.click-row {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px var(--s3); min-height: var(--tap); padding: var(--s3);
    border-bottom: 1px solid var(--border); cursor: pointer;
  }
  .table-wrap table.sized tbody tr.click-row::after { content: ""; order: 2; flex-basis: 100%; height: 0; }
  .table-wrap table.sized tbody tr.click-row td { display: none; padding: 0; border: 0; position: static; background: none; }
  .table-wrap table.sized tbody tr.click-row td.card-date { display: block; order: 0; font-weight: 650; }
  .table-wrap table.sized tbody tr.click-row td.card-lead { display: block; order: 1; flex: 1 1 0; min-width: 0; }
  .table-wrap table.sized tbody tr.click-row td.card-key:not(.card-lead) { display: block; order: 3; max-width: 100%; color: var(--text-muted); font-size: var(--fs-sm); }
  .table-wrap table.sized tbody tr.click-row td.card-key.blank { display: none; }
  .table-wrap table.sized tbody tr.click-row td:is(.card-lead, .card-key) { white-space: normal; overflow-wrap: anywhere; }
  .table-wrap table.sized tbody tr.click-row .tour-link { white-space: normal; text-align: left; overflow-wrap: anywhere; }
  /* Each value says what it is ("Tennis 2.5 h", tableCols.js drawTable), but a chip (Win, Training) says it already. */
  .table-wrap table.sized tbody tr.click-row td .card-word { display: inline; font-size: var(--fs-xs); }
  .table-wrap table.sized tbody tr.click-row td .card-extra { display: inline; }
  /* "2nd round · vs A. Player · Win 6-4 6-3": a dot between the values under the date line (blank ones are hidden). */
  .table-wrap table.sized tbody tr.click-row td.card-key:not(.card-lead, .blank) ~ td.card-key:not(.card-lead, .blank)::before {
    content: "·"; display: inline-block; width: var(--s4); text-align: center; color: var(--text-muted);
  }
  /* A value that wrapped to a new line has no dot before it: the dot keeps its room (so it does not fit back onto the line above) and the text is shifted into it. */
  .table-wrap table.sized tbody tr.click-row td.card-key.line-start { position: relative; left: calc(-1 * var(--s4)); }
  .table-wrap table.sized tbody tr.click-row td.card-key.line-start::before { visibility: hidden; }
  .table-wrap table.sized tbody tr.click-row td .cell-chip { padding: 0 var(--s2); }
  .table-wrap table.sized tbody td.add-cell, .table-wrap table.sized tbody td.empty-cell { display: block; }
  .add-row-inner { position: static; }
  .table-toolbar .table-tools { margin-left: 0; }
}
/* A 24px hit area inside the header's right edge, with a 2px bar on the edge. */
.col-resize { position: absolute; top: 0; right: 0; bottom: 0; width: 24px; cursor: col-resize; touch-action: none; z-index: 1; }
.col-resize::after {
  content: ""; position: absolute; top: 20%; bottom: 20%; right: 1px; width: 2px; border-radius: 1px;
  background: var(--control-border); opacity: 0; transition: opacity 0.15s;
}
th:hover > .col-resize::after { opacity: 1; }
.col-resize:focus-visible::after, .col-resize.dragging::after { opacity: 1; background: var(--clay); }
.col-resize:focus-visible { outline: 2px solid var(--clay); outline-offset: -2px; border-radius: var(--radius-sm); }
/* A finger cannot hover to find the edge: show the bar. */
@media (pointer: coarse) { .col-resize::after { opacity: 1; } }

/* The table's tools at the right of its toolbar: "+ Add column" and the "…" menu (tableTools.js). */
.table-tools { display: flex; align-items: center; gap: var(--s2); margin-left: auto; }
.table-menu { position: relative; }
.table-menu-btn {
  list-style: none; display: grid; place-items: center; width: 40px; height: 40px; border-radius: 999px;
  cursor: pointer; color: var(--text-muted); font-size: var(--fs-lg); line-height: 1;
}
.table-menu-btn::-webkit-details-marker { display: none; }
.table-menu-btn:hover, .table-menu[open] .table-menu-btn { background: var(--surface); color: var(--text); }
.table-menu-btn:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.table-menu-list {
  position: absolute; right: 0; top: 44px; z-index: 5; display: flex; flex-direction: column; min-width: 200px;
  padding: var(--s1); border-radius: var(--radius-sm); border: 1px solid var(--border); background: var(--card-bg); box-shadow: var(--shadow);
}
.table-menu-list button {
  min-height: 40px; padding: 0 var(--s3); border: 0; border-radius: var(--radius-sm); background: none; color: var(--text);
  font: inherit; font-size: var(--fs-md); text-align: left; cursor: pointer;
}
.table-menu-list button:hover { background: var(--surface); }
.table-menu-list button:focus-visible { outline: 2px solid var(--clay); outline-offset: -2px; }
.add-column-dialog p { margin: 0; }
.add-column-dialog .msg:empty { display: none; }
.add-column-boards { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); margin: 0; padding: var(--s2) var(--s3) var(--s3); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.add-column-boards legend { padding: 0 var(--s1); font-size: var(--fs-md); color: var(--text-muted); }
.stack .add-column-boards label { flex-direction: row; align-items: center; gap: var(--s2); min-height: 40px; }

.toast {
  position: fixed; bottom: 24px; right: 24px; background: var(--navy); color: #fff;
  padding: 12px 20px; border-radius: 10px; box-shadow: var(--shadow); font-size: var(--fs-md);
  opacity: 0; transform: translateY(10px); transition: all 0.25s; pointer-events: none; z-index: 100;
}
.toast.show { opacity: 1; transform: translateY(0); pointer-events: auto; } /* auto: the Undo button must take clicks */
.btn-primary:disabled { opacity: 0.55; cursor: not-allowed; }

.analysis-table { width: 100%; border-collapse: collapse; margin-top: 6px; }
.analysis-table th, .analysis-table td { text-align: left; padding: var(--s3); border-bottom: 1px solid var(--border); white-space: nowrap; }
.analysis-table th { color: var(--text-muted); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.06em; }
.analysis-table tr:last-child td { border-bottom: none; }
.analysis-table-wrap { overflow-x: auto; }
.analysis-grid {
  display: grid;
  /* min(): on a phone the column is the card's width, not 320px wider than the screen. */
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
  gap: 18px;
}

/* Insights comparison cards (insights.js): small cards in a grid; Balance and By surface span it. */
.insight-hint { margin: 0 0 var(--s3); font-size: var(--fs-sm); }
.insight-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); gap: var(--s4); margin-bottom: var(--s4); }
.insight-grid > .card { margin-bottom: 0; min-width: 0; }
.insight-wide { grid-column: 1 / -1; }
.insight-card h3 { margin: 0 0 var(--s3); }
.insight-card h3:has(+ .insight-card-hint) { margin-bottom: var(--s1); }
.insight-card-hint { margin: 0 0 var(--s3); }
.insight-groups { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s3); }
.insight-groups li { display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--s3); row-gap: var(--s1); align-items: baseline; }
.insight-label { font-size: var(--fs-md); overflow-wrap: anywhere; }
.insight-value { font-size: var(--fs-lg); font-variant-numeric: tabular-nums; }
.insight-few .insight-value { color: var(--text-muted); font-weight: 400; }
.insight-n { grid-column: 1 / -1; font-size: var(--fs-xs); color: var(--text-muted); }
.insight-bar { grid-column: 1 / -1; height: 6px; border-radius: 999px; background: var(--border); overflow: hidden; }
.insight-bar span { display: block; height: 100%; background: var(--chart-win); }
.insight-say { margin: var(--s3) 0 0; font-size: var(--fs-md); }
.insight-caveat { margin: var(--s1) 0 0; font-size: var(--fs-xs); color: var(--text-muted); }
.insight-card .muted.small { margin: var(--s2) 0 0; }
.insight-table th, .insight-table td { padding: var(--s2) var(--s3); font-variant-numeric: tabular-nums; }
.insight-target td { font-weight: 600; }
.insight-stack { display: flex; width: 96px; height: 6px; border-radius: 999px; overflow: hidden; background: var(--border); margin-top: var(--s1); }
.insight-area-tennis { background: var(--area-tennis); }
.insight-area-physical { background: var(--area-physical); }
.insight-area-mental { background: var(--area-mental); }
.insight-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: var(--s1); }
.compare-form { display: flex; flex-wrap: wrap; gap: var(--s3); margin: var(--s3) 0; }
.compare-form label { display: flex; flex-direction: column; flex: 1 1 140px; min-width: 0; font-size: var(--fs-sm); font-weight: 600; }
.compare-form select, .compare-form input { width: 100%; box-sizing: border-box; }
/* On a phone the share goes under the hours, so Balance fits without scrolling. */
@media (max-width: 400px) { .compare-form label { flex-basis: 100%; } }
@media (max-width: 600px) { .insight-table td .muted { display: block; } .insight-table th, .insight-table td { padding: var(--s2); } .insight-stack { width: 64px; } }

/* ---- Added for the multi-user version ------------------------------------ */
[hidden] { display: none !important; }
body.readonly .owner-only { display: none !important; }
body.coach .self-only { display: none !important; }

.screen-wrap { min-height: 100vh; display: flex; align-items: flex-start; justify-content: center; padding: 40px 16px; box-sizing: border-box; }
.auth-card { width: 100%; max-width: 480px; }
.auth-card h1 { font-size: var(--fs-xl); color: var(--navy); margin: 0; }
.auth-card h2 { margin-top: 0; color: var(--navy); }
.brand-row { display: flex; align-items: center; gap: 10px; }
.auth-tabs { display: flex; gap: 6px; margin: 16px 0; flex-wrap: wrap; }
.auth-tab { border: 1px solid var(--control-border); background: transparent; color: var(--navy); padding: 8px 12px; border-radius: 999px; cursor: pointer; font: inherit; font-size: var(--fs-md); }
.auth-card form, .stack { display: flex; flex-direction: column; gap: 12px; }
.auth-card label, .stack label { display: flex; flex-direction: column; gap: 6px; font-size: var(--fs-md); color: var(--text); }
.auth-card .field label { display: block; }
.auth-card input, .stack input, .form-grid-inline input, .form-grid-inline select {
  padding: 10px 12px; border-radius: 10px; border: 1px solid var(--border); font: inherit; font-size: var(--fs-base);
}
.consent { flex-direction: row !important; align-items: flex-start; gap: 10px !important; font-size: var(--fs-sm) !important; line-height: 1.45; }
.consent input { margin-top: 3px; }
/* Create account's "I am" and Share's "What they can do": a plain group of radio choices. */
.signup-kind, .share-access { display: flex; flex-direction: column; gap: var(--s2); margin: 0; padding: 0; border: 0; min-width: 0; }
.signup-kind legend, .share-access legend { padding: 0; margin-bottom: var(--s2); font-size: var(--fs-sm); font-weight: 600; }
.small { font-size: var(--fs-sm); }
.msg { min-height: 1.2em; font-size: var(--fs-md); color: var(--court-green); margin: 4px 0 0; }
.msg.error { color: var(--loss); }

.readonly-banner { background: #fff4d6; color: #6b4a00; padding: 10px 16px; margin: 12px auto 0; max-width: 1200px; border-radius: 10px; font-size: var(--fs-md); box-sizing: border-box; }
.view { max-width: 1200px; margin: 0 auto; }
#board-root { max-width: 1200px; margin: 0 auto; }

.warning { background: #fdecec; border: 1px solid #f3b8b8; color: #7a1f1f; padding: 12px 14px; border-radius: 10px; font-size: var(--fs-md); margin: 10px 0 14px; }
.form-grid-inline { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin: 10px 0; }
.share-row { display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 12px 0; border-bottom: 1px solid var(--border); align-items: center; }
.share-main { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.share-code { font-size: var(--fs-sm); word-break: break-all; }
.share-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.share-actions button { padding: 8px 12px; font-size: var(--fs-sm); }

fieldset { border: 1px solid #e3e7ee; border-radius: 10px; padding: 8px 12px; margin: 0; }
legend { font-size: var(--fs-sm); color: #5b6676; padding: 0 4px; }

/* ==========================================================================
   Visual refresh. (Shape, type and spacing tokens: top of the file. Colours: Round 5 and 6.)
   ========================================================================== */

h1, h2, h3, .brand h1, .kpi-card .kpi-value {
  font-family: var(--font-display);
  letter-spacing: -0.02em;
  text-wrap: balance;
}
h2 { font-size: var(--fs-lg); font-weight: 700; }
h3 { font-size: var(--fs-base); font-weight: 700; }

/* Form controls: a little more air; the focus ring is the accent ("Design foundation" at the end). */
select, input[type=text], input[type=number], input[type=email], input[type=password],
input[type=date], input[type=file], textarea, input:not([type=checkbox]):not([type=radio]) {
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--control-border);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
  font-family: var(--font-ui);
}

.chart-card h3 { color: var(--text); }

/* Status surfaces that were light-only before. */
.readonly-banner { background: var(--warn-bg); color: var(--warn-fg); border: 1px solid var(--border); }
.warning { background: var(--danger-bg); border-color: var(--danger-border); color: var(--danger-fg); }
.share-code { background: var(--surface); color: var(--text); border: 1px solid var(--border); }

fieldset { border-color: var(--border); }
legend { color: var(--text-muted); }

.auth-tab.active { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.toast { background: var(--navy); color: var(--bg); }

/* Phones: keep the gutter (the main tabs scroll sideways in their bottom bar, "Round 7"). */
@media (max-width: 640px) {
  .charts-grid { grid-template-columns: 1fr; }
  .chart-wrap { height: 240px; }
}

/* Sub-tabs (Dashboard, Insights, ...): a quiet text row under the pills; the open one is ink with a 2px accent underline. */
.board-tabs {
  display: flex; width: auto; max-width: 100%;
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--s5);
  gap: var(--s2);
}
.board-tabs .tab-btn { position: relative; color: var(--text-muted); border-radius: var(--radius-sm) var(--radius-sm) 0 0; padding: 0 var(--s3); font-weight: 600; display: inline-flex; align-items: center; }
.board-tabs .tab-btn:hover { background: var(--accent-soft); color: var(--text); }
.board-tabs .tab-btn.active { background: none; color: var(--text); font-weight: 700; }
.board-tabs .tab-btn.active::after { content: ""; position: absolute; left: var(--s2); right: var(--s2); bottom: -1px; height: 2px; border-radius: 2px; background: var(--accent); }

.card h2 { margin-bottom: var(--s1); }
.card h2 + p, .card h2 + .muted { margin-top: 0; margin-bottom: var(--s3); }
.form-grid-inline { gap: var(--s3); align-items: center; }
.form-grid { gap: var(--s4); }
.share-row {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: var(--s3); padding: var(--s3) 0; border-bottom: 1px solid var(--border);
}
.share-row:last-child { border-bottom: none; }
.share-main { display: flex; flex-direction: column; gap: var(--s1); min-width: 0; }
.share-actions { display: flex; gap: var(--s2); flex-wrap: wrap; }
.charts-grid { gap: var(--s4); }
.stack { gap: var(--s3); }
.readonly-banner { margin: var(--s3) var(--s5) 0; max-width: none; }
@media (max-width: 640px) {
  main { padding: var(--s4) var(--s4) 56px; }
  .card { margin-bottom: var(--s4); }
  .readonly-banner { margin: var(--s3) var(--s4) 0; }
}

/* ---- Logo ---- */
.auth-card .brand-row { display: flex; align-items: center; gap: var(--s3); }

/* ==========================================================================
   Round 3: summary grid
   ========================================================================== */

.redeem-row { display: grid; grid-template-columns: 1fr auto; gap: var(--s3); align-items: center; }
.redeem-row input { min-width: 0; }

/* Summary grid: three columns of three on desktop, two on phones with the odd card spanning. */
.kpi-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 640px) {
  .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .kpi-grid > .kpi-card:last-child:nth-child(odd) { grid-column: span 2; }
}
@media (max-width: 980px) and (min-width: 641px) {
  .kpi-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* ==========================================================================
   Round 5 and 6: tournament hero and whole-app palettes (the only place colours are defined).
   Light = Roland-Garros: clay terracotta on warm paper, white court lines, the dark green of the court surround;
   the hero is the clay court. Dark = the US Open: hard-court blue on deep navy, sky blue and the yellow ball;
   the hero is the hard court. Another hue only where a signal needs it (win green, loss red, gold, the charts' series).
   --accent / --on-accent: primary buttons and the active sub-tab. --bar-1: the main tabs' bar (and the phone's bottom bar).
   --control-border: form and button borders (3:1 on --surface). --row-hover is opaque
   because sticky table cells sit on it.
   The dark block is written twice, once for System (media query) and once for an explicit
   choice, because a media query cannot share a selector with the attribute rule.
   ========================================================================== */
:root, :root[data-theme="light"] {
  --hero: url("hero-roland-garros.svg");
  --bar-scrim: linear-gradient(90deg, rgba(20, 10, 5, 0.84) 0%, rgba(20, 10, 5, 0.7) 55%, rgba(20, 10, 5, 0.62) 100%);
  --bg: #f8f2e8;
  --surface: #f3ece0;
  --card-bg: #ffffff;
  --text: #2a1a12;
  --text-muted: #6b5a4d;
  --border: #e6d9c8;
  --control-border: #968470;
  --row-hover: #f1e8da;
  --navy: #2a1a12;
  --bar-1: #14402f;          /* the court surround's dark green */
  --clay: #c4481c;
  --clay-text: #a23a15;      /* clay as text: 6.7:1 on white */
  --accent: #c4481c;
  --accent-dark: #a23a15;
  --accent-soft: #fbe9e0;    /* a tint for chips, selections and the input glow */
  --on-accent: #ffffff;
  --toast-action: #f2a47a;
  --court-green: #0f6b4f;
  --win: #0f6b4f;
  --loss: #b23b3b;
  --amber: #a87a05;         /* the yellow check-in dot: 3.6:1 on white */
  /* Charts (chartTheme.js reads these; a theme switch recolours drawn charts). --chart-1..8: categories,
     in this order; the areas and win / loss are the same in every chart, Insights and the check-ins. */
  --chart-1: #2b5cad; --chart-2: #e0612f; --chart-3: #0f8fa6; --chart-4: #c27a0e;
  --chart-5: #7b5ce0; --chart-6: #d4467e; --chart-7: #6a9a1f; --chart-8: #6b7a90;
  --chart-tennis: #e0612f; --chart-physical: #0f8fa6; --chart-mental: #7b5ce0;
  --chart-win: var(--win); --chart-loss: var(--loss);
  --warn-bg: #fff1d6;
  --warn-fg: #6b4a00;
  --danger-bg: #fbe9e4;
  --danger-border: #efb8a7;
  --danger-fg: #7a2412;
  --ok-bg: #e6f2ec;
  --shadow: 0 1px 2px rgba(60, 25, 10, 0.05), 0 6px 20px rgba(60, 25, 10, 0.05);
  --shadow-lift: 0 2px 4px rgba(60, 25, 10, 0.06), 0 12px 32px rgba(60, 25, 10, 0.1);
  --overlay: rgba(31, 22, 17, 0.4); /* behind a dialog: 40% ink */
  --shadow-dialog: var(--shadow-lift);
  --scroll-shadow: rgba(60, 25, 10, 0.3); /* the edge of a table with more columns behind it */
  color-scheme: light;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --hero: url("hero-us-open.svg");
    --bar-scrim: linear-gradient(90deg, rgba(4, 12, 26, 0.84) 0%, rgba(4, 12, 26, 0.7) 55%, rgba(4, 12, 26, 0.62) 100%);
    --bg: #0b1522;
    --surface: #101e31;
    --card-bg: #14243a;
    --text: #e6eef9;
    --text-muted: #a3b4ca;
    --border: #22364f;
    --control-border: #5a7396;
    --row-hover: #1a2d46;
    --navy: #e6eef9;
    --bar-1: #0b2a4a;
    --clay: #3f7fc6;
    --clay-text: #7fb3f2;     /* the accent as text: 6.6:1 on cards */
    --accent: #3573b8;        /* US Open blue; white text on it: 4.89:1 */
    --accent-dark: #2b63a8;   /* hover */
    --accent-soft: #15304f;
    --on-accent: #ffffff;
    --toast-action: #1f5fae;  /* the toast is a light bar in dark */
    --court-green: #3fbf8a;
    --win: #3fbf8a;
    --loss: #ef7a7a;
    --amber: #e2b341;
    --chart-1: #5b9dff; --chart-2: #c4e4ff; --chart-3: #3cc6dc; --chart-4: #f5c04a;
    --chart-5: #a990ff; --chart-6: #f47bb0; --chart-7: #a8d84a; --chart-8: #9fb0c8;
    --chart-tennis: #e8d44d; --chart-physical: #3cc6dc; --chart-mental: #a990ff; /* tennis: the yellow ball */
    --chart-win: var(--win); --chart-loss: var(--loss);
    --warn-bg: #33290f;
    --warn-fg: #f2e2ae;
    --danger-bg: #3a1c1c;
    --danger-border: #6b2f2f;
    --danger-fg: #f5c2c2;
    --ok-bg: #123325;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px rgba(0, 0, 0, 0.2);
    --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.3), 0 12px 32px rgba(0, 0, 0, 0.35);
    --overlay: rgba(2, 6, 14, 0.6);
    --scroll-shadow: rgba(0, 0, 0, 0.5);
    --shadow-dialog: var(--shadow-lift), 0 0 0 1px rgba(255, 255, 255, 0.04);
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --hero: url("hero-us-open.svg");
  --bar-scrim: linear-gradient(90deg, rgba(4, 12, 26, 0.84) 0%, rgba(4, 12, 26, 0.7) 55%, rgba(4, 12, 26, 0.62) 100%);
  --bg: #0b1522;
  --surface: #101e31;
  --card-bg: #14243a;
  --text: #e6eef9;
  --text-muted: #a3b4ca;
  --border: #22364f;
  --control-border: #5a7396;
  --row-hover: #1a2d46;
  --navy: #e6eef9;
  --bar-1: #0b2a4a;
  --clay: #3f7fc6;
  --clay-text: #7fb3f2;     /* the accent as text: 6.6:1 on cards */
  --accent: #3573b8;        /* US Open blue; white text on it: 4.89:1 */
  --accent-dark: #2b63a8;   /* hover */
  --accent-soft: #15304f;
  --on-accent: #ffffff;
  --toast-action: #1f5fae;  /* the toast is a light bar in dark */
  --court-green: #3fbf8a;
  --win: #3fbf8a;
  --loss: #ef7a7a;
  --amber: #e2b341;
  --chart-1: #5b9dff; --chart-2: #c4e4ff; --chart-3: #3cc6dc; --chart-4: #f5c04a;
  --chart-5: #a990ff; --chart-6: #f47bb0; --chart-7: #a8d84a; --chart-8: #9fb0c8;
  --chart-tennis: #e8d44d; --chart-physical: #3cc6dc; --chart-mental: #a990ff; /* tennis: the yellow ball */
  --chart-win: var(--win); --chart-loss: var(--loss);
  --warn-bg: #33290f;
  --warn-fg: #f2e2ae;
  --danger-bg: #3a1c1c;
  --danger-border: #6b2f2f;
  --danger-fg: #f5c2c2;
  --ok-bg: #123325;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px rgba(0, 0, 0, 0.2);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.3), 0 12px 32px rgba(0, 0, 0, 0.35);
  --overlay: rgba(2, 6, 14, 0.6);
  --scroll-shadow: rgba(0, 0, 0, 0.5);
  --shadow-dialog: var(--shadow-lift), 0 0 0 1px rgba(255, 255, 255, 0.04);
  color-scheme: dark;
}

/* ==========================================================================
   Round 7: smoother use. Undo toast, links, phone navigation and layout.
   ========================================================================== */
.toast { display: flex; align-items: center; gap: var(--s3); }
/* --clay-text and --toast-action: text-only colours (Round 6); the toast is a light bar in dark and a dark bar in light. */
.toast-action {
  border: none; background: transparent; color: var(--toast-action); font: inherit; font-weight: 700;
  padding: 4px 8px; border-radius: 8px; cursor: pointer;
}
.toast-action:hover { background: rgba(255, 255, 255, 0.12); }
.link-btn {
  border: none; background: transparent; color: var(--clay-text); font: inherit; font-size: var(--fs-md);
  padding: 4px 0; cursor: pointer; text-align: left; align-self: flex-start;
}
.link-btn:hover { text-decoration: underline; }
/* "Find on UTR ↗" under Opponent UTR (matches.js): a quiet link in the links' colour. */
.utr-find { color: var(--clay-text); font-size: var(--fs-xs); align-self: flex-start; text-decoration: none; }
.utr-find:hover { text-decoration: underline; }

@media (max-width: 640px) {
  /* Main tabs move to a thumb-reachable bar at the bottom of the screen: pills that scroll sideways if
     they do not fit (the first and last fade out at the edge; at rest they sit inside the 12px gutter). */
  #main-nav {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
    margin: 0; padding: var(--s2) var(--s3) calc(var(--s2) + env(safe-area-inset-bottom, 0px));
    background: var(--bar-1); box-shadow: 0 -1px 0 var(--on-bar-line);
    align-items: stretch; gap: var(--s1); overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
    --fade-x: linear-gradient(90deg, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%); /* a mask: only its alpha counts */
    -webkit-mask-image: var(--fade-x); mask-image: var(--fade-x);
  }
  #main-nav::-webkit-scrollbar { display: none; }
  #main-nav .tab-btn {
    flex: 1 0 auto; justify-content: center; padding: 0 6px; font-size: var(--fs-sm); border-radius: var(--radius-pill); /* six tabs fit a 360px phone; narrower, the bar scrolls under its edge fade */
  }
  #main-nav .tab-btn.active { box-shadow: none; }
  #main-nav .tab-btn.active .nav-badge { box-shadow: 0 0 0 2px var(--bg); }
  main { padding-bottom: 104px; }
  .topbar-hero { min-height: 128px; padding: 20px var(--s4); column-gap: var(--s3); }
  .topbar .brand h1 { font-size: var(--fs-lg); }
  .topbar .board-pick, body.readonly .topbar .brand h1 { font-size: var(--fs-base); }
  /* The heading gets the whole row and the buttons their own under it, not squeezed beside Log today (round 9, finding 6). */
  .brand { flex: 1 1 100%; }
  .topbar-links { gap: var(--s1); flex: 1 1 100%; margin-left: 0; }
  .topbar-links .account-link { margin-left: auto; }
  .topbar-hero { row-gap: var(--s3); }

  /* Sub-tabs scroll sideways rather than wrapping into a pile. */
  .board-tabs { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .board-tabs::-webkit-scrollbar { display: none; }
  .board-tabs .tab-btn { flex: 0 0 auto; }

  /* Filters and tables get room to breathe on small screens. */
  .filter-bar { gap: var(--s3); }
  .filter-group { min-width: 0; flex: 1 1 140px; }
  .row-actions button { min-width: 36px; min-height: 36px; }
  .share-row { align-items: flex-start; }
  .share-actions { width: 100%; }
  .share-actions .btn-ghost, .share-actions .btn-danger { flex: 1 1 auto; }
}

/* ==========================================================================
   Round 8: one size for controls, one rhythm for spacing.
   Controls share a 44px height (--tap); buttons, selects and inputs line up on the
   same baseline; cards and rows sit on the 4px scale.
   ========================================================================== */
select, input[type=text], input[type=number], input[type=email], input[type=password],
input[type=date], input[type=search], textarea, input:not([type=checkbox]):not([type=radio]) {
  min-height: var(--tap);
  padding: 0 12px;
  font-size: var(--fs-base);
  line-height: 1.2;
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
}
/* Text areas start at three lines and grow with what is typed (where the browser can). */
textarea { padding: 10px 12px; min-height: calc(3lh + 20px); field-sizing: content; max-height: 60dvh; resize: vertical; }
.filter-bar { gap: var(--s4); align-items: flex-end; }
.filter-group { gap: var(--s2); }
.filter-group label { line-height: 1.2; }
.kpi-grid { gap: var(--s4); margin: var(--s3) 0 var(--s4); }
.share-row { padding: var(--s3) 0; }
@media (max-width: 640px) {
  .filter-bar { gap: var(--s3); }
}

.filter-bar > button, .filter-bar .btn-ghost { min-width: 140px; }

.reminder {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3); flex-wrap: wrap;
  /* Same width as the page content below (#board-root is 1200px), not the full window. A calm card in the accent tint. */
  width: min(1200px, calc(100% - 2 * var(--s5))); margin: var(--s4) auto 0; box-sizing: border-box;
  padding: var(--s3) var(--s4); border-radius: var(--radius);
  background: var(--accent-soft); color: var(--text); border: 0;
}
.reminder .btn-primary { min-height: 36px; }
/* A quiet reminder (round 7 #8: the e-mail line on a new player's first screen pushed Start here down): small, not the green banner. */
.reminder-quiet { margin-top: var(--s2); padding: var(--s2) var(--s3); background: var(--surface); border-left: 3px solid var(--amber); font-size: var(--fs-sm); gap: var(--s1) var(--s3); }
.reminder-quiet .verify-actions .btn-ghost { min-height: 32px; }
@media (max-width: 640px) { .reminder { width: calc(100% - 2 * var(--s4)); } }

/* ---- Dropdowns: one arrow, one inset, room for the text ---- */
select {
  -webkit-appearance: none;
  appearance: none;
  padding: 0 40px 0 14px;
  background-color: var(--surface);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%238a7b70' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 12px 8px;
  text-overflow: ellipsis;
  white-space: nowrap;
  overflow: hidden;
  cursor: pointer;
}
select:disabled { opacity: 0.55; cursor: not-allowed; }
/* Keep the label above a select at the same distance everywhere. */
label > select, .filter-group select { margin-top: var(--s1); }
.filter-group label, .field label { margin-bottom: 0; }
option { padding: 6px 10px; }

/* ---- Import / export card ---- */
.io-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); margin-bottom: var(--s4); }
.io-row select { min-width: 160px; }
.io-import { display: flex; flex-direction: column; gap: var(--s2); padding-top: var(--s4); border-top: 1px solid var(--border); }
@media (max-width: 640px) {
  .io-row button, .io-row select { flex: 1 1 100%; }
}

/* Account page (Lucas, 2026-10-18: short and calm): one narrow column, one vertical rhythm. Blocks
   are spaced by a flex gap, so a hidden card never leaves a double or missing gap. */
#view-account { display: flex; flex-direction: column; gap: var(--s4); max-width: 720px; }
#view-account > :not(dialog) { margin: 0 !important; }
#view-account h2 { margin-top: 0; }
#view-account h3 { margin: var(--s5) 0 var(--s2); font-size: var(--fs-base); }
#view-account .card > :last-child { margin-bottom: 0; }

/* Folded parts (join with an invite, delete account, the admin's list): a quiet summary line with a › marker. */
.fold > summary { cursor: pointer; color: var(--text-muted); font-size: var(--fs-md); min-height: 36px; display: flex; align-items: center; gap: var(--s2); list-style: none; }
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary::before { content: "›"; display: inline-block; width: 1ch; font-size: var(--fs-lg); line-height: 1; transition: transform 0.15s; }
.fold[open] > summary::before { transform: rotate(90deg); }
.fold > summary:hover { color: var(--text); }
.fold[open] > summary { margin-bottom: var(--s3); }
.fold > summary:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; border-radius: 4px; }
details.card.fold > summary { font-size: var(--fs-base); font-weight: 600; color: var(--text); }

/* Ideas & feedback: Idea / Good / Bad / Bug as radio chips. */
.fb-kinds { display: flex; flex-wrap: wrap; gap: var(--s2); border: 0; padding: 0; margin: 0; }
.fb-chip { display: inline-flex !important; flex-direction: row !important; align-items: center; min-height: 36px; padding: 0 var(--s3); cursor: pointer; background: var(--card-bg); color: var(--text); font-size: var(--fs-md) !important; }
.fb-chip input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.fb-chip:hover { border-color: var(--clay); }
.fb-chip:has(input:checked) { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }
.fb-chip:has(input:focus-visible) { outline: 2px solid var(--clay); outline-offset: 2px; }
.fb-kind { display: inline-block; padding: 0 8px; font-size: var(--fs-xs); font-weight: 600; color: var(--text-muted); }
.my-requests { margin-top: var(--s3); }
.my-requests h3 { margin-top: var(--s3) !important; color: var(--text-muted); font-size: var(--fs-sm) !important; }
.my-requests .share-row { padding: var(--s2) 0; font-size: var(--fs-md); }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ---- Goals and chips ------------------------------------------------------- */
.chip-row { display: flex; flex-wrap: wrap; gap: var(--s2); }
.chip { border: 1px solid var(--border); border-radius: var(--radius-pill); padding: var(--s1) var(--s3); font-size: var(--fs-sm); }
.chip small { color: var(--text-muted); }
.link-btn { font-size: var(--fs-xs); margin-left: 6px; }

.goal-head { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--s2); }
.goal-head strong { overflow-wrap: anywhere; }
.pill { font-size: var(--fs-xs); font-weight: 600; border-radius: var(--radius-pill); padding: var(--s1) var(--s3); white-space: nowrap; border: 1px solid var(--border); }
.pill-on, .pill-done { color: var(--court-green); border-color: currentColor; }
.pill-off, .pill-overdue { color: var(--clay-text); border-color: currentColor; }
.pill-nodata, .pill-open { color: var(--text-muted); }
.goal-bar { height: 6px; border-radius: 999px; background: var(--border); overflow: hidden; }
.goal-bar span { display: block; height: 100%; background: var(--court-green, #0f6b4f); }
.goal-off .goal-bar span { background: var(--clay); }
.checkins { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; font-size: var(--fs-md); }
.checkin-row { display: flex; gap: var(--s2); flex-wrap: wrap; }
.checkin-row input { flex: 1 1 180px; min-width: 0; }
.goal-actions { display: flex; justify-content: space-between; gap: var(--s2); flex-wrap: wrap; margin-top: auto; }

/* ---- Filter bar (any field) ------------------------------------------------ */
.filter-bar { display: flex; flex-direction: column; gap: var(--s3); }
.filter-add { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: flex-end; }
.filter-add label { display: flex; flex-direction: column; gap: 4px; font-size: var(--fs-sm); color: var(--text-muted); }
.filter-editor { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
.filter-editor input { width: 120px; }

/* ---- Who is signed in, and the read-only banner ----------------------------- */
/* Account's top row: who is signed in (name, e-mail, role). */
.account-who { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s3); margin: 0 0 var(--s4); font-size: var(--fs-sm); overflow-wrap: anywhere; }
.account-who strong { font-size: var(--fs-base); }
.account-who-email { color: var(--text-muted); }
.account-who-role { background: var(--card-bg); }
.readonly-banner { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; justify-content: space-between; }

/* ---- Compact filter row and extra fields ---------
   Later rules win, so these replace the earlier filter-bar card styles. */
.filter-bar { margin: 0 0 var(--s3); background: none; border: 0; padding: 0; box-shadow: none; }
.filter-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: var(--fs-sm); }
.filter-chip { display: inline-flex; align-items: center; gap: var(--s1); min-height: 36px; border: 1px solid var(--clay); border-radius: var(--radius-pill); background: var(--accent-soft); padding: 0 var(--s1) 0 var(--s3); font-size: var(--fs-sm); line-height: 1.6; }
.filter-chip button { border: 0; background: none; color: inherit; font-size: var(--fs-base); line-height: 1; cursor: pointer; padding: 0 var(--s1); }
.filter-select, .filter-input, .filter-add, .filter-clear { height: var(--tap); font-size: var(--fs-md); border-radius: var(--radius-sm); border: 1px solid var(--control-border); background: var(--card-bg); color: inherit; padding: 0 var(--s3); box-sizing: border-box; }
.filter-select { min-width: 0; width: auto; }
.filter-input { width: 92px; }
.filter-input.wide { width: 170px; }
.filter-add, .filter-clear { cursor: pointer; padding: 0 var(--s3); }
.filter-clear { background: none; color: var(--text-muted); }
.filter-editor { display: inline-flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.filter-editor input { width: 92px; }
.filter-editor input.wide { width: 170px; }

.cf-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 10px; margin: 10px 0 4px; }
.cf-field { display: flex; flex-direction: column; gap: 4px; font-size: var(--fs-sm); }
.chip-x { border: 0; background: none; color: inherit; cursor: pointer; margin-left: 4px; font-size: var(--fs-md); }

/* ---- Board toolbar: filters in one row ------------------------------------------- */
.board-toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s3); margin: 0 0 var(--s4); }
.board-toolbar .filter-bar { margin: 0; flex: 1 1 360px; min-width: 0; }
.filter-row { justify-content: flex-start; }

.board-toolbar { justify-content: flex-start; margin: 0 0 var(--s3); gap: var(--s2); }
.board-toolbar .filter-bar { flex: 0 1 auto; }

/* Table rows open their edit form on click. */
.click-row { cursor: pointer; }
.click-row:focus-visible { outline: 2px solid var(--clay); outline-offset: -2px; }

/* ---- Edit in the cell ---------------------------------------------------------- */
.cell-input {
  width: 100%; min-width: 72px; height: 30px; box-sizing: border-box; padding: 0 6px;
  font: inherit; font-size: var(--fs-sm); border-radius: 6px; border: 1px solid var(--clay); background: var(--card-bg); color: var(--text);
}
select.cell-input { min-width: 96px; }
.cell-input[placeholder="dd/mm/yyyy"] { min-width: 108px; } /* room for 31/12/2026 */

/* ---- Simple dashboard: goals full width, checklist and notes side by side --------- */
.dash-section { display: flex; flex-direction: column; gap: var(--s3); min-width: 0; margin-top: var(--s4); }
.dash-section h2 { margin: 0; font-size: var(--fs-lg); }
.dash-section p { margin: 0; }
.dash-two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s4); align-items: start; }
.dash-two > .dash-section { margin-top: var(--s4); }
@media (max-width: 720px) { .dash-two { grid-template-columns: 1fr; gap: 0; } }
.goal-list, .check-list, .note-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.goal-row { display: flex; flex-direction: column; gap: var(--s2); padding: var(--s3); background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius-sm); margin-bottom: var(--s2); min-width: 0; }
.goal-row .goal-actions { justify-content: flex-start; margin-top: 0; }
.goal-more summary { cursor: pointer; color: var(--text-muted); font-size: var(--fs-md); }
.goal-more[open] summary { margin-bottom: var(--s2); }
.goal-add { display: flex; flex-direction: column; gap: var(--s2); }
.inline-add { display: flex; gap: var(--s2); }
.inline-add input { flex: 1 1 auto; min-width: 0; }
.check-list li, .note-list li { display: flex; justify-content: space-between; align-items: flex-start; gap: var(--s2); padding: var(--s2) 0; border-bottom: 1px solid var(--border); min-width: 0; }
.check-list li:last-child, .note-list li:last-child { border-bottom: 0; }
.check-main { display: flex; gap: var(--s2); align-items: flex-start; min-width: 0; overflow-wrap: anywhere; }
.check-main input { margin: 0; flex: none; }
.check-list .is-done .check-main span { text-decoration: line-through; color: var(--text-muted); }
.note-list .share-main { display: flex; flex-direction: column; gap: var(--s1); min-width: 0; overflow-wrap: anywhere; }
#custom-root button { padding: var(--s2) var(--s3); font-size: var(--fs-md); border-radius: var(--radius-sm); }
#custom-root input:not([type="checkbox"]), #custom-root select { padding: var(--s2) var(--s3); font-size: var(--fs-base); }
#custom-root .goal-head { align-items: center; }
#custom-root .checkin-row { margin-top: var(--s2); }
#custom-root input[type="checkbox"] { width: 24px; height: 24px; }

/* A redraw that failed (ui.js setDrawError): one line, in the error colour. */
.draw-error, .load-error { color: var(--loss); font-size: var(--fs-md); margin: 8px 0; }
.load-error .btn-ghost { margin-left: var(--s2); }

/* Coach "remove my access": set apart from plain Sign out */
.leave-box { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s3); margin-top: var(--s4); padding-top: var(--s4); border-top: 1px solid var(--border); }
.leave-box p { margin: 0; }

/* ==========================================================================
   Dialogs: the app's one dialog design (entryDialog.js; Lucas, 2026-10-18: "Everything should pop up
   in the middle and make it look good"). Every popup is a .entry-dialog: centred over a soft, blurred
   backdrop on wide screens, a bottom sheet on phones (< 641px). Three widths, by what it holds:
     small 420px  a question or a short form (Add a column, Expected columns, Delete account, Log a result)
     medium 560px most forms and details (Profile, Import, a note, an entry's detail, Planning, a goal)
     large 760px  the long forms (Add / edit a day or a match, the graph builder): the default
   The header (title and ×) and the footer stay in view while the body scrolls. The footer holds the
   primary action first, then Cancel or Close; a Delete beside them sits at the far right.
   ========================================================================== */
.entry-dialog {
  --dlg-w: 760px; --dlg-pad: var(--s5);
  width: min(var(--dlg-w), calc(100vw - 2 * var(--s4))); max-width: none; max-height: calc(100dvh - 2 * var(--s5));
  overflow-y: auto; overscroll-behavior: contain; padding: 0 var(--dlg-pad);
  border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--card-bg); color: var(--text);
  box-shadow: var(--shadow-dialog);
}
.entry-dialog:is(.dialog-md, .account-dialog, .gl-dialog, .tests-dialog, .note-dialog, .detail-dialog, .evening-dialog, .tour-list-dialog, .plan-panel) { --dlg-w: 560px; }
.entry-dialog:is(.dialog-sm, .add-column-dialog, .columns-dialog, .tests-dialog:has([data-form="log"])) { --dlg-w: 420px; }
/* The identity is a long text: the large width. */
.entry-dialog::backdrop { background: var(--overlay); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }
.entry-dialog-head {
  position: sticky; top: 0; z-index: 2; display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  margin: 0 calc(-1 * var(--dlg-pad)) var(--s1); padding: var(--s4) var(--dlg-pad) var(--s3); background: var(--card-bg);
}
.entry-dialog-head h2 { margin: 0; min-width: 0; font-size: var(--fs-lg); line-height: 1.3; color: var(--navy); overflow-wrap: anywhere; }
.dialog-x { flex: none; width: var(--tap); min-width: var(--tap); height: var(--tap); min-height: var(--tap); margin-right: calc(-1 * var(--s2)); padding: 0; border-color: transparent; border-radius: 999px; background: transparent; color: var(--text-muted); font-size: var(--fs-xl); line-height: 1; }
.dialog-x:hover { background: var(--surface); border-color: transparent; color: var(--text); }
/* The × entryDialog.js keepOneDialog adds to a dialog that has none. */
.dialog-x-auto { position: sticky; top: var(--s3); z-index: 3; float: right; margin: var(--s3) calc(-1 * var(--s2)) 0 var(--s2); background: var(--card-bg); }
/* The body: a column of fields with one rhythm; a hint sits right under its field. */
.entry-dialog > form, .entry-dialog > .plan-form { display: flex; flex-direction: column; gap: var(--s4); }
.entry-dialog > form > *, .entry-dialog > .plan-form > * { margin-top: 0; margin-bottom: 0; }
.entry-dialog :is(input:not([type=checkbox]):not([type=radio]):not([type=file]), select, textarea) { width: 100%; max-width: 100%; }
.entry-dialog .field > .hint, .entry-dialog .field > .muted.small { margin: calc(-1 * var(--s1)) 0 0; }
.entry-dialog > form:not(:has(.form-actions)), .entry-dialog [data-suggest] { padding-bottom: var(--dlg-pad); }
.entry-dialog .form-actions {
  position: sticky; bottom: 0; z-index: 1; display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2) var(--s3);
  margin: var(--s2) calc(-1 * var(--dlg-pad)) 0; padding: var(--s3) var(--dlg-pad) var(--s4);
  background: var(--card-bg); border-top: 1px solid var(--border);
}
.entry-dialog .field { scroll-margin-bottom: 96px; } /* a focused field scrolls clear of the sticky footer */
.entry-dialog .form-actions .msg { flex-basis: 100%; margin: 0; min-height: 0; }
.entry-dialog .form-actions .msg:empty { display: none; }
/* A Delete next to Save and Cancel goes to the far right (a dialog whose only action is to delete keeps it first). */
.entry-dialog .form-actions > .btn-danger:not(:first-child), #training-delete-entry-btn, #matches-delete-entry-btn { margin-left: auto; }
.entry-dialog .msg:empty { display: none; }

/* Opening and closing: a short fade and lift (a slide up on phones); none with reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  dialog.entry-dialog { transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease), overlay var(--dur-2) allow-discrete, display var(--dur-2) allow-discrete; }
  dialog.entry-dialog:not([open]) { opacity: 0; transform: translateY(8px) scale(0.985); }
  @starting-style { dialog.entry-dialog[open] { opacity: 0; transform: translateY(8px) scale(0.985); } }
  dialog.entry-dialog::backdrop { transition: opacity var(--dur-2) var(--ease), overlay var(--dur-2) allow-discrete, display var(--dur-2) allow-discrete; }
  dialog.entry-dialog:not([open])::backdrop { opacity: 0; }
  @starting-style { dialog.entry-dialog[open]::backdrop { opacity: 0; } }
}
/* A suggested value (prefill.js): a dashed border until the player changes the field. */
.suggest-note { margin: 0 0 var(--s4); padding: var(--s2) var(--s3); border: 1px dashed var(--control-border); border-radius: var(--radius-sm); font-size: var(--fs-sm); color: var(--text-muted); }
.entry-dialog .field .suggested { border-style: dashed; border-color: var(--clay-text); }
/* A field the browser refused on Save (entryDialog.js showError), and the after-today date line. */
.entry-dialog .field [aria-invalid="true"] { border-color: var(--loss); }
.field-error, .result-error { margin: 0; padding: var(--s2) var(--s3); border-left: 3px solid var(--loss); border-radius: 0 var(--radius-sm) var(--radius-sm) 0; background: var(--danger-bg); color: var(--danger-fg); font-size: var(--fs-md); line-height: 1.4; }
.date-warn { margin: 0; padding: var(--s2) var(--s3); border-radius: var(--radius-sm); background: var(--warn-bg); color: var(--warn-fg); font-size: var(--fs-sm); }
/* Hours (training) and Details (matches): folded sections, open or closed as last left on this device. */
.form-fold { margin-top: var(--s5); border-top: 1px solid var(--border); }
.form-fold > summary { display: flex; align-items: center; gap: var(--s2); min-height: 48px; cursor: pointer; list-style: none; }
.form-fold > summary::-webkit-details-marker { display: none; }
.form-fold > summary::before { content: "›"; display: inline-block; width: 1ch; font-size: var(--fs-lg); line-height: 1; color: var(--text-muted); transition: transform 0.15s; }
.form-fold[open] > summary::before { transform: rotate(90deg); }
.form-fold > summary:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; border-radius: 4px; }
.form-fold-title { font-size: var(--fs-base); font-weight: 650; color: var(--text); }
.form-fold-sum { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--fs-sm); color: var(--text-muted); }
.form-fold[open] > summary { margin-bottom: var(--s2); }
.form-fold .cf-grid { margin-top: var(--s4); }
.same-day-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s3); margin-bottom: var(--s3); }
.same-day-row p { margin: 0; flex: 1 1 200px; }
.form-day { margin-top: var(--s4); }
/* Fields that belong together stay on one line: the date and the three ratings (3), result and sets (4). */
.form-row { display: grid; gap: inherit; }
.form-row-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.form-row-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.form-row > .field { min-width: 0; }
@media (max-width: 640px) {
  /* A bottom sheet: full width, from the bottom edge, with a grab line on top. */
  .entry-dialog { --dlg-pad: var(--s4); margin: auto 0 0; width: 100vw; max-width: 100vw; max-height: 92dvh; border: none; border-top: 1px solid var(--border); border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
  .entry-dialog-head { padding-top: var(--s5); }
  .entry-dialog-head::before { content: ""; position: absolute; top: var(--s2); left: 50%; width: 40px; height: 4px; margin-left: -20px; border-radius: 2px; background: var(--control-border); }
  .entry-dialog .form-actions > .btn-primary:first-child { flex: 1 1 auto; }
  .entry-dialog .form-actions button { min-height: 44px; }
  .entry-dialog .form-grid { grid-template-columns: 1fr 1fr; gap: var(--s3); }
  .entry-dialog .form-grid > .field { min-width: 0; }
  .entry-dialog .form-row-3 { gap: var(--s2); }
  .entry-dialog .form-row-4 { grid-template-columns: 1fr 1fr; }
  .entry-dialog .form-actions { padding-bottom: calc(var(--s3) + env(safe-area-inset-bottom, 0px)); }
}

/* Entry detail (tableCols.js showDetail): a modal list of every field with Edit and Delete. */
.detail-body { display: flex; flex-direction: column; gap: var(--s4); }
.detail-body .entry-notes { margin-top: 0; }
.detail-dialog dl { display: grid; grid-template-columns: minmax(110px, 40%) 1fr; gap: var(--s1) var(--s3); margin: 0; }
.detail-dialog dt { padding-left: var(--s1); color: var(--text-muted); font-size: var(--fs-sm); }
.detail-dialog dd { margin: 0; overflow-wrap: anywhere; white-space: pre-wrap; }
/* The Open button: a small outlined button; .row-actions button would make it borderless, .btn-ghost 42px tall. */
.row-actions .open-btn { min-height: 36px; padding: 0 var(--s2); font-size: var(--fs-sm); border: 1px solid var(--border); background: var(--card-bg); color: var(--text); }
/* White text on the lighter dark-theme red fails contrast: use the page colour instead. */
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .btn-danger { color: var(--bg); } }
:root[data-theme="dark"] .btn-danger { color: var(--bg); }
.columns-list { margin: 0 0 var(--s3); line-height: 1.6; }
.columns-item { white-space: nowrap; }
.columns-dialog .small { margin: 0; }
.entry-dialog > p { margin: 0 0 var(--s3); }
/* Undo in Account's Import / export line (account.js backupSay): the line's size, a taller tap target. */
#backup-msg .link-btn { margin: 0 0 0 var(--s1); padding: var(--s2) var(--s1); font-size: inherit; }
/* "Already in your log" (importUi.js): three buttons that wrap on a phone instead of squeezing. */
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* Account: admin list of change requests */
#admin-list .row-actions { display: flex; flex-wrap: wrap; gap: var(--s2); }
#admin-list .row-actions button { border: 1px solid var(--border); padding: var(--s2) var(--s3); min-height: 36px; color: var(--navy); }
#admin-list .row-actions button:disabled { opacity: 0.5; cursor: default; }
#admin-list .share-main span { overflow-wrap: anywhere; }
#admin-list .share-main span:last-child { white-space: pre-wrap; }

/* ==========================================================================
   Round 9: UI polish and the "court night" redesign. Most of it edits the original rules
   above (type scale, palettes, top bar, key numbers, tables). What is left here has no earlier home.
   ========================================================================== */
::placeholder { color: var(--text-muted); opacity: 1; }
input[type=checkbox], input[type=radio] { accent-color: var(--accent); }
#view-account .card > h3 { margin: var(--s4) 0 var(--s2); }
.view.account-page { max-width: 760px; }
.account-dialog .field + .muted.small { margin: calc(-1 * var(--s3)) 0 0; } /* the hint under its field */
#view-account .card > h3:first-child { margin-top: 0; }

/* A button alone in a form keeps its natural width. */
.stack > .btn-primary, .stack > .btn-danger, .stack > .btn-ghost,
#custom-root form[data-form="new-note"] .btn-primary { align-self: flex-start; }

/* Secondary actions inside lists (Delete, Mark done) are quiet. */
.goal-row .btn-ghost, .check-list .btn-ghost, .note-list .btn-ghost {
  min-height: 36px; padding: 0 var(--s3); font-size: var(--fs-sm); font-weight: 500; color: var(--text-muted);
}
.goal-row .btn-ghost:hover, .check-list .btn-ghost:hover, .note-list .btn-ghost:hover { color: var(--text); }

/* Delete account: set apart without shouting. */
#delete-dialog-title { color: var(--loss); }
#form-delete .btn-danger { background: transparent; color: var(--loss); border: 1px solid var(--loss); box-shadow: none; }
#form-delete .btn-danger:hover { background: var(--danger-bg); filter: none; }

@media (max-width: 640px) {
  .auth-tabs { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2); }
  .auth-tab { text-align: center; min-height: 40px; }
  .filter-chip button, .chip-x { min-width: 36px; min-height: 36px; }
  .link-btn { min-height: 36px; }
  thead th, tbody td, .analysis-table th, .analysis-table td { padding: var(--s2); }
  .goal-row .btn-ghost, .check-list .btn-ghost, .note-list .btn-ghost { min-height: 40px; }
  #form-delete .btn-danger { align-self: stretch; }
  /* One-handed use: primary actions and inputs are 44px, key numbers sit two to a row. */
  .btn-primary, select, input:not([type=checkbox]):not([type=radio]) { min-height: 44px; }
  .reminder .btn-primary { min-height: 44px; }
  #training-save-btn, #matches-save-btn { min-height: 48px; } /* ids: entryDialog.js boardId */
  .kpi-card { padding: var(--s3) var(--s4) var(--s4); }
  .kpi-grid > .kpi-card .kpi-value, .kpi-grid > .kpi-card:nth-child(-n+3) .kpi-value { font-size: var(--fs-xl); }
  .kpi-grid > .kpi-card:nth-child(-n+2) .kpi-value { font-size: var(--fs-kpi); }
}

/* ==========================================================================
   Classic tabs (task 20261017-2001): Account as a small link, grouped table
   headers, the interval control, and the Tests sub-tab.
   ========================================================================== */
/* Top right of the header: the light / dark button (theme.js) and the Account link. */
.topbar-links { margin-left: auto; display: flex; align-items: center; gap: var(--s2); }
.account-link {
  border: 0; background: transparent; cursor: pointer;
  color: var(--on-bar-text); font: inherit; font-size: var(--fs-md); text-decoration: underline; text-underline-offset: 3px;
  padding: 0 var(--s3); min-height: var(--tap); border-radius: var(--radius-pill);
}
.account-link:hover, .account-link.active { color: var(--on-bar-strong); background: var(--on-bar-fill); }
.account-link.active { font-weight: 650; }
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center; width: var(--tap); height: var(--tap); padding: 0;
  border: 1px solid var(--on-bar-line); border-radius: 50%; background: var(--on-bar-fill); color: var(--on-bar-text); cursor: pointer;
}
.theme-toggle:hover { color: var(--on-bar-strong); border-color: var(--on-bar-text); }
.theme-toggle:focus-visible, .account-link:focus-visible { outline: 2px solid var(--on-bar-strong); outline-offset: 2px; }
.theme-toggle .icon-sun, .theme-toggle[aria-pressed="true"] .icon-moon { display: none; }
.theme-toggle[aria-pressed="true"] .icon-sun { display: block; }

/* Group header row above the column labels (Tennis, Physical, Mental). Both header rows stay in view. */
.table-wrap { --group-row-h: 30px; }
thead tr.group-row th { height: var(--group-row-h); padding: 0 var(--s3); border-bottom: 0; }
thead tr.group-row th.group { text-align: left; color: var(--text); border-bottom: 2px solid var(--clay); }
.table-wrap thead tr.group-row + tr th { top: var(--group-row-h); }
.table-note { margin: 0 0 var(--s3); }

.filter-row .msg { margin: 0; }
/* .filter-add above is also a flex column container: in the filter row its label sits centred like the inputs'. */
.filter-row .filter-add { align-items: center; justify-content: center; }
#name-msg:empty { display: none; } /* Account, Your name: no gap until there is a message */

/* Tests sub-tab: the new UI's tests screen (v2/screens/tests.js) drawn with the classic tokens. */
.tests-nav { display: flex; align-items: center; gap: var(--s3); margin: 0 0 var(--s3); }
.tests-nav h2 { margin: 0; font-size: var(--fs-lg); }
.tests-host { --tennis: var(--court-green); --physical: var(--accent); --mental: #9d7ad8; }
.tests-host .stack { display: flex; flex-direction: column; gap: var(--s3); }
.tests-host .row { display: flex; justify-content: space-between; align-items: center; gap: var(--s3); }
.tests-host .card { margin-bottom: 0; }
.tests-host .lbl { font-size: var(--fs-xs); letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted); font-weight: 650; }
.tests-host .mut { color: var(--text-muted); font-size: var(--fs-sm); }
.tests-host .num { font-variant-numeric: tabular-nums; font-weight: 700; }
.tests-host .big { font-size: var(--fs-kpi); }
.tests-host .g2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s3); }
.tests-host .fld { display: flex; flex-direction: column; gap: var(--s1); min-width: 0; font-size: var(--fs-sm); }
.tests-host .inp { width: 100%; }
.tests-host .btn, .tests-host .ghost {
  min-height: 42px; padding: 0 18px; border-radius: var(--radius-sm); font: inherit; font-size: var(--fs-md); font-weight: 600; cursor: pointer;
}
.tests-host .btn { border: 0; background: var(--accent); color: var(--on-accent); }
.tests-host .btn:hover { background: var(--accent-dark); }
.tests-host .ghost { border: 1px solid var(--control-border); background: var(--card-bg); color: var(--text); }
.tests-host .ghost:hover { background: var(--surface); }
.tests-host .ghost.danger, .tests-host .lnk.danger { color: var(--loss); border-color: var(--loss); }
.tests-host .lnk { min-height: 36px; padding: 0 var(--s1); border: 0; background: transparent; color: var(--clay-text); font: inherit; font-weight: 650; cursor: pointer; }
.tests-host .seg { display: flex; gap: var(--s1); padding: var(--s1); border-radius: var(--radius-sm); background: var(--surface); border: 1px solid var(--border); }
.tests-host .seg button { flex: 1; min-height: 38px; border: 0; border-radius: 8px; background: transparent; color: var(--text-muted); font: inherit; font-weight: 600; cursor: pointer; }
.tests-host .seg button[aria-pressed="true"] { background: var(--card-bg); color: var(--text); box-shadow: var(--shadow); }
.tests-host .win { color: var(--win); }
.tests-host .loss { color: var(--loss); }
.tests-host .msg { min-height: 0; }
.tests-host .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.tests-host .tests-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: var(--s3); }
.tests-host .tests-card { color: var(--text); text-decoration: none; }
.tests-host .tests-card:hover { border-color: var(--text-muted); }
.tests-host .tests-name { font-weight: 650; }
.tests-host .tests-val-row { align-items: flex-end; }
.tests-host .tests-val { font-size: var(--fs-kpi); line-height: 1; }
.tests-host .tests-change { font-size: var(--fs-sm); font-weight: 650; font-variant-numeric: tabular-nums; }
.tests-host .tests-changes { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; text-align: right; }
.tests-host .tests-mini { display: flex; align-items: flex-end; gap: var(--s1); height: 44px; }
.tests-host .tests-mini i { display: block; width: 10px; border-radius: 2px; background: var(--border); }
.tests-host .tests-mini i.tennis { background: var(--tennis); }
.tests-host .tests-mini i.physical { background: var(--physical); }
.tests-host .tests-mini i.mental { background: var(--mental); }
.tests-host .tests-head-acts { display: flex; flex-wrap: wrap; gap: var(--s2); }
.tests-dialog .fld > .mut:first-child { font-weight: 600; color: var(--text); }
.tests-host .tests-examples, .tests-host .tests-manage { display: flex; flex-wrap: wrap; gap: var(--s2); justify-content: flex-start; }
.tests-host .tests-chart { margin: var(--s1) 0 0; display: flex; flex-direction: column; gap: 6px; }
.tests-host .tests-plot { position: relative; height: 160px; }
.tests-host .tests-plot svg { display: block; width: 100%; height: 100%; overflow: visible; }
.tests-host .tests-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke: var(--physical); }
.tests-host .tests-dot { stroke-width: 7; stroke-linecap: round; stroke: var(--physical); }
.tests-host .tests-line.tennis, .tests-host .tests-dot.tennis { stroke: var(--tennis); }
.tests-host .tests-line.mental, .tests-host .tests-dot.mental { stroke: var(--mental); }
.tests-host .tests-target { stroke: var(--text); stroke-width: 1.5; stroke-dasharray: 5 4; }
.tests-host .tests-target-key { display: inline-block; width: 16px; vertical-align: middle; border-top: 2px dashed var(--text); }
.tests-host .tests-ylab { position: absolute; left: 0; padding: 0 var(--s1); border-radius: 4px; background: var(--card-bg); color: var(--text-muted); font-size: var(--fs-xs); }
.tests-host .tests-ylab.top { top: -8px; }
.tests-host .tests-ylab.bottom { bottom: -8px; }
.tests-host .tests-dates { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 6px; }
.tests-host .tests-table td { height: 48px; }
.tests-host .tests-acts { text-align: right; }

@media (max-width: 640px) {
  .tests-host .g2 { grid-template-columns: 1fr; }
}

/* ---- Identity tab (goals.js): one calm page, at most 760px wide and centred: This week, the goals as rows (.gl-rows,
   each with its status, trend, sparkline and weeks), then the identity, one board of post-its (.pn, a soft tint of a palette slot,
   --pc: .plan-cN) under their boxes and parts, which is also where it is written (identityBoard.js, .ib-*). ---- */
.goals-page { max-width: 760px; margin-inline: auto; }
.id-doc { display: flex; flex-direction: column; gap: var(--s6); }
.id-top { display: contents; }
.id-panel { display: flex; flex-direction: column; gap: var(--s6); min-width: 0; }
/* The shown view (goals.js): Identity is the board, Long term the long-term goals, Short term the short-term goals. */
.id-view { display: flex; flex-direction: column; gap: var(--s6); min-width: 0; }
.id-board[hidden] { display: none; }
/* Short term grouped under the long-term goal each goal is part of ("→ Top 200 ITF"), the rest "Other goals". */
.gl-rows > li.gl-group { padding-top: var(--s4); border-bottom: 0; }
.gl-group-rows { border-top: 0; margin-top: var(--s1); }
.id-doc .gl-group-h { margin: 0; font-size: var(--fs-sm); font-weight: 650; color: var(--clay-text); }
.id-doc .gl-group-h button { font: inherit; color: inherit; background: none; border: 0; padding: 0; min-height: 32px; cursor: pointer; text-align: left; }
.id-doc .gl-group-h button:hover { text-decoration: underline; }
.id-doc .gl-group-h.is-plain { color: var(--text-muted); }
/* Under a long-term goal: its steps and how they go, and "+ Add a step". */
.gl-item > .gl-steps { flex: 1 1 100%; }
.gl-steps { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s3); padding: var(--s2) var(--s3) var(--s3); font-size: var(--fs-sm); color: var(--text-muted); }
.gl-steps .gl-step-add { font-size: var(--fs-sm); padding: 0; }
.gl-partof .small { margin: var(--s1) 0 0; }
.id-part { display: flex; flex-direction: column; gap: var(--s3); min-width: 0; }
.id-part-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); flex-wrap: wrap; }
.id-doc h2 { margin: 0; font-family: var(--font-display); font-size: var(--fs-xl); font-weight: 650; letter-spacing: -0.01em; color: var(--navy); }
.id-doc h3 { margin: 0; font-size: var(--fs-base); font-weight: 650; color: var(--text); }
.id-part-head .btn-ghost { min-height: 36px; padding: 0 var(--s3); font-size: var(--fs-sm); }
.id-help { margin: 0; max-width: 62ch; color: var(--text-muted); font-size: var(--fs-md); line-height: 1.55; }
.pn {
  --pc: var(--clay);
  position: relative; display: flex; flex-direction: column; min-width: 0; padding: var(--s4) var(--s3) var(--s3);
  border: 1px solid color-mix(in srgb, var(--pc) 22%, var(--border)); border-radius: var(--radius-note);
  background: color-mix(in srgb, var(--pc) 11%, var(--card-bg)); color: var(--text); box-shadow: var(--shadow-note);
}
.pn::before { content: ""; position: absolute; inset: -1px -1px auto -1px; height: var(--s1); border-radius: var(--radius-note) var(--radius-note) 0 0; background: var(--pc); opacity: 0.75; }
.pn-text { font-size: var(--fs-base); line-height: 1.4; white-space: pre-line; overflow-wrap: anywhere; }
/* The identity board (identityBoard.js): parts of boxes of post-its, written in place. A post-it is a field that looks like paper and
   grows with what is typed; the grip, the title and the ⋯ menu of a part or a box sit in one row; every control is 44px on a phone. */
.ib { gap: var(--s4); }
.ib-bar { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2); margin-left: auto; }
.ib-parts > .ib-part + .ib-part { padding-top: var(--s4); border-top: 1px solid var(--border); }
.ib-bar > .note-chip, .ib-bar > .btn-ghost { min-height: 36px; font-size: var(--fs-md); }
.ib-status { min-width: 3.5em; font-size: var(--fs-sm); color: var(--text-muted); }
.ib-status.is-bad { color: var(--danger-fg); font-weight: 600; }
.ib-parts { display: flex; flex-direction: column; gap: var(--s5); }
.ib-part, .ib-box { display: flex; flex-direction: column; gap: var(--s2); min-width: 0; }
.ib-part { gap: var(--s3); }
.ib-boxes { display: flex; flex-direction: column; gap: var(--s2); }
.ib-head { display: flex; align-items: center; }
.ib-h { flex: 1 1 auto; min-width: 0; margin: 0; }
.ib-head .ib-h { margin-left: calc(var(--s2) * -1); } /* the title's text lines up with the post-its' edge */
.ib-box .ib-h { font-size: var(--fs-md); font-weight: 650; }
h3.ib-h { font-family: var(--font-display); font-size: var(--fs-lg); color: var(--navy); }
/* The two fields every part and box has: its title (one or two words) and under it a one-line description. Both take a light placeholder, and
   the title a hairline at rest (the description's shows on hover and focus, so the board does not read as ruled lines), so a player sees where
   to type without a button or a paragraph; the description is smaller and muted and tucks up under its title. */
.ib input.ib-title, .ib textarea.ib-desc {
  width: 100%; padding: 0 var(--s2); border: 1px solid transparent;
  border-radius: 0; background: transparent; color: inherit; font: inherit; font-weight: inherit;
}
.ib input.ib-title { border-bottom-color: color-mix(in srgb, var(--border) 70%, transparent); }
.ib input.ib-title { min-height: 40px; }
.ib textarea.ib-desc {
  display: block; width: calc(100% + var(--s2)); min-height: 28px; margin: calc(var(--s2) * -1) 0 0 calc(var(--s2) * -1); padding-block: var(--s1);
  resize: none; overflow: hidden; field-sizing: fixed; color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.4;
}
.ib input.ib-title:hover, .ib textarea.ib-desc:hover { border-color: var(--border); border-radius: var(--radius-sm); }
.ib input.ib-title:focus, .ib textarea.ib-desc:focus { border-color: var(--clay); border-radius: var(--radius-sm); background: var(--card-bg); outline: none; }
.ib input.ib-title::placeholder, .ib textarea.ib-desc::placeholder, .ib .pn-input::placeholder { color: var(--text-muted); opacity: 0.8; font-weight: 400; }
.ib-note { margin: 0; max-width: 62ch; color: var(--text-muted); font-size: var(--fs-sm); line-height: 1.4; } /* the same line, read-only */
.ib-note + .ib-postits { margin-top: calc(var(--s1) * -1); }
/* No buttons at rest. What a box or part offers shows only on the one in use (.is-on: the cursor is in it, or it was tapped; .is-in is that one and
   what holds it) and under the mouse: a light "×" beside its title, a light "+" under its post-its and under its boxes. Hidden controls take no touch
   (so a stray tap on the empty place does nothing) and keep their room (nothing moves when they show); they are still in the tab order, and the first
   one a Tab reaches is on a box that is then in use. "Add part" is the one mark that stays: a faint line at the bottom of the board. */
.ib-text {
  min-height: 28px; padding: 0 var(--s2); border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-muted);
  font: inherit; font-size: var(--fs-sm); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; cursor: pointer;
}
.ib-text:hover { color: var(--text); }
.ib-x, .ib-plus, .ib-line, .ib-copy { opacity: 0; pointer-events: none; transition: opacity 0.12s; }
.ib-box.is-on > .ib-head .ib-x, .ib-part.is-on > .ib-head .ib-x, .ib-head:focus-within .ib-x { opacity: 0.8; pointer-events: auto; }
.ib-box.is-in > .ib-plus, .ib-part.is-in > .ib-line { opacity: 0.8; pointer-events: auto; }
.ib.is-in .ib-copy, .ib-foot:focus-within .ib-copy { opacity: 1; pointer-events: auto; }
.ib-x:focus-visible, .ib-plus:focus-visible, .ib-line:focus-visible, .ib-copy:focus-visible { opacity: 1; }
.ib-box:not(.is-in) > .ib-plus:not(:focus-visible), .ib-part:not(.is-in) > .ib-line:not(:focus-visible) { transition: none; }
@media (hover: hover) {
  .ib-head:hover .ib-x, .ib-box:hover > .ib-plus, .ib-part:hover > .ib-line, .ib-foot:hover .ib-copy { opacity: 0.8; pointer-events: auto; }
  .ib-x:hover, .ib-plus:hover, .ib-line:hover { opacity: 1; }
}
.ib-mark::before { content: attr(data-g); } /* the glyph is drawn, not written: the page's text carries no "×" or "+" for every box */
.ib-x, .ib-plus {
  flex: none; display: inline-flex; align-items: center; justify-content: center; min-width: var(--tap-sm); min-height: var(--tap-sm); padding: 0; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-muted); font: inherit; font-size: var(--fs-lg); line-height: 1; cursor: pointer;
}
.ib-x:hover { color: var(--danger-fg); }
.ib-plus { align-self: flex-start; min-height: 24px; color: var(--clay-text); }
.ib-line {
  display: flex; align-items: center; gap: var(--s3); width: 100%; min-height: 28px; padding: 0; border: 0; background: transparent;
  color: var(--clay-text); font: inherit; font-size: var(--fs-lg); line-height: 1; cursor: pointer;
}
.ib-line::before, .ib-line::after { content: ""; flex: 1 1 0; height: 1px; background: var(--border); }
.ib-line:hover::before, .ib-line:hover::after, .ib-line:focus-visible::before, .ib-line:focus-visible::after { background: var(--clay); }
.ib-line:focus-visible, .ib-x:focus-visible, .ib-plus:focus-visible, .ib-text:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; border-radius: var(--radius-sm); }
/* "Add part": a faint line at the bottom of the board, readable at rest: the hairlines are about 40% (their own alpha), the words keep the full muted
   colour (4.5:1); on hover, focus and tap the line goes to the accent and the words to the text colour. */
.ib-line.ib-faint { opacity: 1; pointer-events: auto; min-height: var(--tap-sm); color: var(--text-muted); font-size: var(--fs-sm); font-weight: 600; }
.ib-line.ib-faint::before, .ib-line.ib-faint::after { background: color-mix(in srgb, var(--text-muted) 40%, transparent); }
.ib-line.ib-faint:hover, .ib-line.ib-faint:focus-visible, .ib-line.ib-faint:active { color: var(--text); }
.ib-line.ib-faint:active::before, .ib-line.ib-faint:active::after { background: var(--clay); }
.ib-foot { display: flex; align-items: center; gap: var(--s4); }
.ib-foot > .ib-line { flex: 1 1 auto; width: auto; margin: 0; }
.ib-foot > .ib-text { flex: none; margin-left: auto; }
.ib-postits { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s2); }
.ib-postits .pn { min-height: 0; padding: var(--s3) var(--s3) var(--s2); flex-direction: row; flex-wrap: wrap; align-items: center; column-gap: var(--s3); }
.ib .pn-input {
  display: block; flex: 1 1 9em; min-width: 0; min-height: 1.4em; padding: 0; border: 0; border-radius: 0; background: transparent; box-shadow: none; resize: none; overflow: hidden;
  field-sizing: fixed; color: var(--text); font: inherit; font-size: var(--fs-base); line-height: 1.4; outline: none;
}
.ib .ib-postits textarea.pn-input:focus, .ib .ib-postits textarea.pn-input:focus-visible { border: 0; background: transparent; box-shadow: none; outline: none; }
.ib-postits .pn:focus-within { border-color: var(--pc); box-shadow: 0 0 0 2px color-mix(in srgb, var(--pc) 45%, transparent), var(--shadow-note); }
.ib-postits .pn-foot { margin-left: auto; align-items: center; }
.ib-postits .pn-foot .pn-goal { padding: 0; min-height: var(--tap-sm); display: inline-flex; align-items: center; }
/* "Make this a goal" shows on the post-it the cursor is in (or its controls have focus), not under every one: a post-it without it is one line. It
   goes after a short delay, so a tap on it still lands when a phone drops the focus first. */
.ib-postits .pn:not(:focus-within) .pn-goal[data-act="note-goal"] { visibility: hidden; width: 0; height: 0; min-height: 0; overflow: hidden; transition: visibility 0s 0.4s, width 0s 0.4s, height 0s 0.4s, min-height 0s 0.4s; }
.ib-postits .note-region { flex: 1 0 100%; }
.ib-postits .pn-text { flex: 1 1 9em; min-width: 0; }
.ib-part.is-dragging, .ib-box.is-dragging { position: relative; z-index: 3; border-radius: var(--radius-sm); background: var(--card-bg); box-shadow: var(--shadow-note); }
.ib-postits .pn.is-dragging { background: var(--card-bg); }
.ib.is-lifting, .ib.is-lifting * { user-select: none; -webkit-user-select: none; cursor: grabbing; }
.ib-box.is-dragging > .ib-plus, .ib-part.is-dragging > .ib-line { opacity: 0; }
/* A touch screen: an idle field takes no touch of its own, so a press on a post-it or a title can lift it (the field would start a text
   selection); a tap on it still puts the cursor there (identityBoard.js tapIn), and a field with the cursor is touched as usual. */
@media (pointer: coarse) {
  .ib-postits .pn:not(:focus-within) .pn-input, .ib-head:not(:focus-within) input.ib-title { pointer-events: none; -webkit-touch-callout: none; user-select: none; -webkit-user-select: none; }
  .ib-postits .pn, .ib-head { -webkit-touch-callout: none; }
  .ib-box:not(.is-in) > .ib-plus, .ib-part:not(.is-in) > .ib-line { pointer-events: none; }
}
@media (max-width: 640px) {
  .ib input.ib-title, .ib-x { min-height: var(--tap); }
  .ib-x, .ib-plus { min-width: var(--tap); }
  .ib-plus { min-height: var(--tap-sm); }
  h3.ib-h { font-size: var(--fs-base); }
  .ib-text, .ib-bar > .note-chip, .ib-postits .note-chip, .ib-postits .pn-foot .pn-goal { min-height: var(--tap); }
  .ib-line, .ib-copy { min-height: var(--s3); } /* at rest they only keep a little room; on the box in use they are 44px high */
  .ib-part.is-in > .ib-line, .ib.is-in .ib-copy, .ib-line.ib-faint { min-height: var(--tap); }
  .ib-bar { margin-left: 0; width: 100%; }
  .ib-bar > .note-chip { margin-right: auto; }
}
/* Part 3: a goal per row: area · goal (period, notes, check-in) · now against the target · status and trend ·
   sparkline; the dialog's table is .gl-weeks. */
.gl-rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; border-top: 1px solid var(--border); }
.gl-rows > li { border-bottom: 1px solid var(--border); }
.gl-item { display: flex; flex-wrap: wrap; align-items: stretch; }
.gl-item > .gl-row { flex: 1 1 0; min-width: 0; }
.gl-row {
  --pc: var(--text-muted);
  display: grid; grid-template-columns: 96px minmax(0, 1fr) auto 128px 96px; align-items: center; gap: var(--s1) var(--s3);
  width: 100%; min-height: 56px; padding: var(--s2) var(--s2) var(--s2) var(--s3);
  border: 0; border-left: 3px solid var(--pc); background: transparent; color: var(--text); font: inherit; text-align: left;
}
button.gl-row { cursor: pointer; }
button.gl-row:hover { background: var(--row-hover); }
button.gl-row:focus-visible { outline: 2px solid var(--clay); outline-offset: -2px; }
.gl-row-area { font-size: var(--fs-xs); font-weight: 650; text-transform: uppercase; letter-spacing: 0.06em; color: color-mix(in srgb, var(--pc) 60%, var(--text)); overflow-wrap: anywhere; }
.gl-row-main { display: flex; flex-direction: column; min-width: 0; }
.gl-row-label { font-weight: 600; overflow-wrap: anywhere; }
.gl-row-meta, .gl-row-now { font-size: var(--fs-sm); color: var(--text-muted); }
.gl-row-now { text-align: right; max-width: 11rem; overflow-wrap: anywhere; }
.gl-row-status { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.gl-row-trend { font-size: var(--fs-xs); color: var(--text-muted); padding-left: 16px; }
.gl-row-spark { width: 96px; }
.gl-status { display: inline-flex; align-items: center; font-size: var(--fs-sm); font-weight: 600; }
/* Every row opens its goal: a chevron says so; Check in (or a coach's Add note) sits beside it. */
.gl-row { position: relative; padding-right: var(--s6); }
.gl-row::after { content: ""; position: absolute; right: var(--s3); top: 50%; width: 8px; height: 8px; border: solid var(--text-muted); border-width: 2px 2px 0 0; transform: translateY(-50%) rotate(45deg); }
.gl-row-ci { flex: none; align-self: center; min-height: 36px; margin: 0 var(--s2); padding: 0 var(--s3); font-size: var(--fs-sm); white-space: nowrap; }
/* The weeks (or months) behind a sparkline: label, value, a bar against the target's mark, met or not. */
.gl-weeks { width: 100%; max-width: 480px; border-collapse: collapse; font-size: var(--fs-sm); }
.gl-weeks caption { text-align: left; padding-bottom: var(--s1); }
.gl-weeks th, .gl-weeks td { padding: 3px var(--s2) 3px 0; border: 0; text-align: left; font-weight: 400; white-space: nowrap; }
.gl-weeks tbody tr, .gl-weeks tbody tr:hover { background: none; } /* not the data tables' stripes */
.gl-weeks th { font-weight: 600; padding-right: var(--s3); }
.gl-wk-value { text-align: right !important; font-variant-numeric: tabular-nums; }
.gl-wk-bar { width: 60%; }
.gl-bar { position: relative; display: block; height: 8px; border-radius: 4px; background: var(--surface); box-shadow: inset 0 0 0 1px var(--border); }
.gl-bar-fill { display: block; height: 100%; max-width: 100%; border-radius: 4px; background: var(--amber); }
.is-met .gl-bar-fill { background: var(--win); }
.is-running .gl-bar-fill { background: var(--control-border); }
.gl-bar-target { position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px; background: var(--text); }
.gl-wk-met { color: var(--win); font-weight: 700; }
.gl-wk-missed { color: var(--text-muted); font-weight: 700; }
.gl-suggest::after { display: none; }
.gl-suggest { padding-right: var(--s2); border-left-style: dashed; grid-template-columns: minmax(0, 1fr) auto; background: var(--surface); }
.gl-suggest .gl-row-area { grid-column: 1 / -1; text-transform: none; letter-spacing: 0; font-size: var(--fs-sm); }
.gl-row-actions { display: flex; flex-wrap: wrap; gap: var(--s2); }
.gl-row-actions button, .gl-add { min-height: 36px; padding: 0 var(--s3); font-size: var(--fs-sm); }
.status-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 6px; flex: none; background: var(--control-border); }
.dot-green { background: var(--win); }
.dot-yellow { background: var(--amber); }
.dot-red { background: var(--loss); }
.gl-due { color: var(--clay-text); font-weight: 600; white-space: nowrap; font-size: var(--fs-sm); }
.gl-due.is-plain { color: var(--text-muted); font-weight: 400; }
.gl-offer { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); padding: var(--s2) var(--s3); border: 1px dashed var(--border); border-radius: var(--radius-sm); }
.gl-offer p { margin: 0; flex: 1 1 240px; }
/* Accomplished goals (goalsDoneUi.js): the count under My goals, the list in a dialog, a goal's facts, and the first-time
   celebration. The motion only runs for people who have not asked for reduced motion; without it the same screen is still. */
.gl-done-line { margin: 0 0 var(--s2); }
.gl-done-count { display: inline-flex; align-items: center; gap: var(--s2); min-height: 40px; padding: 0 var(--s3); border: 1px solid var(--win); border-radius: 999px; background: var(--surface); color: var(--win); font: inherit; font-size: var(--fs-md); cursor: pointer; }
.gl-closed-count { display: inline-flex; align-items: center; min-height: 40px; padding: 0 var(--s3); border: 1px solid var(--border); border-radius: var(--radius-pill); background: var(--surface); color: var(--text-muted); font: inherit; font-size: var(--fs-md); cursor: pointer; }
.gl-closed-banner { margin: 0 0 var(--s3); color: var(--text-muted); font-weight: 600; }
.gl-done-count:hover { background: var(--row-hover); }
.gl-done-count:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.gl-done-star { color: var(--amber); }
.gl-done-banner { margin: 0; font-weight: 650; color: var(--win); }
.gl-done-list { list-style: none; margin: var(--s3) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--s3); }
.gl-done-card { --pc: var(--win); padding: var(--s3) var(--s4); border: 1px solid var(--border); border-left: 4px solid var(--pc); border-radius: var(--radius-sm); background: var(--surface); }
.gl-done-card h3 { margin: 0 0 var(--s2); font-size: var(--fs-lg); overflow-wrap: anywhere; }
.gl-done-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s2) var(--s4); margin: var(--s2) 0; }
.gl-done-facts > div { min-width: 0; }
.gl-done-facts dt { font-size: var(--fs-xs); font-weight: 650; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); }
.gl-done-facts dd { margin: 0; font-size: var(--fs-md); overflow-wrap: anywhere; }
.gl-done-notes { margin-top: var(--s3); padding-top: var(--s2); border-top: 1px solid var(--border); }
.gl-done-notes h4 { margin: 0 0 var(--s1); font-size: var(--fs-sm); }
.gl-done-notes ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s2); }
.gl-done-notes li { display: flex; flex-direction: column; overflow-wrap: anywhere; }
.gl-done-actions { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: var(--s3); }
.gl-cele { text-align: center; display: flex; flex-direction: column; align-items: center; gap: var(--s2); padding: var(--s4) 0; }
.gl-cele h3 { margin: 0; font-size: var(--fs-xl); }
.gl-cele p { margin: 0; }
.gl-cele-goal { font-size: var(--fs-lg); font-weight: 650; overflow-wrap: anywhere; }
.gl-cele .form-actions { justify-content: center; margin-top: var(--s3); }
.gl-cele-art { position: relative; width: 112px; height: 112px; display: grid; place-items: center; }
.gl-cele-ring { width: 72px; height: 72px; display: grid; place-items: center; border-radius: 50%; background: var(--win); color: var(--card-bg); font-size: var(--fs-kpi); font-weight: 700; line-height: 1; }
.gl-cele-art i { position: absolute; left: 50%; top: 50%; width: 8px; height: 8px; margin: -4px 0 0 -4px; border-radius: 2px; background: var(--clay);
  transform: rotate(calc(var(--i) * 25.7deg)) translateY(-50px); }
.gl-cele-art i:nth-child(3n + 2) { background: var(--amber); }
.gl-cele-art i:nth-child(3n) { background: var(--win); }
@media (prefers-reduced-motion: no-preference) {
  .gl-cele-ring { animation: gl-pop 0.5s cubic-bezier(0.2, 1.4, 0.4, 1) both; }
  .gl-cele-art i { animation: gl-burst 0.9s ease-out both; animation-delay: 0.15s; }
  .gl-done-count.is-bump { animation: gl-bump 0.6s ease-out; }
  @keyframes gl-pop { from { transform: scale(0.3); opacity: 0; } to { transform: scale(1); opacity: 1; } }
  @keyframes gl-burst { from { transform: rotate(calc(var(--i) * 25.7deg)) translateY(0); opacity: 1; } to { transform: rotate(calc(var(--i) * 25.7deg)) translateY(-50px); opacity: 0.85; } }
  @keyframes gl-bump { 0% { transform: scale(1); } 35% { transform: scale(1.12); } 100% { transform: scale(1); } }
}
.gl-spark { display: block; width: 100%; height: 28px; overflow: visible; }
.gl-spark-line { fill: none; stroke: var(--spark, var(--navy)); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.gl-spark-target { fill: none; stroke: var(--text-muted); stroke-width: 1; stroke-dasharray: 3 3; vector-effect: non-scaling-stroke; }
.gl-spark-end { fill: var(--spark, var(--navy)); }
/* An identity note's "Make this a goal". */
.pn-goal { align-self: flex-start; text-align: left; margin-top: auto; padding: var(--s2) 0 0; min-height: 32px; border: 0; background: none; color: var(--clay-text); font: inherit; font-size: var(--fs-xs); font-weight: 600; cursor: pointer; }
/* Under a post-it: its "Make this a goal" or goal link, and the notes on it (a coach's or parent's). */
.pn-foot { display: flex; flex-wrap: wrap; column-gap: var(--s3); row-gap: var(--s1); }
.pn-foot .pn-goal { margin-top: 0; min-height: 36px; }
.pn-goal:hover { text-decoration: underline; }
.pn-goal:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
/* Part 1, This week (goals.js thisWeekHtml): one summary line, then today's day goals as chips (round 5, #2 and
   #6); the player taps one to tick it (a check in a ring, like the evening banner's), a viewer sees marks. */
.tw { gap: var(--s2); padding: var(--s3) var(--s4); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); }
.tw-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s1) var(--s3); }
.tw-head h2 { font-size: var(--fs-lg); }
.tw-sum { margin: 0; font-size: var(--fs-md); font-weight: 600; color: var(--text); }
.tw-today { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }
.tw-today-h { font-size: var(--fs-xs); font-weight: 650; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); }
.tw-day {
  display: inline-flex; align-items: center; gap: var(--s2); min-height: 36px; max-width: 100%; padding: 0 var(--s3) 0 var(--s2);
  border: 1px solid var(--control-border); border-radius: 999px; background: var(--card-bg); color: var(--text); font: inherit; font-size: var(--fs-md); text-align: left;
}
button.tw-day { cursor: pointer; }
button.tw-day:hover { border-color: var(--clay); }
button.tw-day:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.tw-day > span:last-of-type { overflow-wrap: anywhere; }
.tw-tick { flex: none; display: inline-grid; place-items: center; width: 20px; height: 20px; border: 1.5px solid var(--control-border); border-radius: 999px; font-size: var(--fs-xs); font-weight: 700; }
.tw-day.is-done .tw-tick { border-color: var(--win); background: var(--win); color: var(--card-bg); }
.tw-day.is-done > span:last-of-type { color: var(--text-muted); text-decoration: line-through; }
/* "Start here" (startHere.js): four steps, done ones ticked, the next one with the blue button. */
.sh { gap: var(--s2); padding: var(--s3) var(--s4); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); }
.sh-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s2); }
.sh-step { display: flex; align-items: center; gap: var(--s3); min-height: 44px; }
.sh-mark { flex: none; display: inline-grid; place-items: center; width: 28px; height: 28px; border: 1.5px solid var(--control-border); border-radius: 999px; font-size: var(--fs-sm); font-weight: 700; color: var(--text-muted); }
.sh-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.sh-step > button { flex: none; min-height: 36px; padding: 0 var(--s3); font-size: var(--fs-sm); }
@media (max-width: 640px) {
  /* On a phone each step's button goes under its text (Start here's three steps, no ticks: Erik 2026-10-25). */
  .sh-step { display: grid; grid-template-columns: 28px 1fr; align-items: start; gap: var(--s1) var(--s3); }
  .sh-step > button { grid-column: 2; justify-self: start; min-height: 44px; }
}
/* Adding, renaming, removing and moving boxes and parts: small buttons beside each label, a small form under "+ Add box". */
/* The headline number's own mark (goalStrip.js markHtml): ✓ met, ✗ missed, or "so far", apart from the status. */
.gl-mark { font-weight: 700; white-space: nowrap; }
.gl-mark.is-met { color: var(--win); }
.gl-mark.is-missed { color: var(--loss); }
.gl-mark.is-running { font-weight: 400; color: var(--text-muted); }
/* "Add a field…" in the goal dialog's measure step, in place of the presets. */
.gl-addfield { display: flex; flex-direction: column; gap: var(--s3); }
.gl-af-board { display: inline-flex; align-items: center; gap: var(--s1); margin-right: var(--s4); min-height: 32px; }
.gl-m-addline { margin: 0; }
/* My goals' explanation, folded once there are goals. */
.gl-help summary { min-height: 32px; display: inline-flex; align-items: center; cursor: pointer; color: var(--text-muted); font-size: var(--fs-sm); font-weight: 600; }
.gl-help summary::before { content: ""; width: 6px; height: 6px; margin-right: var(--s2); border: solid currentColor; border-width: 0 2px 2px 0; transform: rotate(-45deg); transition: transform 0.12s; }
.gl-help[open] summary::before { transform: rotate(45deg); }
.gl-help summary:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.gl-help[open] summary { margin-bottom: var(--s1); }
/* The goal form's ways to measure: large choices (round 4, #3), then one sentence, the target and the folded builder. */
.gl-presets { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--s2); }
.gl-preset {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--s1); min-height: 56px; padding: var(--s2) var(--s3);
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--card-bg); color: var(--text); font: inherit; text-align: left; cursor: pointer;
}
.gl-preset strong { font-size: var(--fs-md); font-weight: 600; }
.gl-preset span { font-size: var(--fs-sm); color: var(--text-muted); }
.gl-preset:hover { border-color: var(--clay); }
.gl-preset:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.gl-m-sentence { margin: 0; font-size: var(--fs-md); font-weight: 600; }
.gl-m-target { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--s2) var(--s3); }
.gl-m-target .field { flex: 0 1 140px; }
.gl-m-target > span { padding-bottom: var(--s2); }
.gl-m-target .btn-ghost { margin-left: auto; min-height: 36px; padding: 0 var(--s3); font-size: var(--fs-sm); }
.gl-m-custom summary { min-height: 40px; display: flex; align-items: center; gap: var(--s1); cursor: pointer; font-size: var(--fs-md); font-weight: 600; color: var(--clay-text); list-style: none; }
.gl-m-custom summary::-webkit-details-marker { display: none; }
.gl-m-custom summary::before { content: ""; width: 7px; height: 7px; margin: 0 var(--s1) 0 2px; border: solid currentColor; border-width: 0 2px 2px 0; transform: rotate(-45deg); transition: transform 0.12s; }
.gl-m-custom[open] summary::before { transform: rotate(45deg); }
.gl-m-custom summary:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.gl-m-custom[open] summary { margin-bottom: var(--s2); }
/* The Training and Matches Dashboard's "Goals this week" (goalStrip.js): the goals measured from that board,
   each a chip with its headline number and status that opens the goal on Identity. */
.goal-strip { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); margin-bottom: var(--s3); }
.goal-strip h2 { margin: 0 var(--s1) 0 0; font-size: var(--fs-sm); font-weight: 650; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em; }
.gs-item {
  display: inline-flex; align-items: center; gap: var(--s2); max-width: 100%; min-height: 36px; padding: 0 var(--s3);
  border: 1px solid var(--border); border-radius: 999px; background: var(--card-bg); color: var(--text); font: inherit; font-size: var(--fs-sm); cursor: pointer;
}
.gs-item:hover { border-color: var(--clay); }
.gs-item:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.gs-name { min-width: 0; max-width: 30ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.gs-now, .gs-item .gl-status { white-space: nowrap; }
/* On a phone the pill is too narrow for the name beside the figures (it shrank to nothing): the name takes a line. */
@media (max-width: 640px) {
  .gs-item { flex-wrap: wrap; border-radius: var(--radius); padding: var(--s2) var(--s3); text-align: left; }
  .gs-name { flex: 1 0 100%; max-width: 100%; white-space: normal; }
}
.gs-now { color: var(--text-muted); font-variant-numeric: tabular-nums; }
/* Progress toward a goal as a ring (goalStrip.js ringHtml): the status colour, the accent without one. It decorates; the words say it. */
.ring { flex: none; width: 20px; height: 20px; margin-right: var(--s2); vertical-align: -5px; transform: rotate(-90deg); color: var(--accent); }
.ring circle { fill: none; stroke-width: 3; }
.ring-track { stroke: color-mix(in srgb, currentColor 20%, transparent); }
.ring-fill { stroke: currentColor; }
.ring-green { color: var(--win); }
.ring-yellow { color: var(--amber); }
.ring-red { color: var(--loss); }
@media (prefers-reduced-motion: no-preference) {
  @keyframes ring-in { from { stroke-dasharray: 0 100; } }
  .ring-fill { animation: ring-in var(--dur-2) var(--ease); }
}
.gl-cond { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); width: 100%; }
.gl-cond select[name$="field"] { flex: 1 1 180px; min-width: 0; }
.gl-cond input { flex: 1 1 120px; min-width: 0; }
.gl-cond-op { flex: none; padding: 0 var(--s1); font-size: var(--fs-md); color: var(--text-muted); }
.gl-cond select[name$="op"] { flex: 0 0 84px; }
@media (max-width: 640px) {
  .id-doc, .id-panel { gap: var(--s5); }
  .tw { padding: var(--s3); }
}
/* Narrower than the page's 760px: the five columns leave the goal too little room, so a row stacks, each part on
   the full width: area; goal; now; status and its phrase; sparkline; a player's Check in goes under the row. */
@media (max-width: 800px) {
  .gl-row { grid-template-columns: minmax(0, 1fr); }
  .gl-row-now { text-align: left; white-space: normal; }
  .gl-row-spark { justify-self: start; }
  .gl-item > .gl-row { flex-basis: 100%; }
  .gl-row-ci { flex: 0 0 auto; margin: 0 0 var(--s2) var(--s3); }
  .gl-suggest { grid-template-columns: minmax(0, 1fr); }
}
/* A phone: the goal's title takes the full width and its status sits below (round 7 #9: the status beside it left the title a 90px column). */
@media (max-width: 560px) {
  .gl-row { grid-template-columns: minmax(0, 1fr); }
  .gl-row-status { flex-direction: row; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s3); text-align: left; }
  .gl-row-now, .gl-row-spark { grid-column: 1; justify-self: start; }
}
/* The tab's dialog (entryDialog.js openDialog): a goal with its check-ins and notes, or a form. */
.gl-dialog p { margin: 0; }
.gl-goal { display: flex; flex-direction: column; gap: var(--s3); padding-bottom: var(--card-pad); }
.gl-meta { display: flex; flex-wrap: wrap; gap: 0 var(--s3); }
.gl-fromlog { display: flex; flex-direction: column; gap: var(--s1); padding: var(--s3); background: var(--surface); border-radius: var(--radius-sm); }
.gl-fromlog .gl-spark { height: 56px; max-width: 360px; margin-top: var(--s1); }
.gl-fromlog .gl-weeks { margin-top: var(--s2); }
.gl-fromlog .gl-bar { background: var(--card-bg); }
.gl-st-line { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s1) var(--s3); }
/* The goal form's "Measure it from my log". */
.gl-metric { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 0 var(--s3); }
.gl-metric[open] { padding-bottom: var(--s3); }
.gl-metric > summary { min-height: 44px; display: flex; align-items: center; gap: var(--s1); cursor: pointer; font-weight: 600; font-size: var(--fs-md); list-style: none; }
.gl-metric > summary::-webkit-details-marker { display: none; }
.gl-metric > summary::before { content: ""; width: 7px; height: 7px; margin: 0 var(--s1) 0 2px; border: solid currentColor; border-width: 0 2px 2px 0; transform: rotate(-45deg); transition: transform 0.12s; }
.gl-metric[open] > summary::before { transform: rotate(45deg); }
.gl-metric > summary:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.gl-metric-body { display: flex; flex-direction: column; gap: var(--s3); }
.gl-metric-body > p { margin: 0; }
.gl-where { border: 0; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); min-width: 0; }
.gl-where legend { font-size: var(--fs-sm); font-weight: 600; margin-bottom: var(--s1); padding: 0; }
.gl-actions { display: flex; flex-wrap: wrap; gap: var(--s2); }
.gl-dialog .form-actions { display: flex; align-items: center; }
.gl-delete { margin-left: auto; color: var(--loss); }
.gl-sub { margin: var(--s2) 0 0; font-size: var(--fs-base); }
/* A deleted check-in's Undo, at the top of the History (goals.js historyHtml). */
.gl-undo { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); margin: 0; padding: var(--s1) var(--s3); background: var(--surface); border-radius: var(--radius-sm); font-size: var(--fs-sm); }
.gl-form { display: flex; flex-direction: column; gap: var(--s3); }
.gl-form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s3); align-items: start; }
.gl-dialog .field .chip-row { margin-top: var(--s1); align-items: center; }
.chip-btn { background: var(--card-bg); color: var(--text); font: inherit; font-size: var(--fs-sm); cursor: pointer; min-height: 32px; }
.chip-btn:hover { border-color: var(--clay); color: var(--clay-text); }
/* The goal's History: every row's kind (Check-in or Note) as a label, a check-in's colour as a dot by its words. */
.gl-h-colour { display: inline-flex; align-items: center; color: var(--text); font-weight: 600; }
.gl-h-kind { display: inline-flex; align-items: center; min-height: 24px; padding: 0 var(--s2); border-radius: 999px; border: 1px solid var(--border); background: var(--surface); color: var(--text); font-size: var(--fs-sm); font-weight: 600; }
/* The check-in (goals.js checkinFormHtml): three equal choices in a row. The radios are hidden; the whole
   tile is the control (arrow keys move between them), outlined in its colour, and the chosen one is tinted
   in it with a check mark (--ci: its colour). Focus is a neutral ring in the text colour, so it is never taken
   for the choice. */
.ci-green { --ci: var(--win); }
.ci-yellow { --ci: var(--amber); }
.ci-red { --ci: var(--loss); }
.gl-dialog .ci-goal { margin: calc(-1 * var(--s2)) 0 var(--s3); color: var(--text-muted); font-size: var(--fs-md); overflow-wrap: anywhere; }
.ci-group { border: 0; margin: 0; padding: 0; min-width: 0; }
.ci-group legend { padding: 0; margin-bottom: var(--s2); font-size: var(--fs-base); font-weight: 650; }
.ci-choices { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s2); }
.ci-choice {
  position: relative; display: flex; align-items: center; justify-content: center; gap: var(--s1); min-height: 56px; padding: var(--s2);
  border: 2px solid color-mix(in srgb, var(--ci) 45%, var(--border)); border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--ci) 6%, var(--card-bg)); color: var(--text);
  font-size: var(--fs-base); font-weight: 600; text-align: center; cursor: pointer; user-select: none;
}
.ci-choice input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }
.ci-choice:hover { background: color-mix(in srgb, var(--ci) 14%, var(--card-bg)); }
.ci-choice:has(input:focus-visible) { outline: 2px dashed var(--text); outline-offset: 3px; }
.ci-choice:has(input:checked) { border-color: var(--ci); background: color-mix(in srgb, var(--ci) 32%, var(--card-bg)); box-shadow: inset 0 0 0 1px var(--ci); }
.ci-mark { display: none; }
.ci-choice:has(input:checked) .ci-mark { display: inline; }
.ci-choice:has(input:checked) .ci-mark::before { content: "✓"; font-weight: 700; }
.gl-checkin textarea { min-height: calc(2lh + 20px); }
.ci-date { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }
.ci-date input { max-width: 160px; }
.ci-date-btn { min-height: 36px; padding: 0 var(--s2); border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--clay-text); font: inherit; font-size: var(--fs-md); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
.ci-date-btn:hover { background: var(--row-hover); }
.ci-date-btn:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
@media (max-width: 640px) {
  .gl-form-grid { grid-template-columns: 1fr; }
  .chip-btn { min-height: 36px; }
}

/* ==========================================================================
   Planning (planning.js): the quarter timeline, the month calendar and the item panel.
   --plan-c0 .. --plan-c9: the plan type palette (planDates.js planTypeColour; the five suggestions
   take 0-4, the player's own types 5-9) with --plan-ink as the text on it. Light: deep tones under white text; dark: light
   tones under dark text, so bar labels stay readable in both.
   ========================================================================== */
.planning, .plan-panel, .goals-page, .gl-dialog {
  --plan-c0: #b8421a; --plan-c1: #0f6b4f; --plan-c2: #2f6496; --plan-c3: #6d47a3;
  --plan-c4: #7f5a00; --plan-c5: #9c3263; --plan-c6: #276b74; --plan-c7: #57606e; --plan-c8: #8a4a12; --plan-c9: #4f5f14; --plan-ink: #ffffff;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) :is(.planning, .plan-panel, .goals-page, .gl-dialog) {
    --plan-c0: #f08a5d; --plan-c1: #3fbf8a; --plan-c2: #7fb0ea; --plan-c3: #b79af0;
    --plan-c4: #e3bb52; --plan-c5: #ef8fbf; --plan-c6: #5ccfd9; --plan-c7: #aab6c6; --plan-c8: #d9a77a; --plan-c9: #b7cf6e; --plan-ink: #0b1522;
  }
}
:root[data-theme="dark"] :is(.planning, .plan-panel, .goals-page, .gl-dialog) {
  --plan-c0: #f08a5d; --plan-c1: #3fbf8a; --plan-c2: #7fb0ea; --plan-c3: #b79af0;
  --plan-c4: #e3bb52; --plan-c5: #ef8fbf; --plan-c6: #5ccfd9; --plan-c7: #aab6c6; --plan-c8: #d9a77a; --plan-c9: #b7cf6e; --plan-ink: #0b1522;
}
.plan-c0 { --pc: var(--plan-c0); } .plan-c1 { --pc: var(--plan-c1); } .plan-c2 { --pc: var(--plan-c2); } .plan-c3 { --pc: var(--plan-c3); }
.plan-c4 { --pc: var(--plan-c4); } .plan-c5 { --pc: var(--plan-c5); } .plan-c6 { --pc: var(--plan-c6); } .plan-c7 { --pc: var(--plan-c7); } .plan-c8 { --pc: var(--plan-c8); } .plan-c9 { --pc: var(--plan-c9); }

.plan-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); flex-wrap: wrap; margin-bottom: var(--s4); }
.plan-title { display: flex; align-items: center; gap: var(--s3); }
.card .plan-title h2 { margin: 0; }
.plan-nav { display: inline-flex; gap: var(--s1); }
.btn-ghost.plan-arrow { min-height: 36px; min-width: 36px; padding: 0; font-size: var(--fs-lg); }

/* Quarter timeline: one column per month, week ticks, item bars in lanes, today as a line. */
.plan-track { --qbar: 20px; --qlane: 24px; position: relative; height: calc(30px + var(--lanes) * var(--qlane) + 6px); border-radius: var(--radius-sm); background: var(--surface); border: 1px solid var(--border); overflow: hidden; }
.plan-qmonth {
  position: absolute; top: 0; height: 100%; padding: 6px 8px; display: flex; align-items: flex-start;
  border: none; border-left: 1px solid var(--border); background: transparent; color: var(--text-muted);
  font: inherit; font-size: var(--fs-xs); font-weight: 650; text-transform: uppercase; letter-spacing: 0.06em; cursor: pointer;
}
.plan-qmonth:first-child { border-left: none; }
.plan-qmonth:hover { background: var(--row-hover); }
.plan-qmonth.on { color: var(--clay-text); background: var(--card-bg); }
.plan-tick { position: absolute; top: 28px; bottom: 0; border-left: 1px dashed var(--border); pointer-events: none; }
.plan-now { position: absolute; top: 0; bottom: 0; width: 2px; margin-left: -1px; background: var(--accent); pointer-events: none; }
.plan-qbar, .plan-bar {
  position: absolute; height: 20px; padding: 0 6px; border: none; border-radius: 0;
  background: var(--pc); color: var(--plan-ink); font: inherit; font-size: var(--fs-xs); font-weight: 600; line-height: 20px;
  text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer;
}
.plan-qbar { top: calc(30px + var(--lane) * var(--qlane)); height: var(--qbar); line-height: var(--qbar); }
/* A finger: the quarter's bars are at least 36px tall (lanes 40px apart) on a touch screen or a phone. */
@media (pointer: coarse), (max-width: 640px) { .plan-track { --qbar: 36px; --qlane: 40px; } }
.plan-bar { top: calc(36px + var(--lane) * 24px); }
:is(.plan-qbar, .plan-bar).starts { border-top-left-radius: 6px; border-bottom-left-radius: 6px; }
:is(.plan-qbar, .plan-bar).ends { border-top-right-radius: 6px; border-bottom-right-radius: 6px; }
:is(.plan-qbar, .plan-bar).clipped, :is(.plan-qbar, .plan-bar).plan-tbar.clipped { color: transparent; } /* no letter or two: a quarter bar's name is listed under the track, a calendar bar's is its tooltip */
.plan-qkey { list-style: none; margin: var(--s3) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--s1) var(--s4); font-size: var(--fs-sm); }
.plan-qkey li[hidden] { display: none; }
.plan-qkey .link-btn { font-size: inherit; }
.plan-qbar:hover, .plan-bar:hover { filter: brightness(1.08); }
:is(.plan-qbar, .plan-bar, .plan-qmonth):focus-visible { outline-offset: -2px; } /* the track clips an outer ring */
/* A tournament played (from Matches, planDay.js loggedTournaments): outlined in the Tournament colour, so it
   never reads as a planned item; a month-only one is a chip under the month's heading. */
:is(.plan-qbar, .plan-bar).plan-tbar { background: color-mix(in srgb, var(--pc) 12%, var(--card-bg)); color: var(--text); box-shadow: inset 0 0 0 1.5px var(--pc); }
.plan-swatch.plan-tswatch { background: color-mix(in srgb, var(--pc) 12%, var(--card-bg)); box-shadow: inset 0 0 0 1.5px var(--pc); }
.plan-month-tours { display: flex; flex-wrap: wrap; gap: var(--s2); margin: calc(var(--s2) * -1) 0 var(--s3); }
.plan-legend { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); margin-top: var(--s3); font-size: var(--fs-sm); color: var(--text-muted); }
.plan-legend span { display: inline-flex; align-items: center; gap: 6px; }
.plan-swatch { display: inline-block; width: 12px; height: 12px; border-radius: 3px; background: var(--pc); flex: none; }
.plan-quarter > .muted { margin: var(--s3) 0 0; }

/* Month calendar: Monday to Sunday, six rows; the bars run under the day numbers. */
.plan-dows { display: grid; grid-template-columns: repeat(7, 1fr); border-bottom: 1px solid var(--border); }
.plan-dows span { padding: var(--s1) var(--s2); font-size: var(--fs-xs); font-weight: 650; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em; }
.plan-week { position: relative; display: grid; grid-template-columns: repeat(7, 1fr); min-height: calc(36px + var(--lanes) * 24px + 6px); border-bottom: 1px solid var(--border); }
.plan-day {
  display: flex; align-items: flex-start; justify-content: space-between; min-width: 0; padding: 4px 6px;
  border: none; border-left: 1px solid var(--border); background: transparent; color: var(--text); font: inherit; text-align: left; cursor: pointer;
}
.plan-day:first-child { border-left: none; }
.plan-day:hover { background: var(--row-hover); }
.plan-day.out { background: var(--surface); color: var(--text-muted); }
.plan-day.past .plan-dn { color: var(--text-muted); }
.plan-dn { display: inline-grid; place-items: center; min-width: 26px; height: 26px; border-radius: 999px; font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.plan-day.today .plan-dn { box-shadow: inset 0 0 0 2px var(--accent); color: var(--clay-text); font-weight: 700; }
.plan-dot { display: inline-block; width: 7px; height: 7px; margin: 9px 2px 0 0; border-radius: 999px; background: var(--court-green); flex: none; }
.plan-key { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s4); margin: var(--s3) 0 0; }
.plan-key > span { display: inline-flex; align-items: center; gap: 6px; }
.plan-key .plan-dot, .plan-key .plan-gmark { margin: 0; }
/* Calendar (planning.js openCalendarMenu): one small button in the toolbar opens Import from calendar file and Add to calendar. */
.plan-head-actions { display: inline-flex; align-items: center; gap: var(--s2); }
.plan-cal-menu { min-height: 36px; padding: 0 var(--s3); font-size: var(--fs-md); }
.plan-cal-dialog .plan-cal-sec { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s2); margin: 0 0 var(--s4); }
.plan-cal-dialog .plan-cal-sec h3 { margin: 0; }
.plan-export-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); }
.plan-cal-dialog .hint, .plan-import-help { margin: 0; color: var(--text-muted); font-size: var(--fs-md); }
.plan-cal-dialog .hint { font-size: var(--fs-sm); }
/* Import from calendar (calendarImportUi.js): a file dropped on the tab, and the preview dialog. */
.planning.is-dropping { outline: 2px dashed var(--accent); outline-offset: calc(-1 * var(--s1)); border-radius: var(--radius); }
.cal-import-dialog .cal-summary { margin: 0 0 var(--s2); font-weight: 600; }
.cal-import-dialog .muted.small { margin: 0 0 var(--s2); }
.cal-upcoming { display: flex; align-items: center; gap: var(--s2); margin: var(--s3) 0; font-size: var(--fs-md); }
.cal-rows, .cal-skipped { list-style: none; margin: 0 0 var(--s3); padding: 0; }
.cal-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s2) var(--s3); padding: var(--s2) 0; border-bottom: 1px solid var(--border); }
.cal-pick { display: flex; align-items: flex-start; gap: var(--s2); flex: 1 1 220px; min-width: 0; }
.cal-pick input { margin: var(--s1) 0 0; flex: none; }
.cal-main { display: flex; flex-direction: column; min-width: 0; overflow-wrap: anywhere; }
.cal-main b { font-weight: 600; }
.cal-type select { min-height: 36px; }
.cal-tag { font-size: var(--fs-xs); color: var(--text-muted); }
.cal-skipped li { padding: var(--s1) 0; overflow-wrap: anywhere; }
.cal-skipped summary { cursor: pointer; margin-bottom: var(--s2); font-size: var(--fs-sm); }
.cal-skipped ul { list-style: none; margin: 0; padding: 0; }
/* A day's marks: a ring for day goals (filled once all are done; round 5, #6), the dot for a logged day. */
.plan-marks { display: inline-flex; align-items: flex-start; gap: 3px; }
.plan-gmark { display: inline-block; width: 8px; height: 8px; margin: 8px 0 0; border: 1.5px solid var(--clay); border-radius: 999px; flex: none; }
.plan-gmark.is-done { background: var(--clay); }
/* Under a calendar row, the whole plan's week (planning.js weekRowHtml): one line and a thin bar of the hours done. */
.plan-wsum { margin: 0; padding: var(--s1) var(--s2) var(--s2); border-bottom: 1px solid var(--border); font-size: var(--fs-sm); color: var(--text-muted); overflow-wrap: anywhere; }
.plan-wsum .plan-meter { max-width: 240px; }
.plan-week:has(+ .plan-wsum) { border-bottom: 0; }

/* The item panel and the day summary: a medium .entry-dialog like every other (planning.js). */
.plan-form .form-grid { grid-template-columns: 1fr 1fr; }
.plan-chips { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: var(--s1); }
.plan-chip {
  display: inline-flex; align-items: center; gap: 6px; min-height: 32px; padding: 0 var(--s3); border-radius: 999px;
  border: 1px solid var(--control-border); background: var(--card-bg); color: var(--text); font: inherit; font-size: var(--fs-sm); cursor: pointer;
}
.plan-chip:hover { background: var(--surface); }
.plan-hours { margin: var(--s4) 0 0; padding: var(--s3) var(--s4) var(--s4); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.plan-hours legend { padding: 0 var(--s1); font-size: var(--fs-sm); font-weight: 600; color: var(--text); }
.plan-hours-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s3); }
.plan-form input, .plan-form textarea { width: 100%; }
.plan-form .plan-goal input { width: auto; margin-top: 3px; }
.plan-goals { margin: var(--s4) 0; }
.plan-goal { display: flex; align-items: flex-start; gap: var(--s2); padding: var(--s2) 0; font-size: var(--fs-md); }
.plan-goal-status { display: block; margin-top: 2px; font-size: var(--fs-sm); }

/* The day summary in the same panel: the day's entries, matches, notes and plan items. */
.plan-nothing { margin: 0 0 var(--s4); }
.plan-day-sec { margin: 0 0 var(--s4); }
.plan-day-sec .section-label { margin: 0 0 var(--s2); }
.plan-day-entry { padding: var(--s3); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); }
.plan-day-entry + .plan-day-entry { margin-top: var(--s2); }
.plan-day-entry-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s3); }
.plan-day-entry-head .open-btn { min-height: 32px; padding: 0 var(--s2); font-size: var(--fs-sm); }
@media (max-width: 640px) { .plan-day-entry-head .open-btn { min-height: 36px; } }
.plan-cal-link { margin-left: 0; font-size: var(--fs-sm); min-height: 36px; }
.plan-day-entry-head h4 { margin: 0; font-size: var(--fs-md); color: var(--text); overflow-wrap: anywhere; }
/* A card is at most three lines: the title, the numbers, and the entry's own note cut to one line (Open shows it all). */
.plan-day-line { margin: var(--s1) 0 0; font-size: var(--fs-sm); color: var(--text); overflow-wrap: anywhere; }
.plan-day-text { color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.plan-day-entry > .hint { margin: var(--s2) 0 0; }
/* A plan item covering the day: a card like an entry's, with its hours, linked goals and notes. */
.plan-day-plan h4 { display: flex; align-items: center; gap: var(--s2); }
.plan-day-plan > .plan-day-line.hint { margin-top: var(--s1); color: var(--text-muted); }
.plan-day-notes { white-space: pre-line; color: var(--text-muted); }
/* Day goals (read-only: ticked in the evening routine) and an item's linked goals: plain list marks, no controls. */
.plan-day-goals { list-style: none; margin: var(--s2) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--s2); font-size: var(--fs-md); }
.plan-day-sec > .plan-day-goals { margin: 0; }
.plan-day-goals li { display: flex; align-items: flex-start; gap: var(--s2); }
.plan-mark { flex: none; width: 1.2em; text-align: center; color: var(--text-muted); }
.plan-day-goals li.done .plan-mark { color: var(--court-green); font-weight: 700; }

/* Planned vs logged (planDates.js planVsLog): a week per line in the item's form, a thin bar of the hours done
   under it; on a calendar bar, a line along its bottom edge in the bar's text colour. */
.plan-weeks { margin: var(--s4) 0 0; }
.plan-weeks ul { list-style: none; margin: var(--s2) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--s3); font-size: var(--fs-sm); }
.plan-weeks .hint { margin: var(--s2) 0 0; }
.plan-meter { display: block; height: 6px; margin-top: var(--s1); border-radius: var(--radius-pill); background: var(--border); overflow: hidden; }
.plan-meter > i, .plan-bar-done > i { display: block; height: 100%; background: var(--court-green); }
.plan-meter > i { border-radius: inherit; transition: width var(--dur-2) var(--ease); }
.plan-bar-done { position: absolute; left: 0; right: 0; bottom: 0; height: 4px; background: color-mix(in srgb, var(--plan-ink) 30%, transparent); pointer-events: none; }
.plan-bar-done > i { background: var(--plan-ink); }

@media (max-width: 640px) {
  .plan-title { gap: var(--s2); }
  .plan-day { position: relative; justify-content: center; padding: 2px; }
  .plan-day .plan-marks { position: absolute; top: 3px; right: 3px; gap: 2px; }
  .plan-day .plan-dot { margin: 0; width: 6px; height: 6px; }
  .plan-day .plan-gmark { margin: 0; width: 8px; height: 8px; }
  .plan-dows span { padding: var(--s1) 0; text-align: center; }
  .plan-bar { padding: 0 3px; }
  .plan-form .form-grid { gap: var(--s3); }
  /* + Log floats over the last rows' Sunday cells (usability test round 4, B21). main's bottom room (the
     "+ Log on phones" block) lets them scroll clear; this keeps a day reached by keyboard or brought into
     view clear of the button too. */
  html:has(body.log-fab-on .planning) { scroll-padding-bottom: 152px; }
}

/* Share (members.js, oldShares.js): who has access and the players who share with me, each row boxed.
   Empty lists take no space. */
#member-list, #player-list, #grant-list { display: flex; flex-direction: column; gap: var(--s3); margin-top: var(--s3); }
#player-list:empty, #grant-list:empty { display: none; }
#member-list .share-row, #player-list .share-row, #grant-list .share-row { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--s3) var(--s4); background: var(--surface); }
#member-list .share-main strong, #player-list .share-main strong { overflow-wrap: anywhere; }
#backup-msg:empty, #share-msg:empty { display: none; }
#view-account .card > p.muted:first-child { margin-top: 0; }

/* ---- Notes tab and an entry's notes (notes.js, notesUi.js) ---- */
/* The page: a reading column on the page background, not a card in a card. */
.notes-page { max-width: 860px; margin: 0 auto; display: flex; flex-direction: column; gap: var(--s2); }
.notes-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); }
.notes-head h2 { margin: 0; font-size: var(--fs-xl); }
.notes-count { margin: 0; font-size: var(--fs-sm); color: var(--text-muted); }
.notes-count .link-btn { margin-left: var(--s1); font-size: var(--fs-sm); color: var(--clay-text); }
.notes-new { min-height: 44px; padding: 0 var(--s5); font-size: var(--fs-base); display: inline-flex; align-items: center; gap: var(--s2); }
.notes-new .ico { width: 18px; height: 18px; }
.ico { width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* The filter bar: one row of pills (kind, person, tag, More) and the search icon; a pill with a choice made is outlined. */
.notes-bar { display: flex; flex-direction: column; gap: var(--s2); }
.notes-pills, .notes-more { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }
.notes-more[hidden], .notes-search[hidden] { display: none; }
.notes-pills select.notes-pill, .notes-more select.notes-pill, .notes-pill-btn {
  width: auto; min-height: 36px; padding: 0 var(--s4); border-radius: 999px; border: 1px solid var(--control-border);
  background-color: var(--card-bg); color: var(--text); font: inherit; font-size: var(--fs-md);
}
.notes-pills select.notes-pill { padding-right: 30px; }
.notes-pill-btn { display: inline-flex; align-items: center; gap: var(--s2); cursor: pointer; }
.notes-pill.is-set { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.notes-search-btn { flex: none; border: 1px solid var(--control-border); border-radius: 999px; width: 36px; height: 36px; display: inline-grid; place-items: center; }
.notes-search-btn.is-set { border-color: var(--accent); }
.notes-search { width: 100%; min-height: 40px; }
.notes-log { display: flex; align-items: center; gap: var(--s2); font-size: var(--fs-sm); color: var(--text-muted); }
.notes-log[hidden] { display: none; }
.notes-log input { width: 18px; height: 18px; margin: 0; }
/* A heading per day, week or month that stays at the top while its notes scroll by. */
.notes-day h3 {
  position: sticky; top: 0; z-index: 2; margin: 0 0 var(--s3); padding: var(--s2) 0; background: var(--bg);
  font-size: var(--fs-xs); font-weight: 700; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.4px; border-bottom: 1px solid var(--border);
}
.notes-day + .notes-day { margin-top: var(--s5); }
/* A note is a card in the colour of whoever wrote it (noteColours.js): --tone is theirs, and the card's tint, its band, the
   badge and the name follow it. One slot of the chart palette per person; the player's own notes and the log's are one
   neutral-warm colour. Text stays --text on the tint (tests/note-colours.test.mjs checks the contrast). */
.tone-me { --tone: color-mix(in srgb, var(--chart-4) 45%, var(--text-muted)); }
.tone-0 { --tone: var(--chart-1); } .tone-1 { --tone: var(--chart-2); } .tone-2 { --tone: var(--chart-7); } .tone-3 { --tone: var(--chart-6); }
.tone-4 { --tone: var(--chart-3); } .tone-5 { --tone: var(--chart-5); } .tone-6 { --tone: var(--chart-8); }
.note-card {
  position: relative; display: flex; flex-direction: column; gap: var(--s1); padding: var(--s2) var(--s3) var(--s2) var(--s3);
  background: color-mix(in srgb, var(--tone) 12%, var(--card-bg));
  border: 1px solid var(--border); border-left: 3px solid var(--tone); border-radius: var(--radius-sm);
}
.note-card.is-new { box-shadow: 0 0 0 1px var(--accent); }
/* One line: the New dot, the writer and their role (cut with "…" when long), the kind, the time at the right. */
.note-head { display: flex; align-items: center; gap: var(--s2); min-width: 0; line-height: 1.3; }
.note-badge {
  display: inline-flex; flex: none; align-items: center; gap: var(--s1); padding: 0 var(--s2); border-radius: 999px; white-space: nowrap;
  background: color-mix(in srgb, var(--tone) 26%, var(--card-bg)); color: var(--text); font-size: var(--fs-xs); font-weight: 650;
}
.note-badge .ico { color: var(--tone); width: 12px; height: 12px; }
.note-when { flex: none; margin-left: auto; font-size: var(--fs-xs); font-weight: 500; color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.note-byline { display: flex; flex: 0 1 auto; align-items: baseline; gap: var(--s1); min-width: 0; overflow: hidden; white-space: nowrap; }
.note-name { flex: 0 0 auto; max-width: 100%; font-size: var(--fs-md); font-weight: 700; overflow: hidden; text-overflow: ellipsis; color: color-mix(in srgb, var(--tone) 60%, var(--text)); }
.note-role { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-size: var(--fs-xs); color: var(--text-muted); }
.note-card > .note-text, .note-card .note-under { max-width: 72ch; }
.note-card .note-text { font-size: var(--fs-md); line-height: 1.4; }
.note-clamp { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
.note-clamp.is-open { display: block; }
.note-more { margin: 0; font-size: var(--fs-xs); color: var(--clay-text); }
.note-more[hidden] { display: none; }
/* Under the text, on one line when it fits: Read more, the link and tags, and the buttons at the right. */
.note-under { display: flex; flex-wrap: wrap; align-items: center; column-gap: var(--s3); min-height: 0; }
.note-under .note-actions { margin-left: auto; gap: 0; }
.note-foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1); }
.note-under > .note-foot { flex: 1 1 0; min-width: 0; }
.note-foot .note-link { min-height: 24px; max-width: 100%; padding: 0 var(--s2); font-size: var(--fs-xs); }
.note-foot .note-link:not(.plan-note-link) span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.note-foot .note-link .ico { width: 12px; height: 12px; }
.note-foot .chip-row { margin: 0; }
.note-link { display: inline-flex; align-items: center; gap: var(--s1); }
.note-link span { min-width: 0; overflow-wrap: anywhere; }
.note-link .ico { width: 14px; height: 14px; color: var(--text-muted); }
.note-list-plain { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s3); }
.note-item { display: flex; flex-direction: column; gap: var(--s2); padding: var(--s3); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.note-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); font-size: var(--fs-sm); color: var(--text-muted); }
/* The Planning day summary: a note's link on one line, cut with "…" (planning.js; the whole text on hover). */
.note-foot .plan-note-link { display: inline-block; max-width: min(100%, 32ch); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.note-text { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.chip-btn { background: var(--card-bg); color: var(--text); font: inherit; font-size: var(--fs-sm); cursor: pointer; }
.chip-btn:hover { border-color: var(--text-muted); }
.note-link.unlinked { border-style: dashed; color: var(--text-muted); }
.note-link.chip-btn { text-align: left; }
@media (max-width: 640px) { .note-link { border-radius: var(--radius-sm); } }
.note-actions { display: flex; flex-wrap: wrap; gap: var(--s2); }
.note-actions .note-btn { min-height: 32px; padding: 0 var(--s3); font-size: var(--fs-sm); }
/* A note's own buttons are small text buttons: seen on hover or focus where there is a pointer, always on touch. */
.note-under .note-actions .note-btn { min-height: 24px; padding: 0 var(--s2); border: 0; background: transparent; color: var(--text-muted); font-size: var(--fs-xs); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; }
.note-under .note-actions .note-btn:hover { color: var(--text); background: transparent; }
.note-under .note-actions .note-btn:focus-visible { outline: 2px solid var(--clay-text); outline-offset: 1px; } /* 3:1 on the tint in both themes */
@media (hover: hover) and (min-width: 641px) {
  .note-reply { position: relative; }
  .note-under > .note-actions {
    position: absolute; top: var(--s2); right: var(--s3); z-index: 1; opacity: 0; padding-left: var(--s2);
    background: color-mix(in srgb, var(--tone) 12%, var(--card-bg));
  }
  .note-reply > .note-under > .note-actions { background: color-mix(in srgb, var(--tone) 9%, var(--surface)); }
  .note-card:hover:not(:has(.note-reply:hover)) > .note-under > .note-actions, .note-reply:hover > .note-under > .note-actions,
  .note-under > .note-actions:focus-within { opacity: 1; }
  .note-card:hover:not(:has(.note-reply:hover)) > .note-head .note-when, .note-reply:hover > .note-head .note-when,
  .note-card:has(> .note-under > .note-actions:focus-within) > .note-head .note-when { visibility: hidden; }
}
.entry-notes { margin-top: var(--s4); padding-top: var(--s3); border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: var(--s2); }
.entry-notes-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); }
.entry-notes h3 { margin: 0; font-size: var(--fs-base); }
.note-form { display: flex; flex-direction: column; gap: var(--s3); }
.note-form h2 { margin: 0; }
.note-pick { display: flex; flex-direction: column; gap: var(--s3); margin: 0; padding: 0; border: 0; }
.note-pick legend { padding: 0; margin-bottom: var(--s2); font-size: var(--fs-sm); font-weight: 600; }
.note-pick [hidden] { display: none; }
.note-pick select[size] { height: auto; min-height: 0; padding: var(--s1); background-image: none; }
.note-fixed { margin: 0; display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
/* Replies under a note (notesUi.js repliesHtml): nested and indented with a line in the colour of their writer's kind. */
.note-replies { list-style: none; margin: var(--s2) 0 0 var(--s4); padding: 0; display: flex; flex-direction: column; gap: var(--s2); }
.note-reply {
  display: flex; flex-direction: column; gap: 2px; padding: var(--s1) var(--s2); background: color-mix(in srgb, var(--tone) 9%, var(--surface));
  border: 1px solid var(--border); border-left: 2px solid var(--tone); border-radius: var(--radius-sm); font-size: var(--fs-sm);
}
.note-reply.is-new { box-shadow: 0 0 0 1px var(--accent); }
.note-reply .note-text { font-size: var(--fs-sm); max-width: 64ch; line-height: 1.35; }
.note-reply .note-name { font-size: var(--fs-sm); }
.note-badge-reply { background: transparent; padding: 0; color: var(--text-muted); }
/* The person filter on the Notes tab (notes.js drawPeople): a pill that opens a list of buttons, each with the dot of that person's colour. */
.person-menu { position: relative; }
.person-menu > summary { list-style: none; }
.person-menu > summary::-webkit-details-marker { display: none; }
.person-menu > summary::after { content: ""; width: 6px; height: 6px; margin-left: var(--s1); border: solid currentColor; border-width: 0 2px 2px 0; transform: translateY(-2px) rotate(45deg); }
.person-list {
  position: absolute; z-index: 5; left: 0; top: calc(100% + var(--s1)); min-width: 100%; width: max-content; max-width: min(80vw, 320px);
  margin: 0; padding: var(--s1); list-style: none; background: var(--card-bg); border: 1px solid var(--border);
  border-radius: var(--radius-sm); box-shadow: var(--shadow);
}
.person-opt {
  display: flex; align-items: center; gap: var(--s2); width: 100%; min-height: 36px; padding: 0 var(--s3); border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--text); font: inherit; font-size: var(--fs-md); text-align: left; cursor: pointer;
}
.person-opt:hover { background: var(--row-hover); }
.person-opt[aria-pressed="true"] { font-weight: 700; }
.person-dot { flex: none; width: 10px; height: 10px; border-radius: 50%; background: var(--tone); }
/* The reply dialog quotes the note it answers. */
.note-quote { margin: 0; padding: var(--s2) var(--s3); border-left: 2px solid var(--border); color: var(--text-muted); font-size: var(--fs-sm); white-space: pre-wrap; overflow-wrap: anywhere; }
.note-quote .note-by { display: block; font-weight: 600; color: var(--text); }
/* "+2 notes" after a table's Notes text (tableCols.js commentsCell). */
.note-count { color: var(--text-muted); font-size: var(--fs-xs); white-space: nowrap; }
/* The app's "Are you sure?" (confirmDialog.js). */
.confirm-dialog .confirm-text { margin: 0 0 var(--s3); overflow-wrap: anywhere; }
.note-suggest { align-items: center; }
@media (max-width: 640px) {
  .note-actions .note-btn { min-height: 36px; }
  .note-under .note-actions .note-btn { min-height: 28px; }
  .notes-more select.notes-pill, .notes-more [data-interval] { flex: 1 1 140px; }
  .notes-head h2 { font-size: var(--fs-lg); }
  .notes-new { padding: 0 var(--s4); }
  .note-replies { margin-left: var(--s3); }
  .note-head { gap: var(--s1); }
  .note-when .note-wd { display: none; } /* a phone drops the weekday, so the role is not cut */
}

/* ---- Graphs (graphs.js): on the Dashboard of Training and Matches, above the table on All ---- */
/* No heading: only the graphs (an empty grid takes no room). "+ Add graph" is at the bottom of the
   Dashboard ([data-graphs-add]) or under All's graphs; the builder opens in a dialog. */
.graphs-area { display: flex; flex-direction: column; gap: var(--s4); margin: 0 0 var(--s4); }
.graphs-area:has(> .graphs-grid:empty):not(:has(.graphs-add-row)) { display: none; }
/* The small cards (numberCards.js): the ⋯ of a card (owner), a number card's unit, the "Hidden cards" button. */
.graphs-add-row { display: flex; flex-wrap: wrap; gap: var(--s2); }
.card-more {
  position: absolute; top: 0; right: 0; z-index: 1; width: 40px; height: 40px; display: grid; place-items: center;
  border: 0; border-radius: 0 var(--radius) 0 var(--radius-sm); background: none; color: var(--text-muted);
  font: inherit; font-size: var(--fs-lg); line-height: 1; cursor: pointer;
}
.card-more:hover { background: var(--surface); color: var(--text); }
.card-more:focus-visible { outline: 2px solid var(--clay); outline-offset: -2px; }
.kpi-card:has(.card-more) .kpi-label { padding-right: var(--s5); }
.kpi-card .kpi-unit { font-size: var(--fs-md); font-weight: 650; color: var(--text-muted); letter-spacing: 0; }
.kpi-grid > .kpi-card:nth-child(-n+3) .kpi-unit { font-size: var(--fs-lg); }
/* The change from the period before: a quiet chip under the card's period, an arrow for up and down (never the colour alone). */
.kpi-card .kpi-delta {
  display: inline-flex; align-items: center; gap: var(--s1); margin-top: var(--s2); padding: 2px var(--s2); border-radius: var(--radius-pill);
  background: var(--surface); color: var(--text); font-size: var(--fs-xs); font-weight: 600; font-variant-numeric: tabular-nums;
}
.kpi-delta.is-up::before { content: "↑"; content: "↑" / ""; }
.kpi-delta.is-down::before { content: "↓"; content: "↓" / ""; }
.kpi-grid > .kpi-more { grid-column: 1 / -1; }
.kpi-more .btn-ghost { padding: var(--s1) var(--s3); font-size: var(--fs-sm); min-height: 36px; }
.card-options { display: flex; flex-wrap: wrap; gap: var(--s2); margin: var(--s3) 0; }
.card-options .btn-ghost:disabled { opacity: 0.45; cursor: default; }
.card-hidden-list { list-style: none; margin: var(--s3) 0; padding: 0; display: flex; flex-direction: column; gap: var(--s2); }
.card-hidden-list li { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); }
.number-preview { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s2); padding: var(--s3); border-radius: var(--radius-sm); background: var(--surface); }
.number-preview strong { font-size: var(--fs-lg); font-variant-numeric: tabular-nums; }
.graphs-add:empty { display: none; }
.graphs-add { margin-top: var(--s2); }
.graphs-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--s4); align-items: start; }
.graphs-grid:empty { display: none; }
.graph-card { min-width: 0; }
.graph-card.graph-wide { grid-column: 1 / -1; }
@media (min-width: 1000px) { .graph-card.graph-wide { grid-column: span 2; } }
.graph-head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--s2); margin: 0 0 var(--s3); }
.graph-head > div:first-child { flex: 1 1 140px; min-width: 0; }
.graph-head h3 { margin: 0; font-size: var(--fs-base); color: var(--navy); overflow-wrap: anywhere; }
.graph-head p { margin: var(--s1) 0 0; }
.graph-tools { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--s1); flex-shrink: 0; }
.graph-tools .btn-ghost { padding: var(--s1) var(--s3); font-size: var(--fs-sm); min-height: 32px; }
.graph-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: var(--s3); }
.graph-stats > div { display: flex; flex-direction: column; gap: var(--s1); min-width: 0; }
.graph-stats .stat-value { font-size: var(--fs-kpi); font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.graph-table { max-height: 420px; overflow: auto; }
.graph-builder { display: flex; flex-direction: column; gap: var(--s3); }
.graph-builder label { display: flex; flex-direction: column; gap: var(--s1); font-size: var(--fs-sm); font-weight: 600; }
.graph-builder .form-grid-inline { align-items: flex-end; margin: 0; }
.graph-builder .form-grid-inline label { flex: 1 1 220px; }
.graph-builder fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
.graph-builder legend, .graph-label { display: block; font-weight: 600; font-size: var(--fs-sm); margin-bottom: var(--s2); }
.graph-builder .muted.small { margin: var(--s1) 0 0; font-weight: 400; }
.chart-options { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s2); }
.graph-builder .chart-option { flex-direction: row; align-items: flex-start; gap: var(--s3); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--s3); cursor: pointer; font-weight: 400; }
.chart-option input { margin-top: 3px; }
.chart-option span { display: flex; flex-direction: column; gap: var(--s1); }
.chart-option small { color: var(--text-muted); }
.chart-option:has(input:checked) { border-color: var(--clay); box-shadow: inset 0 0 0 1px var(--clay); }
.series-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); margin: 0 0 var(--s2); }
.series-row select, .series-row input { min-width: 0; max-width: 100%; }
.series-row input { flex: 0 1 160px; }
.graph-columns { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: var(--s1) var(--s3); }
.graph-builder .consent { font-weight: 400; }
.graph-builder .board-toolbar { margin: 0; }
.chip .chip-x { min-width: 24px; min-height: 24px; }
@media (max-width: 640px) {
  .graph-head { flex-direction: column; }
  .graph-head > div:first-child { flex-basis: auto; } /* in a column the 140px basis was an empty gap */
  .graph-tools { justify-content: flex-start; }
  .graph-tools .btn-ghost { min-height: 36px; min-width: 36px; }
}

/* ---- A coach-only account (app.js updateCoachOnly, body.coach-only): its players, no own log ---- */
.coach-only-show { display: none; }
body.coach-only .coach-only-show { display: revert; }
body.coach-only .player-only { display: none; }
.own-log { margin: var(--s3) 0 0; }
.own-log .link-btn { font-size: inherit; margin-left: 0; }
.coach-answered { margin-bottom: 0; }
.coach-answered .muted { display: block; } /* the answer on its own line, under the goal */
/* A suggestion still waiting for the player: its goal, then Edit and Withdraw (coachTools.js). */
.coach-waiting { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s2); padding: var(--s1) 0; }
.coach-waiting-actions { display: flex; gap: var(--s2); }
.coach-waiting-actions .btn-ghost { min-height: 36px; padding: 0 var(--s3); font-size: var(--fs-sm); }

/* "+ Log" on phones (quickLog.js): a round button above the bottom tab bar, its chooser above it.
   Below 768px only; app.js shows it for the owner and hides it on Identity (its own + adds a goal). */
.log-fab { display: none; }
@media (max-width: 767px) {
  .log-fab:not([hidden]) { display: flex; flex-direction: column; align-items: flex-end; gap: var(--s2); position: fixed; right: var(--s4); bottom: calc(var(--s5) + env(safe-area-inset-bottom, 0px)); z-index: 35; }
  .log-fab-btn { min-height: 56px; padding: 0 var(--s5); border: 0; border-radius: 28px; background: var(--accent); color: var(--on-accent); font-size: var(--fs-base); font-weight: 650; box-shadow: var(--shadow); cursor: pointer; }
  .log-fab-btn:focus-visible, .log-fab-menu button:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
  .log-fab-menu:not([hidden]) { display: flex; flex-direction: column; min-width: 180px; padding: var(--s1); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--card-bg); box-shadow: var(--shadow); }
  .log-fab-menu button { min-height: 48px; padding: 0 var(--s3); border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text); font-size: var(--fs-base); text-align: left; cursor: pointer; }
  .log-fab-menu button:hover { background: var(--row-hover); }
  body.log-fab-on main { padding-bottom: 96px; } /* the last row can scroll clear of the button */
  body.log-fab-on .toast { bottom: calc(96px + env(safe-area-inset-bottom, 0px)); } /* above the button, not under it */
}
@media (max-width: 640px) {
  .log-fab:not([hidden]) { bottom: calc(80px + env(safe-area-inset-bottom, 0px)); }
  body.log-fab-on main { padding-bottom: 152px; }
  body.log-fab-on .toast { bottom: calc(152px + env(safe-area-inset-bottom, 0px)); }
}
/* A toast must not hide a control for good (functional test, 2026-10-18: it sat on the Share page's Remove
   buttons). From 768px it sits bottom left, away from the right-hand buttons (Remove, Revoke, a row's
   Open); on a phone above + Log (above). While one shows, main has room below, so anything under it can
   be scrolled clear. */
.toast { max-width: min(420px, calc(100vw - 32px)); }
@media (min-width: 768px) { .toast { left: 24px; right: auto; } }
body:has(.toast.show) main { padding-bottom: 120px; }
@media (max-width: 767px) { body.log-fab-on:has(.toast.show) main { padding-bottom: 168px; } }
@media (max-width: 640px) {
  body:not(.log-fab-on) .toast { bottom: calc(72px + env(safe-area-inset-bottom, 0px)); } /* above the bottom tab bar */
  body:not(.log-fab-on):has(.toast.show) main { padding-bottom: 168px; }
  body.log-fab-on:has(.toast.show) main { padding-bottom: 224px; }
}

/* ==========================================================================
   Account as a settings list (task 20261018-0701; Lucas, 2026-10-18): one short row each, the
   detail in a dialog or a sub-page with Back. Rows are one card with dividers, like a phone's settings.
   ========================================================================== */
.account-title { margin: 0 0 var(--s3); font-size: var(--fs-xl); }
.settings-list { list-style: none; margin: 0; padding: 0; background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.settings-list > li + li { border-top: 1px solid var(--border); }
.settings-list > li[hidden] { display: none; }
.settings-row {
  display: flex; align-items: center; gap: var(--s3); width: 100%; min-height: 52px; padding: var(--s3) var(--s4);
  border: 0; border-radius: 0; background: transparent; color: var(--text); font: inherit; font-size: var(--fs-base);
  text-align: left; cursor: pointer;
}
.settings-row:hover { background: var(--row-hover); }
.settings-row:focus-visible { outline: 2px solid var(--clay); outline-offset: -2px; }
.settings-label { font-weight: 600; flex: 0 0 auto; }
.settings-value { flex: 1 1 auto; min-width: 0; text-align: right; color: var(--text-muted); font-size: var(--fs-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A row that opens something shows a ›; Sign out and Delete account act at once or ask in a dialog. */
.settings-row:not(.settings-plain)::after { content: "›"; color: var(--text-muted); font-size: var(--fs-lg); line-height: 1; }
.settings-plain .settings-label { font-weight: 500; }
.settings-quiet { min-height: 44px; }
.settings-quiet .settings-label { color: var(--text-muted); font-size: var(--fs-sm); font-weight: 500; }
.settings-quiet:hover .settings-label { color: var(--loss); }

/* A sub-page: Back above its title, then its cards. */
.account-sub { display: flex; flex-direction: column; gap: var(--s3); }
.account-sub[hidden] { display: none; }
.account-sub > h2 { margin: 0; font-size: var(--fs-xl); }
.account-sub > h2:focus { outline: none; }
.account-sub h3 { margin: var(--s3) 0 0 !important; }
.sub-back { align-self: flex-start; margin: 0; padding: var(--s2) 0; font-size: var(--fs-md); min-height: 36px; }
.account-sub .card { margin: 0; }

/* Share: the address and who it is side by side, stacked on a phone. */
.share-form { display: flex; flex-direction: column; gap: var(--s3); }
.share-form > p { margin: 0; }
.share-fields { display: grid; grid-template-columns: 3fr 2fr; gap: var(--s3); }
.share-sent { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s2); }
.share-sent[hidden] { display: none; }
.share-sent label { font-size: var(--fs-sm); font-weight: 600; }
.share-sent textarea { width: 100%; }
.verify-actions { display: inline-flex; flex-wrap: wrap; gap: var(--s2); margin-left: var(--s1); vertical-align: middle; }
.verify-actions .btn-ghost { min-height: 36px; padding: 0 var(--s3); font-size: var(--fs-sm); }
#share-verify, #waiting-verify { margin: 0; }
/* A calm notice: the text with its quiet links on one row (stacked on a phone). */
.notice-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s4); padding: var(--s3) var(--s4); border: 1px solid var(--border); border-left: 3px solid var(--amber); border-radius: var(--radius-sm); background: var(--surface); font-size: var(--fs-sm); }
.notice-row[hidden] { display: none; }
/* A coach's Identity: the player's new answers to their suggestions (goals.js answersHtml), the first thing on the page. */
.coach-answers { display: grid; gap: var(--s2); margin: 0; padding: 0; list-style: none; }
.coach-answers .notice-row { margin: 0; }
.notice-row p { flex: 1 1 280px; margin: 0; color: var(--text); overflow-wrap: anywhere; }
.notice-actions { display: inline-flex; flex-wrap: wrap; gap: 0 var(--s4); }
.notice-actions .link-btn { margin: 0; padding: var(--s1) 0; min-height: 32px; font-size: var(--fs-sm); }
/* The result of an import, above the table it went into (importUi.js showImportBanner). */
.import-banner { flex-wrap: nowrap; align-items: flex-start; margin: 0 0 var(--s3); border-left-color: var(--win); }
.import-banner p { flex: 1 1 0; min-width: 0; padding-top: var(--s1); font-weight: 600; }
@media (max-width: 640px) { .import-banner .notice-actions { flex-direction: column-reverse; align-items: flex-end; } }
.import-banner .notice-actions { align-items: center; }
.import-banner .dialog-x { width: 32px; min-width: 32px; height: 32px; min-height: 32px; margin: 0; font-size: var(--fs-lg); }

/* The optional profile fields (profileUi.js): two to a row, one on a phone. */
.profile-fields { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3); }
.profile-fields .field { min-width: 0; }
.profile-fields .profile-why { grid-column: 1 / -1; margin: calc(-1 * var(--s2)) 0 0; }
#signup-more input, #signup-more select { width: 100%; }
/* Create account's profile fields look like its other fields (plain labels), with the same refused border. */
#signup-more .field label { font-size: inherit; font-weight: inherit; }
#form-signup [aria-invalid="true"] { border-color: var(--loss); }

/* Import (account.js, importUi.js): numbered steps (the file, its board, what it will do), then the result. */
.import-step { display: flex; flex-direction: column; gap: var(--s3); min-width: 0; margin: 0; padding: 0; border: 0; }
.import-step[hidden] { display: none; }
.import-step + .import-step { padding-top: var(--s4); border-top: 1px solid var(--border); } /* sections, not fieldsets: a legend would sit on the line */
.import-step p { margin: 0; }
.import-step-title { display: flex; align-items: center; gap: var(--s2); margin: 0; padding: 0; font-size: var(--fs-base); font-weight: 650; color: var(--text); }
.step-n { display: inline-grid; place-items: center; flex: none; width: 24px; height: 24px; border-radius: 999px; background: var(--surface); border: 1px solid var(--border); font-size: var(--fs-xs); font-weight: 700; color: var(--text-muted); }
.import-flow .link-btn { margin-left: 0; font-size: inherit; }
/* The file: one wide, dashed button with the file's name under its label. */
.file-pick { display: flex; align-items: center; gap: var(--s3); width: 100%; min-height: 64px; padding: var(--s3) var(--s4); border: 1px dashed var(--control-border); border-radius: var(--radius-sm); background: var(--surface); color: var(--text); font: inherit; text-align: left; cursor: pointer; }
.file-pick:hover { border-color: var(--clay); }
.file-pick:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.file-pick-icon { display: inline-grid; place-items: center; flex: none; width: 36px; height: 36px; border-radius: 999px; background: var(--card-bg); border: 1px solid var(--border); color: var(--clay-text); font-weight: 700; }
.file-pick-text { display: flex; flex-direction: column; min-width: 0; }
.file-pick-text .muted { overflow-wrap: anywhere; }
/* A choice as a card: the radio, a bold label and a line under it (the board; Keep mine / Use the file's / Keep both). */
.choice-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s2); }
.choice-card { display: flex; align-items: flex-start; gap: var(--s2); padding: var(--s3); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--card-bg); cursor: pointer; }
.choice-card:hover { border-color: var(--text-muted); }
.choice-card:has(input:checked) { border-color: var(--clay); box-shadow: inset 0 0 0 1px var(--clay); }
.choice-card:has(input:focus-visible) { outline: 2px solid var(--clay); outline-offset: 2px; }
.choice-card input { margin: 3px 0 0; flex: none; }
.choice-card > span { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* What the file will do, row kind by row kind, before anything is written. */
.import-preview { display: flex; flex-direction: column; gap: var(--s3); }
.import-preview p { margin: 0; }
.import-counts { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; }
.import-counts li { display: flex; align-items: baseline; gap: var(--s3); padding: var(--s2) var(--s3); font-size: var(--fs-md); }
.import-counts li + li { border-top: 1px solid var(--border); }
.import-n { flex: none; min-width: 3ch; font-weight: 700; font-variant-numeric: tabular-nums; text-align: right; }
.import-counts .is-new .import-n { color: var(--win); }
.import-counts .is-differ { box-shadow: inset 3px 0 0 var(--amber); } /* the number stays in the text colour: amber is too light for text */
.import-counts .is-bad .import-n { color: var(--loss); }
.import-choice { display: flex; flex-direction: column; gap: var(--s2); min-width: 0; margin: 0; padding: 0; border: 0; }
.import-choice legend { margin-bottom: var(--s2); padding: 0; font-size: var(--fs-sm); font-weight: 600; }
.import-undated { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: var(--s2) var(--s3); display: flex; flex-direction: column; gap: var(--s2); font-size: var(--fs-md); }
.import-undated ul { margin: 0; padding-left: var(--s4); }
.import-undated-choice { display: flex; align-items: flex-start; gap: var(--s2); min-height: 40px; cursor: pointer; }
.import-diffs { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 0 var(--s3); }
.import-diffs summary { min-height: 40px; display: flex; align-items: center; cursor: pointer; font-size: var(--fs-sm); font-weight: 600; color: var(--clay-text); }
.import-diffs summary:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.import-diffs ul { list-style: none; margin: 0 0 var(--s3); padding: 0; display: flex; flex-direction: column; gap: var(--s2); max-height: 240px; overflow-y: auto; }
.import-diffs li { padding: var(--s2) 0; border-top: 1px solid var(--border); font-size: var(--fs-sm); }
.import-diffs dl { display: grid; grid-template-columns: minmax(90px, 35%) 1fr; gap: 2px var(--s3); margin: var(--s1) 0 0; }
.import-diffs dt { color: var(--text-muted); }
.import-diffs dd { margin: 0; overflow-wrap: anywhere; }
.import-mine { text-decoration: line-through; color: var(--text-muted); }
.import-file { font-weight: 600; }
.import-safe { padding-left: var(--s3); border-left: 3px solid var(--win); font-size: var(--fs-sm); color: var(--text); }
.import-none { padding: var(--s3); border-radius: var(--radius-sm); background: var(--danger-bg); color: var(--text); font-size: var(--fs-sm); }
@media (max-width: 640px) { .choice-cards { grid-template-columns: 1fr; } }

/* Sign in: Forgot password is a link under it (no tab). */
.auth-forgot { align-self: flex-start; margin: 0; font-size: var(--fs-md); padding: var(--s1) 0; }
.auth-sub { font-size: var(--fs-lg); margin: var(--s2) 0 0; }

/* A coach whose players all stopped sharing (app.js updateWaiting). */
.waiting-card { max-width: 640px; margin: var(--s5) auto; }
.waiting-card h2 { margin-top: 0; }
.waiting-card strong { overflow-wrap: anywhere; }
#verify-reminder .verify-actions { margin-left: 0; }

@media (max-width: 640px) {
  .share-fields, .profile-fields { grid-template-columns: 1fr; }
  .verify-actions { display: flex; margin: var(--s2) 0 0; }
  .verify-actions .btn-ghost { min-height: 44px; flex: 1 1 auto; }
}
.verify-actions[hidden] { display: none; }
/* The confirm-your-e-mail bar: full text on a desktop; on a phone one slim line, the short message, one button and a ×
   that hides it for the browser session (app.js drawVerifyBar), so the first goal area stays on the first screen. */
.verify-x { display: none; }
@media (max-width: 640px) {
  #verify-reminder { flex-wrap: nowrap; gap: var(--s2); padding: var(--s1) var(--s2) var(--s1) var(--s3); margin-top: var(--s2); font-size: var(--fs-sm); }
  #verify-reminder .verify-msg, #verify-reminder [data-confirmed] { flex: 1 1 auto; min-width: 0; }
  #verify-reminder .verify-msg { white-space: nowrap; }
  #verify-reminder .verify-more, #verify-reminder .verify-resend { display: none; }
  #verify-reminder .verify-actions { margin: 0; flex: none; }
  #verify-reminder .verify-actions .btn-ghost { padding: 0 var(--s2); white-space: nowrap; }
  #verify-reminder .verify-x { display: inline-grid; place-items: center; flex: none; width: 40px; min-height: 44px; border: 0; background: transparent; color: var(--text-muted); font-size: var(--fs-xl); cursor: pointer; }
  #verify-reminder.reminder-dismissed { display: none; }
}
/* "Shared with you" only above something shared. */
#shared-with-me:not(:has(.share-row)) #shared-with-me-title { display: none; }

/* Every dialog opens in the middle of the screen (a bottom sheet on phones): page rules that zero the
   margin of a view's children must not move a modal into the corner. */
dialog:modal { margin: auto; inset: 0; }
@media (max-width: 640px) { dialog.entry-dialog:modal { margin: auto 0 0; } }

/* An empty Training, Matches or All tab (firstRun.js): one start card; its zero cards, empty charts,
   graphs and filter bar (on All: its empty table, whose first row would offer the same) wait until
   something is logged. */
.is-first-run > :is(.kpi-grid, .graphs-area, .charts-grid, .graphs-add, [data-filter-slot], #all-table-card) { display: none; }
.first-run { max-width: 640px; margin-inline: auto; }
.first-run .empty-state { padding: var(--s3) 0; }
.first-run .form-actions { margin-top: var(--s2); flex-wrap: wrap; justify-content: center; }
@media (max-width: 640px) { .first-run .form-actions button { min-height: 44px; flex: 1 1 auto; } }

/* "New from your coach" (coachNews.js): the count on the Notes tab (app.js updateCoachNews) and the
   "New" mark on a coach's or parent's note the player has not seen yet. */
.nav-badge {
  display: inline-block; min-width: 18px; margin-left: 6px; padding: 0 5px; border-radius: 9px;
  background: var(--accent); color: var(--on-accent); font-size: var(--fs-xs); font-weight: 650; font-variant-numeric: tabular-nums;
  line-height: 18px; text-align: center; vertical-align: 1px;
}
.note-new {
  padding: 0 var(--s2); border-radius: var(--radius-sm); background: var(--accent); color: var(--on-accent);
  font-size: var(--fs-xs); font-weight: 650; white-space: nowrap;
}
/* Notes behind a chip (goals.js noteChipHtml, notesRegionHtml; Lucas, 2026-10-21): "2 notes" with a dot when something is new,
   closed until tapped, one open at a time. An open region never grows past a third of the screen: it scrolls, and the
   text of each note is cut to two lines (Read more), so the notes never take the page from what they are about. */
.note-chip {
  display: inline-flex; align-items: center; gap: var(--s1); min-height: 36px; padding: 0 var(--s3); border: 1px solid var(--border);
  border-radius: var(--radius-pill); background: var(--card-bg); color: var(--text); font: inherit; font-size: var(--fs-sm); font-weight: 650;
  white-space: nowrap; cursor: pointer;
}
.note-chip:hover, .note-chip.is-open { border-color: var(--clay); }
.note-chip:focus-visible { outline: 2px solid var(--clay); outline-offset: 2px; }
.note-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
.note-head .note-dot { width: 10px; height: 10px; background: var(--clay-text); box-shadow: 0 0 0 2px var(--card-bg); } /* readable on a dark tint */
.note-region {
  display: flex; flex-direction: column; gap: var(--s2); margin-top: var(--s2); padding: var(--s2); max-height: min(32vh, 18rem); overflow-y: auto;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); font-size: var(--fs-md); line-height: 1.4; white-space: normal;
}
.note-region .note-clamp { -webkit-line-clamp: 2; line-clamp: 2; }
.note-region .note-region-add { align-self: flex-start; min-height: 36px; font-size: var(--fs-sm); }
.note-region p { margin: 0; }
.gl-notes-line { margin: 0 0 var(--s2); }
.id-part-head + .note-region { margin: 0 0 var(--s3); }
/* On the phone's bottom bar the tab is narrow: the count sits on its top right corner, not under the word. */
@media (min-width: 641px) { #main-nav .tab-btn:not(.active) .nav-badge { box-shadow: 0 0 0 1px var(--on-bar-text); } } /* the badge's edge on the dark bar */
@media (max-width: 640px) {
  #main-nav .tab-btn:has(.nav-badge) { position: relative; }
  #main-nav .nav-badge { position: absolute; top: 2px; right: 0; margin: 0; min-width: 16px; line-height: 16px; box-shadow: 0 0 0 2px var(--bar-1); }
}

/* "+ Add match" on the Matches Dashboard (round 6, #4): at the end of the toolbar row, wherever the filter bar sits. */
.board-toolbar .board-add { order: 2; margin-left: auto; }
.empty-state .link-btn { font-size: inherit; margin-left: 0; }

/* The Dashboard's "Charts by Week | Month" switch (buckets.js drawBucketSwitch). */
.bucket-bar { display: flex; align-items: center; gap: var(--s2); margin: 0 0 var(--s3); }
.bucket-label { font-size: var(--fs-sm); color: var(--text-muted); }
.bucket-seg, .gl-seg { display: flex; gap: var(--s1); padding: var(--s1); border-radius: var(--radius-sm); background: var(--surface); border: 1px solid var(--border); }
.bucket-seg button, .gl-seg button { min-height: 36px; padding: 0 var(--s3); border: 0; border-radius: calc(var(--radius-sm) - var(--s1)); background: transparent; color: var(--text-muted); font: inherit; font-size: var(--fs-sm); font-weight: 600; cursor: pointer; }
.bucket-seg button[aria-pressed="true"], .gl-seg button[aria-selected="true"] { background: var(--card-bg); color: var(--text); box-shadow: var(--shadow); }
/* Goals: Short term | Long term | Identity (goals.js partsHtml, a tablist), under Start here; each goal part with the number of its goals. */
.gl-parts { display: flex; flex-direction: column; gap: var(--s2); }
.gl-parts .id-help { margin: 0; }
.gl-seg { align-self: flex-start; max-width: 100%; }
.gl-seg button { min-height: 44px; font-size: var(--fs-md); display: inline-flex; align-items: center; justify-content: center; gap: var(--s2); }
.gl-seg button[aria-selected="true"] { font-weight: 700; box-shadow: var(--shadow), inset 0 0 0 1px var(--border); }
.gl-seg button:focus-visible { outline: 2px solid var(--clay); outline-offset: -2px; }
.gl-seg-n { font-size: var(--fs-sm); font-variant-numeric: tabular-nums; color: var(--text-muted); }
@media (max-width: 640px) {
  .gl-seg { align-self: stretch; }
  .gl-seg button { flex: 1 1 0; padding: 0 var(--s2); }
}

/* ==========================================================================
   Design foundation (2026-10-20, "Clay Court Editorial"): warm paper, near-black ink, the accent of the
   tournament in each theme (clay in light, US Open blue in dark). The tokens are in :root and the theme blocks above; the rules that set the look (headings, controls,
   buttons, the sub-tabs, dialogs) were edited where they are. This block adds what had no home: the focus ring, tappable
   and nested cards and the motion and transparency rules. Later rules win, so it sits last.
   ========================================================================== */
/* Focus: one ring, 2px in the accent, 2px off the control; white on the always-dark header. */
:is(button, a, summary, select, input, textarea, [tabindex], .tab-btn):focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.topbar :is(button, select, .tab-btn):focus-visible { outline-color: var(--on-bar-strong); }
/* A text field also gets the accent border and a soft glow while it has focus. */
:is(select, input:not([type=checkbox]):not([type=radio]), textarea):focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); outline: 2px solid transparent; }
input[type=checkbox], input[type=radio] { accent-color: var(--accent); }

/* Cards: white on paper, a hairline and a soft shadow. A card in a card is a plain --surface fill. */
.card :is(.card, .kpi-card) { border: 0; box-shadow: none; background: var(--surface); }
:is(a, button, [role=button], [tabindex]):is(.card, .kpi-card) { cursor: pointer; transition: box-shadow var(--dur-2) var(--ease); }
:is(a, button, [role=button], [tabindex]):is(.card, .kpi-card):hover { box-shadow: var(--shadow-lift); }

/* Motion, only where the person has not asked for less: a panel fades in with a 6px rise when its tab opens. */
@media (prefers-reduced-motion: no-preference) {
  @keyframes panel-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
  .tab-panel.active, #view-page:not([hidden]) { animation: panel-in var(--dur-2) var(--ease); }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; }
}
/* Less transparency: no blur behind a dialog and no art under the header's text (the plain bar colour instead). */
@media (prefers-reduced-transparency: reduce) {
  .entry-dialog::backdrop { backdrop-filter: none; -webkit-backdrop-filter: none; }
  .topbar-hero { background: var(--bar-1); }
}

/* Insights with nothing logged: only the empty state shows (insights.js draw). */
.tab-panel.insights-empty > :not([id$="insights-cards"]) { display: none; }

/* A coach or parent nobody has shared with yet: only Account is a place to go (app.js updateWhoLine). */
body.coach-idle #main-nav { display: none; }

/* ---- Overview tab (overview.js; Erik, 2026-10-26): the player's status at a glance, at most 760px wide and centred ---- */
.overview-page { max-width: 760px; margin-inline: auto; }
.ov { display: flex; flex-direction: column; gap: var(--s4); }
.ov .card { padding: var(--s3) var(--s4); }
.ov-who { display: flex; align-items: center; gap: var(--s3); }
.ov-avatar { flex: none; width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center; background: var(--accent-soft); color: var(--clay-text); font-weight: 650; }
.ov-name { margin: 0; font-size: var(--fs-lg); }
.ov-who p { margin: 2px 0 0; }
.ov-highlight { margin: 0; padding: var(--s2) var(--s3); border-radius: var(--radius-sm); background: color-mix(in srgb, var(--win) 12%, var(--card-bg)); color: var(--win); font-size: var(--fs-sm); font-weight: 600; }
.ov-label { margin: 0 0 var(--s2); font-size: var(--fs-xs); font-weight: 600; color: var(--text-muted); }
.ov-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: var(--s3); }
.ov-big { font-size: var(--fs-xl); font-weight: 650; line-height: 1.2; margin-top: 2px; }
.ov-mid { font-size: var(--fs-base); font-weight: 650; }
.ov-trend { font-size: var(--fs-sm); }
.ov-up { color: var(--win); }
.ov-down { color: var(--amber); }
.ov-same { color: var(--text-muted); }
.ov-arrow { font-weight: 700; }
.ov-sub { margin-top: var(--s2); padding-top: var(--s2); border-top: 1px solid var(--border); }
.ov-note { margin: var(--s2) 0 0; }
.ov-goal + .ov-goal { margin-top: var(--s3); }
.ov-pick + .ov-pick { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--border); }
.ov-area-name { font-weight: 650; }
.ov-all-goals { margin-top: var(--s3); }
.ov-goal-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: var(--s1) var(--s3); }
.ov-goal-title { font: inherit; font-size: var(--fs-base); font-weight: 650; color: var(--text); background: none; border: 0; padding: 0; text-align: left; }
button.ov-goal-title { cursor: pointer; }
button.ov-goal-title:hover { text-decoration: underline; }
.ov-steps { list-style: none; margin: var(--s2) 0 0; padding: 0; }
.ov-steps li + li { border-top: 1px solid var(--border); }
.ov-step { display: block; width: 100%; font: inherit; color: inherit; background: none; border: 0; padding: var(--s2) 0; text-align: left; cursor: pointer; }
.ov-step-top { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--s1) var(--s3); font-size: var(--fs-sm); }
.ov-step-now { color: var(--text-muted); }
.ov-chip { display: inline-block; margin-left: var(--s1); padding: 0 var(--s2); border-radius: var(--radius-pill); font-size: var(--fs-xs); font-weight: 600; background: var(--surface); color: var(--text-muted); }
.ov-chip-green { background: color-mix(in srgb, var(--win) 14%, var(--card-bg)); color: var(--win); }
.ov-chip-yellow { background: color-mix(in srgb, var(--amber) 18%, var(--card-bg)); color: var(--text); }
.ov-chip-red { background: color-mix(in srgb, var(--loss) 14%, var(--card-bg)); color: var(--loss); }
.ov-bar { display: block; height: 8px; margin-top: 6px; border-radius: var(--radius-pill); background: var(--surface); overflow: hidden; }
.ov-bar i { display: block; height: 100%; border-radius: inherit; background: var(--text-muted); }
.ov-bar .ov-fill-green { background: var(--win); }
.ov-bar .ov-fill-yellow { background: var(--amber); }
.ov-bar .ov-fill-red { background: var(--loss); }
.ov-tests { list-style: none; margin: var(--s2) 0 0; padding: 0; font-size: var(--fs-sm); }
.ov-tests li { display: flex; justify-content: space-between; gap: var(--s2); padding: var(--s1) 0; }
.ov-tests li + li { border-top: 1px solid var(--border); }
/* The three areas side by side on every width; on a phone smaller, so they fit 360px. */
.ov-areas { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.ov-fun { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s1) var(--s4); margin-top: var(--s3); }
.ov-fun .ov-sub { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s1) var(--s3); margin: 0; padding: 0; border: 0; }
@media (max-width: 640px) {
  .ov-areas { gap: var(--s2); }
  .ov-areas .card { padding: var(--s2); }
  .ov-areas .ov-big { font-size: var(--fs-lg); }
  .ov-areas .ov-trend, .ov-areas .small { font-size: var(--fs-xs); }
}
/* "Why Tennis Tracker" under the sign-in card (why.js): one control of four texts, then the text. */
#screen-auth { flex-direction: column; align-items: center; gap: var(--s4); }
.why-card { width: 100%; max-width: 480px; box-sizing: border-box; }
.why-head { margin: 0 0 var(--s3); color: var(--navy); }
.why-seg { display: flex; gap: var(--s1); padding: var(--s1); border-radius: var(--radius-sm); background: var(--surface); border: 1px solid var(--border); }
.why-seg button { flex: 1; min-height: var(--tap); border: 0; border-radius: 8px; background: transparent; color: var(--text-muted); font: inherit; font-size: var(--fs-sm); font-weight: 600; cursor: pointer; padding: 0 var(--s1); }
.why-seg button[aria-pressed="true"] { background: var(--card-bg); color: var(--text); box-shadow: var(--shadow); }
.why-title { margin: var(--s4) 0 var(--s2); font-size: var(--fs-lg); line-height: 1.3; }
.why-lead { margin: 0 0 var(--s3); line-height: 1.5; }
.why-points { margin: 0 0 var(--s3); padding: 0; list-style: none; display: grid; gap: var(--s2); font-size: var(--fs-md); line-height: 1.5; }
.why-points li { display: flex; gap: var(--s2); }
.why-icon { flex: 0 0 auto; width: 1.5em; font-size: var(--fs-lg); line-height: 1.2; text-align: center; }
.why-close { margin: 0 0 var(--s2); font-weight: 650; line-height: 1.5; }
