/* =============================================================================
   TurfTime Team App — one stylesheet, in layout order.

   Bootstrap is gone. It was 200KB spent to make an app for parents look like
   every other Bootstrap page, and its defaults fight this one at every turn:
   38px controls where we need 44, a grid where we need a single column, and a
   focus ring that assumes a mouse. The handful of pieces we actually used are
   below, sized for a thumb.

   The file is ordered the way the app is built — reset, shell, primitives, then
   the coach workspace, then the screens that hang off it. Anything that is only
   true on a phone or only true on a desktop lives in the responsive block at the
   bottom, not scattered as a second definition halfway down.

   That ordering is the point. What was here before had grown a second layer: a
   redesign pass appended forty single-line rules re-declaring .tt-appbar,
   .tt-card, .tt-tab and the rest below their own definitions, so the value that
   won depended on how far down the file you looked. Those are merged into the
   originals. If a rule here is superseded, it is edited — not out-specified from
   the bottom.

   Layout lives here rather than in scoped .razor.css because this is the app
   frame: one shell, referred to by name from several components, where reaching
   through ::deep for every rule would be worse than a global.
   ========================================================================== */

/* --- Reset ---------------------------------------------------------------- */

*, *::before, *::after {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    font-family: var(--tt-font);
    font-size: var(--tt-text-body);
    line-height: var(--tt-leading-normal);
    color: var(--tt-ink);
    background:
        radial-gradient(900px 340px at 100% -80px, color-mix(in srgb, var(--tt-brand) 14%, transparent), transparent 62%),
        var(--tt-ground);
    -webkit-font-smoothing: antialiased;

    /* A native app does not rubber-band the whole page, and held text does not
       flash a blue highlight. These two lines are most of what separates "app"
       from "website in a wrapper". */
    overscroll-behavior-y: none;
    -webkit-tap-highlight-color: transparent;
}

h1, h2, h3, h4, p, figure {
    margin: 0;
}

ul, ol {
    margin: 0;
}

button, input, select, textarea {
    font: inherit;
    color: inherit;
}

a {
    color: var(--tt-brand);
    text-decoration: none;
}

:focus-visible {
    outline: 2px solid var(--tt-brand);
    outline-offset: 2px;
}

/* --- App shell ------------------------------------------------------------
   Three parts: a bar at the top, a scrolling middle, a bar at the bottom. On a
   desktop the bottom bar becomes a left rail — same markup, one media query. */

.tt-app {
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
}

.tt-skip-link {
    position: fixed;
    top: -80px;
    left: var(--tt-space-4);
    z-index: 100;
    padding: var(--tt-space-3) var(--tt-space-5);
    border-radius: var(--tt-radius-md);
    background: var(--tt-surface);
}

.tt-skip-link:focus {
    top: var(--tt-space-3);
}

/* Only the desktop rail has a brandmark; on a phone the tab bar is the frame. */
.tt-shell-brand {
    display: none;
}

.tt-appbar {
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    min-height: calc(var(--tt-appbar-height) + var(--tt-safe-top));
    padding: calc(var(--tt-space-2) + var(--tt-safe-top)) calc(var(--tt-gutter) + var(--tt-safe-right)) var(--tt-space-2) calc(var(--tt-gutter) + var(--tt-safe-left));
    background: color-mix(in srgb, var(--tt-surface) 82%, transparent);
    border-bottom: 1px solid color-mix(in srgb, var(--tt-hairline) 75%, transparent);
    backdrop-filter: saturate(1.4) blur(16px);
    -webkit-backdrop-filter: saturate(1.4) blur(16px);
}

.tt-appbar-titles {
    min-width: 0;
    flex: 1;
}

/* The heading and the screen's own actions share a line; the title gives way first. */
.tt-appbar-headline {
    display: flex;
    align-items: center;
    gap: var(--tt-space-2);
    min-width: 0;
}

/* Air between the context chip and the title: touching, they read as one cramped block. */
.tt-appbar-context + .tt-appbar-headline {
    margin-top: 4px;
}

.tt-appbar-headline > .tt-appbar-title {
    flex: 1;
}

/* Header-sized: 36px to the eye, 44px to the thumb (the pad below). A 44px filled
   circle beside a 22px heading read as a floating button dropped on the bar. */
.tt-appbar-headline .tt-appbar-action,
.tt-appbar-headline .tt-menu-trigger {
    position: relative;
    width: 36px;
    height: 36px;
    min-width: 36px;
    min-height: 36px;
}

.tt-appbar-headline .tt-appbar-action::after,
.tt-appbar-headline .tt-menu-trigger::after {
    content: "";
    position: absolute;
    inset: -4px;
}

.tt-appbar-title {
    display: block;
    min-width: 0;
    font-size: var(--tt-text-title);
    font-weight: var(--tt-weight-bold);
    line-height: var(--tt-leading-tight);
    letter-spacing: -.03em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* FocusOnNavigate parks focus on the title after every tab change so a screen reader announces the
   new screen. The title isn't something you can act on, so the ring it drew there only looked like a
   web page highlighting a heading on every tap. Screen readers don't need the ring. */
.tt-appbar-title:focus {
    outline: none;
}

.tt-appbar-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--tt-space-1);
    width: var(--tt-tap);
    height: var(--tt-tap);
    min-width: var(--tt-tap);
    min-height: var(--tt-tap);
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--tt-brand);
    color: var(--tt-brand-on);
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-semibold);
    cursor: pointer;
}

.tt-main {
    flex: 1;
    padding: var(--tt-space-3) calc(var(--tt-gutter) + var(--tt-safe-right)) var(--tt-space-4) calc(var(--tt-gutter) + var(--tt-safe-left));
}

/* Opened on a desktop this is still the same app, not a wide one — but the
   roster is a table, and 560px of it is a phone column with columns drawn on.

   The overlays are excluded by name: they are fixed to the viewport, not blocks
   in the column, and being centred inside a 960px measure is how a full-screen
   scrim ends up as a 960px rectangle with the page showing either side of it. */
.tt-main:not(.tt-main-plain) > *:not(.tt-panel):not(.tt-panel-scrim):not(.tt-confirm):not(.tt-main-club),
.tt-main-club > *:not(.tt-panel):not(.tt-panel-scrim):not(.tt-confirm) {
    width: 100%;
    max-width: none;
    margin-inline: 0;
}

/* The page's club key (MainLayout). No box of its own, so the page lays out as
   if it sat directly in .tt-main. */
.tt-main-club {
    display: contents;
}

/* --- Tab bar --------------------------------------------------------------
   The primary navigation: thumb-reachable, always visible, and never more than
   four destinations. A fifth means the app needs a different idea, not a
   smaller icon — which is why "Manage" became the team workspace's own nav. */

.tt-tabbar {
    position: sticky;
    bottom: 0;
    z-index: 20;
    display: flex;
    gap: 2px;
    margin: 0;
    padding: 6px var(--tt-space-2) calc(6px + var(--tt-safe-bottom));
    border-radius: 0;
    background: color-mix(in srgb, var(--tt-surface) 86%, transparent);
    /* A hairline and a soft lift, not a slab: the bar belongs to the screen it frames. */
    box-shadow: 0 -1px 0 color-mix(in srgb, var(--tt-ink) 8%, transparent), 0 -10px 28px rgba(16, 24, 20, .06);
    backdrop-filter: saturate(1.4) blur(18px);
    -webkit-backdrop-filter: saturate(1.4) blur(18px);
}

.tt-tab {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--tt-space-1);
    min-height: var(--tt-tabbar-height);
    padding: var(--tt-space-2) var(--tt-space-1);
    border: 0;
    border-radius: 16px;
    background: transparent;
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-semibold);
    letter-spacing: -.01em;
    cursor: pointer;
    transition: color .15s ease;
}

/* The icon carries the state: a pill behind it marks the tab you're on, the way every current phone
   OS draws its own tab bars. A whole tab filled solid shouted louder than anything on the screen. */
.tt-tab > .tt-icon {
    box-sizing: content-box;
    padding: 4px 18px;
    border-radius: var(--tt-radius-pill);
    transition: background-color .15s ease, color .15s ease;
}

.tt-tab.active {
    color: var(--tt-ink);
    font-weight: var(--tt-weight-bold);
}

/* Mixed from the brand rather than --tt-brand-soft: in dark mode that soft tint is a shade off the
   card colour and the pill disappears. */
.tt-tab.active > .tt-icon {
    background: color-mix(in srgb, var(--tt-brand) 18%, transparent);
    color: var(--tt-brand-strong);
}

/* --- Screen furniture -----------------------------------------------------
   Every screen is a single column of blocks with one gap between them. Sections
   inside a screen get a heading bar, a count, and — where the section needs one
   — a sentence saying what it is for, tucked under the heading rather than
   floating a full gap away from it. */

.tt-stack {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-3);
}

.tt-stack-tight {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
}

.tt-screen-title {
    font-size: var(--tt-text-display);
    font-weight: var(--tt-weight-bold);
    line-height: var(--tt-leading-tight);
    letter-spacing: -.02em;
}

.tt-screen-lede {
    color: var(--tt-ink-muted);
}

.tt-section-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--tt-space-2);
}

.tt-section-bar:not(:first-child) {
    margin-top: var(--tt-space-3);
}

.tt-section-title {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--tt-space-2);
    font-size: var(--tt-text-headline);
    font-weight: var(--tt-weight-semibold);
    letter-spacing: -.01em;
}

/* The count rides with the heading it counts — "Roster 20 players", never a
   separate line of statistics above the thing being counted. */
.tt-count {
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-regular);
    color: var(--tt-ink-muted);
    font-variant-numeric: tabular-nums;
}

.tt-section-bar-actions {
    display: flex;
    align-items: center;
    gap: var(--tt-space-2);
}

/* Pulls back against the column gap so the sentence belongs to the heading
   above it rather than sitting equidistant between two sections. */
.tt-section-note {
    margin-top: calc(var(--tt-space-1) - var(--tt-space-3));
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

.tt-section-heading,
.tt-day-heading {
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
    letter-spacing: -.01em;
    color: var(--tt-ink-muted);
}

.tt-day-heading {
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-bold);
    color: var(--tt-ink);
}

.tt-team-heading {
    font-size: var(--tt-text-headline);
    font-weight: var(--tt-weight-semibold);
    line-height: var(--tt-leading-tight);
}

/* --- Card -----------------------------------------------------------------
   Two surfaces only: the page ground, and a card on it. A card inside a card is
   a bug, not a style — which is why the detail panel groups its sections with
   headings and rules rather than with more boxes. */

.tt-card {
    padding: var(--tt-space-4);
    background: var(--tt-surface);
    border: 1px solid color-mix(in srgb, var(--tt-ink) 7%, transparent);
    border-radius: var(--tt-radius-lg);
    box-shadow: var(--tt-shadow-card);
}

.tt-card-flush {
    padding: 0;
    overflow: hidden;
}

/* The card clips its rounded corners, which also clipped an open ⋯ menu to nothing when its row was
   the last one: a card holding one chat, alert or post showed the trigger highlight and no menu.
   While a menu is open the card lets it hang out. */
.tt-card-flush:has(.tt-menu-panel) {
    overflow: visible;
}

/* --- List primitives ------------------------------------------------------ */

.tt-list {
    display: flex;
    flex-direction: column;
    list-style: none;
    margin: 0;
    padding: 0;
}

.tt-list-row {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    width: 100%;
    min-height: var(--tt-tap-lg);
    padding: var(--tt-space-3) var(--tt-space-4);
    border: 0;
    border-bottom: 1px solid var(--tt-hairline);
    background: transparent;
    color: inherit;
    text-align: left;
}

.tt-list-row:last-child {
    border-bottom: 0;
}

button.tt-list-row {
    cursor: pointer;
}

button.tt-list-row:active {
    background: var(--tt-surface-sunken);
}

/* A phone row is lines, not columns: what this is, what qualifies it, and what
   you can do about it. */
.tt-list-row-stacked {
    display: block;
    padding: var(--tt-space-4);
}

.tt-list-row-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* One anchor per row: the name is the only thing at full weight and everything
   under it drops a step. */
.tt-row-title {
    font-weight: var(--tt-weight-semibold);
    overflow-wrap: anywhere;
}

.tt-row-meta {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
    overflow-wrap: anywhere;
}

.tt-row-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--tt-space-3);
}

.tt-row-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tt-space-2);
    margin-top: var(--tt-space-3);
}

.tt-row-actions-inline {
    margin-top: 0;
    flex: none;
}

.tt-status {
    display: inline-flex;
    align-items: center;
    flex: none;
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
    white-space: nowrap;
}

.tt-item-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--tt-space-3);
}

/* The workhorse list: one line per thing, a name, a describing line under it,
   and its actions at the end. Surveys, events and the directory all use it, so
   they read as the same kind of list rather than three inventions. */
.tt-items {
    list-style: none;
    margin: 0;
    padding: 0;
    background: var(--tt-surface);
    border: 1px solid color-mix(in srgb, var(--tt-ink) 7%, transparent);
    border-radius: var(--tt-radius-lg);
    box-shadow: var(--tt-shadow-card);
}

.tt-items > li {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--tt-space-2) var(--tt-space-3);
    min-height: 56px;
    padding: var(--tt-space-3) var(--tt-space-4);
    border-bottom: 1px solid var(--tt-hairline);
}

.tt-items > li:last-child {
    border-bottom: 0;
}

.tt-item-body {
    flex: 1 1 12rem;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* A whole row that is one checkbox (Settings' family directory): the label is the row, so a tap
   anywhere on the name ticks it. */
.tt-items > li > .tt-checkbox {
    flex: 1 1 auto;
    min-width: 0;
}

.tt-item-actions {
    display: flex;
    align-items: center;
    gap: var(--tt-space-2);
    flex: none;
}

/* --- Buttons --------------------------------------------------------------
   One filled button per block, at most. Everything beside it is outlined, and
   everything destructive is in a menu — so the coloured button on a screen is
   always the thing the coach came to do. */

.tt-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--tt-space-2);
    min-height: var(--tt-tap);
    padding: 0 var(--tt-space-4);
    border: 1px solid transparent;
    border-radius: var(--tt-radius-pill);
    background: var(--tt-brand);
    color: var(--tt-ink-on-brand);
    box-shadow: 0 1px 2px color-mix(in srgb, var(--tt-brand-strong) 28%, transparent);
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-semibold);
    text-decoration: none;
    cursor: pointer;
    transition: background var(--tt-duration-fast) var(--tt-ease), opacity var(--tt-duration-fast) var(--tt-ease);
}

.tt-btn:active {
    opacity: .82;
}

.tt-btn:disabled {
    opacity: .5;
    cursor: default;
}

.tt-btn-secondary {
    background: var(--tt-surface-sunken);
    border-color: transparent;
    color: var(--tt-ink);
    box-shadow: none;
}

.tt-btn-ghost {
    background: transparent;
    color: var(--tt-brand);
    padding-inline: var(--tt-space-3);
    box-shadow: none;
}

/* Text-coloured, for a destructive item that sits among ordinary ones. */
.tt-btn-danger {
    background: transparent;
    border-color: transparent;
    color: var(--tt-danger);
    box-shadow: none;
}

/* Filled, for the confirm button of a destructive dialog — where it is the one
   thing being asked about and needs to look like it. */
.tt-btn-destructive {
    background: var(--tt-danger);
    border-color: transparent;
    color: #fff;
}

/* An <a> cannot be disabled. This is what aria-disabled looks like. */
.tt-btn-inert {
    opacity: .45;
    pointer-events: none;
}

.tt-btn-block {
    width: 100%;
}

/* Caption-size, for buttons that ride inside a row or a panel and must not
   outshout the name they belong to. The ::after pad keeps the tap target at
   44px without spending the row height on it. */
.tt-btn.tt-btn-compact {
    position: relative;
    min-height: 36px;
    padding-inline: var(--tt-space-3);
    border-radius: var(--tt-radius-sm);
    font-size: var(--tt-text-caption);
}

.tt-btn.tt-btn-compact::after {
    content: "";
    position: absolute;
    inset: -4px 0;
}

/* Google/Microsoft sign-in: white/outlined rather than filled with the brand
   colour, matching how every app presents a provider button — it reads as "hand
   off to them", not as this app's own primary action. */
.tt-btn-provider {
    background: var(--tt-surface);
    border-color: color-mix(in srgb, var(--tt-ink) 12%, transparent);
    color: var(--tt-ink);
    box-shadow: none;
}

.tt-divider {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    color: var(--tt-ink-faint);
    font-size: var(--tt-text-caption);
}

.tt-divider::before,
.tt-divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--tt-hairline-strong);
}

.tt-checkbox {
    display: flex;
    align-items: center;
    gap: var(--tt-space-2);
    min-height: var(--tt-tap);
    font-size: var(--tt-text-callout);
    color: var(--tt-ink);
    cursor: pointer;
}

.tt-checkbox input[type="checkbox"],
.tt-checkbox input[type="radio"],
.tt-roster-sel input[type="checkbox"] {
    width: 20px;
    height: 20px;
    accent-color: var(--tt-brand);
}

/* --- Fields ---------------------------------------------------------------
   16px minimum on the input itself: below that, iOS Safari zooms the viewport
   when the field takes focus and the layout lurches sideways mid-form. */

.tt-field {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
}

.tt-label {
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
    letter-spacing: -.01em;
    color: var(--tt-ink);
}

/* A name that opens that person's details: label-sized, but brand-coloured and underlined so it
   reads as something to tap, at a full tap height. */
.tt-name-link {
    display: inline-flex;
    align-items: center;
    min-height: var(--tt-tap);
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
    color: var(--tt-brand);
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
}

.tt-name-link:disabled {
    opacity: .5;
    cursor: default;
}

.tt-input {
    width: 100%;
    min-height: var(--tt-tap);
    padding: var(--tt-space-3);
    font-size: var(--tt-text-body);
    background: var(--tt-surface-sunken);
    border: 1px solid transparent;
    border-radius: var(--tt-radius-md);
    appearance: none;
}

.tt-input::placeholder {
    color: var(--tt-ink-faint);
}

.tt-input:focus {
    outline: none;
    background: var(--tt-surface);
    border-color: var(--tt-brand);
    box-shadow: var(--tt-focus-ring);
}

.tt-input:disabled {
    background: var(--tt-surface-sunken);
    color: var(--tt-ink-muted);
}

/* The one-time code: wide, spaced, unmistakably the thing to fill in. */
.tt-input-code {
    min-height: var(--tt-tap-lg);
    font-size: var(--tt-text-display);
    font-weight: var(--tt-weight-semibold);
    letter-spacing: .3em;
    text-align: center;
}

.tt-field-pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--tt-space-3);
}

.tt-hint {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

/* A hint that says something went wrong, under the field it is about. Was an inline
   style="color: var(--tt-danger)" on five screens. */
.tt-hint-error {
    color: var(--tt-danger);
}

/* A form that opens inside a panel or a card, set off from what is above it. */
.tt-inline-form {
    padding-top: var(--tt-space-3);
    border-top: 1px solid var(--tt-hairline);
}

/* --- Notices --------------------------------------------------------------
   Never a bare red sentence floating on the page: a notice is a surface with a
   role, so a screen reader announces it and a hurried eye finds it. */

.tt-notice {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--tt-space-2) var(--tt-space-3);
    padding: var(--tt-space-3) var(--tt-space-4);
    border-radius: var(--tt-radius-md);
    font-size: var(--tt-text-callout);
    background: var(--tt-surface-sunken);
    color: var(--tt-ink);
}

.tt-notice > span {
    flex: 1 1 14rem;
    min-width: 0;
}

/* A trailing "Complete"/"Review" label stays its own size; letting it grow too
   splits the row with the message and crushes the message to a word per line. */
.tt-notice > .tt-row-meta {
    flex: 0 0 auto;
}

/* Message on top, buttons below on the right: two buttons beside a sentence
   leave the sentence a word per line on a phone. */
.tt-notice-stacked {
    flex-direction: column;
    align-items: stretch;
}

.tt-notice-body {
    display: flex;
    align-items: flex-start;
    gap: var(--tt-space-3);
}

.tt-notice-body > svg {
    flex: 0 0 auto;
    margin-top: 0.15em;
}

.tt-notice-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: var(--tt-space-2);
}

.tt-notice-danger {
    background: var(--tt-danger-soft);
    color: var(--tt-danger);
}

.tt-notice-success {
    background: var(--tt-success-soft);
    color: var(--tt-success);
}

.tt-notice-warning {
    background: var(--tt-warning-soft);
    color: var(--tt-warning);
}

/* For "here's something to do", not "something went wrong" — a survey due or an
   offer waiting reads as an error in the warning palette, so it gets the brand's
   own tint instead. */
.tt-notice-brand {
    background: var(--tt-brand-soft);
    color: var(--tt-brand-strong);
}

.tt-text-danger { color: var(--tt-danger); }
.tt-text-warning { color: var(--tt-warning); }

/* --- Empty and loading ----------------------------------------------------
   An empty screen states what would be here and what to do about it, and where
   the emptiness is the result of a search it offers the way back out of it. */

.tt-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--tt-space-3);
    padding: var(--tt-space-6) var(--tt-space-5);
    text-align: center;
}

.tt-empty .tt-icon {
    width: 28px;
    height: 28px;
    padding: 14px;
    box-sizing: content-box;
    border-radius: 18px;
    background: var(--tt-brand-soft);
    color: var(--tt-brand);
}

.tt-empty-title {
    font-size: var(--tt-text-headline);
    font-weight: var(--tt-weight-semibold);
}

.tt-empty-body {
    max-width: 40ch;
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-callout);
}

.tt-skeleton {
    height: 12px;
    border-radius: var(--tt-radius-sm);
    background: var(--tt-surface-sunken);
    animation: tt-pulse 1.4s var(--tt-ease) infinite;
}

@keyframes tt-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: .45; }
}

.tt-spinner {
    width: 16px;
    height: 16px;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: tt-spin .7s linear infinite;
}

@keyframes tt-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
    }
}

/* --- Status ---------------------------------------------------------------
   A status is a sentence with a dot in front of it. The sentence carries the
   whole meaning — "Awaiting offer response" is legible in greyscale, in a screen
   reader, and to someone who has never seen this app; the dot only makes the
   amber ones findable while scrolling twenty of them.

   Twelve pills in twelve colours is a rainbow nobody reads at arm's length. */

.tt-dot {
    display: inline-block;
    flex: none;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--tt-ink-faint);
}

.tt-dot-success { background: var(--tt-success); }
.tt-dot-warning { background: var(--tt-warning); }
.tt-dot-danger  { background: var(--tt-danger); }

.tt-status-text {
    display: inline-flex;
    align-items: center;
    gap: var(--tt-space-2);
    min-width: 0;
    font-size: var(--tt-text-callout);
    color: var(--tt-ink);
}

.tt-status-text > span:last-child {
    min-width: 0;
    overflow-wrap: anywhere;
}

.tt-status-text-muted {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

/* --- Icons ---------------------------------------------------------------- */

.tt-icon {
    display: block;
    flex: none;
}

/* Present to a screen reader, absent to the eye. */
.tt-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* Shared by the roster, the offers list and the detail panel, so the same child
   is recognisable as the same child on every coach screen. */
.tt-avatar {
    display: grid;
    place-items: center;
    flex: none;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--tt-brand-soft);
    color: var(--tt-brand-strong);
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
}

/* Notifications are off on this device: one line at the top of Home, until they're back on. */
.tt-notif-off {
    background: var(--tt-warning-soft, var(--tt-surface-sunken));
}

.tt-notif-off > .tt-icon {
    flex: none;
}

.tt-signed-in-as {
    text-align: center;
    overflow-wrap: anywhere;
}

.tt-profile .tt-avatar {
    width: 56px;
    height: 56px;
    background: var(--tt-brand);
    color: var(--tt-brand-on);
    font-size: 20px;
}

.tt-row-cta {
    flex: none;
    padding: 4px 10px;
    border-radius: var(--tt-radius-pill);
    background: var(--tt-brand-soft);
    color: var(--tt-brand-strong);
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
}

.tt-list-row > .tt-icon {
    color: var(--tt-ink-faint);
}

a.tt-list-row {
    cursor: pointer;
    text-decoration: none;
    color: inherit;
}

.tt-avatar-lg {
    width: 42px;
    height: 42px;
    font-size: var(--tt-text-callout);
}

/* A player's headshot in the avatar's place: same circle, the photo filling it. */
img.tt-avatar-photo {
    display: block;
    object-fit: cover;
    background: var(--tt-surface-sunken);
}

.tt-avatar-xl {
    width: 88px;
    height: 88px;
    font-size: 28px;
}

/* The Photo group: the headshot as it stands, then what it's for and the action. */
.tt-headshot {
    display: flex;
    align-items: flex-start;
    gap: var(--tt-space-4);
}

.tt-headshot-body {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
    min-width: 0;
}

.tt-headshot-pick {
    position: relative;
    cursor: pointer;
}

.tt-headshot-pick[aria-disabled="true"] {
    opacity: .5;
    cursor: default;
}

/* The real file input, stretched invisibly over the button so a tap anywhere on it opens the chooser,
   and it keeps the focus ring the keyboard needs. */
.tt-headshot-input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: inherit;
}

/* On Your family the photo sits above the child's guardians, inside the same card. */
.tt-family-guardians-heading {
    margin-top: var(--tt-space-4);
    padding-top: var(--tt-space-4);
    border-top: 1px solid var(--tt-hairline);
}

.tt-headshot-pick:focus-within {
    outline: 2px solid var(--tt-focus, var(--tt-brand));
    outline-offset: 2px;
}

/* --- Menus ----------------------------------------------------------------
   Secondary and destructive actions only. The panel is absolutely positioned,
   so opening it never pushes the row below it down the page — which is what a
   <details> does, and why the old roster jumped under the thumb that opened it.

   Three ways out, all of which return focus to the trigger: outside click (that
   fixed transparent button), Escape, and choosing something. */

.tt-menu {
    position: relative;
    display: inline-flex;
    flex: none;
}

.tt-menu-trigger {
    display: grid;
    place-items: center;
    width: var(--tt-tap);
    height: var(--tt-tap);
    border: 0;
    border-radius: var(--tt-radius-sm);
    background: transparent;
    color: var(--tt-ink-muted);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
}

.tt-menu-trigger[aria-expanded="true"] {
    background: var(--tt-brand-soft);
    color: var(--tt-brand-strong);
}

.tt-menu-trigger:disabled {
    opacity: .4;
    cursor: default;
}

.tt-menu-dismiss {
    position: fixed;
    inset: 0;
    z-index: 60;
    border: 0;
    background: transparent;
    cursor: default;
}

.tt-menu-panel {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    z-index: 61;
    min-width: 224px;
    max-width: calc(100dvw - 2 * var(--tt-gutter));
    padding: var(--tt-space-1);
    background: var(--tt-surface);
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-md);
    box-shadow: 0 12px 32px rgba(20, 24, 26, .16);
}

.tt-menu-left .tt-menu-panel {
    right: auto;
    left: 0;
}

/* Laid out but unseen while menu.js measures it, so it never flashes off screen. */
.tt-menu-panel.tt-menu-placing {
    visibility: hidden;
}

.tt-menu-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tt-space-3);
    width: 100%;
    min-height: var(--tt-tap);
    /* Vertical padding as well as a height: a long league name wraps, and without it the second line
       ran into the next item while short items floated in their 40px. */
    padding: 10px var(--tt-space-3);
    border: 0;
    border-radius: var(--tt-radius-sm);
    background: transparent;
    color: var(--tt-ink);
    font-size: var(--tt-text-callout);
    line-height: 1.3;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.tt-menu-item:disabled {
    opacity: .4;
    cursor: default;
}

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

.tt-menu-item-selected {
    font-weight: var(--tt-weight-semibold);
}

/* --- Team workspace -------------------------------------------------------
   The coach's frame: the team name as the heading and the picker, then the four
   views of that team. The club is not repeated here — the app bar's eyebrow has
   already said it, once, for the whole screen. */

.tt-workspace {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-4);
}

/* A quiet label over a group of menu items: "which club these teams are at". */
.tt-menu-heading {
    padding: var(--tt-space-2) var(--tt-space-3) var(--tt-space-1);
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-semibold);
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--tt-ink-faint);
}

.tt-menu-separator {
    height: 1px;
    margin: var(--tt-space-1) 0;
    background: var(--tt-hairline);
}

.tt-menu-item-two-line {
    padding-block: var(--tt-space-2);
}

.tt-menu-item-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.tt-menu-item-meta {
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-regular);
    line-height: 1.2;
    color: var(--tt-ink-muted);
}

/* The schedule's "whose week" toggles: This club / All clubs and This team /
   All teams, side by side, wrapping onto two lines on a narrow phone. Each is a
   segmented control at the width of its labels, not stretched across the page. */
.tt-schedule-filters {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tt-space-2);
}

.tt-schedule-filter {
    align-self: flex-start;
    max-width: 100%;
}

.tt-schedule-filter .tt-seg-option {
    flex: 0 1 auto;
    min-height: 36px;
    padding: 0 var(--tt-space-4);
}

/* The context row: one chip for the club, the team in focus and the seat. */
.tt-appbar-context {
    display: flex;
    align-items: center;
    gap: var(--tt-space-2);
    min-width: 0;
}

/* The wrapper is the positioned box for the sheet; only the chip's text truncates. */
.tt-context {
    position: relative;
    display: flex;
    max-width: 100%;
    min-width: 0;
}

/* Coach filled, family outlined: the shape carries the difference, so it holds
   in every club theme and in dark mode. The team gives way first — it truncates
   before the club does — and the sheet holds both names in full. */
.tt-context-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    max-width: 100%;
    min-width: 0;
    min-height: 28px;
    padding: 0 8px 0 10px;
    border: 1px solid transparent;
    border-radius: var(--tt-radius-pill);
    background: var(--tt-surface-sunken);
    color: var(--tt-ink);
    font: inherit;
    font-size: 12px;
    font-weight: var(--tt-weight-semibold);
    letter-spacing: -.01em;
    line-height: 1.2;
    white-space: nowrap;
    cursor: pointer;
}

.tt-context-chip > .tt-icon {
    flex: none;
}

.tt-context-chip > .tt-spinner {
    flex: none;
    width: 12px;
    height: 12px;
}

.tt-context-chip:disabled {
    cursor: progress;
}

/* A tint rather than solid: beside the Coach | Family switch, two solid pills competed for the eye.
   Tinted (coach) against outlined (family) still carries the difference in shape. */
.tt-context-chip-coach {
    background: color-mix(in srgb, var(--tt-brand) 14%, transparent);
    color: var(--tt-brand-strong);
}

.tt-context-chip-family {
    border-color: var(--tt-brand);
    color: var(--tt-brand);
}

.tt-context-club,
.tt-context-team,
.tt-context-children {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tt-context-club {
    flex: 0 0 auto;
    max-width: 55%;
}

.tt-context-team,
.tt-context-children {
    flex: 0 1 auto;
}

/* A child in the family sheet: a tick box, so it reads as "show this one" (several can be on),
   not as a single choice like the club and team rows. */
.tt-context-child {
    justify-content: flex-start;
    gap: var(--tt-space-3);
}

.tt-context-box {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border: 1.5px solid var(--tt-ink-muted);
    border-radius: 5px;
}

.tt-context-child-on .tt-context-box {
    border-color: var(--tt-brand);
    background: var(--tt-brand);
    color: var(--tt-brand-on);
}

.tt-context-club + .tt-context-team::before {
    content: "·";
    margin-right: 5px;
    opacity: .6;
}

/* The sheet: the Coach | Family switch, then clubs, then the club's teams. Hung from the chip's left
   edge, as wide as the screen allows, so a long team name reads in full. Sections are parted by space
   and a quiet label, not rules: three hairlines in a short sheet read as three separate menus. */
.tt-context-panel {
    left: 0;
    right: auto;
    min-width: min(320px, calc(100dvw - 2 * var(--tt-gutter)));
    max-width: calc(100dvw - 2 * var(--tt-gutter));
    max-height: min(70dvh, 560px);
    overflow-y: auto;
    padding: var(--tt-space-2);
    border-radius: var(--tt-radius-lg);
    box-shadow: 0 16px 40px rgba(20, 24, 26, .18), 0 2px 6px rgba(20, 24, 26, .06);
    font-size: var(--tt-text-body);
    font-weight: var(--tt-weight-regular);
    letter-spacing: normal;
    white-space: normal;
}

.tt-context-group {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.tt-context-group + .tt-context-group {
    margin-top: var(--tt-space-3);
}

.tt-context-panel .tt-menu-heading {
    padding: var(--tt-space-1) var(--tt-space-3);
    font-size: var(--tt-text-micro);
    letter-spacing: .06em;
}

/* Rows: a soft wash on hover, and the chosen one tinted in the club's colour with a plain tick —
   bold text and a small circled check alone did not say "you are here" at a glance. */
.tt-context-panel .tt-menu-item {
    min-height: 44px;
    border-radius: var(--tt-radius-md);
    transition: background var(--tt-duration-fast) var(--tt-ease);
}

.tt-context-panel .tt-menu-item:hover:not(:disabled) {
    background: var(--tt-surface-sunken);
}

.tt-context-panel .tt-menu-item-selected,
.tt-context-panel .tt-menu-item-selected:hover:not(:disabled) {
    background: color-mix(in srgb, var(--tt-brand) 10%, transparent);
    color: var(--tt-ink);
}

.tt-context-panel .tt-menu-item-selected .tt-menu-item-meta {
    color: var(--tt-ink-muted);
}

/* Every team coached and every child, at every club: one grouped list per club, a row each. The
   icon carries the side a row opens — a person for a child, a clipboard for a team coached — so the
   two never look alike; the row on screen is tinted in the club's colour and ticked. */
.tt-context-rows {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-md);
    background: var(--tt-surface);
    overflow: hidden;
}

.tt-context-row {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    width: 100%;
    min-height: 52px;
    padding: var(--tt-space-2) var(--tt-space-3);
    border: 0;
    background: transparent;
    color: var(--tt-ink);
    font: inherit;
    text-align: left;
    cursor: pointer;
    transition: background var(--tt-duration-fast) var(--tt-ease);
}

.tt-context-row + .tt-context-row {
    border-top: 1px solid var(--tt-hairline);
}

.tt-context-row:hover {
    background: var(--tt-surface-sunken);
}

.tt-context-row:focus-visible {
    outline: none;
    box-shadow: inset var(--tt-focus-ring);
}

.tt-context-row .tt-menu-item-text {
    flex: 1;
}

.tt-context-row-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-semibold);
}

.tt-context-row-icon {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--tt-surface-sunken);
    color: var(--tt-ink-muted);
}

/* Coach rows wear the club colour on their icon, family rows stay neutral: the chip's language. */
.tt-context-row-coach .tt-context-row-icon {
    background: color-mix(in srgb, var(--tt-brand) 14%, var(--tt-surface));
    color: var(--tt-brand);
}

.tt-context-row-on,
.tt-context-row-on:hover {
    background: color-mix(in srgb, var(--tt-brand) 8%, var(--tt-surface));
}

.tt-context-row[aria-disabled="true"] {
    cursor: default;
}

.tt-context-tick {
    flex: none;
    display: inline-flex;
    color: var(--tt-brand);
}

/* "Coach U15G" / "Back to Alice", after the chip (CoachToggle). Always outlined: it's a way to go,
   not a state — the chip's coach fill already says which view is on. Chip height, so the row doesn't
   grow; the chip truncates first, this only past its cap. The 44px target comes from the pad under
   "Hit areas". */
.tt-coach-toggle {
    flex: none;
    max-width: 50%;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    min-height: 28px;
    padding: 0 10px;
    border: 1px solid var(--tt-hairline-strong);
    border-radius: var(--tt-radius-pill);
    background: transparent;
    color: var(--tt-ink-muted);
    font: inherit;
    font-size: 12px;
    font-weight: var(--tt-weight-semibold);
    white-space: nowrap;
    cursor: pointer;
}

.tt-coach-toggle:hover {
    border-color: var(--tt-brand);
    color: var(--tt-brand);
}

/* The label names where it goes, so it can truncate a long team name like the chip does. */
.tt-coach-toggle span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* A child whose team this coach-parent coaches, in the context sheet. */
.tt-context-you-coach {
    flex: none;
    padding: 2px 6px;
    border-radius: var(--tt-radius-sm);
    background: color-mix(in srgb, var(--tt-brand) 14%, var(--tt-surface));
    color: var(--tt-brand-strong);
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-semibold);
}

.tt-context-note {
    display: block;
    padding: var(--tt-space-1) var(--tt-space-3) 0;
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

/* The club or team when there is only one: said in full, nothing to choose. */
.tt-context-plain {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--tt-space-2) var(--tt-space-3);
    font-weight: var(--tt-weight-semibold);
    overflow-wrap: anywhere;
}

/* A segmented control, not four more tabs: these are views of one team, and the
   bar has to still read as belonging under the team's name. */
.tt-teamnav-row {
    display: flex;
    align-items: center;
    gap: var(--tt-space-2);
}

.tt-teamnav-row > .tt-teamnav {
    flex: 1;
    min-width: 0;
}

.tt-teamnav-gear {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: var(--tt-radius-pill);
    color: var(--tt-ink-muted);
}

.tt-teamnav-gear:hover {
    color: var(--tt-ink);
}

.tt-teamnav-gear.active {
    background: var(--tt-brand-soft);
    color: var(--tt-brand-strong);
}

.tt-teamnav {
    display: flex;
    gap: 2px;
    padding: 3px;
    background: var(--tt-surface-sunken);
    border-radius: var(--tt-radius-md);
    overflow-x: auto;
    scrollbar-width: none;
}

.tt-teamnav::-webkit-scrollbar {
    display: none;
}

.tt-teamnav-link {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: max-content;
    min-height: 44px;
    padding: 0 var(--tt-space-2);
    border-radius: calc(var(--tt-radius-md) - 3px);
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-medium);
    white-space: nowrap;
}

.tt-teamnav-link.active {
    background: var(--tt-surface);
    color: var(--tt-ink);
    font-weight: var(--tt-weight-semibold);
    box-shadow: var(--tt-shadow-card);
}

/* --- Search, filters and selection ---------------------------------------- */

.tt-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--tt-space-2);
}

/* Small enough a basis that the Select players button keeps it company on a phone line rather
   than being pushed onto a row of its own. */
.tt-toolbar-search {
    flex: 1 1 10rem;
    min-width: 0;
}

/* Only present while selecting, and it follows the list rather than scrolling
   away from it — the count and the actions belong together with what they act
   on, so the coach can see both the tick marks and the verb at once. */
.tt-selectbar {
    position: sticky;
    bottom: var(--tt-space-2);
    z-index: 15;
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
    padding: var(--tt-space-3) var(--tt-space-4);
    background: var(--tt-surface);
    border: 1px solid var(--tt-hairline-strong);
    border-radius: var(--tt-radius-lg);
    box-shadow: 0 10px 30px rgba(20, 24, 26, .12);
}

.tt-selectbar-count {
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-semibold);
    font-variant-numeric: tabular-nums;
}

.tt-selectbar-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--tt-space-2);
}

.tt-selectbar-note {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

/* --- Roster ---------------------------------------------------------------
   A directory: name, offer, guardian access, actions — the same four things in
   the same four places on every line, so a coach reads down a column instead of
   re-parsing a small layout per child.

   The columns are a grid on the row itself rather than a <table>, because the
   same row has to become two stacked lines on a phone, and because .tt-roster-
   meta can then be display:contents on a desktop (its two children join the
   row's grid) and a flex line on a phone. Table semantics are deliberately not
   claimed in ARIA: display:contents breaks them, and every cell here already
   reads as a full sentence on its own. */

.tt-roster {
    /* Every column a fixed width except the name. An `auto` actions column would be as wide as the
       widest button *in that row*, so the heading row — which has no buttons — would sit 44px off
       from the values underneath it. */
    --tt-roster-cols: minmax(0, 1fr) 12rem 12rem 13rem;

    background: var(--tt-surface);
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-lg);
}

.tt-roster-selecting {
    --tt-roster-cols: 32px minmax(0, 1fr) 12rem 12rem 13rem;
}

/* A settled roster drops the offer column — every row on it would read "Offer accepted" — and gives
   the width back to the names. */
.tt-roster-settled {
    --tt-roster-cols: minmax(0, 1fr) 12rem 13rem;
}

.tt-roster-settled.tt-roster-selecting {
    --tt-roster-cols: 32px minmax(0, 1fr) 12rem 13rem;
}

.tt-roster-head,
.tt-roster-row {
    display: grid;
    grid-template-columns: var(--tt-roster-cols);
    align-items: center;
    column-gap: var(--tt-space-3);
    padding-inline: var(--tt-space-4);
}

.tt-roster-head {
    padding-block: var(--tt-space-2);
    border-bottom: 1px solid var(--tt-hairline);
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-semibold);
    letter-spacing: .02em;
    background: var(--tt-surface-sunken);
    border-radius: var(--tt-radius-lg) var(--tt-radius-lg) 0 0;
    color: var(--tt-ink-muted);
}

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

.tt-roster-row {
    position: relative;
    min-height: 56px;
    border-bottom: 1px solid var(--tt-hairline);
}

.tt-roster-row:last-child {
    border-bottom: 0;
}

.tt-roster-row-open {
    background: var(--tt-brand-soft);
}

/* One control per row, stretched over the whole row by the ::after, so a click
   anywhere that is not an action opens this player. The actions and the
   checkbox lift above it. */
.tt-roster-name {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    width: 100%;
    min-height: 48px;
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
    text-align: left;
    cursor: pointer;
}

/* The row's position in its section, so the coach can see how many players there are. Fixed
   width and tabular figures keep the avatars lined up from 1 through 30. */
.tt-roster-num {
    flex: none;
    min-width: 1.75rem;
    text-align: right;
    font-size: var(--tt-text-caption);
    font-variant-numeric: tabular-nums;
    color: var(--tt-ink-muted);
}

.tt-roster-name::after {
    content: "";
    position: absolute;
    inset: 0;
}

.tt-roster-name-block {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
}

/* The one thing a settled roster still needs from the offer: why this player is on it with a
   caveat. A column for it would be blank on every other row. */
.tt-roster-name-note {
    font-size: var(--tt-text-caption);
    color: var(--tt-warning);
    overflow-wrap: break-word;
}

/* Left-aligned and the only thing on the line at headline size: the name is
   what the coach is looking for, every other cell is what they then read. */
.tt-roster-name-text {
    min-width: 0;
    font-size: var(--tt-text-headline);
    font-weight: var(--tt-weight-semibold);
    line-height: var(--tt-leading-tight);

    /* break-word, not anywhere: "Papadopoulos-Lindqvist" wraps at its hyphen instead of
       mid-syllable, which is the difference between a long name and a mangled one. */
    overflow-wrap: break-word;
}

/* The jersey number rides after the name, quieter, so the name still leads the row. */
.tt-roster-jersey {
    margin-left: var(--tt-space-2);
    font-weight: var(--tt-weight-regular);
    font-variant-numeric: tabular-nums;
    color: var(--tt-ink-muted);
    white-space: nowrap;
}

.tt-roster-meta {
    display: contents;
}

.tt-roster-offer,
.tt-roster-access {
    min-width: 0;
}

.tt-roster-sel,
.tt-roster-actions {
    position: relative;
    z-index: 1;
}

/* That z-index makes each actions cell its own stacking context, which traps the menu's
   click-outside layer inside the row that opened it: every later row painted over it, so a click
   on another row's "..." reached that button instead of closing the open menu. The row holding an
   open menu is lifted above the others for as long as it is open. */
.tt-roster-actions:has(.tt-menu-panel) {
    z-index: 70;
}

.tt-roster-sel {
    display: flex;
    align-items: center;
}

.tt-roster-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--tt-space-1);
}

.tt-roster-next:empty {
    display: none;
}

/* --- Side panel -----------------------------------------------------------
   Player details, add player, import, review invitations. It opens *over* the
   roster instead of inside it, which is the whole reason the list behind it
   keeps its search, filters, selection and scroll position: nothing about the
   list changed, and no navigation happened.

   A drawer on a desktop, so the list stays visible beside it. A screen on a
   phone, where there is no room for both. */

.tt-panel-scrim {
    position: fixed;
    inset: 0;
    z-index: 70;
    background: rgba(9, 14, 11, .38);
}

.tt-panel-scrim-top {
    z-index: 80;
}

.tt-panel {
    position: fixed;
    inset: 0 0 0 auto;
    z-index: 71;
    display: flex;
    flex-direction: column;
    /* Percent, not vw: 100vw includes the scrollbar the page already has, so a "full width" panel
       is a scrollbar's width too wide and takes its own right-hand controls off the screen. */
    width: min(460px, 100%);
    background: var(--tt-surface);
    border-left: 1px solid var(--tt-hairline);
    box-shadow: -12px 0 40px rgba(20, 24, 26, .16);
}

.tt-panel-head {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    padding: calc(var(--tt-space-3) + var(--tt-safe-top)) var(--tt-space-4) var(--tt-space-3);
    background: var(--tt-surface);
    border-bottom: 1px solid var(--tt-hairline);
}

.tt-panel-close {
    display: inline-flex;
    align-items: center;
    gap: var(--tt-space-1);
    flex: none;
    min-height: var(--tt-tap);
    padding-inline: var(--tt-space-2);
    margin-left: calc(var(--tt-space-2) * -1);
    border: 0;
    border-radius: var(--tt-radius-sm);
    background: transparent;
    color: var(--tt-brand);
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-medium);
    cursor: pointer;
}

.tt-panel-close-narrow {
    display: none;
}

.tt-panel-titles {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.tt-panel-title {
    font-size: var(--tt-text-title);
    font-weight: var(--tt-weight-bold);
    line-height: var(--tt-leading-tight);
    letter-spacing: -.01em;
    overflow-wrap: anywhere;
}

.tt-panel-subtitle {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

.tt-panel-head-extra {
    display: flex;
    align-items: center;
    gap: var(--tt-space-1);
    flex: none;
}

.tt-panel-body {
    flex: 1;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-4);
    padding: var(--tt-space-4) var(--tt-space-4) calc(var(--tt-space-8) + var(--tt-safe-bottom));
}

/* Groups inside the panel are headings and rules, never boxes: a card in a
   panel is the nested-card bug wearing a different hat, and four of them stacked
   is most of what made the old expansion feel empty. */
.tt-detail-group {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
}

.tt-detail-group + .tt-detail-group {
    padding-top: var(--tt-space-4);
    border-top: 1px solid var(--tt-hairline);
}

.tt-detail-heading {
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-semibold);
    letter-spacing: -.015em;
    color: var(--tt-ink);
}

.tt-detail-subheading {
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-semibold);
}

.tt-detail-note {
    font-size: var(--tt-text-callout);
    color: var(--tt-ink-muted);
}

.tt-detail-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tt-space-2);
    margin-top: var(--tt-space-1);
}

/* One guardian, and everything about them. Separated by a rule rather than
   wrapped in a box, so two guardians read as two entries in one group. */
.tt-guardian {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-1);
}

.tt-guardian + .tt-guardian {
    padding-top: var(--tt-space-3);
    border-top: 1px solid var(--tt-hairline);
}

.tt-guardian-top {
    display: flex;
    align-items: flex-start;
    gap: var(--tt-space-2);
}

.tt-guardian-id {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.tt-guardian-name {
    font-weight: var(--tt-weight-semibold);
    font-size: var(--tt-text-callout);
    overflow-wrap: anywhere;
}

.tt-guardian-contact {
    display: flex;
    flex-wrap: wrap;
    gap: 0 var(--tt-space-4);
    font-size: var(--tt-text-caption);
    overflow-wrap: anywhere;
}

.tt-import-rows,
.tt-recipients {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
}

.tt-import-rows > li {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding-block: var(--tt-space-2);
    border-bottom: 1px solid var(--tt-hairline);
}

.tt-recipients > li {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--tt-space-2) var(--tt-space-3);
    min-height: var(--tt-tap);
    padding-block: var(--tt-space-2);
    border-bottom: 1px solid var(--tt-hairline);
}

.tt-import-rows > li:last-child,
.tt-recipients > li:last-child {
    border-bottom: 0;
}

.tt-recipient-who {
    flex: 1 1 12rem;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

/* --- Confirmation ---------------------------------------------------------
   The two things here that a second click cannot undo. Centred rather than in
   the flow, because a confirmation that unfolds inside a list moves everything
   under it — and the next tap lands on whatever slid into that spot. */

.tt-confirm {
    position: fixed;
    z-index: 81;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: min(420px, calc(100% - 2 * var(--tt-gutter)));
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-3);
    padding: var(--tt-space-5);
    background: var(--tt-surface);
    border-radius: var(--tt-radius-lg);
    box-shadow: 0 24px 60px rgba(9, 14, 11, .32);
}

.tt-confirm-title {
    font-size: var(--tt-text-headline);
    font-weight: var(--tt-weight-bold);
    line-height: var(--tt-leading-tight);
}

.tt-confirm-body {
    font-size: var(--tt-text-callout);
    color: var(--tt-ink-muted);
}

.tt-confirm-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--tt-space-2);
}

/* --- Offers ---------------------------------------------------------------
   Where the team's offers stand, then the letters themselves. The letter is a
   record — its words cannot be edited here — but a guardian's own copy can be
   sent again, because "sent" only ever meant "handed to the mail server". */

.tt-stats {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--tt-space-2);
}

.tt-stats > li {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--tt-space-3);
    background: var(--tt-surface);
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-md);
}

.tt-stat-value {
    font-size: var(--tt-text-title);
    font-weight: var(--tt-weight-bold);
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}

.tt-stat-label {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

.tt-letters {
    list-style: none;
    margin: 0;
    padding: 0;
    background: var(--tt-surface);
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-lg);
    overflow: hidden;
}

.tt-letters > li {
    border-bottom: 1px solid var(--tt-hairline);
}

.tt-letters > li:last-child {
    border-bottom: 0;
}

.tt-letter-row {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    width: 100%;
    min-height: 60px;
    padding: var(--tt-space-3) var(--tt-space-4);
    border: 0;
    background: transparent;
    color: inherit;
    text-align: left;
    cursor: pointer;
}

.tt-letter-who {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.tt-letter-time {
    flex: none;
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.tt-letter-row > .tt-icon {
    color: var(--tt-ink-faint);
    transition: transform var(--tt-duration-fast) var(--tt-ease);
}

.tt-letter-row[aria-expanded="true"] {
    background: var(--tt-surface-sunken);
}

.tt-letter-row[aria-expanded="true"] > .tt-icon {
    transform: rotate(90deg);
}

.tt-letter-detail {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-3);
    padding: var(--tt-space-2) var(--tt-space-4) var(--tt-space-4);
    background: var(--tt-surface-sunken);
}

.tt-letter-heading {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.tt-letter-subject {
    font-weight: var(--tt-weight-semibold);
    overflow-wrap: anywhere;
}

/* The letter as the guardian saw it: its own paper, so it does not read as more
   page chrome. */
.tt-letter-body {
    padding: var(--tt-space-3) var(--tt-space-4);
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-md);
    background: var(--tt-surface);
    font-size: var(--tt-text-callout);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.tt-post-body {
    font-size: var(--tt-text-callout);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* --- Guardian's own Team tab ----------------------------------------------
   A directory of the families on the team. Guardian rows sit inside a padded
   player card, so the list is pulled out to the card's edges rather than
   doubling the inset — which is what keeps a guardian lined up under the child
   they belong to. */

.tt-guardian-list {
    margin: 0 calc(var(--tt-space-4) * -1);
    border-top: 1px solid var(--tt-hairline);

    /* A tint, not just an indent: a guardian is a contact riding under the
       child's row, not a second roster entry. */
    background: var(--tt-surface-sunken);
}

.tt-player-head {
    align-items: center;
}

.tt-player-head .tt-icon {
    color: var(--tt-brand);
}

.tt-player-name {
    flex: 1;
    min-width: 0;
    font-size: var(--tt-text-headline);
}

.tt-guardian-row .tt-row-head {
    align-items: center;
}

.tt-guardian-meta-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--tt-space-2);
}

.tt-guardian-meta-row .tt-row-meta {
    flex: 1 1 auto;
    min-width: 0;
}

/* --- Your player (guardian Team tab) ---------------------------------------
   A family's own child as a profile card: photo, name and team on one line with
   the single Edit, then the facts a guardian checks, as label/value rows. */

.tt-profile-card {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-3);
}

.tt-profile-card-head {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
}

.tt-avatar.tt-avatar-profile {
    width: 64px;
    height: 64px;
    font-size: 22px;
}

.tt-profile-card-name {
    flex: 1;
    min-width: 0;
    font-size: var(--tt-text-headline);
    font-weight: var(--tt-weight-semibold);
    line-height: var(--tt-leading-tight);
    color: var(--tt-ink);
    overflow-wrap: anywhere;
}

.tt-profile-card-edit {
    flex: none;
}

.tt-profile-facts {
    display: flex;
    flex-direction: column;
    margin: 0;
    border-top: 1px solid var(--tt-hairline);
}

.tt-profile-fact {
    display: grid;
    grid-template-columns: 6.5rem 1fr;
    gap: var(--tt-space-3);
    align-items: baseline;
    padding-top: var(--tt-space-3);
}

.tt-profile-fact dt {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

.tt-profile-fact dd {
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 2px var(--tt-space-3);
    font-size: var(--tt-text-callout);
    color: var(--tt-ink);
}

.tt-profile-guardian {
    white-space: nowrap;
}

/* A status in a word: amber while the coach is waiting on the family, green once it's done. */
.tt-status-pill {
    display: inline-flex;
    align-items: center;
    padding: 2px 10px;
    border-radius: var(--tt-radius-pill);
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
}

.tt-status-pill-warning {
    background: var(--tt-warning-soft);
    color: var(--tt-warning);
}

.tt-status-pill-success {
    background: var(--tt-success-soft);
    color: var(--tt-success);
}

/* A coach's line in the directory: their initials lead, as a player's do. */
.tt-directory-person {
    gap: var(--tt-space-3);
    padding-top: var(--tt-space-2);
    padding-bottom: var(--tt-space-2);
}

/* Name over role, whatever the name's length, so every coach's line reads the same. */
.tt-directory-person .tt-directory-who {
    flex-direction: column;
    align-items: flex-start;
}

/* "Take photo" opens the camera on a phone; a desktop browser ignores capture and would show a second
   file picker, so there it's left to "Choose photo". */
@media (pointer: fine) {
    .tt-headshot-camera {
        display: none;
    }
}

/* --- Team directory (guardian view) ----------------------------------------
   A phone-book, not a roster: one line per person, name and what they are on
   the left, round phone/mail icons on the right. */

.tt-directory > * + * {
    border-top: 1px solid var(--tt-hairline);
}

.tt-directory-row {
    display: flex;
    align-items: center;
    gap: var(--tt-space-2);
    min-height: 44px;
    padding: var(--tt-space-1) var(--tt-space-3) var(--tt-space-1) var(--tt-space-4);
}

.tt-directory-player {
    display: flex;
    flex-direction: column;
    padding: var(--tt-space-2) 0;
}

/* The child's line: avatar, name, and (closed) a one-line summary of their family.
   On a teammate it's the button that opens them. */
.tt-directory-player-head {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    width: 100%;
    min-height: 44px;
    padding: 0 var(--tt-space-3) 0 var(--tt-space-4);
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
}

.tt-directory-toggle {
    cursor: pointer;
}

.tt-directory-player-text {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.tt-directory-player-text .tt-directory-type {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.tt-directory-player-name {
    font-weight: var(--tt-weight-semibold);
}

.tt-directory-chevron {
    flex: none;
    display: inline-flex;
    color: var(--tt-ink-faint);
    transition: transform .15s ease;
}

.tt-directory-toggle[aria-expanded="true"] .tt-directory-chevron {
    transform: rotate(90deg);
}

/* Your own child's line: the same head as a teammate's, with a Profile button at its end. */
.tt-directory-mine {
    display: flex;
    align-items: center;
    gap: var(--tt-space-2);
    padding: var(--tt-space-1) var(--tt-space-3) var(--tt-space-1) 0;
}

.tt-directory-mine .tt-directory-player-head {
    flex: 1;
    min-width: 0;
    padding-right: 0;
}

/* Compact to look at, 44px to tap. */
.tt-directory-mine > .tt-btn {
    flex: none;
    min-height: 44px;
}

.tt-directory-needs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tt-space-1);
    margin-top: var(--tt-space-1);
}

/* Guardians line up under the child's name, past the avatar. */
.tt-directory-player .tt-directory-row-guardian {
    padding-left: calc(var(--tt-space-4) + 34px + var(--tt-space-3));
}

.tt-directory-empty {
    padding: var(--tt-space-1) var(--tt-space-4) 0 calc(var(--tt-space-4) + 34px + var(--tt-space-3));
}

.tt-section-count {
    margin-left: var(--tt-space-1);
    color: var(--tt-ink-faint);
    font-weight: var(--tt-weight-regular, 400);
}

/* Guardians sit a step in under their child, with no rule between them. */
.tt-directory-row-guardian {
    min-height: 40px;
    padding-top: 0;
    padding-bottom: 0;
}

.tt-directory-who {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0 var(--tt-space-2);
}

.tt-directory-name {
    overflow-wrap: anywhere;
}

.tt-directory-row-guardian .tt-directory-name {
    font-size: var(--tt-text-callout);
    color: var(--tt-ink);
}

.tt-directory-you {
    margin-left: .3em;
    color: var(--tt-ink-faint);
}

/* What a guardian is to the child, as a chip so it never reads as part of their name. */
.tt-directory-tag {
    padding: 1px 8px;
    border-radius: var(--tt-radius-pill);
    background: var(--tt-hairline);
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
    white-space: nowrap;
}

/* Holds the phone column open when there's no number, so mail icons stay in one column. */
.tt-directory-icon-slot {
    width: 36px;
    height: 36px;
}

.tt-directory-type {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-faint);
}

/* 8px apart so each icon's 44px hit area (below) stops short of its neighbour's. */
.tt-directory-actions {
    flex: none;
    display: flex;
    gap: var(--tt-space-2);
}

.tt-directory-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border: 0;
    border-radius: 50%;
    background: var(--tt-brand-soft);
    color: var(--tt-brand);
    cursor: pointer;
    position: relative;
}

/* A 36px circle, a 44px target. */
.tt-directory-icon::after {
    content: "";
    position: absolute;
    inset: -4px;
}

.tt-directory-icon[aria-pressed="true"] {
    background: var(--tt-hairline);
    color: var(--tt-ink);
}

.tt-directory-icon:disabled {
    opacity: .6;
    cursor: default;
}

.tt-directory-form {
    margin: var(--tt-space-1) var(--tt-space-4) var(--tt-space-3);
}

/* A coach's email is shown in full, so it has to be allowed to wrap. */
.tt-contact-link {
    white-space: normal;
    overflow-wrap: anywhere;
    text-align: left;
    gap: var(--tt-space-1);
}

/* --- Schedule -------------------------------------------------------------
   A time column the eye can run down, and a body that answers "what and where".
   Times are tabular so 9:00 and 11:30 line up their colons. */

/* A block, not the list row's flex line: the details on top, the family's
   answer bar full-width underneath. */
.tt-session {
    display: block;
    min-height: 0;
    padding: 0;
}

.tt-session-main {
    display: flex;
    align-items: flex-start;
    gap: var(--tt-space-3);
    padding: var(--tt-space-3) var(--tt-space-4);
}

.tt-session-time {
    flex: none;
    width: 64px;
    padding-top: 1px;
    font-variant-numeric: tabular-nums;
    line-height: var(--tt-leading-tight);
}

.tt-session-start {
    display: block;
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-bold);
    color: var(--tt-brand-strong);
}

.tt-session-end {
    display: block;
    margin-top: 2px;
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

/* When to be there, under the start and end: "arrive" in small caps so the three times read as
   kick-off, end, arrive without a label eating the 64px column. */
.tt-session-arrive {
    display: block;
    margin-top: 4px;
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold, 600);
    color: var(--tt-ink-muted);
}

.tt-session-arrive::before {
    content: "arrive";
    display: block;
    margin-top: 2px;
    font-size: var(--tt-text-micro);
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--tt-ink-faint, var(--tt-ink-muted));
}

/* When to be out the door, in the brand colour: the one time on the row to act on. */
.tt-session-leave {
    display: block;
    margin-top: 2px;
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-bold);
    color: var(--tt-brand-strong);
}

.tt-session-leave::before {
    content: "leave";
    display: block;
    margin-top: 2px;
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-semibold);
    letter-spacing: .04em;
    text-transform: uppercase;
}

.tt-leave-offer { align-items: center; }
.tt-leave-offer-text { flex: 1; min-width: 0; }
.tt-leave-offer-text a { font-weight: var(--tt-weight-semibold); }

.tt-session-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.tt-session-meta {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

/* Marks a coach-added event (vs. a booking synced from the club) inline with
   the title, since both can carry the same kind label. */
.tt-session-kind-icon {
    display: inline-flex;
    vertical-align: -2px;
    margin-right: var(--tt-space-1);
    color: var(--tt-ink-faint);
}

.tt-session-field {
    display: flex;
    align-items: center;
    gap: var(--tt-space-2);
    font-size: var(--tt-text-callout);
    color: var(--tt-ink);
}

.tt-session-address {
    display: inline-flex;
    align-items: center;
    min-height: var(--tt-tap);
    font-size: var(--tt-text-caption);
    color: var(--tt-brand);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.tt-session-note {
    margin-top: var(--tt-space-1);
    padding: var(--tt-space-2) var(--tt-space-3);
    border-radius: var(--tt-radius-sm);
    background: var(--tt-surface-sunken);
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

/* A coach's event called off: kept on the schedule so families see it's off,
   with the time struck through and the title and reason in the danger colour. */
.tt-session-cancelled .tt-session-start,
.tt-session-cancelled .tt-session-end {
    text-decoration: line-through;
    color: var(--tt-ink-faint);
}

.tt-session-cancelled .tt-row-title {
    color: var(--tt-danger);
}

.tt-session-cancelled .tt-session-note {
    background: var(--tt-danger-soft);
    color: var(--tt-danger);
}

.tt-schedule-heading {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--tt-space-2);
}

.tt-schedule-heading h2 {
    font-size: var(--tt-text-headline);
    letter-spacing: -.02em;
}

.tt-schedule-heading > span {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

/* Past: the same rows, quieter, below a rule — reference, not a to-do. */
.tt-schedule-heading-past {
    margin-top: var(--tt-space-4);
    padding-top: var(--tt-space-4);
    border-top: 1px solid var(--tt-hairline);
}

.tt-schedule-heading-past h2 {
    color: var(--tt-ink-muted);
}

.tt-schedule-past .tt-card {
    opacity: .75;
}

/* The one place in the app that takes a full-bleed brand surface: the next
   thing on the calendar, which is what most people open this app to find. */
.tt-next-session {
    position: relative;
    /* clip, not hidden: a hidden box is still scrollable from code, and focusing
       a control inside it slid the whole card sideways under the ring below. */
    overflow: clip;
    padding: var(--tt-space-3) var(--tt-space-4);
    border-radius: var(--tt-radius-lg);
    background:
        radial-gradient(120% 90% at 100% 0%, color-mix(in srgb, var(--tt-accent) 32%, transparent), transparent 58%),
        linear-gradient(165deg, color-mix(in srgb, var(--tt-brand) 55%, var(--tt-rail)), var(--tt-rail) 72%);
    color: #fff;
    box-shadow: 0 8px 20px color-mix(in srgb, var(--tt-rail) 28%, transparent);
}

.tt-next-session::after {
    content: "";
    position: absolute;
    right: -90px;
    top: -90px;
    width: 280px;
    height: 280px;
    background: radial-gradient(circle, color-mix(in srgb, var(--tt-accent) 28%, transparent), transparent 68%);
    pointer-events: none;
}

.tt-next-top {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--tt-space-3);
    font-size: var(--tt-text-caption);
    color: var(--tt-rail-ink);
}

.tt-next-label {
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-bold);
    letter-spacing: .12em;
    color: var(--tt-accent-ink);
}

.tt-next-session h2 {
    margin-top: var(--tt-space-1);
    font-size: clamp(19px, 2.6vw, 24px);
    line-height: var(--tt-leading-tight);
    letter-spacing: -.03em;
}

.tt-next-team {
    margin-top: 2px;
    font-size: var(--tt-text-callout);
    color: var(--tt-rail-ink);
}

.tt-next-details {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tt-space-1) var(--tt-space-4);
    margin-top: var(--tt-space-2);
    font-size: var(--tt-text-callout);
}

.tt-next-details > span {
    display: flex;
    align-items: center;
    gap: var(--tt-space-2);
}

.tt-next-directions {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    gap: var(--tt-space-3);
    min-height: 36px;
    margin-top: var(--tt-space-2);
    padding: 0 var(--tt-space-3);
    border-radius: var(--tt-radius-pill);
    background: var(--tt-accent);
    color: var(--tt-accent-on);
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-semibold);
}

/* The Schedule tab's version: same card, less of it. It shares the top of the
   screen with the calendar, and the list underneath carries the detail. */
/* Doubled class so the phone breakpoint's padding for the full card, which
   comes later in the file, doesn't win. */
.tt-next-session.tt-next-session-compact {
    padding: var(--tt-space-4) var(--tt-space-5);
    border-radius: var(--tt-radius-lg);
}

.tt-next-session-compact::after {
    display: none;
}

.tt-next-session-compact h2 {
    margin-top: var(--tt-space-2);
    font-size: var(--tt-text-headline);
    letter-spacing: -.02em;
}

.tt-next-session-compact .tt-next-team {
    font-size: var(--tt-text-caption);
}

.tt-next-session-compact .tt-next-details {
    gap: var(--tt-space-2) var(--tt-space-4);
    margin-top: var(--tt-space-3);
    font-size: var(--tt-text-caption);
}

.tt-next-session-compact .tt-next-details .tt-icon {
    width: 16px;
    height: 16px;
}

.tt-next-session-compact .tt-next-directions {
    min-height: 36px;
    margin-top: var(--tt-space-3);
    padding: 0 var(--tt-space-3);
    gap: var(--tt-space-2);
    font-size: var(--tt-text-caption);
}

.tt-schedule-layout,
.tt-schedule-list {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-3);
    min-width: 0;
}

.tt-schedule-top {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--tt-space-3);
    align-items: start;
}

/* On a phone the month stays pinned under the app bar and the list scrolls
   beneath it, so the grid is always there to answer "what's on the 24th".
   The top row dissolves (display: contents) so the month card's sticky range
   is the whole schedule, not just its own row — a coach's next-up card above
   it scrolls away as normal. sticky-month.js publishes the bar's and the
   card's real heights; the fallbacks are their usual sizes.

   Pinned, the month is a band across the screen rather than a card in a
   column: tighter cells, square top, and a band of ground above it so the
   list doesn't show through the gap under the bar. */
@media (max-width: 719.98px) {
    .tt-schedule-top {
        display: contents;
    }

    .tt-month-card {
        position: sticky;
        top: calc(var(--tt-appbar-live, calc(var(--tt-appbar-height) + var(--tt-safe-top))) + var(--tt-space-2));
        z-index: 10;
        box-shadow: 0 8px 20px -10px rgba(16, 24, 20, .28);
    }

    .tt-month-card::before {
        content: "";
        position: absolute;
        left: calc(-1 * var(--tt-gutter));
        right: calc(-1 * var(--tt-gutter));
        bottom: 100%;
        height: calc(var(--tt-space-2) + 1px);
        background: var(--tt-ground);
    }

    .tt-month-card .tt-month-day {
        min-height: 36px;
    }

    .tt-month-card .tt-month-step {
        width: 40px;
        height: 40px;
    }

    .tt-month-card:not(.is-open) {
        padding-block: 0;
    }

    .tt-month-card:not(.is-open) .tt-month-body {
        display: none;
    }

    .tt-month-card.is-open .tt-month-toggle {
        margin-bottom: var(--tt-space-2);
    }

    /* A day jumped to from the grid lands under the pinned month, not behind it. */
    .tt-schedule-layout .tt-day-anchor {
        scroll-margin-top: calc(var(--tt-appbar-live, calc(var(--tt-appbar-height) + var(--tt-safe-top))) + var(--tt-month-live, 320px) + var(--tt-space-4));
    }
}

@media (min-width: 720px) {
    .tt-schedule-top {
        grid-template-columns: minmax(0, 1fr) minmax(280px, 340px);
    }

    /* A guardian's Schedule has the month alone up top. */
    .tt-schedule-top.tt-schedule-top-single {
        grid-template-columns: minmax(280px, 420px);
    }
}

/* --- Home ------------------------------------------------------------------
   Each Home section is a heading with its "all of them" link on the same line,
   so the way to the full list is where the eye already is. */

.tt-home-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--tt-space-3);
}

.tt-home-head-link {
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
    color: var(--tt-brand);
    white-space: nowrap;
}

.tt-message-head {
    display: flex;
    align-items: flex-start;
    gap: var(--tt-space-2);
}

.tt-icon-close {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    margin: -6px -8px 0 0;
    border: 0;
    border-radius: var(--tt-radius-md);
    background: transparent;
    color: var(--tt-ink-muted);
    cursor: pointer;
}

.tt-icon-close:hover {
    background: var(--tt-hairline);
    color: var(--tt-ink);
}

/* A distinct next-event surface, with availability kept on its own light action bar. */
.tt-upnext {
    padding: 0;
    overflow: hidden;
}

.tt-dashboard-next > .tt-upnext + .tt-upnext {
    margin-top: var(--tt-space-3);
}

/* Whose card this is: the child's name above the tinted block, so a parent with two children on two
   teams reads "Emma" before anything else. */
.tt-upnext-who {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px var(--tt-space-4) 0;
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-semibold);
    color: var(--tt-ink);
}

.tt-upnext-main {
    display: flex;
    gap: var(--tt-space-3);
    align-items: center;
    padding: var(--tt-space-3) var(--tt-space-4);
    background: var(--tt-brand-soft);
    color: var(--tt-ink);
    /* A brand edge and a solid date tile carry the emphasis: present, without a dark block (2026-10-21). */
    box-shadow: inset 4px 0 0 var(--tt-brand);
}

.tt-upnext-date {
    flex: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 52px;
    min-height: 64px;
    border-radius: 10px;
    background: var(--tt-brand);
    color: var(--tt-ink-on-brand);
    line-height: 1;
}

.tt-upnext-dow {
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-semibold);
    letter-spacing: .06em;
    text-transform: uppercase;
}

.tt-upnext-day {
    margin-top: 2px;
    font-size: var(--tt-text-title);
    font-weight: var(--tt-weight-bold);
    letter-spacing: -.02em;
}

.tt-upnext-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.tt-upnext-title {
    font-size: clamp(16px, 2vw, 19px);
    font-weight: var(--tt-weight-bold);
    line-height: 1.2;
    letter-spacing: -.025em;
    overflow-wrap: anywhere;
}
.tt-upnext-meta { font-size: var(--tt-text-micro); color: var(--tt-ink-muted); margin-block: 2px; }
.tt-upnext-time { font-size: var(--tt-text-callout); font-weight: 600; margin-bottom: 2px; }
.tt-upnext-arrive, .tt-next-arrive { display: inline-flex; align-items: center; gap: 6px; font-size: var(--tt-text-micro); color: var(--tt-ink-muted); margin-bottom: 2px; }
.tt-upnext-month { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; margin-top: 4px; }
/* The whole tinted block is one button that opens the item's details; the chevron at its end says so. */
button.tt-upnext-main {
    width: 100%;
    border: 0;
    font: inherit;
    letter-spacing: inherit;
    text-align: left;
    cursor: pointer;
}

button.tt-upnext-main > .tt-icon:last-child {
    flex: none;
    color: var(--tt-ink-muted);
}

.tt-upnext-place {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    min-width: 0;
    font-size: var(--tt-text-micro);
    color: var(--tt-ink-muted);
}

.tt-upnext-place > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Directions on their own line under the block: a link can't live inside the button. */
.tt-upnext-footer {
    display: flex;
    align-items: center;
    padding: 0 var(--tt-space-4);
    border-bottom: 1px solid var(--tt-hairline);
}

.tt-upnext-footer .tt-place-link {
    min-height: var(--tt-tap);
}
.tt-event-notes { white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.65; }
.tt-home-tasks .tt-row-title, .tt-home-tasks .tt-row-meta { display: block; }
.tt-task-symbol { display: grid; place-items: center; width: 36px; height: 36px; flex: none; color: var(--tt-brand); background: var(--tt-brand-soft); border-radius: 10px; }

/* Where it is: the directions link on Home's cards, and plain text in a
   Schedule row (PlaceLabel), where the row itself opens the item. One line; a
   long school name ellipsizes rather than wrapping. */
.tt-place-label {
    display: flex;
    align-items: center;
    gap: 4px;
    min-width: 0;
    max-width: 100%;
    align-self: flex-start;
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

.tt-place-label > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tt-place-label .tt-icon {
    flex: none;
}

.tt-place-link {
    display: flex;
    align-items: center;
    gap: 4px;
    min-width: 0;
    max-width: 100%;
    align-self: flex-start;
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-medium);
    color: var(--tt-brand);
}

.tt-place-link > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tt-place-link .tt-icon {
    flex: none;
}

/* The answer bar: the card's bottom edge, split three ways. */
.tt-avail-list.tt-avail-slim {
    gap: 0;
    margin: 0;
    border-top: 1px solid var(--tt-hairline);
}

.tt-avail-slim .tt-avail {
    gap: 0;
}

.tt-avail-slim .tt-avail + .tt-avail {
    border-top: 1px solid var(--tt-hairline);
}

.tt-avail-slim .tt-avail-head:not(:empty) {
    padding: var(--tt-space-2) var(--tt-space-4) 0;
}

/* Three coloured buttons rather than a hairline-split bar: a parent at the
   field should see green / red / yellow and tap, not read three grey words. */
.tt-avail-slim .tt-avail-options {
    max-width: none;
    gap: var(--tt-space-2);
    padding: var(--tt-space-2) var(--tt-space-3);
}

.tt-avail-slim .tt-avail-option {
    min-height: var(--tt-tap);
    font-size: var(--tt-text-caption);
}

.tt-avail-slim .tt-avail-note-link,
.tt-avail-slim .tt-avail-note-sent,
.tt-avail-slim .tt-avail-note-edit,
.tt-avail-slim .tt-avail-locked,
.tt-avail-slim .tt-avail-problem,
.tt-avail-slim > .tt-hint {
    margin: 0 var(--tt-space-4) var(--tt-space-2);
    padding-top: var(--tt-space-2);
    font-size: var(--tt-text-caption);
}

.tt-avail-slim .tt-avail-name,
.tt-avail-slim .tt-avail-meta {
    font-size: var(--tt-text-caption);
}

/* --- Month at a glance ----------------------------------------------------
   Ported from the coach portal's phone calendar: a dot says something is on,
   the list below says what. */

.tt-month {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
}

.tt-month-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tt-space-2);
}

.tt-month-title {
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-semibold);
}

.tt-month-step {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--tt-tap);
    height: var(--tt-tap);
    border: 0;
    border-radius: var(--tt-radius-sm);
    background: var(--tt-surface-sunken);
    color: var(--tt-ink);
    cursor: pointer;
}

.tt-month-step:disabled {
    opacity: .35;
    cursor: default;
}

.tt-month-row {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 4px;
}

.tt-month-wd {
    padding: 2px 0;
    text-align: center;
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-bold);
    letter-spacing: .05em;
    color: var(--tt-ink-faint);
}

.tt-month-day {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    min-height: var(--tt-tap);
    padding: 2px;
    border: 1px solid transparent;
    border-radius: var(--tt-radius-sm);
    background: var(--tt-brand-soft);
    color: var(--tt-ink);
    font: inherit;
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
    font-variant-numeric: tabular-nums;
    line-height: 1;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.tt-month-day.is-empty {
    background: transparent;
    color: var(--tt-ink-faint);
    font-weight: var(--tt-weight-regular);
    cursor: default;
}

.tt-month-day.is-past {
    opacity: .45;
}

/* A day already played still says so: dimmer than what's coming, but its dot
   stays readable and it still jumps to the Past list below. */
.tt-month-day.is-past:not(.is-empty) {
    opacity: .7;
}

.tt-month-day.is-past:not(.is-selected) .tt-month-dot {
    background: var(--tt-ink-muted);
}

.tt-month-day.is-today {
    box-shadow: inset 0 0 0 2px var(--tt-brand);
}

.tt-month-day.is-selected {
    background: var(--tt-brand);
    color: var(--tt-brand-on);
}

.tt-month-dot {
    display: inline-block;
    flex-shrink: 0;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--tt-brand);
}

.tt-month-day.is-selected .tt-month-dot {
    background: var(--tt-brand-on);
}

/* The mark under the day number: the plain dot, or the family's answer as a
   glyph. A fixed slot, so a day with a tick and a day with a dot keep their
   numbers on the same line. */
.tt-month-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

/* The 24-unit glyphs drawn at 16px: the default stroke reads as hairline. */
.tt-month-mark .tt-icon {
    stroke-width: 3;
}

/* An answered day is tinted to its answer, so the month reads at a glance
   without hunting for the glyph. The class sits on the day itself. */
.tt-month-day.is-yes {
    background: var(--tt-success-soft);
    border-color: color-mix(in srgb, var(--tt-answer-yes) 45%, transparent);
    color: var(--tt-answer-yes);
}

.tt-month-day.is-no {
    background: var(--tt-danger-soft);
    border-color: color-mix(in srgb, var(--tt-answer-no) 45%, transparent);
    color: var(--tt-answer-no);
}

.tt-month-day.is-maybe {
    background: var(--tt-answer-maybe-soft);
    border-color: color-mix(in srgb, var(--tt-answer-maybe) 70%, transparent);
    color: var(--tt-answer-maybe-ink);
}

.tt-month-day.is-mixed {
    background: var(--tt-surface-sunken);
    border-color: var(--tt-hairline-strong);
}

.tt-month-day.is-mixed .tt-month-mark {
    color: var(--tt-ink-muted);
}

/* Still to answer: a touch bigger than the plain dot, so it reads as the one
   thing on the month left to do. */
.tt-month-day.is-open .tt-month-dot {
    width: 8px;
    height: 8px;
}

/* A day already played keeps its tint but drops its colour to grey, so last
   week's answers don't compete with this week's. */
.tt-month-day.is-past:not(.is-selected):is(.is-yes, .is-no, .is-maybe, .is-mixed) {
    background: var(--tt-surface-sunken);
    border-color: transparent;
    color: var(--tt-ink-muted);
}

.tt-month-day.is-selected:is(.is-yes, .is-no, .is-maybe, .is-mixed) {
    background: var(--tt-brand);
    border-color: transparent;
    color: var(--tt-brand-on);
}

.tt-month-day.is-selected .tt-month-mark {
    color: var(--tt-brand-on);
}

/* A schedule row opens the item, for a coach or a family: the whole row is the target. */
.tt-session-openable {
    cursor: pointer;
}

.tt-session-openable:hover {
    background: var(--tt-surface-sunken);
}

.tt-session-open {
    display: block;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

/* --- Item panel: details, then players by answer ----------------------- */

.tt-event-detail {
    display: flex;
    align-items: flex-start;
    gap: var(--tt-space-3);
}

.tt-event-detail > .tt-icon {
    flex: none;
    margin-top: 2px;
    color: var(--tt-ink-faint);
}

.tt-event-detail > span {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.tt-event-detail-sub {
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-caption);
}

/* Day and time side by side ("Tomorrow  6:30 PM – 7:30 PM"), wrapping only if they don't fit. */
.tt-event-detail > .tt-event-detail-inline {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: var(--tt-space-2);
}

.tt-avail-group-heading {
    display: flex;
    align-items: center;
    gap: var(--tt-space-2);
}

.tt-avail-group-count {
    min-width: 22px;
    padding: 1px 7px;
    border-radius: 999px;
    background: var(--tt-surface-sunken);
    color: var(--tt-ink-muted);
    text-align: center;
    letter-spacing: 0;
}

.tt-avail-group-yes .tt-avail-group-count {
    background: var(--tt-brand-soft);
    color: var(--tt-brand);
}

/* The row a changed answer just moved here, so the eye can find where it went. */
.tt-avail-row.is-moved {
    border-radius: var(--tt-radius-sm);
    animation: tt-avail-moved 1.6s ease-out;
}

@keyframes tt-avail-moved {
    from { background: var(--tt-brand-soft); box-shadow: 0 0 0 6px var(--tt-brand-soft); }
    to { background: transparent; box-shadow: 0 0 0 6px transparent; }
}

@media (prefers-reduced-motion: reduce) {
    .tt-avail-row.is-moved {
        animation: none;
    }
}

/* --- Item panel header: a calendar tile in the club's colour, then the place as a
   tappable card. Gives the panel a face before the roster starts. */
.tt-hero {
    display: flex;
    align-items: center;
    gap: var(--tt-space-4);
}

.tt-hero-date {
    flex: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 64px;
    overflow: hidden;
    border-radius: var(--tt-radius-md);
    background: var(--tt-surface);
    box-shadow: 0 0 0 1px var(--tt-hairline), var(--tt-shadow-card);
    line-height: 1;
}

.tt-hero-date-month {
    align-self: stretch;
    padding: 4px 0;
    background: var(--tt-brand);
    color: var(--tt-brand-on);
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-bold);
    letter-spacing: .08em;
    text-align: center;
    text-transform: uppercase;
}

.tt-hero-date-day {
    padding-top: 6px;
    font-size: var(--tt-text-display);
    font-weight: var(--tt-weight-bold);
    letter-spacing: -.03em;
    color: var(--tt-ink);
}

.tt-hero-date-dow {
    padding: 2px 0 7px;
    font-size: var(--tt-text-micro);
    color: var(--tt-ink-muted);
}

.tt-hero-when {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    min-width: 0;
}

.tt-hero-day {
    font-size: var(--tt-text-title);
    font-weight: var(--tt-weight-bold);
    letter-spacing: -.02em;
    color: var(--tt-ink);
}

.tt-hero-time {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--tt-space-2);
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-callout);
    font-variant-numeric: tabular-nums;
}

.tt-hero-duration {
    padding: 1px 7px;
    border-radius: var(--tt-radius-pill);
    background: var(--tt-surface-sunken);
    font-size: var(--tt-text-micro);
}

/* The time strip under the hero: Arrive · Kick-off, each a labelled time, as one bar. A third step
   (Leave) joins it once the family has a leave-from address. */
.tt-hero-strip {
    display: flex;
    margin-top: var(--tt-space-3);
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-lg, 12px);
    background: var(--tt-surface);
    overflow: hidden;
}

.tt-hero-strip-step {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--tt-space-2) var(--tt-space-3);
    min-height: 44px;
    justify-content: center;
}

.tt-hero-strip-step + .tt-hero-strip-step {
    border-left: 1px solid var(--tt-hairline);
}

.tt-hero-strip-label {
    font-size: var(--tt-text-micro);
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--tt-ink-muted);
}

.tt-hero-strip-time {
    font-size: var(--tt-text-callout);
    font-variant-numeric: tabular-nums;
    color: var(--tt-ink);
}

.tt-hero-strip-step-leave .tt-hero-strip-time {
    color: var(--tt-brand-strong);
}

a.tt-hero-strip-step {
    color: inherit;
    text-decoration: none;
}

a.tt-hero-strip-step:hover .tt-hero-strip-time { text-decoration: underline; }

.tt-hero-strip-note {
    font-size: var(--tt-text-micro);
    color: var(--tt-ink-muted);
}

.tt-hero-place {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    margin-top: var(--tt-space-1);
    padding: var(--tt-space-3);
    border-radius: var(--tt-radius-md);
    background: var(--tt-surface-sunken);
    color: var(--tt-ink);
    text-decoration: none;
}

a.tt-hero-place:hover {
    background: var(--tt-brand-soft);
}

a.tt-hero-place > .tt-icon {
    flex: none;
    margin-left: auto;
    color: var(--tt-ink-faint);
}

.tt-hero-place-icon {
    flex: none;
    display: grid;
    place-items: center;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    background: var(--tt-brand);
    color: var(--tt-brand-on);
}

.tt-hero-place-text {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.tt-hero-place-text > span {
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-caption);
    overflow-wrap: anywhere;
}

/* Turnout: one big figure and a bar in the group colours; the group headings
   below (each with its dot) are the legend. */
.tt-turnout-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--tt-space-1) var(--tt-space-2);
}

.tt-turnout-figure {
    font-size: 40px;
    font-weight: var(--tt-weight-bold);
    letter-spacing: -.04em;
    line-height: 1;
    color: var(--tt-success);
    font-variant-numeric: tabular-nums;
}

.tt-turnout-of {
    font-size: var(--tt-text-headline);
    font-weight: var(--tt-weight-semibold);
    color: var(--tt-ink);
}

.tt-turnout-figure.is-zero {
    color: var(--tt-ink-faint);
}

.tt-turnout-tag {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: auto;
    padding: 2px 8px;
    border-radius: var(--tt-radius-pill);
    background: var(--tt-success-soft);
    color: var(--tt-success);
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-semibold);
}

.tt-turnout-bar {
    display: flex;
    gap: 3px;
    height: 10px;
}

.tt-turnout-seg {
    flex-basis: 0;
    min-width: 6px;
    border-radius: var(--tt-radius-pill);
    transition: flex-grow .35s ease;
}

/* A group's heading, with the group's one action (Nudge, on Not answered) across from it, over the
   answer buttons it chases. */
.tt-avail-group-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tt-space-3);
    min-height: 44px;
}

.tt-avail-group-top > .tt-btn {
    flex: none;
}

.tt-avail-group-heading > .tt-avail-dot {
    width: 10px;
    height: 10px;
}

/* Each player wears an avatar tinted by their answer, so the groups read by
   colour as well as by heading. */
.tt-avail-roster > .tt-avail-row {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
}

/* With the note box open the row is tall; the avatar stays beside the name, not halfway down the box. */
.tt-avail-roster > .tt-avail-row:has(.tt-avail-note-edit) {
    align-items: flex-start;
}

.tt-avail-row > .tt-avail {
    flex: 1;
    min-width: 0;
}

.tt-avail-row-yes > .tt-avatar { background: var(--tt-success-soft); color: var(--tt-success); }
.tt-avail-row-maybe > .tt-avatar { background: var(--tt-warning-soft); color: var(--tt-warning); }
.tt-avail-row-no > .tt-avatar { background: var(--tt-danger-soft); color: var(--tt-danger); }
.tt-avail-row-none > .tt-avatar { background: var(--tt-surface-sunken); color: var(--tt-ink-muted); }

@media (prefers-reduced-motion: reduce) {
    .tt-turnout-seg {
        transition: none;
    }
}

/* A day heading is focused from code when the month grid jumps to it; clear
   the sticky app bar and skip the outline a tap never asked for. */
.tt-day-anchor {
    scroll-margin-top: calc(var(--tt-appbar-height) + var(--tt-safe-top) + var(--tt-space-4));
}

.tt-day-anchor:focus {
    outline: none;
}

/* --- Plain shell ----------------------------------------------------------
   Sign-in and the invitation link: no tabs, no club bar, because neither has
   anything true to say yet. */

.tt-main-plain {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    min-height: 100dvh;
    padding-top: calc(var(--tt-space-5) + var(--tt-safe-top));
    padding-bottom: calc(var(--tt-space-5) + var(--tt-safe-bottom));
    background:
        radial-gradient(720px 360px at 50% -40px, color-mix(in srgb, var(--tt-brand) 22%, transparent), transparent 70%),
        var(--tt-ground);
}

.tt-main-plain > .tt-stack {
    width: min(440px, 100%);
    max-width: 440px;
    margin-inline: auto;
    padding: var(--tt-space-5) var(--tt-space-5);
    background: var(--tt-surface);
    border: 0;
    border-radius: 28px;
    box-shadow: 0 24px 70px rgba(0, 0, 0, .16);
}

.tt-main-plain .tt-screen-title {
    font-size: 32px;
    letter-spacing: -.035em;
}

.tt-main-plain .tt-brandmark {
    margin-bottom: var(--tt-space-4);
}

.tt-main-plain .tt-card {
    padding: 0;
    border: 0;
    box-shadow: none;
}

/* --- Sign-in --------------------------------------------------------------
   Plain and quiet on purpose: brand above, one bordered card, legal below — the
   layout every account screen people already trust uses. Square-ish controls
   rather than the app's pills, because here they read as a form, not a chip. */

.tt-main-plain:has(> .tt-auth) {
    background: var(--tt-ground);
}

.tt-auth {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--tt-space-6);
    width: min(420px, 100%);
    margin: auto;
}

.tt-auth-brand {
    display: flex;
    align-items: center;
    gap: var(--tt-space-2);
    font-size: var(--tt-text-headline);
    font-weight: var(--tt-weight-bold);
    letter-spacing: -.02em;
    color: var(--tt-ink);
}

.tt-auth-logo {
    display: inline-grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border-radius: var(--tt-radius-sm);
    background: var(--tt-brand);
    color: var(--tt-brand-on);
}

.tt-auth-card {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-5);
    width: 100%;
    box-sizing: border-box;
    padding: var(--tt-space-8);
    background: var(--tt-surface);
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-lg);
    box-shadow: var(--tt-shadow-card);
}

.tt-auth-head {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
}

.tt-auth-title {
    font-size: 24px;
    font-weight: var(--tt-weight-bold);
    line-height: var(--tt-leading-tight);
    letter-spacing: -.02em;
}

.tt-auth-lede {
    font-size: var(--tt-text-callout);
    line-height: var(--tt-leading-normal);
    color: var(--tt-ink-muted);
}

.tt-auth-lede strong {
    color: var(--tt-ink);
    font-weight: var(--tt-weight-semibold);
    overflow-wrap: anywhere;
}

.tt-auth-icon {
    display: inline-grid;
    place-items: center;
    width: 44px;
    height: 44px;
    margin-bottom: var(--tt-space-2);
    border-radius: var(--tt-radius-md);
    background: var(--tt-brand-soft);
    color: var(--tt-brand);
}

.tt-auth-form,
.tt-auth-providers {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-3);
}

.tt-auth .tt-input {
    min-height: 48px;
    background: var(--tt-surface);
    border-color: var(--tt-hairline-strong);
    border-radius: var(--tt-radius-sm);
}

.tt-auth .tt-input:hover:not(:focus) {
    border-color: var(--tt-ink-faint);
}

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

.tt-auth .tt-btn {
    min-height: 48px;
    border-radius: var(--tt-radius-sm);
    box-shadow: none;
}

.tt-auth .tt-btn:not(.tt-btn-provider):not(.tt-btn-secondary):not(.tt-btn-ghost):hover:not(:disabled) {
    background: var(--tt-brand-strong);
}

.tt-auth .tt-btn-provider:hover {
    background: var(--tt-surface-sunken);
}

.tt-auth-remember {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

.tt-auth-remember input[type="checkbox"] {
    width: 18px;
    height: 18px;
}

.tt-auth-aside {
    font-size: var(--tt-text-caption);
    line-height: var(--tt-leading-normal);
    color: var(--tt-ink-muted);
    text-align: center;
}

/* Inline text action. The ::after pad keeps the tap target at 44px without
   breaking the sentence it sits in. */
.tt-auth-link {
    position: relative;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-weight: var(--tt-weight-semibold);
    color: var(--tt-brand);
    cursor: pointer;
}

.tt-auth-link::after {
    content: "";
    position: absolute;
    inset: -14px -4px;
}

.tt-auth-link:hover {
    text-decoration: underline;
    text-underline-offset: 2px;
}

.tt-auth-clubs {
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-md);
    overflow: hidden;
}

.tt-auth-foot {
    max-width: 340px;
    font-size: var(--tt-text-micro);
    line-height: var(--tt-leading-normal);
    color: var(--tt-ink-faint);
    text-align: center;
}

.tt-auth-foot a {
    color: var(--tt-ink-muted);
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* On a phone the card is the screen: no frame around a frame. */
@media (max-width: 479px) {
    .tt-main-plain:has(> .tt-auth) {
        background: var(--tt-surface);
    }

    .tt-auth {
        margin-top: 0;
        align-items: stretch;
    }

    .tt-auth-brand {
        padding-inline: var(--tt-space-1);
    }

    .tt-auth-card {
        padding: 0 var(--tt-space-1);
        border: 0;
        box-shadow: none;
    }

    .tt-auth-foot {
        max-width: none;
    }
}

.tt-brandmark {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    font-size: var(--tt-text-title);
    font-weight: var(--tt-weight-bold);
    letter-spacing: -.03em;
    color: var(--tt-ink);
}

.tt-brandmark .tt-icon {
    width: 40px;
    height: 40px;
    padding: 8px;
    box-sizing: border-box;
    border-radius: 12px;
    background: var(--tt-brand);
    color: var(--tt-brand-on);
}

/* --- Framework surfaces ---------------------------------------------------
   Blazor's own error and load UI, restyled so a bad moment still looks like
   this app rather than a default yellow strip. */

#blazor-error-ui {
    position: fixed;
    left: 0;
    bottom: 0;
    z-index: 100;
    display: none;
    width: 100%;
    padding: var(--tt-space-4) var(--tt-gutter) calc(var(--tt-space-4) + var(--tt-safe-bottom));
    background: var(--tt-danger);
    color: #fff;
    box-shadow: var(--tt-shadow-sheet);
}

#blazor-error-ui .reload {
    color: #fff;
    text-decoration: underline;
}

#blazor-error-ui .dismiss {
    position: absolute;
    top: var(--tt-space-3);
    right: var(--tt-space-4);
    cursor: pointer;
}

.blazor-error-boundary {
    padding: var(--tt-space-4);
    border-radius: var(--tt-radius-md);
    background: var(--tt-danger);
    color: #fff;
}

.blazor-error-boundary::after {
    content: "Something went wrong on this screen.";
}

/* The launch screen, shown while the app downloads and the session is restored. An app opens on its
   own mark, not a spinner with a percentage: the icon from the Home Screen, the name under it, and a
   thin bar that fills as Blazor reports progress (it sits full while the session is restored). */
.tt-boot {
    position: fixed;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--tt-space-3);
    background: var(--tt-ground);
}

.tt-boot-icon {
    width: 72px;
    height: 72px;
    border-radius: 18px;
}

.tt-boot-name {
    font-size: var(--tt-text-headline);
    font-weight: var(--tt-weight-bold);
    color: var(--tt-ink);
}

.tt-boot-bar {
    width: 120px;
    height: 4px;
    margin-top: var(--tt-space-2);
    border-radius: 2px;
    background: var(--tt-hairline);
    overflow: hidden;
}

.tt-boot-bar::after {
    content: "";
    display: block;
    height: 100%;
    width: var(--blazor-load-percentage, 0%);
    background: var(--tt-brand);
    transition: width .15s ease-out;
}

/* =============================================================================
   Responsive and input-dependent
   ========================================================================== */

@media (hover: hover) {
    .tt-tab:hover:not(.active) {
        color: var(--tt-ink);
    }

    .tt-tab:hover:not(.active) > .tt-icon {
        background: var(--tt-surface-sunken);
    }

    button.tt-list-row:hover,
    a.tt-list-row:hover {
        background: var(--tt-brand-soft);
    }

    .tt-btn:hover:not(:disabled):not(.tt-btn-ghost):not(.tt-btn-danger) {
        filter: brightness(1.06);
    }

    .tt-appbar-action:hover {
        filter: brightness(1.08);
    }

    .tt-menu-trigger:hover:not(:disabled),
    .tt-panel-close:hover {
        background: var(--tt-brand-soft);
        color: var(--tt-brand-strong);
    }

    .tt-menu-item:hover:not(:disabled) {
        background: var(--tt-surface-sunken);
    }

    .tt-roster-row:hover:not(.tt-roster-row-open),
    .tt-letter-row:hover:not([aria-expanded="true"]) {
        background: var(--tt-surface-sunken);
    }
}


/* Four columns need about 840px of list to hold two status sentences and a button without any of
   them wrapping. Below that the row becomes three lines: the name, both statuses under it, and the
   row's next step on its own line.

   Two ranges, because the list is not the window: the desktop rail appears at 960px and takes 208px
   of it back. So a 950px window (no rail) has the room and a 1000px window (rail) does not, and one
   breakpoint would be wrong at one end or the other.

   A container query would say this in one line — and would also make .tt-roster a containing block
   for the fixed-position overlay that catches outside clicks on a row menu, shrinking it from the
   page to the list. Two ranges, and menus that dismiss properly. */
@media (max-width: 940px), (min-width: 960px) and (max-width: 1150px) {
    /* The settled roster has one fewer column and would still fit here, but a coach switching
       between the two tabs at the same window size should not get two different row shapes. */
    .tt-roster-head {
        display: none;
    }

    /* The name gets the whole first line. Sharing it with the row's action button is what turned
       "Christopher Papadopoulos-Lindqvist" into three ragged lines beside a button. The next step
       rides on the status line instead, right-aligned: a line of its own spent a third of every
       row's height on one button. The middle column exists so the button can span under the menu
       without its width being taken out of the name's line. */
    .tt-roster-row {
        grid-template-columns: minmax(0, 1fr) auto auto;
        grid-template-areas:
            "player player  menu"
            "meta   actions actions";
        row-gap: 2px;
        min-height: 0;
        padding-block: var(--tt-space-2);
    }

    .tt-roster-selecting .tt-roster-row {
        grid-template-columns: 28px minmax(0, 1fr) auto auto;
        grid-template-areas:
            "sel player player  menu"
            "sel meta   actions actions";
    }

    /* The two halves of the actions cell split across two lines here. */
    .tt-roster-actions { display: contents; }
    .tt-roster-menu { grid-area: menu; justify-self: end; }
    .tt-roster-next { grid-area: actions; justify-self: end; align-self: center; }

    /* The 44px "..." target bleeds into the row's padding instead of setting the name line's height,
       and sits out to the edge so its dots line up over the button's right edge. */
    .tt-roster-menu { margin-block: -4px; margin-inline-end: -10px; }

    .tt-roster-sel   { grid-area: sel; align-self: start; padding-top: 8px; }
    .tt-roster-player { grid-area: player; }

    /* One status per line. Side by side they fit until a row also carries an action button, and
       then the first one wraps inside itself — "Awaiting offer / response" — which is worse than
       two tidy lines. */
    .tt-roster-meta {
        grid-area: meta;
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 2px;
    }

    .tt-roster-name {
        min-height: 36px;
        gap: var(--tt-space-2);
    }

    .tt-roster-row .tt-avatar {
        width: 30px;
        height: 30px;
    }

    .tt-roster-name-text {
        font-size: var(--tt-text-body);
    }

    /* Secondary by size once they are stacked, so the name still wins the row outright. */
    .tt-roster-meta .tt-status-text {
        font-size: var(--tt-text-caption);
        color: var(--tt-ink-muted);
    }
}

/* --- Cards (2026-10-30, Arnie): the Team tab's other way of showing its people. The List | Cards
   switch in the Coaching staff bar turns both lists into a grid of cards — photo large, name under it,
   then the status and the row's next step — and is remembered on the device. The same markup as the
   rows, so every behaviour (open, select, the ⋯ menu) is the same; only the layout changes. Written
   after the roster's width rules so its three-class selectors win at every width. */
.tt-view-seg .tt-seg-option {
    flex: none;
    min-width: 44px;
    min-height: 36px;
    padding: 0 var(--tt-space-2);
}
/* Two copies of the switch are rendered; the width picks one. A phone's tab bar is full with its
   three views and Settings, so the switch sits in the players' search row there (2026-10-30). */
@media (max-width: 719.98px) { .tt-teamnav-row .tt-view-seg { display: none; } }
@media (min-width: 720px) { .tt-toolbar .tt-view-seg { display: none; } }

.tt-card.tt-people-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: var(--tt-space-3);
    background: transparent;
    border: 0;
    box-shadow: none;
    overflow: visible;
}
.tt-people-cards .tt-list-row {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--tt-space-2);
    padding: var(--tt-space-3);
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-lg);
    background: var(--tt-surface);
    text-align: left;
}
.tt-people-cards .tt-list-row .tt-avatar { width: 56px; height: 56px; font-size: var(--tt-text-headline); }
.tt-people-cards .tt-list-row > .tt-icon { display: none; }

.tt-roster.tt-roster-cards {
    background: transparent;
    border: 0;
    border-radius: 0;
}
.tt-roster-cards .tt-roster-head { display: none; }
.tt-roster-cards .tt-roster-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: var(--tt-space-3);
}
.tt-roster-cards .tt-roster-row,
.tt-roster-cards.tt-roster-selecting .tt-roster-row {
    grid-template-columns: minmax(0, 1fr) auto;
    /* The name block takes the slack, so every card in a row keeps its status and next step on the
       same bottom line (2026-10-30). */
    grid-template-rows: 1fr auto auto;
    grid-template-areas:
        "player  menu"
        "meta    meta"
        "actions actions";
    row-gap: var(--tt-space-2);
    min-height: 0;
    padding: var(--tt-space-3);
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-lg);
    background: var(--tt-surface);
}
.tt-roster-cards.tt-roster-selecting .tt-roster-row {
    grid-template-columns: 28px minmax(0, 1fr) auto;
    grid-template-areas:
        "sel player  menu"
        "sel meta    meta"
        "sel actions actions";
}
.tt-roster-cards .tt-roster-row-open { background: var(--tt-brand-soft); border-color: var(--tt-brand); }
.tt-roster-cards .tt-roster-actions { display: contents; }
.tt-roster-cards .tt-roster-menu { grid-area: menu; justify-self: end; align-self: start; margin: -6px -8px 0 0; }
.tt-roster-cards .tt-roster-next { grid-area: actions; justify-self: start; align-self: end; }
.tt-roster-cards .tt-roster-sel { grid-area: sel; align-self: start; padding-top: 4px; }
.tt-roster-cards .tt-roster-player { grid-area: player; }
.tt-roster-cards .tt-roster-meta {
    grid-area: meta;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
}
.tt-roster-cards .tt-roster-meta .tt-status-text { font-size: var(--tt-text-caption); color: var(--tt-ink-muted); }
/* A card has no column to count down; the section heading already carries the number. */
.tt-roster-cards .tt-roster-num { display: none; }
.tt-roster-cards .tt-roster-name {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--tt-space-2);
    min-height: 0;
}
.tt-roster-cards .tt-roster-row .tt-avatar { width: 56px; height: 56px; font-size: var(--tt-text-headline); }
.tt-roster-cards .tt-roster-name-text { font-size: var(--tt-text-body); }
/* A family's note is two lines on a card; the player panel has all of it (2026-10-30). */
.tt-roster-cards .tt-roster-name-note {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
/* The jersey number as a badge on the photo's corner; the inline one stays for screen readers. */
.tt-roster-avatar { position: relative; display: inline-flex; flex: none; }
.tt-roster-avatar-num {
    position: absolute;
    right: -6px;
    bottom: -4px;
    min-width: 22px;
    height: 22px;
    padding: 0 5px;
    display: grid;
    place-items: center;
    border-radius: 999px;
    border: 2px solid var(--tt-surface);
    background: var(--tt-brand);
    color: var(--tt-surface);
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-bold);
    font-variant-numeric: tabular-nums;
    line-height: 1;
}
.tt-roster-cards .tt-roster-jersey {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* Phone-sized chrome. A viewport question, unlike the roster above. */
@media (max-width: 760px) {
    /* A phone has no room for a full 16px between every block of a coach screen — tabs, heading,
       search, filters and list read as one tool, not five sections. */
    .tt-workspace {
        gap: var(--tt-space-3);
    }

    /* Still a 16px font (iOS zooms on anything smaller), just less padding around it. */
    .tt-toolbar-search .tt-input {
        min-height: 40px;
        padding-block: var(--tt-space-2);
    }

    .tt-panel-close-wide { display: none; }
    .tt-panel-close-narrow { display: inline; }

    /* A drawer on a phone is a 40px-wide sliver of roster nobody wanted. */
    .tt-panel {
        inset: 0;
        width: auto;
        border-left: 0;
    }

    .tt-panel-scrim:not(.tt-panel-scrim-top) {
        display: none;
    }
}

@media (max-width: 600px) {
    .tt-stats {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .tt-field-pair {
        grid-template-columns: minmax(0, 1fr);
    }

    .tt-main-plain > .tt-stack {
        padding: var(--tt-space-5);
    }
}

/* From here the tab bar is a rail down the left and the app has room for the
   roster's full width. Nothing else about the layout changes. */
@media (min-width: 960px) {
    .tt-app:has(.tt-tabbar) {
        padding-left: 208px;
    }

    .tt-shell-brand {
        position: fixed;
        top: 0;
        left: 0;
        z-index: 30;
        display: flex;
        align-items: center;
        gap: var(--tt-space-3);
        width: 208px;
        height: 64px;
        padding: var(--tt-space-3) var(--tt-space-4);
        background: var(--tt-rail);
        color: #fff;
        font-size: 21px;
        font-weight: var(--tt-weight-bold);
        letter-spacing: -.04em;
    }

    .tt-shell-brand .tt-icon {
        width: 36px;
        height: 36px;
        padding: 6px;
        box-sizing: border-box;
        border-radius: 10px;
        background: color-mix(in srgb, var(--tt-accent) 22%, transparent);
        color: var(--tt-accent);
    }

    .tt-brand-caption {
        display: block;
        margin-top: 3px;
        font-size: var(--tt-text-micro);
        letter-spacing: .18em;
        color: var(--tt-rail-ink-faint);
    }

    .tt-tabbar {
        position: fixed;
        inset: 64px auto 0 0;
        width: 208px;
        flex-direction: column;
        gap: var(--tt-space-2);
        margin: 0;
        padding: var(--tt-space-4);
        border-radius: 0;
        background: var(--tt-rail);
        box-shadow: none;
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
    }

    .tt-tab {
        position: relative;
        flex: none;
        flex-direction: row;
        justify-content: flex-start;
        gap: var(--tt-space-3);
        min-height: 44px;
        padding: var(--tt-space-2) var(--tt-space-3);
        border-radius: var(--tt-radius-sm);
        color: var(--tt-rail-ink);
        font-size: var(--tt-text-callout);
        transition: background-color .15s ease, color .15s ease;
    }

    /* The phone's pill is the rail item's own background here. */
    .tt-tab > .tt-icon,
    .tt-tab.active > .tt-icon {
        padding: 0;
        background: none;
        color: inherit;
    }

    /* A tint and a marker, not a block of accent: the screen is what's bright, the rail says where. */
    .tt-tab.active {
        background: color-mix(in srgb, var(--tt-accent) 16%, transparent);
        color: #fff;
    }

    .tt-tab.active::before {
        content: "";
        position: absolute;
        left: calc(-1 * var(--tt-space-4));
        top: 10px;
        bottom: 10px;
        width: 3px;
        border-radius: 0 3px 3px 0;
        background: var(--tt-accent);
    }

    .tt-tab.active > .tt-icon {
        color: var(--tt-accent);
    }
}

@media (min-width: 960px) and (hover: hover) {
    .tt-tab:hover:not(.active) {
        background: rgba(255, 255, 255, .07);
        color: #fff;
    }

    .tt-tab:hover:not(.active) > .tt-icon {
        background: none;
    }


    .tt-appbar {
        min-height: 64px;
        padding: var(--tt-space-2) var(--tt-space-5);
    }

    .tt-appbar-title {
        font-size: var(--tt-text-title);
    }

    .tt-main:not(.tt-main-plain) {
        padding: var(--tt-space-4) var(--tt-space-5) var(--tt-space-5);
    }

    /* Coach Home: the calendar in the main column, what needs the coach beside it. With nothing
       beside it, the main column takes the width and Coming up sits next to Up next. */
    .tt-home {
        display: grid;
        grid-template-columns: minmax(0, 1.7fr) minmax(300px, 1fr);
        /* Dense, so the main column comes back up beside the side column that precedes it in the page. */
        grid-auto-flow: row dense;
        column-gap: 28px;
        align-items: start;
    }
    .tt-home > .tt-notice { grid-column: 1 / -1; }
    .tt-home > .tt-home-main { grid-column: 1; }
    .tt-home > .tt-home-side { grid-column: 2; }
    .tt-home:not(:has(> .tt-home-side > :not([hidden]))) { grid-template-columns: minmax(0, 1fr); }
    .tt-home:not(:has(> .tt-home-side > :not([hidden]))) > .tt-home-main {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        column-gap: 28px;
        align-items: start;
    }

    .tt-schedule-layout {
        display: grid;
        grid-template-columns: minmax(0, 1fr) 300px;
        gap: var(--tt-space-4);
        align-items: start;
    }

    .tt-schedule-layout .tt-schedule-list {
        grid-column: 1;
        grid-row: 1;
        min-width: 0;
    }

    .tt-schedule-layout .tt-schedule-top,
    .tt-schedule-layout .tt-schedule-top.tt-schedule-top-single {
        grid-column: 2;
        grid-row: 1;
        grid-template-columns: minmax(0, 1fr);
        position: sticky;
        top: calc(64px + var(--tt-safe-top));
    }

    .tt-main-plain {
        padding: var(--tt-space-6) var(--tt-space-5);
    }
}

@media (hover: hover) and (min-width: 960px) {
    .tt-tab:hover:not(.active) {
        background: #ffffff12;
        color: #fff;
    }
}

/* --- Availability ----------------------------------------------------------
   Whether a player can make one item. The chosen answer is the only coloured
   thing in the row, so a family's scan down the week lands on "No" at once;
   an unanswered row stays neutral rather than looking like a Yes. */

.tt-avail-list {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-3);
    margin-top: var(--tt-space-2);
}

.tt-avail-list.tt-card {
    margin-top: 0;
}

.tt-avail {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
    min-width: 0;
}

.tt-avail-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--tt-space-1) var(--tt-space-2);
}

.tt-avail-name {
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-semibold);
    overflow-wrap: anywhere;
}

.tt-avail-meta,
.tt-avail-locked {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

.tt-avail-options {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--tt-space-2);
    max-width: 22rem;
}

.tt-avail-option {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--tt-space-1);
    min-height: var(--tt-tap);
    /* space-2, not 3: three to a row in a 375px panel, "✕ Can't go" broke onto two lines. */
    padding: 0 var(--tt-space-2);
    border: 1px solid var(--tt-hairline-strong);
    border-radius: var(--tt-radius-md);
    background: var(--tt-surface);
    color: var(--tt-ink);
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-medium);
    cursor: pointer;
}

.tt-avail-option:disabled {
    opacity: .6;
    cursor: default;
}

/* Unchosen answers are neutral, with only the icon in its colour; the chosen one
   fills solid (2026-10-24: three tints on every unanswered item read as a traffic
   light and drowned the answers people had given). When one is chosen the other
   two step back, so the answer reads from across the field. */

.tt-avail-option.tt-avail-yes[aria-pressed="true"] {
    border-color: var(--tt-answer-yes);
    background: var(--tt-answer-yes);
    color: var(--tt-answer-yes-on);
    font-weight: var(--tt-weight-semibold);
}

.tt-avail-option.tt-avail-no[aria-pressed="true"] {
    border-color: var(--tt-answer-no);
    background: var(--tt-answer-no);
    color: var(--tt-answer-no-on);
    font-weight: var(--tt-weight-semibold);
}

.tt-avail-option.tt-avail-maybe[aria-pressed="true"] {
    border-color: var(--tt-answer-maybe);
    background: var(--tt-answer-maybe);
    color: var(--tt-answer-maybe-on);
    font-weight: var(--tt-weight-semibold);
}

.tt-avail-options .tt-avail-option[aria-pressed="true"] .tt-icon {
    color: inherit;
}

.tt-avail-options:has([aria-pressed="true"]) .tt-avail-option[aria-pressed="false"] {
    opacity: .55;
}

.tt-avail-options:has([aria-pressed="true"]) .tt-avail-option[aria-pressed="false"]:hover {
    opacity: 1;
}

.tt-avail-problem {
    font-size: var(--tt-text-caption);
    color: var(--tt-danger);
}

/* A coach's count: one quiet line naming only the answers someone gave. Colour
   sits on the dots, never on the numbers, so an all-zero "no" can't read as an
   alarm. */
.tt-avail-count {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
    max-width: 22rem;
    margin-top: var(--tt-space-3);
}

.tt-avail-count-text {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tt-space-1) var(--tt-space-3);
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

.tt-avail-count-part {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

.tt-avail-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
}

/* A coach's roster: one line per player, hairlines between, the switch on the
   right. Wraps under the name only when the panel is too narrow for both. */
.tt-avail-roster {
    display: flex;
    flex-direction: column;
}

.tt-avail-roster > .tt-avail-row {
    padding: var(--tt-space-3) 0;
    border-bottom: 1px solid var(--tt-hairline);
}

.tt-avail-roster > .tt-avail-row:last-child {
    border-bottom: 0;
}

.tt-avail-compact {
    gap: var(--tt-space-2);
}

.tt-avail-compact-main {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--tt-space-2) var(--tt-space-3);
}

.tt-avail-who {
    flex: 1 1 9rem;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
}

.tt-avail-note-inline {
    max-width: 100%;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-caption);
    text-align: left;
    overflow-wrap: anywhere;
    cursor: pointer;
}

.tt-avail-seg {
    display: inline-flex;
    flex: none;
    padding: 2px;
    border-radius: var(--tt-radius-md);
    background: var(--tt-surface-sunken);
}

.tt-avail-seg-option {
    min-width: 3.25rem;
    min-height: 34px;
    padding: 0 var(--tt-space-2);
    border: 1px solid transparent;
    border-radius: calc(var(--tt-radius-md) - 2px);
    background: transparent;
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-medium);
    cursor: pointer;
}

.tt-avail-seg-option:disabled {
    cursor: default;
}

.tt-avail-seg-option[aria-pressed="true"] {
    box-shadow: var(--tt-shadow-card);
}

/* Tryouts sections: a heading, its count and one line on what the coach does
   next, set close above the list it names. The heading folds its list; [hidden]
   needs saying because .tt-roster's own display would otherwise win. */
.tt-roster[hidden] { display: none !important; }
.tt-roster-section-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--tt-space-1) var(--tt-space-3);
    margin-top: var(--tt-space-2);
    margin-bottom: calc(-1 * var(--tt-space-2));
}

.tt-roster-section-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.5rem;
    height: 1.5rem;
    margin-left: var(--tt-space-1);
    padding: 0 var(--tt-space-2);
    border-radius: var(--tt-radius-pill);
    background: var(--tt-surface-sunken);
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-micro);
    letter-spacing: normal;
}

/* Availability answer colours, shared by the dots and the tick / cross / ? icons. */
.tt-avail-bar-yes { background: var(--tt-success); }
.tt-avail-bar-maybe { background: var(--tt-warning); }
.tt-avail-bar-no { background: var(--tt-danger); }
.tt-avail-bar-none { background: var(--tt-hairline-strong); }

.tt-avail-option.tt-avail-yes .tt-icon,
.tt-avail-seg-option.tt-avail-yes { color: var(--tt-success); }
.tt-avail-option.tt-avail-no .tt-icon,
.tt-avail-seg-option.tt-avail-no { color: var(--tt-danger); }
.tt-avail-option.tt-avail-maybe .tt-icon,
.tt-avail-seg-option.tt-avail-maybe { color: var(--tt-warning); }

/* Icons only: square, and centred. Unchosen answers stay coloured but faint, so
   the chosen one is what the eye lands on. */
.tt-avail-seg-option {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.5rem;
}

.tt-avail-seg-option[aria-pressed="false"] .tt-icon {
    opacity: .45;
}

.tt-avail-seg-option[aria-pressed="true"] .tt-icon {
    stroke-width: 2.5;
}

/* Notes: a link, a one-line input and two text buttons - never a box. */
.tt-avail-note-link {
    align-self: flex-start;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--tt-brand);
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-medium);
    cursor: pointer;
}

.tt-avail-note-cancel {
    color: var(--tt-ink-muted);
}

.tt-avail-note-sent {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

.tt-avail-note-inline.is-empty {
    color: var(--tt-brand);
}

/* The note box under a roster row: a field, then Cancel and Save on their own line, set apart from the
   ✓ ✗ ? switch above so a thumb can't reach for one and hit the other. */
.tt-avail-note-edit {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
    margin-top: var(--tt-space-2);
    padding: var(--tt-space-3);
    border-radius: var(--tt-radius-md);
    background: var(--tt-surface-sunken);
}

.tt-avail-note-input {
    width: 100%;
    min-height: var(--tt-tap);
    padding: 0 var(--tt-space-3);
    border: 1px solid var(--tt-hairline-strong);
    border-radius: var(--tt-radius-md);
    background: var(--tt-surface);
    color: var(--tt-ink);
    font-size: var(--tt-text-body);
}

.tt-avail-note-input:focus {
    outline: 2px solid var(--tt-brand);
    outline-offset: -1px;
}

.tt-avail-note-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--tt-space-3);
}

/* --- Segmented choice -----------------------------------------------------
   One row, one answer, all the answers visible. For a small closed set where
   seeing the alternatives is the point — Appearance is light, dark or follow
   the phone, and a dropdown would hide two thirds of that behind a tap.

   Related to .tt-avail-seg, which is the same idea sized for a schedule row.
   This one fills its card and carries a label, so it is its own thing rather
   than a modifier. */

.tt-seg {
    display: flex;
    gap: 2px;
    padding: 2px;
    border-radius: var(--tt-radius-md);
    background: var(--tt-surface-sunken);
}

.tt-seg-option {
    flex: 1 1 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--tt-space-2);
    min-width: 0;
    min-height: 44px;
    padding: 0 var(--tt-space-2);
    border: 0;
    border-radius: calc(var(--tt-radius-md) - 2px);
    background: transparent;
    color: var(--tt-ink-muted);
    font-family: inherit;
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-medium);
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--tt-duration-fast) var(--tt-ease), color var(--tt-duration-fast) var(--tt-ease);
}

/* The chosen one is lifted onto the card's own surface — the same move as a
   physical segmented switch, and the only cue that survives being read at arm's
   length in sunlight. */
.tt-seg-option[aria-pressed="true"] {
    background: var(--tt-surface);
    color: var(--tt-ink);
    box-shadow: var(--tt-shadow-card);
}

.tt-seg-option:disabled {
    cursor: default;
    opacity: .6;
}

.tt-seg-option:focus-visible {
    outline: none;
    box-shadow: var(--tt-focus-ring);
}

/* A wrapped label is the one thing that breaks a segmented control: the taller
   option stops looking like a peer of the other two and starts looking like the
   answer. So the label never wraps, and on a phone it takes caption size to buy
   the room — the hint above the control is where the explaining happens. */
@media (max-width: 30rem) {
    .tt-seg-option {
        gap: var(--tt-space-1);
        font-size: var(--tt-text-caption);
    }
}

/* --- Numbered steps -------------------------------------------------------
   For the few places the app has to talk someone through something it cannot
   do for them — Apple's Add to Home Screen, which no web app may trigger. Real
   <ol> markup, so a screen reader says "3 items" and "step 2 of 3". */

.tt-steps {
    margin: 0;
    padding-left: 1.35em;
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
    color: var(--tt-ink);
    font-size: var(--tt-text-callout);
}

.tt-steps li {
    padding-left: var(--tt-space-1);
}

/* An icon inside a step is part of the sentence, not a line of its own — and
   .tt-icon is display:block everywhere else, which is exactly what it would
   become without this. */
.tt-steps .tt-icon {
    display: inline-block;
    vertical-align: -.2em;
    margin-right: .15em;
}

/* --- Survey alert ---------------------------------------------------------
   One line for "you owe a survey": an icon wearing a small red "!", short
   words, a chevron. Stands out by the badge alone; the rest stays quiet. */
.tt-task-alert {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    padding: var(--tt-space-3) var(--tt-space-4);
    border: 1px solid var(--tt-hairline);
    border-left: 3px solid var(--tt-danger);
    border-radius: var(--tt-radius-md);
    background: var(--tt-surface);
    color: var(--tt-ink);
    text-decoration: none;
}

.tt-task-alert:hover {
    background: var(--tt-danger-soft);
}

.tt-task-alert > .tt-icon:last-child {
    flex: none;
    color: var(--tt-ink-faint);
}

.tt-task-alert-icon {
    position: relative;
    flex: none;
    display: inline-flex;
    color: var(--tt-ink-muted);
}

.tt-task-alert-badge {
    position: absolute;
    top: -6px;
    right: -7px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    background: var(--tt-danger);
    color: var(--tt-surface);
    font-size: 12px;
    font-weight: var(--tt-weight-semibold);
    line-height: 1;
    box-shadow: 0 0 0 2px var(--tt-surface);
}

.tt-task-alert-text {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.tt-task-alert-title {
    font-weight: var(--tt-weight-semibold);
}

.tt-task-alert-detail {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

/* --- Chat: what this is, and how to join ------------------------------------ */
/* One short line with WhatsApp's mark: what this screen is, before anything is listed. */
.tt-chat-intro {
    display: flex;
    align-items: flex-start;
    gap: var(--tt-space-3);
    font-size: var(--tt-text-callout);
    color: var(--tt-ink-muted);
    line-height: 1.45;
}

.tt-chat-intro-icon {
    flex: none;
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--tt-whatsapp-soft);
    color: var(--tt-whatsapp);
}

:root[data-theme="dark"] .tt-chat-intro-icon {
    color: var(--tt-whatsapp-strong);
}

/* The coach's team heading, with the way to edit its chats at the end of the line. */
.tt-chat-staff-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tt-space-2);
}

.tt-chat-manage {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 44px;
    padding-inline: var(--tt-space-2);
    margin-right: calc(-1 * var(--tt-space-2));
    color: var(--tt-brand);
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-semibold);
    text-decoration: none;
}

.tt-chat-row-icon.tt-chat-row-icon-wa {
    background: var(--tt-whatsapp-soft);
    color: var(--tt-whatsapp);
}

:root[data-theme="dark"] .tt-chat-row-icon.tt-chat-row-icon-wa {
    color: var(--tt-whatsapp-strong);
}

.tt-chat-row-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px var(--tt-space-2);
    margin-top: 2px;
}

/* Who a chat is for, as a pill: the thing a coach checks before sharing a link. */
.tt-chat-audience {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 1px 8px;
    border-radius: var(--tt-radius-pill);
    background: var(--tt-success-soft);
    color: var(--tt-success);
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
}

.tt-chat-audience.tt-chat-audience-coaches {
    background: var(--tt-surface-sunken);
    color: var(--tt-ink-muted);
}

/* No flex/gap on the <details> itself: Chrome lays out the hidden content slot
   as a flex item, so a closed card kept a blank band under its summary. Spacing
   comes from the summary's open margin and the hint's own top margin instead. */
.tt-howto > .tt-hint {
    display: block;
    margin-top: var(--tt-space-1);
}

/* 44px tall to tap, without growing the card: the extra height comes out of its padding. */
.tt-howto-summary {
    display: flex;
    align-items: center;
    min-height: 44px;
    margin-block: -10px;
    gap: var(--tt-space-2);
    font-weight: var(--tt-weight-semibold);
    cursor: pointer;
    list-style: none;
}

.tt-howto-summary::-webkit-details-marker {
    display: none;
}

/* The trailing chevron says "this opens"; it turns down while open. */
.tt-howto-summary > .tt-icon:last-child {
    margin-left: auto;
    color: var(--tt-ink-muted);
    transition: transform .15s ease;
}

.tt-howto[open] .tt-howto-summary > .tt-icon:last-child {
    transform: rotate(90deg);
}

.tt-howto[open] .tt-howto-summary {
    margin-bottom: var(--tt-space-1);
}

.tt-steps {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-3);
    margin: 0;
    padding-left: 1.4rem;
    line-height: 1.45;
}

.tt-steps li::marker {
    font-weight: var(--tt-weight-semibold);
    color: var(--tt-brand);
}

.tt-howto-help {
    margin: var(--tt-space-3) 0;
    padding: var(--tt-space-3);
    border-radius: var(--tt-radius-md);
    background: var(--tt-brand-soft);
    font-size: var(--tt-text-caption);
    line-height: 1.5;
}

/* WhatsApp's green, for the buttons that hand a family over to it. */
.tt-btn.tt-btn-whatsapp {
    border-color: var(--tt-whatsapp);
    background: var(--tt-whatsapp);
    color: #fff;
}

.tt-btn.tt-btn-whatsapp:hover {
    border-color: var(--tt-whatsapp-strong);
    background: var(--tt-whatsapp-strong);
}

/* A family's Chat tab: per child's team, the team's own chat as one card with one big button,
   then any other chats as a quieter list, then what joining shares. */
.tt-chat-team {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
}

.tt-chat-team-head {
    display: flex;
    flex-direction: column;
}

.tt-chat-main {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-3);
}

.tt-chat-main-head {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
}

.tt-chat-badge {
    flex: none;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--tt-whatsapp-soft);
    color: var(--tt-whatsapp);
}

:root[data-theme="dark"] .tt-chat-badge {
    color: var(--tt-whatsapp-strong);
}

.tt-chat-main-text {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.tt-chat-main-title {
    font-size: var(--tt-text-headline);
    font-weight: var(--tt-weight-semibold);
    line-height: var(--tt-leading-tight);
    overflow-wrap: anywhere;
}

.tt-chat-joined {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 10px;
    border-radius: var(--tt-radius-pill);
    background: var(--tt-success-soft);
    color: var(--tt-success);
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
}

/* Under the big button, for the family that joined long ago: quiet, but a full-height target. */
.tt-btn.tt-chat-already {
    align-self: center;
    margin-top: calc(-1 * var(--tt-space-1));
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-medium);
}

/* Wider than a phone, a full-width button is a bar across the screen: the buttons sit side by side
   under the chat's name instead, at their own width. */
@media (min-width: 40rem) {
    .tt-chat-main {
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
    }

    .tt-chat-main-head {
        flex-basis: 100%;
    }

    .tt-chat-main > .tt-btn.tt-btn-block {
        width: auto;
        min-width: 14rem;
    }

    .tt-btn.tt-chat-already {
        margin-top: 0;
    }
}

.tt-chat-more-heading {
    margin-top: var(--tt-space-2);
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
    color: var(--tt-ink-muted);
    letter-spacing: .02em;
    text-transform: uppercase;
}

.tt-chat-row {
    gap: var(--tt-space-3);
    min-height: 60px;
}

.tt-chat-row-icon {
    flex: none;
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--tt-surface-sunken);
    color: var(--tt-ink-muted);
}

.tt-chat-row-text {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.tt-chat-privacy {
    display: flex;
    align-items: flex-start;
    gap: var(--tt-space-2);
    padding: 0 var(--tt-space-1);
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
    line-height: 1.45;
}

.tt-chat-privacy > .tt-icon {
    flex: none;
    margin-top: 1px;
}

/* The select bar sticks to the bottom of the screen, and so does the tab bar (z-index 20 over the
   bar's 15) — on a phone the bar has to stand on the tab bar, not slide under it. From 960px the
   tab bar is a side rail and the bar's own offset is enough. */
@media (max-width: 959.98px) {
    .tt-app:has(.tt-tabbar) .tt-selectbar {
        bottom: calc(var(--tt-tabbar-height) + 8px + var(--tt-safe-bottom) + var(--tt-space-2));
    }
}

/* A field's map, shown inside a booking's panel: the full width of the panel, never taller than the
   screen, so a tall site plan scrolls with the panel instead of pushing the actions off it. */
.tt-field-map {
    display: block;
    width: 100%;
    max-height: 70vh;
    object-fit: contain;
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-md);
    background: var(--tt-surface);
}

/* The coach's own tools above the schedule — asking for field time, reschedules, the download. A
   wrapping row of quiet buttons: they are things a coach does now and then, not the reason they came. */
.tt-coach-tools {
    display: flex;
    align-items: center;
    gap: var(--tt-space-2);
}

.tt-coach-tools-strip {
    display: flex;
    flex: 1;
    gap: var(--tt-space-2);
    min-width: 0;
    padding-block: 4px;
    overflow-x: auto;
    scrollbar-width: none;
    overflow-anchor: none;
    /* A soft edge says there is more to the right without a scrollbar. */
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 20px), transparent);
    mask-image: linear-gradient(90deg, #000 calc(100% - 20px), transparent);
}

.tt-coach-tools-strip::-webkit-scrollbar {
    display: none;
}

.tt-coach-tools-strip > .tt-btn {
    flex: none;
    white-space: nowrap;
}

/* A to-do among shortcuts. */
.tt-btn.tt-btn-attention {
    border-color: color-mix(in srgb, var(--tt-warning) 50%, transparent);
    background: var(--tt-warning-soft);
    color: var(--tt-warning);
    font-weight: var(--tt-weight-semibold);
}

/* A group of choices (teams, field sizes, players) with its legend reading as the field's label,
   not the browser's etched box around a caption. */
.tt-choices {
    border: 0;
    padding: 0;
    margin: 0;
    min-width: 0;
}

.tt-choices > legend {
    padding: 0;
    margin-bottom: var(--tt-space-1);
}

/* --- Which side you're on -------------------------------------------------
   A coach's frame carries a brand rule along the app bar's foot; a family's
   doesn't. The chip names the seat; this makes it visible at a glance. A
   coach-parent's Coach toggle sits after the chip (CoachToggle). */

.tt-seat-coach .tt-appbar {
    border-bottom: 3px solid var(--tt-brand);
}

/* --- Attendance report ----------------------------------------------------
   Period and session kind on one row where there's room; the figure at the end
   of each row is the one number a coach scans down. */

.tt-attendance-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--tt-space-3);
}

/* Two pickers side by side even on a phone: their values say what they are. */
.tt-attendance-filters > .tt-field {
    flex: 1 1 9rem;
    margin: 0;
}

.tt-attendance-filters > .tt-seg {
    flex: 1 1 16rem;
}

.tt-attendance-figure {
    flex: none;
    display: inline-flex;
    flex-direction: column;
    align-items: flex-end;
    font-weight: var(--tt-weight-semibold);
    font-variant-numeric: tabular-nums;
}

.tt-attendance-of {
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-medium);
    color: var(--tt-ink-muted);
}

/* Sessions: the latest 5, 10, 20, … as one line of figures above the list. */
.tt-attendance-trend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tt-space-2) var(--tt-space-5);
    margin: 0;
    font-size: var(--tt-text-caption);
}
.tt-attendance-trend > div { display: flex; align-items: baseline; gap: 6px; }
.tt-attendance-trend dt { color: var(--tt-ink-muted); font-weight: var(--tt-weight-medium); }
.tt-attendance-trend dd {
    margin: 0;
    font-size: var(--tt-text-body);
    font-weight: var(--tt-weight-semibold);
    font-variant-numeric: tabular-nums;
}

/* A coach's session or change row opens the session's player list. */
.tt-attendance-open { font: inherit; }
.tt-attendance-open > .tt-icon:last-child { flex: none; color: var(--tt-ink-faint); }
.tt-attendance-open:disabled { cursor: default; }
.tt-attendance-open:disabled > .tt-icon:last-child { visibility: hidden; }

/* A family's own report: who, the figure, the star — then a line per session. */
.tt-family-attendance-head {
    display: flex;
    align-items: center;
    gap: var(--tt-space-4);
}
.tt-family-attendance-who { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.tt-family-attendance-figure { flex: none; display: flex; flex-direction: column; align-items: flex-end; }
.tt-family-attendance-figure .tt-attendance-figure { font-size: var(--tt-text-title); flex-direction: row; align-items: baseline; gap: 6px; }
.tt-family-attendance-outcome {
    flex: none;
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
    white-space: nowrap;
}
.tt-family-attendance-outcome.is-came { color: var(--tt-success); }
.tt-family-attendance-outcome.is-missed { color: var(--tt-ink-muted); }

/* --- Hit areas (UI audit 2026-10) ------------------------------------------
   Controls that read better small keep their look and get a 44px target from
   an invisible pad, the same move .tt-btn-compact already makes. Measured with
   elementFromPoint, not getBoundingClientRect: the box is not the target. */
.tt-context-chip,
.tt-coach-toggle,
.tt-icon-close,
.tt-home-head-link,
.tt-place-link {
    position: relative;
}

.tt-context-chip::after,
.tt-coach-toggle::after {
    content: "";
    position: absolute;
    inset: -9px -2px;
}

.tt-icon-close::after {
    content: "";
    position: absolute;
    inset: -6px;
}

/* 2026-10-24 pass: "Add a note for the coach" (27px) and a Schedule item's title (23px). */
.tt-avail-note-link,
.tt-session-open {
    position: relative;
}

/* 28px tall plus 8px each way: 44px, without reaching the answer buttons 8px above. */
.tt-avail-note-link {
    min-height: 28px;
}

.tt-avail-note-link::after {
    content: "";
    position: absolute;
    inset: -8px -4px;
}

.tt-session-open::after {
    content: "";
    position: absolute;
    inset: -11px 0;
}

.tt-home-head-link::after,
.tt-place-link::after {
    content: "";
    position: absolute;
    inset: -13px -4px;
}

/* A tablet is not a stretched phone: the bottom bar stays (the rail starts at
   960px) but the column stops growing, so a line of text and the three answer
   buttons keep the proportions they were designed at. */
@media (min-width: 720px) and (max-width: 959.98px) {
    .tt-main {
        width: 100%;
        max-width: 44rem;
        margin-inline: auto;
    }
}

/* A one-line nudge: icon, sentence, one action, a way to put it away. */
.tt-notice-compact {
    flex-wrap: nowrap;
    padding: var(--tt-space-2) var(--tt-space-3);
    gap: var(--tt-space-2);
}

.tt-notice-compact > svg {
    flex: none;
}

.tt-notice-compact .tt-notice-line {
    flex: 1;
    min-width: 0;
    line-height: var(--tt-leading-tight);
}

.tt-notice-compact .tt-notice-sub {
    display: block;
    font-size: var(--tt-text-caption);
    opacity: .85;
}

.tt-notice-compact .tt-btn,
.tt-notice-compact .tt-icon-close {
    flex: none;
}

/* The month's fold-away bar (phone only; see ScheduleFeed). Hidden where the month is always shown. */
.tt-month-toggle {
    display: none;
}

@media (max-width: 719.98px) {
    .tt-month-toggle {
        display: flex;
        align-items: center;
        gap: var(--tt-space-2);
        width: 100%;
        min-height: var(--tt-tap);
        padding: 0;
        border: 0;
        background: transparent;
        color: var(--tt-ink);
        font: inherit;
        font-size: var(--tt-text-callout);
        text-align: left;
        cursor: pointer;
    }

    .tt-month-toggle-label {
        font-weight: var(--tt-weight-semibold);
    }

    .tt-month-toggle-hint {
        flex: 1;
        text-align: right;
        font-size: var(--tt-text-caption);
        color: var(--tt-ink-muted);
    }

    .tt-month-toggle > svg:last-child {
        flex: none;
        transform: rotate(90deg);
        color: var(--tt-ink-faint);
    }

    .tt-month-card.is-open .tt-month-toggle > svg:last-child {
        transform: rotate(-90deg);
    }
}

/* "How formatting works" under a letter box: a quiet disclosure, not a line of syntax. */
.tt-letter-help {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

.tt-letter-help > summary {
    display: inline-flex;
    align-items: center;
    min-height: var(--tt-tap);
    cursor: pointer;
    font-weight: var(--tt-weight-medium);
    color: var(--tt-brand);
}

/* Save / Send on a long form stays on screen, just above the tab bar, instead of waiting at the end
   of a thousand pixels of fields. It sits on its own band of ground so fields scrolling under it
   don't show through. */
.tt-row-actions.tt-form-actions {
    position: sticky;
    bottom: calc(var(--tt-tabbar-height) + var(--tt-safe-bottom) + var(--tt-space-3));
    z-index: 5;
    margin-inline: calc(-1 * var(--tt-space-2));
    padding: var(--tt-space-2);
    border-radius: var(--tt-radius-md);
    background: color-mix(in srgb, var(--tt-ground) 92%, transparent);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    box-shadow: 0 -8px 16px -12px rgba(16, 24, 20, .35);
}

@media (min-width: 960px) {
    .tt-row-actions.tt-form-actions {
        bottom: var(--tt-space-3);
    }
}

/* The roster's search and filter chips, pinned beneath the app bar. The ground colour and the
   negative margin make it a band across the page, so rows don't show through the gutters. */
.tt-roster-controls {
    position: sticky;
    top: var(--tt-appbar-live, calc(var(--tt-appbar-height) + var(--tt-safe-top)));
    z-index: 8;
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
    margin-inline: calc(-1 * var(--tt-gutter));
    padding: var(--tt-space-2) var(--tt-gutter);
    background: var(--tt-ground);
}


/* The day-before ask on an answer card: a maybe or silence still needs a yes or a no. */
.tt-confirm-hint {
    display: flex;
    align-items: flex-start;
    gap: var(--tt-space-2);
    color: var(--tt-warning);
    font-weight: var(--tt-weight-semibold);
}

.tt-confirm-hint > svg {
    flex: none;
    margin-top: .1em;
}

/* Family Home: what's next, what's owed, what's coming, the team — one column on a phone, two on a
   wide screen with the to-do list and the team beside the events. */
.tt-family-dashboard {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--tt-space-4);
    align-items: start;
}
.tt-family-dashboard > * { min-width: 0; }

/* Your team leads the side column; the same gap between it and the next section as between sections. */
.tt-dashboard-side { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--tt-space-4); align-content: start; }

/* Your team on Home: attendance, the chat and Messages as one row of equal tiles (2026-10-21). Two
   children and two chats still fit a phone's width; a long label ellipsizes rather than wrapping. */
.tt-quick-tiles {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 100px);
    justify-content: start;
    gap: var(--tt-space-2);
}
/* 64px tall, not 76: a tile is one icon and one word, and four of them were a quarter of the screen. */
.tt-quick-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    min-width: 0;
    min-height: 64px;
    padding: 6px 6px;
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-lg);
    background: var(--tt-surface);
    color: var(--tt-ink);
    text-decoration: none;
}
.tt-quick-tile:hover { border-color: var(--tt-brand); }
/* To do's tile is a button (it opens the drawer), dressed like the link tiles. */
button.tt-quick-tile { font: inherit; cursor: pointer; }
/* Rendered before To do knows whether it has a tile: with no tile and no cheer it takes no room. */
.tt-dashboard-team:not(:has(.tt-quick-tile, .tt-attendance-cheer)) { display: none; }
.tt-quick-tile-icon {
    position: relative;
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--tt-brand-soft);
    color: var(--tt-brand);
}
.tt-quick-tile-label {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-semibold);
}

/* Messages' count on the icon's corner: new ones in a solid badge, otherwise the total, quietly. */
.tt-quick-tile-badge {
    position: absolute;
    top: -4px;
    right: -7px;
    min-width: 18px;
    height: 18px;
    padding: 0 4px;
    display: grid;
    place-items: center;
    border-radius: 999px;
    border: 2px solid var(--tt-surface);
    background: var(--tt-surface-sunken);
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-bold);
    line-height: 1;
}
.tt-quick-tile-badge-new { background: var(--tt-danger); color: var(--tt-surface); }

/* The Team tab's tool tiles (2026-10-30): four across at every width, so the row never scrolls, with
   an On / Off pill under the label — On, guardians see the tool; Off, it is hidden from them. Labels
   may wrap to two lines on a narrow phone instead of ellipsizing "Player numbers" to "Player…". */
.tt-tool-tiles {
    grid-auto-flow: row;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    max-width: calc(4 * 108px + 3 * var(--tt-space-2));
}
.tt-tool-tiles .tt-quick-tile { gap: 4px; padding-bottom: var(--tt-space-2); justify-content: space-between; }
/* Two lines for every label, so "Budget" sits where "Player numbers" does and the four pills share a
   baseline (2026-10-30). */
.tt-tool-tiles .tt-quick-tile-label {
    display: flex;
    align-items: center;
    min-height: calc(2 * 1.2em);
    white-space: normal;
    text-align: center;
    line-height: 1.2;
}
/* On a desktop column the row spans the content: icon left, label, pill right (2026-10-30). The
   phone's square tiles stopped at 432px and left two thirds of the column empty. */
@media (min-width: 720px) {
    .tt-tool-tiles { max-width: none; }
    .tt-tool-tiles .tt-quick-tile {
        flex-direction: row;
        justify-content: flex-start;
        gap: var(--tt-space-3);
        min-height: 60px;
        padding: var(--tt-space-2) var(--tt-space-3);
    }
    .tt-tool-tiles .tt-quick-tile-label { flex: 1; min-height: 0; text-align: left; font-size: var(--tt-text-callout); }
    .tt-tool-tiles .tt-quick-tile-icon { width: 36px; height: 36px; }
}
.tt-tool-state {
    padding: 1px 8px;
    border-radius: 999px;
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-bold);
    line-height: 1.5;
    letter-spacing: .02em;
    text-transform: uppercase;
}
.tt-tool-state-on { background: var(--tt-success-soft); color: var(--tt-success); }
.tt-tool-state-off { background: var(--tt-surface-sunken); color: var(--tt-ink-muted); }

/* Attendance as a ring: how much of it is filled is the rate, its colour the level, the count inside.
   The star the server awards sits on the ring's corner. The level colours are a fixed scale — green,
   blue, amber, red — not the club brand, which would make Good and Excellent the same green. */
.tt-attendance-ring {
    position: relative;
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    --tt-level: var(--tt-brand);
    color: var(--tt-level);
}
.tt-attendance-ring > svg { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.tt-attendance-ring circle { fill: none; stroke-width: 3.5; }
.tt-attendance-ring-track { stroke: var(--tt-hairline); }
.tt-attendance-ring-fill { stroke: var(--tt-level); stroke-linecap: round; }
.tt-attendance-ring-text { font-size: 10px; font-weight: var(--tt-weight-bold); letter-spacing: -.02em; color: var(--tt-ink); }
.tt-attendance-ring-star { position: absolute; top: -6px; right: -8px; }
.tt-level-excellent { --tt-level: var(--tt-success); }
.tt-level-good { --tt-level: var(--tt-info); }
.tt-level-ok { --tt-level: var(--tt-warning); }
.tt-level-low { --tt-level: var(--tt-danger); }

/* The encouragement under the tiles: plain body text with the child's name in bold. Green italic
   read as a handwritten note (2026-10-24); the ring already carries the colour. */
.tt-attendance-cheer {
    margin: 0;
    font-size: var(--tt-text-micro);
    line-height: 1.35;
    color: var(--tt-ink-muted);
}
.tt-attendance-cheer strong { font-weight: var(--tt-weight-semibold); color: var(--tt-ink); }
.tt-star { display: inline-flex; flex: none; }
.tt-star-gold { color: #b8860b; }
.tt-star-gold .tt-icon { fill: #f5c542; }
.tt-star-silver { color: #8a9099; }
.tt-star-silver .tt-icon { fill: #d3d7dd; }
.tt-home-tasks .tt-task-button { width: 100%; }

/* The to-do list: one bordered list, each row an icon, two lines and a chevron or its buttons. */
.tt-home-tasks {
    overflow: hidden;
    background: var(--tt-surface);
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-lg);
}
.tt-home-tasks .tt-list-row { padding: 16px; min-height: 72px; border-bottom: 0; }
.tt-home-tasks > * + * { border-top: 1px solid var(--tt-hairline); }
.tt-home-tasks .tt-row-title, .tt-home-tasks .tt-row-meta { display: block; }

/* A row that reads from the top: its menu sits on the title's line, not centred on a taller row. */
.tt-list-row-top { align-items: flex-start; }

/* A row's one button, under its lines rather than beside them. */
.tt-row-inline-action { display: flex; margin-top: var(--tt-space-2); }

/* A task row whose buttons go under its sentence, lined up with the text rather than the icon. */
.tt-list-row-wrap { flex-wrap: wrap; align-items: flex-start; row-gap: var(--tt-space-2); }
.tt-list-row-wrap > .tt-list-row-body { flex: 1 1 calc(100% - 48px); }
.tt-row-actions-below {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tt-space-2);
    width: 100%;
    padding-left: 48px; /* the 36px symbol and the row's 12px gap */
}

/* Coach Home's side column with nothing in it leaves no gap behind. */
.tt-home-side:not(:has(> :not([hidden], .tt-quick-tiles:empty))) { display: none; }
/* Coach Home's tile row before To do has a tile to put in it. */
.tt-home-side > .tt-quick-tiles:empty { display: none; }

/* To do (TodoSection, TodoItem): a tile in Home's row (.tt-quick-tile), the rows in a drawer.
   [hidden] needs saying here: .tt-panel's display: flex would otherwise win over the browser's own
   display: none, and the closed drawer would stay on screen. */
.tt-todo[hidden], .tt-todo [hidden] { display: none !important; }
/* Only fixed overlays inside: no box of its own, so it leaves no gap on Home. */
.tt-todo { display: contents; }
.tt-roster-section-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--tt-space-2);
    min-height: var(--tt-tap);
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    letter-spacing: inherit;
    cursor: pointer;
}
.tt-todo-chevron { display: inline-flex; transform: rotate(90deg); transition: transform .15s ease; }
.tt-roster-section-toggle[aria-expanded="false"] .tt-todo-chevron { transform: none; }
/* A row and its ⋯: the row keeps its own tap area and chevron, the menu sits at its end. */
.tt-todo-item { display: flex; align-items: center; }
.tt-todo-row { flex: 1; min-width: 0; }
.tt-todo-item > .tt-menu { margin-right: var(--tt-space-2); }
/* The list clips its rounded corners; an open menu must hang out of it. */
.tt-home-tasks:has(.tt-menu-panel) { overflow: visible; }
/* Every row snoozed: no empty box above the "snoozed" line. */
.tt-home-tasks:not(:has(> *)) { display: none; }
.tt-todo-snoozed {
    display: flex;
    align-items: center;
    gap: var(--tt-space-2);
    margin: 0;
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}
.tt-todo-snoozed button {
    min-height: var(--tt-tap);
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--tt-brand);
    font: inherit;
    font-weight: var(--tt-weight-semibold);
    cursor: pointer;
}
@media (prefers-reduced-motion: reduce) {
    .tt-todo-chevron { transition: none; }
}

/* Coming up: one line per event after Up next — the date, what and when, and where the answer stands.
   A family's row opens in place to the answer buttons. */
.tt-soon {
    overflow: hidden;
    background: var(--tt-surface);
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-lg);
}
.tt-soon-row + .tt-soon-row { border-top: 1px solid var(--tt-hairline); }
.tt-soon-main {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    width: 100%;
    min-height: 56px;
    padding: 8px var(--tt-space-4);
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}
.tt-soon-main:active { background: var(--tt-surface-sunken); }
.tt-soon-main > .tt-icon:last-child { flex: none; color: var(--tt-ink-faint); transition: transform var(--tt-duration-fast) var(--tt-ease); }
.tt-soon-row.is-open .tt-soon-main > .tt-icon:last-child { transform: rotate(90deg); }
.tt-soon-date { flex: none; width: 36px; text-align: center; line-height: var(--tt-leading-tight); }
.tt-soon-wd {
    display: block;
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-semibold);
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--tt-ink-muted);
}
.tt-soon-day { display: block; font-size: var(--tt-text-headline); font-weight: var(--tt-weight-bold); color: var(--tt-ink); }
.tt-soon-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.tt-soon-title { font-size: var(--tt-text-callout); font-weight: var(--tt-weight-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tt-soon-meta { font-size: var(--tt-text-micro); color: var(--tt-ink-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tt-soon-status {
    flex: none;
    padding: 2px 9px;
    border-radius: var(--tt-radius-pill);
    background: color-mix(in srgb, var(--tt-ink-muted) 12%, transparent);
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
    white-space: nowrap;
}
/* Waiting is a quiet line with a dot, not a pill: the heading's "3 to answer" carries the ask. */
.tt-soon-status.is-waiting { display: inline-flex; align-items: center; gap: 6px; padding-inline: 0; background: none; color: var(--tt-ink-muted); font-weight: var(--tt-weight-medium); }
.tt-soon-status.is-waiting::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--tt-warning); }
.tt-soon-to-answer { margin-left: var(--tt-space-2); color: var(--tt-warning); font-weight: var(--tt-weight-semibold); }
.tt-soon-status.is-yes { background: var(--tt-success-soft); color: var(--tt-success); }
.tt-soon-status.is-no { background: var(--tt-danger-soft); color: var(--tt-danger); }
.tt-soon-status.is-maybe { background: var(--tt-answer-maybe-soft); color: var(--tt-answer-maybe-ink); }
.tt-soon-detail { padding: 0 0 var(--tt-space-3); }
.tt-soon-detail .tt-avail-list.tt-avail-slim { border-top: 0; }
.tt-soon-links { display: flex; align-items: center; gap: var(--tt-space-4); padding: 0 var(--tt-space-4); }

/* A list row that is a button opening a panel, beside the row's "..." menu. */
.tt-list-row-open {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    min-height: var(--tt-tap);
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    letter-spacing: inherit;
    text-align: left;
    cursor: pointer;
}
.tt-list-row-open > .tt-icon:last-child { flex: none; color: var(--tt-ink-faint); }

/* The first line of a message, under its title: one line, cut short. */
.tt-row-preview {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tt-roster-status-field { flex: 0 1 15rem; min-width: 0; }
.tt-roster-status-field .tt-input { width: 100%; }
@media (min-width: 1200px) {
    .tt-family-dashboard {
        grid-template-columns: minmax(0, 1.7fr) minmax(300px, 1fr);
        column-gap: 28px;
        /* Attendance and the team's chat lead the side column (Arnie, 2026-10-18). To do is a button
           on the heading's line since 2026-10-21, so the side column runs down beside Up next and Coming up. */
        grid-template-areas: "notice notice" "next team" "soon team";
    }
    /* Scoped to the family grid: unscoped, a named area on Coming up added columns to coach Home's grid. */
    .tt-family-dashboard > .tt-notif-off { grid-area: notice; }
    .tt-family-dashboard > .tt-dashboard-next { grid-area: next; }
    .tt-family-dashboard > .tt-dashboard-soon { grid-area: soon; }
    .tt-family-dashboard > .tt-dashboard-side { grid-area: team; }
    .tt-family-dashboard:not(:has(.tt-notif-off)) { grid-template-areas: "next team" "soon team"; }
}
@media (max-width: 599.98px) {
    .tt-roster-controls .tt-toolbar-search { flex-basis: 100%; }
    /* A 7rem basis so the filter, Select players and List | Cards share one 343px line (2026-10-30);
       the filter still grows into whatever is left. */
    .tt-roster-status-field { flex: 1 1 7rem; }
    .tt-roster-status-field .tt-input { min-height: 44px; }
}

/* A directory row that goes somewhere (Manage guardians) or opens something (Teammates):
   the row's own look, a chevron at the end. The button form drops the browser's button chrome. */
.tt-directory-link {
    min-height: 48px;
    color: inherit;
    text-decoration: none;
}

button.tt-directory-link {
    width: 100%;
    border: 0;
    border-radius: 0;
    background: none;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.tt-directory-link > .tt-icon:last-child {
    flex: none;
    color: var(--tt-ink-faint);
}

.tt-directory-link:hover {
    background: var(--tt-surface-sunken);
}

/* --- App bar actions ------------------------------------------------------
   The trailing slot holds one filled action ("+") and, beside it, quiet ones: the
   settings gear on the Team screens, the "..." of field-time tools on the Schedule. */
.tt-appbar-actions {
    display: flex;
    align-items: center;
    gap: var(--tt-space-1);
    flex: none;
}

.tt-appbar-action.tt-appbar-action-quiet {
    background: transparent;
    color: var(--tt-ink-muted);
    text-decoration: none;
}

.tt-appbar-action.tt-appbar-action-quiet:hover {
    background: var(--tt-surface-sunken);
    color: var(--tt-ink);
}

/* Help, Settings and Account beside the titles line up with the context chip above the screen's
   heading. Centred on the whole bar they floated between the two rows, belonging to neither. Three
   44px targets touching each other: the icons are 22px, so the gaps people see are the pads. */
.tt-appbar-corner {
    display: flex;
    align-self: flex-start;
    flex-shrink: 0;
    margin-top: -6px;
    margin-right: -6px;
}

.tt-appbar-corner > .tt-appbar-action.active {
    color: var(--tt-brand-strong);
    background: color-mix(in srgb, var(--tt-brand) 18%, transparent);
}

/* A status line the app-bar menu needs for a moment ("Preparing the spreadsheet…"). */
.tt-appbar-status {
    position: absolute;
    right: var(--tt-gutter);
    top: 100%;
    margin-top: 2px;
    white-space: nowrap;
}

/* --- Coach Home -------------------------------------------------------------
   The next item's count, as a row under the card that opens the item. */
.tt-next-tally {
    width: 100%;
    border: 0;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

a.tt-next-tally,
button.tt-next-tally {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    min-height: var(--tt-tap);
    padding: var(--tt-space-2) var(--tt-space-4);
    color: inherit;
    text-decoration: none;
}

.tt-next-tally .tt-avail-count {
    flex: 1;
    min-width: 0;
}

.tt-next-tally > .tt-icon:last-child {
    flex: none;
    color: var(--tt-ink-faint);
}

/* The Schedule's own Up next hero is for a wide screen's side column. On a phone the first list row
   is the same item, a few lines further down, so the hero is not drawn. */
@media (max-width: 959.98px) {
    .tt-schedule-top > .tt-next-session {
        display: none;
    }
}

/* --- Task screens -----------------------------------------------------------
   A survey being built, a letter being written, a form being answered: the app
   bar shows where back goes in place of the context chip, and on a phone the
   tab bar steps aside so the screen reads as a task, not a place. The desktop
   rail stays — it is the frame there, not a thumb bar. */
.tt-appbar-back {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    max-width: 100%;
    min-height: 28px;
    margin-left: -4px;
    padding: 0 10px 0 6px;
    border-radius: var(--tt-radius-pill);
    color: var(--tt-brand);
    font-size: 13px;
    font-weight: var(--tt-weight-semibold);
    letter-spacing: -.01em;
    text-decoration: none;
    white-space: nowrap;
}

.tt-appbar-back::after {
    content: "";
    position: absolute;
    inset: -9px -2px;
}

.tt-appbar-back > .tt-icon {
    flex: none;
}

.tt-appbar-back > span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

@media (hover: hover) {
    .tt-appbar-back:hover {
        background: var(--tt-brand-soft);
    }
}

@media (max-width: 959.98px) {
    .tt-app-task .tt-tabbar {
        display: none;
    }
}

/* Calendar subscribe: one full-width button per calendar, then the plain link with a Copy beside it. */
.tt-cal-options {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
}

.tt-input-row {
    display: flex;
    gap: var(--tt-space-2);
    align-items: center;
}

.tt-input-row > .tt-input {
    flex: 1;
    min-width: 0;
}

/* A single on/off setting: words on the left, the switch on the right. */
.tt-switch-row {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    cursor: pointer;
}

/* A team switch closing a flush list (Coaching staff), ruled off from the rows above it. */
.tt-card-flush > .tt-team-switch-wrap {
    padding: var(--tt-space-3) var(--tt-space-4);
    border-top: 1px solid var(--tt-hairline);
}
/* Opening the list instead (Coaching staff, 2026-10-30): one quieter line, ruled off below. */
.tt-card-flush > .tt-team-switch-wrap:first-child {
    border-top: 0;
    border-bottom: 1px solid var(--tt-hairline);
    padding-block: var(--tt-space-2);
    background: var(--tt-surface-sunken);
}
.tt-team-switch-compact .tt-row-title { font-size: var(--tt-text-callout); font-weight: var(--tt-weight-medium); color: var(--tt-ink-muted); }
.tt-team-switch-compact .tt-switch-row { min-height: 40px; }
/* In the card grid the switch line spans every column. */
.tt-people-cards > .tt-team-switch-wrap {
    grid-column: 1 / -1;
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-lg);
}

.tt-switch-text {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.tt-switch {
    flex: none;
    appearance: none;
    position: relative;
    width: 44px;
    height: 26px;
    margin: 0;
    border-radius: 13px;
    background: var(--tt-hairline-strong, var(--tt-ink-faint));
    cursor: pointer;
    transition: background .15s;
}

.tt-switch::after {
    content: "";
    position: absolute;
    top: 3px;
    left: 3px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .25);
    transition: transform .15s;
}

.tt-switch:checked {
    background: var(--tt-brand);
}

.tt-switch:checked::after {
    transform: translateX(18px);
}

.tt-switch:disabled {
    opacity: .6;
    cursor: progress;
}

.tt-switch:focus-visible {
    outline: 2px solid var(--tt-brand);
    outline-offset: 2px;
}

/* The schedule's filters on the left, Sync to calendar on the right; wraps under on a narrow phone. */
.tt-schedule-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--tt-space-2);
}

.tt-schedule-toolbar > .tt-schedule-sync {
    margin-left: auto;
}

/* The coach's Schedule actions: New event, Field time, and More. They fit one
   row on a 375px phone; on a 320px one, or with large text, More wraps under
   rather than sliding off the edge. */
.tt-schedule-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--tt-space-2);
    padding-block: 4px;
}

.tt-schedule-actions > .tt-btn {
    flex: none;
    white-space: nowrap;
}

/* More is an ActionMenu whose trigger is dressed as the row's buttons. */
.tt-menu-trigger.tt-schedule-more {
    display: inline-flex;
    gap: var(--tt-space-1);
    width: auto;
    height: auto;
    color: var(--tt-ink);
    background: var(--tt-surface-sunken);
    font-size: var(--tt-text-caption);
    white-space: nowrap;
}

.tt-menu-trigger.tt-schedule-more[aria-expanded="true"] {
    background: var(--tt-brand-soft);
    color: var(--tt-brand-strong);
}

.tt-schedule-more > svg {
    transform: rotate(90deg);
    transition: transform var(--tt-duration-fast) var(--tt-ease);
}

.tt-schedule-more[aria-expanded="true"] > svg {
    transform: rotate(-90deg);
}

.tt-schedule-more-count {
    min-width: 18px;
    height: 18px;
    padding-inline: 5px;
    border-radius: var(--tt-radius-pill);
    background: var(--tt-brand);
    color: var(--tt-ink-on-brand);
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-semibold);
    line-height: 18px;
    font-variant-numeric: tabular-nums;
}


/* --- Schedule agenda --------------------------------------------------------
   One card, a row per item. The date sits over the time, on a day's first row
   only; a band of ground marks where a day starts. The answer buttons are a
   short bar under the details, padded out to a 44px target rather than
   spending 44px of every row on it. */
.tt-schedule-toolbar:not(:has(*)) {
    display: none;
}

.tt-schedule-heading {
    align-items: center;
}

.tt-schedule-window {
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-medium);
    letter-spacing: 0;
    color: var(--tt-ink-muted);
}

.tt-agenda .tt-session-time {
    width: 88px;
}

/* Stacked: a small spaced weekday over the date, so the day reads at a glance down the column. */
.tt-session-date {
    margin: 0 0 4px;
    line-height: var(--tt-leading-tight);
    white-space: nowrap;
}

.tt-session-wd {
    display: block;
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-semibold);
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--tt-ink-muted);
}

.tt-session-day {
    display: block;
    font-size: var(--tt-text-body);
    font-weight: var(--tt-weight-bold);
    letter-spacing: -.01em;
    color: var(--tt-ink);
}

/* The title beside "2 days" / "Next week" in a day's first row; the title wraps, the pill holds the corner. */
.tt-session-head {
    display: flex;
    align-items: flex-start;
    gap: var(--tt-space-2);
}

.tt-session-head > .tt-session-open {
    flex: 1;
    min-width: 0;
}

.tt-session-when {
    flex: none;
    margin-top: 2px;
    padding: 2px 8px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--tt-ink-muted) 12%, transparent);
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-semibold);
    line-height: var(--tt-leading-tight);
    white-space: nowrap;
}

.tt-session-when-soon {
    background: color-mix(in srgb, var(--tt-brand) 14%, transparent);
    color: var(--tt-brand-strong);
}

.tt-agenda .tt-session-start {
    font-weight: var(--tt-weight-semibold);
}

.tt-agenda .tt-session-daystart:not(:first-child) {
    border-top: 6px solid var(--tt-ground);
}

.tt-agenda .tt-session-main {
    padding-bottom: var(--tt-space-2);
}

.tt-agenda .tt-avail-list.tt-avail-slim {
    border-top: 0;
}

.tt-agenda .tt-avail-slim .tt-avail-head:not(:empty) {
    padding-top: 0;
}

.tt-agenda .tt-avail-slim .tt-avail-options {
    gap: var(--tt-space-2);
    padding: 0 var(--tt-space-4) var(--tt-space-3);
}

.tt-agenda .tt-avail-slim .tt-avail-option {
    position: relative;
    gap: 4px;
    min-height: 36px;
    padding-inline: var(--tt-space-2);
    white-space: nowrap;
}

.tt-agenda .tt-avail-slim .tt-avail-option::after {
    content: "";
    position: absolute;
    inset: -4px 0;
}

/* Once one answer is chosen the other two step back to neutral, not to a
   faded tint: faded text on a pale tint fell below 4.5:1 contrast. */
.tt-avail-options:has([aria-pressed="true"]) .tt-avail-option[aria-pressed="false"],
.tt-avail-options:has([aria-pressed="true"]) .tt-avail-option[aria-pressed="false"]:hover {
    opacity: 1;
    border-color: var(--tt-hairline-strong);
    background: var(--tt-surface);
    color: var(--tt-ink-muted);
}

.tt-avail-options:has([aria-pressed="true"]) .tt-avail-option[aria-pressed="false"] .tt-icon {
    color: var(--tt-ink-muted);
}

/* Up next opens the item: the whole card is the target, the title its keyboard stop. */
.tt-next-session-openable {
    cursor: pointer;
}

.tt-next-session-openable h2 > .tt-session-open {
    display: inline;
}

/* The club's announcements, above Home's own layout (see ClubAnnouncements.razor): the same gap
   below them as between Home's sections. */
.tt-announcements { margin-bottom: 24px; }

/* A coach's alerts sit above even the club's: urgent and about this team. The warning palette, since
   "the field is closed" is exactly what it's for; the team and last day in a quieter line under it. */
.tt-team-alerts { margin-bottom: var(--tt-space-4); }
/* Red, not amber (Arnie, 2026-10-30: amber wasn't "alert-y" enough): the danger surface with a solid
   red edge and a red bold title, the body in ink so a long message stays readable, the meta muted. */
.tt-team-alert {
    border: 1px solid color-mix(in srgb, var(--tt-danger) 35%, transparent);
    box-shadow: inset 4px 0 0 var(--tt-danger);
    color: var(--tt-ink);
}
.tt-team-alert > .tt-notice-body > .tt-icon { color: var(--tt-danger); }
.tt-team-alert strong { font-size: var(--tt-text-headline); font-weight: var(--tt-weight-bold); color: var(--tt-danger); }
.tt-team-alert .tt-icon-close { color: var(--tt-danger); }
.tt-team-alert .tt-icon-close:hover { background: color-mix(in srgb, var(--tt-danger) 14%, transparent); color: var(--tt-danger); }
.tt-team-alert-meta { font-size: var(--tt-text-micro); color: var(--tt-ink-muted); }
/* The coach's list of them on Messages: the same symbol in the warning tint. */
.tt-task-symbol-warning { color: var(--tt-warning); background: var(--tt-warning-soft); }

/* --- Join: the club search ------------------------------------------------
   Matches drop under the box as tappable rows; the chosen club replaces the box
   with its name and a Change button. */
.join-club-matches {
    display: flex;
    flex-direction: column;
    margin-top: var(--tt-space-1);
    overflow: hidden;
}

.join-club-match {
    min-height: var(--tt-tap);
    padding: var(--tt-space-2) var(--tt-space-4);
    border: 0;
    border-bottom: 1px solid var(--tt-hairline);
    background: transparent;
    color: var(--tt-ink);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.join-club-match:last-child {
    border-bottom: 0;
}

.join-club-match:hover,
.join-club-match:focus-visible {
    background: var(--tt-brand-soft);
}

.join-club-chosen {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tt-space-3);
    padding: var(--tt-space-2) var(--tt-space-3);
    border-radius: var(--tt-radius-md);
    background: var(--tt-surface-sunken);
}

/* --- Settings rows ----------------------------------------------------------
   Grouped rows that each say what a setting is now and open it on its own screen — the shape of a
   phone's own Settings, which is the one people already know. Settings and Team settings both use
   them (moved here from TeamSettings.razor.css when Team settings became a row of Settings). */

.settings-group {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
}

.settings-group + .settings-group {
    margin-top: var(--tt-space-2);
}

.settings-group-heading {
    margin: 0;
    padding: 0 var(--tt-space-1);
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
    letter-spacing: .04em;
    text-transform: uppercase;
}

.settings-row {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    min-height: var(--tt-tap-lg);
    padding: var(--tt-space-3) var(--tt-space-3) var(--tt-space-3) var(--tt-space-4);
    border-bottom: 1px solid var(--tt-hairline);
    color: inherit;
    text-decoration: none;
    transition: background var(--tt-duration-fast) var(--tt-ease);
}

.settings-row:last-child {
    border-bottom: 0;
}

.settings-row:hover {
    background: var(--tt-surface-sunken);
}

.settings-row:focus-visible {
    outline: none;
    box-shadow: inset var(--tt-focus-ring);
}

.settings-row-icon {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: var(--tt-radius-md);
    background: var(--tt-brand-soft);
    color: var(--tt-brand-strong);
}

.settings-row-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.settings-row-title {
    font-weight: var(--tt-weight-semibold);
}

.settings-row-summary {
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-callout);
    overflow-wrap: anywhere;
}

/* --- Inside a section ------------------------------------------------------------------------- */

.settings-section-title {
    margin: 0;
    font-size: var(--tt-text-title);
    font-weight: var(--tt-weight-bold);
    line-height: var(--tt-leading-tight);
}

/* --- Legal pages ---------------------------------------------------------------
   Privacy Policy and Terms of Service, on the plain shell so they open without signing in. Wider
   than the sign-in card, and set for reading: these are the longest text in the app. */

.tt-main-plain > .tt-legal {
    width: min(720px, 100%);
    margin-inline: auto;
    padding: var(--tt-space-5);
    box-sizing: border-box;
    background: var(--tt-surface);
    border-radius: 28px;
    box-shadow: 0 24px 70px rgba(0, 0, 0, .16);
    line-height: 1.55;
}

.tt-legal .tt-brandmark {
    text-decoration: none;
}

.tt-legal h2 {
    margin: var(--tt-space-5) 0 var(--tt-space-2);
    font-size: var(--tt-text-title);
    font-weight: var(--tt-weight-bold);
    line-height: var(--tt-leading-tight);
}

.tt-legal h3 {
    margin: var(--tt-space-4) 0 var(--tt-space-1);
    font-size: inherit;
    font-weight: var(--tt-weight-semibold);
}

.tt-legal p,
.tt-legal ul {
    margin: 0 0 var(--tt-space-3);
}

.tt-legal ul {
    padding-left: 1.25em;
}

.tt-legal li + li {
    margin-top: var(--tt-space-1);
}

.tt-legal-notice {
    text-align: center;
}

/* ── "Sign in as" (ImpersonationBar) ──
   Dev builds and super admins only. Floats bottom-left, above the tab bar when there is one, in the
   warning palette while a borrowed session is in use so nobody forgets whose app they are in. */
.tt-imp {
    position: fixed;
    left: var(--tt-space-3);
    bottom: calc(var(--tt-space-3) + var(--tt-safe-bottom));
    z-index: 60;
    max-width: calc(100vw - 2 * var(--tt-space-3));
}

body:has(.tt-tabbar) .tt-imp {
    bottom: calc(88px + var(--tt-safe-bottom));
}

.tt-imp-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--tt-space-2);
    min-height: 44px;
    max-width: 100%;
    padding: 0 var(--tt-space-4);
    border: 1px dashed var(--tt-hairline-strong);
    border-radius: 999px;
    background: var(--tt-surface);
    color: var(--tt-ink-muted);
    font: inherit;
    font-size: var(--tt-text-caption);
    box-shadow: 0 6px 18px rgba(16, 24, 20, .12);
    cursor: pointer;
}

.tt-imp-chip span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tt-imp-active .tt-imp-chip {
    border: 1px solid var(--tt-warning);
    background: var(--tt-warning-soft);
    color: var(--tt-warning);
    font-weight: 600;
}

.tt-imp-panel {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
    width: min(360px, calc(100vw - 2 * var(--tt-space-3)));
    padding: var(--tt-space-4);
    border: 1px solid var(--tt-hairline);
    border-radius: var(--tt-radius-sm);
    background: var(--tt-surface);
    color: var(--tt-ink);
    box-shadow: 0 12px 32px rgba(16, 24, 20, .18);
}

.tt-imp-active .tt-imp-panel {
    border-color: var(--tt-warning);
}

.tt-imp-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.tt-imp-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin: -10px -10px -10px 0;
    border: 0;
    background: none;
    color: var(--tt-ink-muted);
    cursor: pointer;
}

.tt-imp-now {
    margin: 0;
    padding: var(--tt-space-2) var(--tt-space-3);
    border-radius: var(--tt-radius-sm);
    background: var(--tt-warning-soft);
    color: var(--tt-warning);
    font-size: var(--tt-text-caption);
    overflow-wrap: anywhere;
}

.tt-imp-error {
    margin: 0;
    color: var(--tt-danger);
    font-size: var(--tt-text-caption);
}

.tt-imp-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tt-space-2);
    margin-top: var(--tt-space-2);
}

/* Inside the app "Sign in as…" at the top of Settings opens the picker (AccountSection, 2026-10-24; the
   corner's account menu before that), so the chip shows only on the sign-in screen. */
body:has(.tt-appbar) .tt-imp-chip {
    display: none;
}

body:has(.tt-appbar) .tt-imp {
    top: calc(var(--tt-appbar-height) + var(--tt-safe-top) + var(--tt-space-2));
    right: calc(var(--tt-space-3) + var(--tt-safe-right));
    bottom: auto;
    left: auto;
}

/* A borrowed session: the corner's gear carries the warning the "As …" chip used to — on Settings
   (where "Sign in as…" is) and under the pointer too, so it beats the active and hover looks. */
.tt-appbar-corner > .tt-appbar-action.tt-account-borrowed,
.tt-appbar-corner > .tt-appbar-action.tt-account-borrowed.active,
.tt-appbar-corner > .tt-appbar-action.tt-account-borrowed:hover {
    background: var(--tt-warning-soft);
    color: var(--tt-warning);
}

/* ── Account (AccountSection) ── who's signed in and Sign out, at the top of Settings. */
.tt-account {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-3);
}

.tt-account-who {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    min-width: 0;
}

.tt-account-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.tt-account-email {
    font-size: var(--tt-text-body);
    font-weight: var(--tt-weight-semibold);
    color: var(--tt-ink);
    overflow-wrap: anywhere;
}

.tt-account-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tt-space-2);
}

.tt-account-actions > .tt-btn {
    flex: 1 1 8rem;
}

.tt-account-signout {
    color: var(--tt-danger);
}

/* --- Team budget ------------------------------------------------------------
   The same view for families (Team > Team budget) and the coach (Team settings >
   Team budget): the whole first, then each line with its own bar. Over budget is
   amber, never red — information for families, not an alarm about their coach.
   Figures are tabular so a column of amounts lines up. */
.budget-summary {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
}

.budget-summary-figures {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0 var(--tt-space-2);
}

.budget-summary-spent {
    font-size: var(--tt-text-title);
    font-weight: var(--tt-weight-bold);
    line-height: var(--tt-leading-tight);
    font-variant-numeric: tabular-nums;
}

.budget-summary-of {
    color: var(--tt-ink-muted);
    font-variant-numeric: tabular-nums;
}

.budget-summary-foot,
.budget-line-bottom {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0 var(--tt-space-3);
}

.budget-bar {
    display: block;
    height: 6px;
    border-radius: var(--tt-radius-pill);
    background: var(--tt-surface-sunken);
    overflow: hidden;
}

.budget-bar-lg {
    height: 10px;
}

.budget-bar-fill {
    display: block;
    height: 100%;
    border-radius: inherit;
    background: var(--tt-brand);
}

.budget-bar.is-over .budget-bar-fill {
    background: var(--tt-warning);
}

.budget-left {
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-semibold);
    color: var(--tt-ink);
    font-variant-numeric: tabular-nums;
}

.budget-left.is-over {
    color: var(--tt-warning);
}

.budget-note {
    margin: var(--tt-space-1) 0 0;
    padding-top: var(--tt-space-3);
    border-top: 1px solid var(--tt-hairline);
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-callout);
    line-height: 1.45;
    white-space: pre-line;
}

.budget-line + .budget-line {
    border-top: 1px solid var(--tt-hairline);
}

.budget-line-head {
    display: flex;
    align-items: center;
    padding-right: var(--tt-space-2);
}

/* The whole line is the button that opens its spending; the chevron says so. */
.budget-line-toggle {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    min-height: 44px;
    padding: var(--tt-space-3) var(--tt-space-2) var(--tt-space-3) var(--tt-space-4);
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.budget-line-text {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.budget-line-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--tt-space-3);
}

.budget-line-label {
    font-weight: var(--tt-weight-semibold);
    overflow-wrap: anywhere;
}

.budget-line-amounts {
    flex: none;
    font-variant-numeric: tabular-nums;
}

.budget-line-of {
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-caption);
}

.budget-line-chevron {
    flex: none;
    display: inline-flex;
    color: var(--tt-ink-faint);
    transition: transform .15s ease;
}

.budget-line-toggle[aria-expanded="true"] .budget-line-chevron {
    transform: rotate(90deg);
}

.budget-entries {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
    padding: 0 var(--tt-space-4) var(--tt-space-3);
}

.budget-line-note {
    margin: 0;
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

.budget-entry-list {
    margin: 0;
    padding: 0;
    list-style: none;
    border-radius: var(--tt-radius-md);
    background: var(--tt-surface-sunken);
}

.budget-entry {
    display: flex;
    align-items: center;
    gap: var(--tt-space-3);
    min-height: 44px;
    padding: var(--tt-space-2) var(--tt-space-1) var(--tt-space-2) var(--tt-space-3);
    font-size: var(--tt-text-callout);
}

.budget-entry + .budget-entry {
    border-top: 1px solid var(--tt-hairline);
}

.budget-entry-date {
    flex: none;
    width: 3.6em;
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-caption);
    font-variant-numeric: tabular-nums;
}

.budget-entry-what {
    flex: 1;
    min-width: 0;
    overflow-wrap: anywhere;
}

.budget-refund {
    margin-left: var(--tt-space-1);
    padding: 0 6px;
    border-radius: var(--tt-radius-pill);
    background: var(--tt-success-soft);
    color: var(--tt-success);
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-semibold);
    white-space: nowrap;
}

.budget-entry-amount {
    flex: none;
    padding-right: var(--tt-space-2);
    font-variant-numeric: tabular-nums;
}

.budget-entry-amount.is-refund {
    color: var(--tt-success);
}

.budget-actions {
    justify-content: flex-start;
}

.budget-note-edit {
    align-self: flex-start;
    min-height: 44px;
}

.budget-form-title {
    font-size: var(--tt-text-headline);
    font-weight: var(--tt-weight-semibold);
}

/* "$" sits inside the box's left edge, so the box reads as money before anything is typed. */
.budget-money-input {
    position: relative;
}

.budget-money-input > span {
    position: absolute;
    left: var(--tt-space-3);
    top: 50%;
    transform: translateY(-50%);
    color: var(--tt-ink-muted);
    pointer-events: none;
}

.budget-money-input > .tt-input {
    padding-left: calc(var(--tt-space-3) + 1ch);
    font-variant-numeric: tabular-nums;
}

.budget-form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--tt-space-3);
}

.budget-door .budget-bar {
    margin-top: 6px;
}

/* Four sections — The plan, So far, Spending, Income — each a small-caps heading above one card.
   The plan reads as a sum: spending, less income, equals the guardian fees, which carry the weight
   because that line is what a family came for. */
.budget-section {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
}

.budget-section-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tt-space-3);
    min-height: 28px;
}

.budget-section-title {
    margin: 0;
    padding-left: var(--tt-space-1);
    font-size: var(--tt-text-caption);
    font-weight: var(--tt-weight-semibold);
    color: var(--tt-ink-muted);
    text-transform: uppercase;
    letter-spacing: .06em;
}

.budget-section-add {
    min-height: 36px;
    padding: 0 var(--tt-space-3);
}

.budget-plan {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-2);
}

.budget-plan-rows {
    margin: 0;
    display: flex;
    flex-direction: column;
}

.budget-plan-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--tt-space-3);
    min-height: 36px;
    padding: var(--tt-space-1) 0;
}

.budget-plan-row + .budget-plan-row {
    border-top: 1px solid var(--tt-hairline);
}

.budget-plan-row > dt {
    min-width: 0;
    color: var(--tt-ink-muted);
    overflow-wrap: anywhere;
}

.budget-plan-row > dd {
    margin: 0;
    flex: none;
    font-variant-numeric: tabular-nums;
}

/* The sum's answer: a heavier rule above, the figure in headline weight. */
.budget-plan-fees {
    border-top-width: 2px !important;
    border-top-color: var(--tt-ink) !important;
    padding-top: var(--tt-space-2);
}

.budget-plan-fees > dt {
    color: var(--tt-ink);
    font-weight: var(--tt-weight-semibold);
}

.budget-plan-fees > dd {
    font-size: var(--tt-text-headline);
    font-weight: var(--tt-weight-bold);
}

/* The share under the fees: a plain row, with how it was split in small print. */
.budget-plan-each > dt {
    display: flex;
    flex-direction: column;
    color: var(--tt-ink);
}

.budget-plan-each > dt > small {
    font-size: var(--tt-text-caption);
    color: var(--tt-ink-muted);
}

.budget-plan-each > dd {
    font-weight: var(--tt-weight-semibold);
}

.budget-plan-note {
    margin: 0;
}

.budget-note-row {
    display: flex;
    align-items: flex-start;
    gap: var(--tt-space-2);
    margin-top: var(--tt-space-1);
    padding-top: var(--tt-space-3);
    border-top: 1px solid var(--tt-hairline);
}

/* The row carries the rule, so it spans the pencil too. */
.budget-note-row > .budget-note {
    flex: 1;
    min-width: 0;
    margin: 0;
    padding-top: 0;
    border-top: 0;
}

.budget-note-pencil {
    flex: none;
    min-height: 36px;
    margin-top: var(--tt-space-2);
    padding: 0 var(--tt-space-3);
}

.budget-sofar {
    display: flex;
    flex-direction: column;
    gap: var(--tt-space-3);
}

.budget-sofar-row {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.budget-sofar-row + .budget-sofar-row {
    padding-top: var(--tt-space-3);
    border-top: 1px solid var(--tt-hairline);
}

.budget-sofar-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--tt-space-3);
}

.budget-sofar-label {
    font-weight: var(--tt-weight-semibold);
}

.budget-share-row {
    padding: var(--tt-space-2) var(--tt-space-1);
}

.budget-form-kind {
    margin: 0;
}

/* A budget is a column of figures: past this width the amounts drift too far from their names. */
.budget-page {
    max-width: 44rem;
}

/* --- Rental import ----------------------------------------------------------
   One row per weekly slot read off a venue's PDF: what it is, then the fields
   the coach may need to correct, with dates and times folded away unless the
   reader flagged them. */
.tt-rental-slot-fields {
    margin-top: var(--tt-space-3);
}

.tt-rental-slot-more > summary {
    display: inline-flex;
    align-items: center;
    min-height: var(--tt-tap);
    cursor: pointer;
    font-size: var(--tt-text-callout);
    font-weight: var(--tt-weight-medium);
    color: var(--tt-brand);
}

.tt-rental-slot-more[open] > summary {
    margin-bottom: var(--tt-space-2);
}

/* A linked device's QR code (Family > a child's devices). Always black on white with its own quiet zone,
   whatever the theme: a phone camera reads that best. Square, and no wider than the panel. */
.tt-qr {
    align-self: center;
    width: min(16rem, 100%);
    aspect-ratio: 1;
    padding: var(--tt-space-2);
    background: #ffffff;
    border-radius: var(--tt-radius-md);
    border: 1px solid var(--tt-hairline);
}

.tt-qr svg {
    display: block;
    width: 100%;
    height: 100%;
}
/* Framing a headshot: a square to drag the photo in, the roster's circle drawn over it, and a zoom row. */
.tt-crop {
    max-height: calc(100% - 2 * var(--tt-gutter));
    overflow-y: auto;
}

.tt-crop-viewport {
    position: relative;
    width: min(100%, 320px);
    aspect-ratio: 1;
    margin-inline: auto;
    overflow: hidden;
    border-radius: var(--tt-radius-md);
    background: var(--tt-surface-sunken);
    touch-action: none;
    cursor: grab;
    user-select: none;
}

.tt-crop-viewport:active {
    cursor: grabbing;
}

.tt-crop-viewport:focus-visible {
    outline: 2px solid var(--tt-focus, var(--tt-brand));
    outline-offset: 2px;
}

.tt-crop-image {
    position: absolute;
    max-width: none;
    pointer-events: none;
}

/* Everything outside the circle is dimmed: what's inside is what the roster shows. */
.tt-crop-guide {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: radial-gradient(circle closest-side, transparent 98%, rgba(9, 14, 11, .5) 100%);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .35);
}

.tt-crop-zoom {
    display: flex;
    align-items: center;
    gap: var(--tt-space-2);
}

.tt-crop-slider {
    flex: 1;
    min-width: 0;
    min-height: var(--tt-tap);
}

.tt-crop-step {
    flex: none;
    min-width: var(--tt-tap);
    min-height: var(--tt-tap);
    padding: 0;
    font-size: 1.375rem;
    line-height: 1;
}

/* Where each family stands: what the plan asks of a child, less sponsorship, team funds and payments. */
.budget-funds {
    margin: 0;
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-caption);
}

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

.budget-family {
    padding: var(--tt-space-3) 0;
    border-top: 1px solid var(--tt-hairline);
}

.budget-family:first-child {
    border-top: 0;
    padding-top: 0;
}

.budget-family-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--tt-space-3);
}

.budget-family-name {
    min-width: 0;
    overflow-wrap: anywhere;
    font-weight: var(--tt-weight-semibold);
}

.budget-family-owes {
    flex: none;
    font-variant-numeric: tabular-nums;
    font-weight: var(--tt-weight-semibold);
}

.budget-family-owes.is-clear {
    color: var(--tt-success);
}

.budget-family-sum {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tt-space-1) var(--tt-space-4);
    margin: var(--tt-space-1) 0 0;
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-caption);
}

.budget-family-sum > div {
    display: flex;
    gap: var(--tt-space-1);
}

.budget-family-sum dt,
.budget-family-sum dd {
    margin: 0;
    font-variant-numeric: tabular-nums;
}

.budget-source {
    margin-left: var(--tt-space-1);
    padding: 0 6px;
    border-radius: var(--tt-radius-pill);
    background: var(--tt-surface-sunken, var(--tt-hairline));
    color: var(--tt-ink-muted);
    font-size: var(--tt-text-micro);
    font-weight: var(--tt-weight-semibold);
    white-space: nowrap;
}
