/*
 * Flag Football Play Designer - shared responsive stylesheet.
 *
 * Visual system: a flag-football product identity inspired by NFL Flag's
 * energy + Squarespace's editorial restraint. Field-green primary, end-zone
 * yellow accent, generous whitespace, oversized display type for marketing
 * surfaces, and a tighter utilitarian rhythm for the playbook/editor surfaces
 * where coaches need to scan fast.
 *
 * Mobile-first throughout; breakpoints progressively unlock horizontal real
 * estate. Below the smallest tablet breakpoint the layout collapses to a
 * single column.
 */

/* --------------------------------------------------------------------
   1. Design tokens
   -------------------------------------------------------------------- */

:root {
    /* Field palette */
    --c-field-900: #07371f;      /* Midnight field — header, dark surfaces */
    --c-field-700: #0b6b3a;      /* Primary brand green — buttons, mark */
    --c-field-500: #2d8a4e;      /* Mid green — hover, accents */
    --c-field-300: #3fb56b;      /* Highlight green — focus rings, success */
    --c-field-50:  #ecf6f0;      /* Faint tint — surfaces / hover wash */

    /* Accents */
    --c-endzone:      #f5c116;   /* End-zone yellow — eyebrow, highlights */
    --c-endzone-deep: #d99f08;   /* End-zone hover */
    /* Copy that sits ON the end-zone yellow. White is unreadable there
       (1.7:1 — it fails AA by a mile), so anything filled with --c-endzone
       reverses to dark text: --c-ink-900 for the headline (10.4:1) and this
       for the secondary lines (7.2:1). */
    --c-endzone-ink:  #43350a;

    /* Neutrals */
    --c-chalk:     #fafaf7;      /* Off-white page background */
    --c-paper:     #ffffff;      /* Card / sheet background */
    --c-ink-900:   #0f1d17;      /* Primary text — dark green-black */
    --c-ink-600:   #4b5853;      /* Secondary text */
    --c-ink-400:   #65706b;      /* Muted text: 4.9:1 on chalk, 5.1:1 on paper (AA) */
    --c-line:      #e3e9e5;      /* Hairline borders */
    --c-line-soft: #eef2ef;      /* Even softer dividers */

    /* Status */
    --c-error-bg:  #fdecea;
    --c-error-line:#f5c6cb;
    --c-error-ink: #8a1218;

    /* Alert red — a SOLID call-to-action red, for the one action a coach has
       to find fastest on a sideline. Distinct from the --c-error-* trio above,
       which tints a failure message rather than filling a control.
       --c-danger-600 / --c-danger-50 were already referenced with these exact
       values as inline fallbacks (.editor-toolbar__btn--danger); naming them
       here settles the dangling reference without changing that button. */
    --c-danger-700: #8f1b13;     /* Hover / pressed — 9.0:1 on white text */
    --c-danger-600: #b42318;     /* Base fill — 6.6:1 on white text (AA) */
    --c-danger-100: #ffe4e1;     /* Tinted body copy ON the red — 5.5:1 */
    --c-danger-50:  #fef3f2;     /* Faint wash */

    /* Typography */
    --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    --font-display: "Bebas Neue", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

    /* Type scale — display sizes scale fluidly between mobile and desktop. */
    --t-section:clamp(1.65rem, 2.4vw + 1rem, 2.5rem);
    --t-card:   1.125rem;
    --t-body:   1rem;
    --t-meta:   0.875rem;
    --t-eyebrow:0.78rem;

    /* Radii */
    --r-tile: 14px;
    --r-card: 12px;
    --r-pill: 999px;
    --r-input: 10px;

    /* Elevation — used sparingly to keep the surface editorial */
    --shadow-card: 0 1px 2px rgba(15, 29, 23, 0.04),
                   0 8px 24px -12px rgba(15, 29, 23, 0.18);
    --shadow-soft: 0 1px 2px rgba(15, 29, 23, 0.04);
}

/* --------------------------------------------------------------------
   2. Base + reset
   -------------------------------------------------------------------- */

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

html, body {
    margin: 0;
    padding: 0;
    overflow-x: hidden;  /* horizontal scrolling is forbidden by Req 16.1 */
}

body {
    font-family: var(--font-sans);
    font-size: var(--t-body);
    line-height: 1.55;
    color: var(--c-ink-900);
    background: var(--c-chalk);
    padding-top: env(safe-area-inset-top, 0);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

img, svg {
    max-width: 100%;
    height: auto;
    display: block;
}

a {
    color: var(--c-field-700);
    text-decoration: none;
    transition: color 120ms ease;
}

a:hover, a:focus {
    color: var(--c-field-500);
    text-decoration: underline;
}

button { font: inherit; cursor: pointer; }

::selection {
    background: color-mix(in srgb, var(--c-endzone) 50%, transparent);
    color: var(--c-ink-900);
}

/* --------------------------------------------------------------------
   Accessibility (P2 pass)
   -------------------------------------------------------------------- */

/* A clear keyboard focus ring for every interactive element. ``:focus-visible``
   only shows for keyboard/AT users, so mouse clicks stay clean. */
a:focus-visible,
button:focus-visible,
.btn:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 3px solid var(--c-endzone-deep);
    outline-offset: 2px;
    border-radius: 4px;
}

/* A link inside running text needs more than colour to stand out (WCAG
   1.4.1). Class-less links in a paragraph are the inline ones; buttons,
   nav and card links all carry a class and keep their own styling. */
p a:not([class]) {
    text-decoration: underline;
    text-underline-offset: 0.15em;
}

/* Smooth in-page anchor scrolling (e.g. the CTA's "See it in action"),
   but only when the user hasn't asked for reduced motion. */
@media (prefers-reduced-motion: no-preference) {
    html { scroll-behavior: smooth; }
}

/* Reduced-motion safety net for the marketing page: even if a transition
   or animation is added later without its own guard, it collapses to a
   near-instant change for users who ask for reduced motion. */
@media (prefers-reduced-motion: reduce) {
    .landing *,
    .landing *::before,
    .landing *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

/* --------------------------------------------------------------------
   3. App shell + header
   -------------------------------------------------------------------- */

.app-shell {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    width: 100%;
    max-width: 100vw;
}

.app-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.85rem 1.1rem;
    background: var(--c-field-900);
    color: #ffffff;
    position: sticky;
    top: 0;
    z-index: 10;
    /* Yard-line accent — a single thin band of end-zone yellow that
       reads as a referee's sideline marker under the header. */
    border-bottom: 3px solid var(--c-endzone);
    transition: padding 0.2s ease, box-shadow 0.2s ease;
}
/* Condensed state once the page scrolls (toggled by the inline script in
   base.html) — tighter and lifted so the brand + CTA stay compact. The
   doubled class out-specifies the responsive ``.app-header`` padding rule
   so the vertical shrink wins at every breakpoint. */
.app-header.app-header--scrolled {
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
    box-shadow: 0 10px 26px -18px rgba(7, 24, 14, 0.75);
}
@media (prefers-reduced-motion: reduce) {
    .app-header { transition: none; }
}

.app-header__brand {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    font-weight: 700;
    color: #ffffff;
    font-size: 1rem;
    letter-spacing: -0.005em;
}

.app-header__brand:hover,
.app-header__brand:focus {
    color: #ffffff;
    text-decoration: none;
}

.brand-mark {
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    border-radius: 7px;
    display: block;
}

.brand-wordmark {
    display: inline-flex;
    /* Single-line wordmark — the "Flag Football" tagline was removed
       once tackle support was added to the roadmap (sport is per-user
       now). Center-align so the wordmark vertically meets the football
       mark next to it. */
    align-items: center;
    line-height: 1;
}

.brand-wordmark__name {
    font-weight: 800;
    font-size: 1.05rem;
    letter-spacing: -0.01em;
}

.app-header__nav {
    display: flex;
    gap: 0.5rem;
    margin-left: auto;
    min-height: 44px;
    align-items: center;
}

/* Mobile nav toggle (design item #7). Hidden controls on desktop; the
   hamburger + collapsible dropdown only engage below the breakpoint. */
.app-header__nav-toggle {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}
.app-header__hamburger {
    display: none;
    margin-left: auto;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: var(--r-input);
    color: #ffffff;
    cursor: pointer;
}
.app-header__hamburger:hover,
.app-header__nav-toggle:focus-visible + .app-header__hamburger {
    background: rgba(255, 255, 255, 0.1);
}

@media (max-width: 1023px) {
    .app-header__hamburger { display: inline-flex; }
    .app-header__nav--collapsible {
        /* Drop the authenticated nav into a full-width panel under the
           header instead of letting 9 links overflow and clip. */
        display: none;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        margin-left: 0;
        flex-direction: column;
        align-items: stretch;
        gap: 0.1rem;
        padding: 0.5rem 1rem 0.85rem;
        background: var(--c-field-900);
        border-bottom: 3px solid var(--c-endzone);
        box-shadow: 0 12px 24px -12px rgba(7, 24, 14, 0.6);
    }
    .app-header__nav-toggle:checked ~ .app-header__nav--collapsible {
        display: flex;
    }
    .app-header__nav--collapsible a,
    .app-header__nav--collapsible .app-header__logout {
        width: 100%;
        padding: 0.7rem 0.6rem;
    }
    .app-header__nav--collapsible .app-header__logout-btn {
        width: 100%;
    }
    .app-header__nav--collapsible .app-header__cta {
        text-align: center;
        margin-top: 0.25rem;
    }
}

.app-header__nav a {
    color: #ffffff;
    padding: 0.5rem 0.6rem;
    border-radius: var(--r-pill);
    font-size: 0.95rem;
    font-weight: 500;
}

.app-header__nav a:hover,
.app-header__nav a:focus {
    background: rgba(255, 255, 255, 0.08);
    text-decoration: none;
    color: #ffffff;
}

.app-header__cta {
    background: var(--c-endzone);
    color: var(--c-ink-900) !important;
    padding: 0.5rem 1rem !important;
    border-radius: var(--r-pill);
    line-height: 1;
    font-weight: 700;
}

.app-header__cta:hover,
.app-header__cta:focus {
    background: var(--c-endzone-deep);
    color: var(--c-ink-900) !important;
}

.app-header__logout {
    margin: 0;
    display: inline-flex;
    align-items: center;
}

.app-header__logout-btn {
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.35);
    color: #ffffff;
    padding: 0.4rem 0.95rem;
    border-radius: var(--r-pill);
    font-size: 0.85rem;
    line-height: 1;
    font-weight: 500;
}

.app-header__logout-btn:hover,
.app-header__logout-btn:focus {
    border-color: #ffffff;
    background: rgba(255, 255, 255, 0.1);
}

/* Anonymous header fit (design item #7). The signed-out nav is three
   short inline links (Pricing / Sign in / Sign up) with no hamburger —
   it overflowed and clipped on phones. Rather than hide the Sign-up CTA
   behind a menu, tighten the inline nav so all three fit, and drop the
   wordmark text to the brand mark alone on the narrowest phones. Scoped
   with :not(--collapsible) so it never touches the authenticated dropdown
   (whose links need full tap padding). */
@media (max-width: 540px) {
    .app-header { gap: 0.5rem; }
    .app-header__nav:not(.app-header__nav--collapsible) { gap: 0.3rem; }
    .app-header__nav:not(.app-header__nav--collapsible) a {
        padding: 0.4rem 0.45rem;
        font-size: 0.85rem;
    }
    .app-header__nav:not(.app-header__nav--collapsible) .app-header__cta {
        padding: 0.4rem 0.7rem !important;
    }
}
/* Signed-out nav on phones. Its four links plus the one-line wordmark need
   ~426px, so between 361px and 440px the links broke mid-label ("Sign" /
   "in"). Labels never wrap; the wordmark sets on two lines instead, and the
   links' padding tightens. The signed-in nav collapses behind the hamburger
   and is left alone. */
.app-header__nav:not(.app-header__nav--collapsible) a { white-space: nowrap; }
@media (max-width: 440px) {
    .app-header { gap: 0.5rem; }
    .app-header__brand { gap: 0.45rem; }
    .brand-wordmark__name { display: block; max-width: 5.5em; font-size: 0.95rem; line-height: 1.1; word-spacing: 0.12em; }
    .app-header__nav:not(.app-header__nav--collapsible) { gap: 0.05rem; }
    .app-header__nav:not(.app-header__nav--collapsible) a { padding: 0.5rem 0.34rem; font-size: 0.88rem; }
    .app-header__nav:not(.app-header__nav--collapsible) .app-header__cta { padding: 0.5rem 0.75rem !important; }
}

@media (max-width: 380px) {
    .brand-wordmark { display: none; }   /* mark-only on the smallest phones (incl. 375px) */
}

.app-main {
    flex: 1 1 auto;
    width: 100%;
    padding: 1rem;
    max-width: 1120px;
    margin: 0 auto;
}

/* Opt-in modifier: pages that want the full viewport width (the
   playbook detail page's play grid + the editor's two-column field
   layout) set ``{% block main_modifiers %}app-main--wide{% endblock %}``
   in the template. Caps at the viewport with a small breathing-room
   padding instead of the global 1120px cap. */
.app-main--wide {
    max-width: none;
    padding-left: 1.25rem;
    padding-right: 1.25rem;
}

@media (min-width: 1280px) {
    .app-main--wide {
        padding-left: 2rem;
        padding-right: 2rem;
    }
}

/* --------------------------------------------------------------------
   4. Button system (.btn)
   --------------------------------------------------------------------
   New shared primitives used by the landing surfaces. Existing
   ``.auth-button`` rules below are preserved for the other auth pages
   so we don't churn 10 templates in a single pass.
   ----------------------------------------------------------------- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.45rem;
    min-height: 48px;
    padding: 0.75rem 1.4rem;
    border-radius: var(--r-pill);
    border: 1.5px solid transparent;
    font-weight: 600;
    font-size: 1rem;
    line-height: 1;
    letter-spacing: -0.005em;
    text-decoration: none;
    transition: background 140ms ease, border-color 140ms ease,
                color 140ms ease, transform 80ms ease;
    cursor: pointer;
}

.btn:hover, .btn:focus { text-decoration: none; }
.btn:active { transform: translateY(1px); }

.btn--primary {
    background: var(--c-field-700);
    color: #ffffff;
    border-color: var(--c-field-700);
}
.btn--primary:hover,
.btn--primary:focus {
    background: var(--c-field-500);
    border-color: var(--c-field-500);
    color: #ffffff;
}

.btn--ghost {
    background: transparent;
    color: var(--c-ink-900);
    border-color: var(--c-ink-900);
}
.btn--ghost:hover,
.btn--ghost:focus {
    background: var(--c-ink-900);
    color: #ffffff;
}

/* Solid end-zone yellow, dark letters (owner: red 2026-09-04, yellow
   2026-09-06). Used for "Get in-game advice" on both surfaces it appears on,
   so the sideline action looks the same wherever a coach meets it. Fill
   rather than outline, because a ghost button reads as secondary and this is
   the one control a coach hunts for mid-game.

   THE TEXT COLOUR IS NOT A STYLE CHOICE. White on this yellow is 1.7:1 —
   illegible in a gym, let alone in sunlight on a sideline — so the fill
   change forces dark text. Kept as --c-ink-900 (10.4:1) rather than pure
   black to match every other dark label on the page. */
.btn--alert {
    background: var(--c-endzone);
    color: var(--c-ink-900);
    border-color: var(--c-endzone);
}
.btn--alert:hover,
.btn--alert:focus {
    background: var(--c-endzone-deep);
    border-color: var(--c-endzone-deep);
    color: var(--c-ink-900);
}

.btn--large {
    min-height: 56px;
    padding: 0.95rem 1.85rem;
    font-size: 1.05rem;
}

.btn .btn__arrow {
    transition: transform 140ms ease;
}
.btn:hover .btn__arrow,
.btn:focus .btn__arrow { transform: translateX(3px); }

.btn .btn__icon {
    flex: 0 0 auto;
    margin-right: -0.05rem;
}

/* --------------------------------------------------------------------
   5. Landing hero
   -------------------------------------------------------------------- */

.landing { margin: 0; }

/* Landing only: the main column drops its 1120px cap and padding so colour
   bands run edge to edge. Every landing section caps its own text. */
.app-main.app-main--bleed {
    max-width: none;
    padding: 0;
}

/* TEMPORARY: beta-launch banner. Full-bleed strip above the hero in the
   brand's field-green / end-zone-yellow language. Remove this block (and the
   matching markup in templates/auth/landing.html) to retire the callout. */
.landing-beta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.6rem;
    padding: 0.7rem 1.25rem;
    background: var(--c-field-900);
    color: #ffffff;
    text-align: center;
    font-size: 0.95rem;
    line-height: 1.35;
}
.landing-beta__tag {
    flex: none;
    padding: 0.15rem 0.6rem;
    border-radius: 999px;
    background: var(--c-endzone);
    color: var(--c-ink-900);
    font-family: var(--font-display);
    font-size: 0.8rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}
.landing-beta__text a {
    color: var(--c-endzone);
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 2px;
}
.landing-beta__text a:hover {
    text-decoration: none;
}

/* "You're signed out" on the landing page: a full-width strip under the
   beta banner. The page's main column has no padding here, so the shared
   .pricing-notice box would sit flush against the left edge. */
.landing .landing-flash {
    max-width: none;
    margin: 0;
    padding: 0.7rem 1.25rem;
    border-width: 0 0 1px 0;
    border-radius: 0;
    text-align: center;
}

/* The hero is the field: night turf with mowing stripes, full bleed. The
   drawn play brings its own yard lines, so the band paints none. */
.landing-hero {
    position: relative;
    overflow: hidden;
    padding: 3rem 1.25rem 3.5rem;
    color: var(--c-chalk);
    background: repeating-linear-gradient(to bottom,
        var(--c-field-900) 0 80px, #093f25 80px 160px);
}

.landing-hero__inner {
    position: relative;
    display: grid;
    gap: 2.5rem;
    max-width: 1120px;
    margin: 0 auto;
    align-items: center;
}

.landing-hero__copy { max-width: 36rem; }

.landing-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    font-family: var(--font-display);
    font-size: var(--t-eyebrow);
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--c-field-700);
    margin: 0 0 1.1rem 0;
    line-height: 1;
}
.landing-eyebrow::before {
    /* Small accent tab before the eyebrow text. ``currentColor`` ties
       it to the eyebrow's own color so the hero (Electric Blue) and the
       inverted steps section (Turf Green) both look intentional. */
    content: "";
    width: 28px;
    height: 3px;
    background: currentColor;
    display: inline-block;
    border-radius: 2px;
}

/* The page's <h1>. Bebas Neue is the scoreboard face; at hero size in
   chalk white it is the loudest thing on the page by design. */
.landing-hero__title {
    margin: 0 0 1.25rem 0;
    font-family: var(--font-display);
    font-weight: 400;          /* Bebas Neue ships single-weight */
    font-size: clamp(2.9rem, 4.4vw + 1rem, 5.1rem);
    line-height: 0.94;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: #ffffff;
    text-wrap: balance;
}

.landing-hero__accent { color: var(--c-endzone); }

.landing-hero__lede {
    margin: 0 0 1.75rem 0;
    max-width: 34rem;
    font-size: 1.125rem;
    line-height: 1.55;
    color: rgba(250, 250, 247, 0.84);
}

.landing-hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}
/* On turf the brand green button would disappear; the primary action takes
   the end-zone paint, the secondary is an outline in chalk. */
.landing-hero .btn--primary {
    background: var(--c-endzone);
    border-color: var(--c-endzone);
    color: var(--c-ink-900);
}
.landing-hero .btn--primary:hover,
.landing-hero .btn--primary:focus-visible {
    background: var(--c-endzone-deep);
    border-color: var(--c-endzone-deep);
    color: var(--c-ink-900);
}
.landing-hero .btn--ghost {
    color: #ffffff;
    border-color: rgba(255, 255, 255, 0.55);
}
.landing-hero .btn--ghost:hover,
.landing-hero .btn--ghost:focus-visible {
    background: rgba(255, 255, 255, 0.08);
    border-color: #ffffff;
    color: #ffffff;
}
.landing-hero .btn:focus-visible {
    outline: 3px solid #ffffff;
    outline-offset: 3px;
}

.landing-hero__formats {
    margin: 1.5rem 0 0 0;
    max-width: 34rem;
    font-size: 0.9rem;
    line-height: 1.5;
    color: rgba(250, 250, 247, 0.62);
}

/* The trial terms, right under the hero buttons (SEO T-03). */
.landing-hero__trial {
    margin: 0.9rem 0 0 0;
    max-width: 34rem;
    font-size: 0.95rem;
    line-height: 1.5;
    color: rgba(250, 250, 247, 0.85);
}
.landing-hero__trial strong { color: var(--c-endzone); }

/* The demo: the typed call, the play it becomes, and the calls to try. */
.landing-hero__demo {
    display: flex;
    flex-direction: column;
    gap: 1.1rem;
    width: 100%;
    max-width: 30rem;
    margin: 0 auto;
}

.landing-hero__visual { position: relative; }

/* Scoped under .landing-hero__visual: the bare ``.play-card`` selector
   belongs to the print module further down this file. No card chrome —
   the drawing sits straight on the hero turf, its yard lines faded out at
   every edge so they dissolve into the band's own. */
.landing-hero__visual .play-card {
    position: relative;
    width: 100%;
    max-width: 22rem;
    margin: 0 auto;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    color: #ffffff;
}

.landing-hero__visual .play-card__field {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 4 / 5;
    overflow: visible;
    -webkit-mask-image:
        linear-gradient(to bottom, transparent, #000 9%, #000 88%, transparent),
        linear-gradient(to right, transparent, #000 8%, #000 94%, transparent);
    -webkit-mask-composite: source-in;
    mask-image:
        linear-gradient(to bottom, transparent, #000 9%, #000 88%, transparent),
        linear-gradient(to right, transparent, #000 8%, #000 94%, transparent);
    mask-composite: intersect;
}
/* The yard lines, hashes and numbers are context, not content. */
.landing-hero__visual .play-card__field g[stroke="#ffffff"],
.landing-hero__visual .play-card__field g[fill="#ffffff"] { opacity: 0.3; }
.landing-hero__visual .play-card__field g[stroke="#f5c116"] { stroke-width: 2.6; }

.landing-hero__visual .play-card__name {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    margin: 0.35rem 0 0 0;
    justify-content: center;
}
.landing-hero__visual .play-card__name [data-hero-chip] {
    font-family: var(--font-display);
    font-size: 1.5rem;
    letter-spacing: 0.04em;
    color: var(--c-endzone);
}
.landing-hero__visual .play-card__meta {
    font-size: 0.85rem;
    color: rgba(250, 250, 247, 0.7);
}

@media (min-width: 960px) {
    .landing-hero { padding: 4.5rem 2.5rem 5rem; }
    .landing-hero__inner {
        grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
        gap: 3.5rem;
    }
    .landing-hero__demo { margin: 0; justify-self: end; }
}

/* The coach's call — the "words in" half of the hero. Large and readable,
   set like a message the coach just sent. */
.play-prompt {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.95rem 1.1rem;
    background: var(--c-chalk);
    color: var(--c-ink-900);
    border-radius: 16px 16px 16px 4px;
    box-shadow: 0 18px 40px rgba(0, 0, 0, 0.28);
}
.play-prompt__badge {
    flex: none;
    margin-top: 0.1rem;
    padding: 0.2rem 0.55rem;
    border-radius: var(--r-pill);
    background: var(--c-field-900);
    color: #ffffff;
    font-size: 0.75rem;
    font-weight: 700;
}
.play-prompt__text {
    display: inline-flex;
    align-items: center;
    min-width: 0;
    font-size: 1.1rem;
    font-weight: 500;
    line-height: 1.35;
}
.play-prompt__typed {
    display: inline-block;
    max-width: 30em;
    overflow: hidden;
    white-space: nowrap;
    vertical-align: bottom;
}
.play-prompt__caret {
    flex: none;
    width: 2px;
    height: 1.15em;
    margin-left: 2px;
    background: var(--c-field-700);
}

/* The "watch it draw itself" half: each route reveals itself by sliding
   its dash gap off the end of the path, then the arrowhead fades in.
   Pure CSS, looping. The default state is the *finished* play (routes
   drawn, arrows shown) so prefers-reduced-motion lands on a coherent
   static play; the no-preference block below hides them first, then loops
   the draw. */
/* Scoped to ``.landing`` so this self-drawing treatment never leaks onto
   the editor's (or the playbook thumbnails') ``.play-route`` polylines,
   which share the class name but must render as static, fully-drawn
   routes. */
.landing .play-route {
    stroke-dasharray: 240;
    stroke-dashoffset: 0;
}
.landing .play-arrow { opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
    .play-prompt__typed {
        max-width: 0;
        animation: prompt-type 6s ease-in-out infinite;
    }
    .play-prompt__caret {
        animation: prompt-caret 0.9s steps(1, end) infinite;
    }
    .landing .play-route {
        stroke-dashoffset: 240;
        animation: route-draw 6s ease-in-out infinite;
    }
    .landing .play-route--2 { animation-name: route-draw-2; }
    .landing .play-route--3 { animation-name: route-draw-3; }
    .landing .play-arrow {
        opacity: 0;
        animation: arrow-pop 6s ease-in-out infinite;
    }
    .landing .play-arrow--2 { animation-name: arrow-pop-2; }
    .landing .play-arrow--3 { animation-name: arrow-pop-3; }
}

@keyframes prompt-type {
    0%        { max-width: 0; }
    22%, 90%  { max-width: 30em; }
    97%, 100% { max-width: 0; }
}
@keyframes prompt-caret {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0; }
}
/* Each route stays hidden through the typing phase, draws in its own
   window (staggered), holds, then resets just before the loop. */
@keyframes route-draw {
    0%, 30%   { stroke-dashoffset: 240; }
    50%, 95%  { stroke-dashoffset: 0; }
    99%, 100% { stroke-dashoffset: 240; }
}
@keyframes route-draw-2 {
    0%, 36%   { stroke-dashoffset: 240; }
    56%, 95%  { stroke-dashoffset: 0; }
    99%, 100% { stroke-dashoffset: 240; }
}
@keyframes route-draw-3 {
    0%, 42%   { stroke-dashoffset: 240; }
    64%, 95%  { stroke-dashoffset: 0; }
    99%, 100% { stroke-dashoffset: 240; }
}
@keyframes arrow-pop {
    0%, 48%   { opacity: 0; }
    54%, 95%  { opacity: 1; }
    99%, 100% { opacity: 0; }
}
@keyframes arrow-pop-2 {
    0%, 54%   { opacity: 0; }
    60%, 95%  { opacity: 1; }
    99%, 100% { opacity: 0; }
}
@keyframes arrow-pop-3 {
    0%, 62%   { opacity: 0; }
    68%, 95%  { opacity: 1; }
    99%, 100% { opacity: 0; }
}

/* Calls to try — they drive the hero script (type the call, redraw). */
.play-chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    margin: 0;
}
.play-chips__label {
    flex-basis: 100%;
    text-align: center;
    font-size: 0.85rem;
    color: rgba(250, 250, 247, 0.7);
}
.play-chip {
    min-height: 40px;
    padding: 0.4rem 0.95rem;
    font-family: inherit;
    font-size: 0.88rem;
    font-weight: 600;
    color: #ffffff;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.45);
    border-radius: var(--r-pill);
    cursor: pointer;
    transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}
.play-chip:hover { border-color: #ffffff; }
.play-chip:focus-visible { outline: 3px solid #ffffff; outline-offset: 2px; }
.play-chip.is-active {
    background: #ffffff;
    color: var(--c-field-900);
    border-color: #ffffff;
}

/* When the hero script is live it adds ``js-hero`` to <html>: hand the
   card's routes / arrows / prompt over to JS-driven transitions by
   switching off the CSS-only loop. */
.js-hero .landing-hero__visual .play-card .play-route,
.js-hero .landing-hero__visual .play-card .play-arrow { animation: none !important; }
.js-hero .play-prompt__typed {
    animation: none !important;
    max-width: none;
    white-space: normal;   /* JS types char-by-char; let it wrap instead of clip */
    overflow: visible;
}
.js-hero .play-prompt__text { display: block; }
.js-hero .play-prompt__typed { display: inline; }
.js-hero .play-prompt__caret { display: inline-block; vertical-align: text-bottom; }

/* --------------------------------------------------------------------
   5a. Landing — scroll-reveal motion (P1.1)

   Opt-in: the inline script adds ``.reveal-on`` to <html> only when
   motion is allowed and IntersectionObserver exists. With JS off or under
   prefers-reduced-motion the class is absent, so [data-reveal] elements
   render fully visible — the motion is purely additive.
   -------------------------------------------------------------------- */
.reveal-on [data-reveal] {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.6s ease, transform 0.6s ease;
}
.reveal-on [data-reveal].is-visible {
    opacity: 1;
    transform: none;
}
/* Cascade siblings inside the grids / CTA so they arrive in sequence. */
.reveal-on .step[data-reveal]:nth-child(2),
.reveal-on .ai-card[data-reveal]:nth-child(2),
.reveal-on .landing-cta__lede[data-reveal] { transition-delay: 0.08s; }
.reveal-on .step[data-reveal]:nth-child(3),
.reveal-on .ai-card[data-reveal]:nth-child(3),
.reveal-on .landing-cta .btn[data-reveal] { transition-delay: 0.16s; }

/* --------------------------------------------------------------------
   5a2. Landing — full-bleed action-photo band
   A breather between the abstract product hero and the "why it's
   different" pitch: a real game under a field-green scrim. The image
   covers a height-capped band (so a 16:9 shot never dominates on wide
   screens), the overlaid line sits bottom-left within the page gutter,
   and the end-zone hairline mirrors .landing-steps.
   -------------------------------------------------------------------- */

.landing-photo {
    position: relative;
    margin: 0;
    height: clamp(300px, 42vw, 520px);
    overflow: hidden;
    background: var(--c-field-900);
}
.landing-photo::before {
    /* End-zone hairline along the top — ties to .landing-steps. */
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 4px;
    background: var(--c-endzone);
    z-index: 3;
}
.landing-photo__img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 50% 36%;   /* keep the runners, not the sky */
}
.landing-photo__scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    background:
        linear-gradient(90deg,
            rgba(7, 55, 31, 0.88) 0%,
            rgba(7, 55, 31, 0.55) 36%,
            rgba(7, 55, 31, 0.08) 66%,
            rgba(7, 55, 31, 0.00) 100%),
        linear-gradient(0deg,
            rgba(7, 55, 31, 0.45) 0%,
            rgba(7, 55, 31, 0.00) 45%);
}
.landing-photo__copy {
    position: absolute;
    inset: 0;
    z-index: 2;
    max-width: 1120px;
    margin: 0 auto;
    padding: 2.25rem 1.5rem;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
}
.landing-photo .landing-eyebrow { color: var(--c-endzone); }
.landing-photo__line {
    margin: 0;
    max-width: 22ch;
    color: #ffffff;
    font-family: var(--font-display);
    font-size: clamp(1.7rem, 3.4vw + 0.5rem, 2.85rem);
    line-height: 1.02;
    letter-spacing: 0.01em;
    text-shadow: 0 2px 22px rgba(0, 0, 0, 0.45);
}

/* --------------------------------------------------------------------
   5b. Landing — "Not a drawing tool" positioning band
   -------------------------------------------------------------------- */

.landing-versus {
    background: color-mix(in srgb, var(--c-field-900) 4%, #ffffff);
    border-top: 1px solid var(--c-line);
    border-bottom: 1px solid var(--c-line);
    padding: 4rem 1.25rem;
}
.landing-versus__inner {
    max-width: 1120px;
    margin: 0 auto;
}
.landing-versus__head {
    max-width: 40rem;
    margin: 0 0 2rem 0;
}
.landing-versus__title {
    font-size: var(--t-section);
    line-height: 1.1;
    letter-spacing: -0.02em;
    font-weight: 800;
    margin: 0 0 0.85rem 0;
    color: var(--c-ink-900);
}
.landing-versus__lede {
    margin: 0;
    color: var(--c-ink-600);
    font-size: 1.05rem;
}

.versus-grid {
    display: grid;
    gap: 0.6rem;
}
.versus-row {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.5rem;
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
    padding: 1rem 1.1rem;
    box-shadow: var(--shadow-soft);
}
.versus-row--header { display: none; }
.versus-cell { font-size: 0.95rem; }
.versus-cell--label {
    font-weight: 700;
    color: var(--c-ink-900);
}
.versus-cell--them {
    color: var(--c-ink-600);
}
.versus-cell--them::before { content: "Drawing tools: "; font-weight: 700; color: var(--c-ink-400); }
.versus-cell--us {
    color: var(--c-ink-900);
    font-weight: 600;
}
.versus-cell--us::before {
    content: "Playwrist: ";
    font-weight: 700;
    color: var(--c-endzone-deep);
}

@media (min-width: 760px) {
    .versus-grid { gap: 0; }
    .versus-row {
        grid-template-columns: 1.1fr 1fr 1fr;
        align-items: center;
        gap: 1.25rem;
        border-radius: 0;
        border-bottom: none;
        box-shadow: none;
        padding: 1.1rem 1.35rem;
    }
    .versus-row:first-child { border-radius: var(--r-card) var(--r-card) 0 0; }
    .versus-row:last-child {
        border-bottom: 1px solid var(--c-line);
        border-radius: 0 0 var(--r-card) var(--r-card);
    }
    .versus-row--header {
        display: grid;
        background: var(--c-field-900);
        color: #ffffff;
    }
    .versus-row--header .versus-cell {
        font-size: 0.9rem;
        font-weight: 700;
    }
    .versus-row--header .versus-cell--us { color: var(--c-endzone); }
    /* "Drawing tools" header sat as dark-gray ink on the dark-green header
       band, so it read as nearly invisible. Give it a legible light chip that
       mirrors the "Playwrist" box geometry — neutral white for the
       competitor column, yellow for ours. */
    .versus-row--header .versus-cell--them {
        color: #ffffff;
        background: color-mix(in srgb, #ffffff 14%, transparent);
        border: 1px solid color-mix(in srgb, #ffffff 24%, transparent);
        border-radius: 8px;
        padding: 0.55rem 0.75rem;
        margin: -0.55rem -0.2rem;
    }
    /* On wide screens the column headers carry the labels, so drop the
       per-cell "Drawing tools:" / "Playwrist:" prefixes. */
    .versus-cell--them::before,
    .versus-cell--us::before { content: none; }
    .versus-cell--us {
        background: color-mix(in srgb, var(--c-endzone) 12%, #ffffff);
        border-radius: 8px;
        padding: 0.55rem 0.75rem;
        margin: -0.55rem -0.2rem;
    }
}

/* --------------------------------------------------------------------
   6. Landing — pillars, steps, final CTA
   -------------------------------------------------------------------- */

.landing-section {
    padding: 4rem 1.25rem;
    max-width: 1120px;
    margin: 0 auto;
}

.landing-section__head {
    max-width: 38rem;
    margin: 0 0 2.25rem 0;
}

.landing-section__title {
    font-size: var(--t-section);
    line-height: 1.1;
    letter-spacing: -0.02em;
    font-weight: 800;
    margin: 0 0 0.85rem 0;
    color: var(--c-ink-900);
}

.landing-section__lede {
    margin: 0;
    color: var(--c-ink-600);
    font-size: 1.05rem;
}

/* About page — contact + version cards under the "Who we are" lede. */
.about-meta {
    display: grid;
    gap: 1rem;
    margin-top: 2rem;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}

.about-card {
    border: 1px solid var(--c-line, #e2e8e6);
    border-radius: 12px;
    padding: 1.25rem 1.5rem;
    background: #fff;
}

.about-card__title {
    margin: 0 0 0.4rem;
    font-size: 1.1rem;
}

.about-card__body {
    margin: 0;
    color: var(--c-ink-600);
}

/* The About page's story and "what it does" paragraphs under the lede. */
.about-story {
    max-width: 44rem;
    margin: 0 0 2.5rem;
    color: var(--c-ink-600);
    line-height: 1.65;
}
.about-story p { margin: 0 0 1rem; }
.about-story__title {
    margin: 2rem 0 0.75rem;
    font-size: 1.25rem;
    color: var(--c-ink-900);
}

/* Everything else the product does: plain rows on hairlines, two columns
   on wide screens. The AI story has its own band above; these are the
   supporting features, so they stay quiet. */
.feature-list {
    display: grid;
    grid-template-columns: 1fr;
    column-gap: 3rem;
    border-top: 1px solid var(--c-line);
}
.feature {
    display: grid;
    grid-template-columns: 2rem 1fr;
    align-content: start;
    column-gap: 1rem;
    padding: 1.35rem 0;
    border-bottom: 1px solid var(--c-line);
}
.feature__icon {
    grid-row: span 2;
    padding-top: 0.1rem;
    color: var(--c-field-700);
}
.feature__icon svg { display: block; width: 24px; height: 24px; }
.feature__title {
    margin: 0 0 0.3rem 0;
    font-size: 1.05rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--c-ink-900);
}
.feature__body {
    margin: 0;
    font-size: 0.97rem;
    line-height: 1.55;
    color: var(--c-ink-600);
}
@media (min-width: 760px) {
    .feature-list { grid-template-columns: 1fr 1fr; }
}

.landing-trust {
    padding: 3.25rem 1.25rem;
    background: var(--c-chalk);
    border-top: 1px solid var(--c-line);
}
.landing-trust__inner {
    max-width: 56rem;
    margin: 0 auto;
    text-align: center;
}
.landing-trust__eyebrow {
    margin: 0 0 1.25rem 0;
    font-family: var(--font-display);
    font-size: var(--t-eyebrow);
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--c-ink-400);
}
.trust-leagues {
    list-style: none;
    margin: 0 0 1.75rem 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 0.6rem 1rem;
}
.trust-leagues li {
    font-family: var(--font-display);
    font-size: 1.15rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--c-field-700);
    padding: 0.3rem 0.85rem;
    border: 1px solid var(--c-line);
    border-radius: var(--r-pill);
    background: var(--c-paper);
}
.trust-note {
    margin: 0;
    color: var(--c-ink-600);
    font-size: 1rem;
    line-height: 1.6;
}
.trust-note strong { color: var(--c-ink-900); }

/* --------------------------------------------------------------------
   Pricing page — Pro / Max / Enterprise tier cards
   -------------------------------------------------------------------- */
.pricing-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 1.1rem;
    max-width: 1040px;
    margin: 2rem auto 0;
    align-items: stretch;
}

.price-card {
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
    padding: 1.6rem 1.5rem 1.7rem;
    box-shadow: var(--shadow-soft);
    display: flex;
    flex-direction: column;
    position: relative;
}
.price-card--featured {
    border-color: var(--c-field-700);
    border-width: 2px;
    box-shadow: var(--shadow-card);
}
.price-card--enterprise {
    background: var(--c-field-50);
}

.price-card__name {
    margin: 0 0 0.5rem 0;
    font-family: var(--font-display);
    font-size: 1.6rem;
    letter-spacing: 0.02em;
    color: var(--c-ink-900);
}

.price-card__price {
    margin: 0 0 0.75rem 0;
    display: flex;
    align-items: baseline;
    gap: 0.35rem;
}
.price-card__amount {
    font-size: 2.4rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--c-ink-900);
    line-height: 1;
}
.price-card__amount--contact {
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--c-field-700);
}
.price-card__interval {
    color: var(--c-ink-400);
    font-size: var(--t-meta);
}

.price-card__blurb {
    margin: 0 0 1rem 0;
    color: var(--c-ink-600);
    line-height: 1.5;
    font-size: 0.95rem;
}

.price-card__features {
    list-style: none;
    margin: 0 0 1.5rem 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    flex: 1 1 auto;
}
.price-card__features li {
    position: relative;
    padding-left: 1.4rem;
    color: var(--c-ink-900);
    font-size: 0.95rem;
    line-height: 1.4;
}
.price-card__features li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.4em;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: var(--c-field-300);
}

/* Sprint 3: outcome-anchored breakdown nested inside the credit row
   on each price card. The bullet styling resets so the breakdown
   reads as a sub-list rather than a flat feature list. */
.price-card__outcomes {
    list-style: none;
    margin: 0.45rem 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.18rem;
}
.price-card__outcomes li {
    padding-left: 0;
    font-size: 0.84rem;
    font-weight: 500;
    color: var(--c-ink-600, #475569);
}
.price-card__outcomes li::before {
    /* Override the green dot — the sub-list reads tighter as plain
       text. */
    content: none;
}
.price-card__outcome-note {
    margin: 0.35rem 0 0;
    font-size: 0.78rem;
    line-height: 1.4;
    color: var(--c-ink-400, #94a3b8);
    font-style: italic;
}

.price-card__cta {
    width: 100%;
    justify-content: center;
}

.price-card__cta-form {
    /* Wraps the POST button for logged-in users; should not change the
       card's visual rhythm vs. the anchor variant used for anonymous
       visitors. */
    margin: 0;
}

/* The skip-the-trial offer, directly beneath the primary CTA: a solid
   end-zone yellow button (.btn--alert) under the green one, owner 2026-09-29.
   Two markup variants, ONE appearance on purpose: authed coaches get a second
   submit button in the plan's POST form (it sends ``skip_trial=1``), anonymous
   visitors get an ``<a>`` that rides the choice into signup on the query
   string. Same offer either way, so a coach must not perceive a difference.
   The credits promise sits under it as a note, so the button label stays one
   line in a card this narrow. */
.price-card__skip-trial-btn {
    margin-top: 0.6rem;
}
.price-card__skip-trial-note {
    margin: 0.45rem 0 0;
    font-size: 0.8rem;
    line-height: 1.45;
    color: var(--c-ink-600);
    text-align: center;
}

.pricing-error,
.pricing-success,
.pricing-notice {
    margin: 0 0 1.25rem 0;
    padding: 0.75rem 1rem;
    border: 1px solid;
    border-radius: var(--r-card);
    font-size: 0.95rem;
    max-width: 56rem;
}

.pricing-error {
    background: var(--c-error-bg);
    border-color: var(--c-error-line);
    color: var(--c-error-ink);
}

.pricing-success {
    /* Field-green wash — Checkout returned successfully, user is in. */
    background: var(--c-field-50);
    border-color: var(--c-field-300);
    color: var(--c-field-900);
}
.pricing-success a {
    color: var(--c-field-700);
    font-weight: 600;
    margin-left: 0.25rem;
}

.pricing-notice {
    /* End-zone yellow wash — payment didn't complete, gentle nudge. */
    background: color-mix(in srgb, var(--c-endzone) 18%, #ffffff);
    border-color: var(--c-endzone-deep);
    color: var(--c-ink-900);
}

/* Sign-up plan selector — radio cards reusing the auth-form field rhythm. */
.signup-plans {
    border: none;
    margin: 0;
    padding: 0;
}
.signup-plans legend {
    font-weight: 600;
    color: var(--c-ink-900);
    padding: 0;
    margin-bottom: 0.35rem;
}
/* Billing-interval toggle inside the sign-up plan picker. Reuses the
   .billing-toggle pills from the pricing page; just tightens the spacing so
   it sits neatly above the stacked tier cards. */
.billing-toggle--signup {
    justify-content: flex-start;
    gap: 0.6rem;
    margin: 0.35rem 0 0.85rem;
    flex-wrap: wrap;
}
/* The narrow sign-up card can be as little as ~240px wide, so let the two
   pills wrap instead of forcing the card (and the page) into a horizontal
   scroll at 320px. */
.billing-toggle--signup .billing-toggle__switch {
    flex-wrap: wrap;
    justify-content: center;
}

/* Sign-up plan picker — one selectable card per tier (Playbook / Coach /
   Program), stacked in the narrow auth card, with a Monthly/Annual toggle.
   Carries the pricing page's color language: featured green outline on
   Coach, a field-green wash on the checked card, and the yellow "2 months free" save line under the annual price. */
.plan-choice {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.7rem;
    margin: 0.4rem 0 0.5rem;
}
.plan-choice__option {
    position: relative;
    display: block;
    cursor: pointer;
}
.plan-choice__option input {
    position: absolute;
    opacity: 0;
    inset: 0;
    margin: 0;
    cursor: pointer;
}
.plan-choice__body {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    border: 1.5px solid var(--c-line);
    border-radius: var(--r-card);
    padding: 0.85rem 0.95rem;
    background: var(--c-paper);
    transition: border-color 140ms ease, background 140ms ease,
        box-shadow 140ms ease;
}
.plan-choice__option--featured .plan-choice__body {
    border-color: var(--c-field-700);
    border-width: 2px;
}
.plan-choice__option:hover .plan-choice__body {
    border-color: var(--c-field-300);
}
.plan-choice__option input:checked + .plan-choice__body {
    border-color: var(--c-field-700);
    background: var(--c-field-50);
    box-shadow: var(--shadow-soft);
}
.plan-choice__option input:focus-visible + .plan-choice__body {
    outline: 2px solid var(--c-field-300);
    outline-offset: 2px;
}
.plan-choice__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.2rem 0.75rem;
}
.plan-choice__name {
    font-family: var(--font-display);
    font-size: 1.2rem;
    letter-spacing: 0.02em;
    font-weight: 700;
    color: var(--c-ink-900);
}
.plan-choice__price {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.25rem;
}
/* Keep the dollar figure itself intact; only the "/ mo, billed annually"
   suffix may wrap, so the price never forces the card past a 320px viewport. */
.plan-choice__amount {
    white-space: nowrap;
}
.plan-choice__amount {
    font-size: 1.5rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1;
    color: var(--c-ink-900);
}
.plan-choice__interval {
    font-size: var(--t-meta);
    color: var(--c-ink-400);
}
.plan-choice__save {
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--c-field-700);
}
/* Monthly view has no annual discount to advertise — keep the line's
   height so cards don't jump when toggling, just hide the text. */
.plan-choice.is-monthly .plan-choice__save {
    visibility: hidden;
}
.plan-choice__meta {
    font-size: var(--t-meta);
    color: var(--c-ink-600);
}
/* Sprint 3: outcome-anchored breakdown next to credit count in
   the signup plan picker. */
.plan-choice__outcomes {
    display: block;
    margin-top: 0.18rem;
    font-size: 0.78rem;
    color: var(--c-ink-400, #94a3b8);
    line-height: 1.4;
}

/* AI-credit indicator (editor toolbar). Populated by the editor bundle. */
.credits-indicator {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    margin: 0.75rem 0;
    padding: 0.4rem 0.75rem;
    border: 1px solid var(--c-line);
    border-radius: var(--r-pill);
    background: var(--c-paper);
    font-size: var(--t-meta);
}
.credits-indicator[data-state="exhausted"] {
    border-color: var(--c-error-line);
    background: var(--c-error-bg);
}
.credits-indicator__label {
    color: var(--c-ink-700, var(--c-ink-900));
    font-weight: 600;
}
.credits-indicator__label--empty {
    color: var(--c-error-ink);
}
.credits-indicator__cta {
    font-weight: 600;
    color: var(--c-field-700);
    white-space: nowrap;
}
/* Sprint 3: outcome-anchored secondary line under the credit count.
   Visually quiet — italic + smaller + muted — so it reads as
   supplemental info, not a primary status. */
.credits-indicator__outcomes {
    margin-left: 0.55rem;
    color: var(--c-ink-400, #94a3b8);
    font-size: 0.78rem;
    font-style: italic;
    white-space: nowrap;
}

/* "How it works" — three numbered steps with end-zone-yellow numerals. */
.landing-steps {
    background: var(--c-field-900);
    color: #ffffff;
    border-radius: 0;
    margin: 0;
    padding: 4rem 1.25rem;
    position: relative;
    overflow: hidden;
}
.landing-steps::before {
    /* Decorative yard-line band along the top, end-zone yellow. */
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: var(--c-endzone);
}

.landing-steps::after {
    /* Cinematic vignette — deepens the turf toward the edges so the
       glowing routes and step cards float. Sits above the turf motif
       (z 0) but below the content (z 2). */
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    background: radial-gradient(120% 75% at 50% 0%,
                transparent 42%, rgba(0, 0, 0, 0.38) 100%);
}

.landing-steps__inner {
    position: relative;
    z-index: 2;
    max-width: 1120px;
    margin: 0 auto;
}

.landing-steps .landing-eyebrow { color: var(--c-endzone); }
.landing-steps .landing-section__title { color: #ffffff; }
.landing-steps .landing-section__lede { color: rgba(255, 255, 255, 0.78); }

.step-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 1.5rem;
    counter-reset: steps;
}

.step { position: relative; }

/* Same scoreboard numerals as the pre-game rep band. */
.step__num {
    display: block;
    margin-bottom: 0.9rem;
    font-family: var(--font-display);
    font-size: 3.5rem;
    line-height: 0.9;
    color: var(--c-endzone);
}

/* Tiny self-drawing route accent in each step card's corner — the brand
   motion motif, threaded through "how it works". Reuses the .play-route /
   .play-arrow keyframes (dasharray inherited from .play-route). */
.step__route {
    position: absolute;
    top: 1.45rem;
    right: 1.35rem;
    width: 64px;
    height: 24px;
}
.step__route .play-route {
    stroke: var(--c-endzone);
    stroke-width: 3;
    filter: drop-shadow(0 0 4px rgba(245, 193, 22, 0.5));
}
.step__route .play-arrow { fill: var(--c-endzone); }

.step__title {
    margin: 0 0 0.4rem 0;
    font-size: 1.15rem;
    font-weight: 700;
    color: #ffffff;
    letter-spacing: -0.01em;
}

.step__body {
    margin: 0;
    color: rgba(255, 255, 255, 0.78);
    line-height: 1.55;
    font-size: 0.98rem;
}

/* Final CTA band — chalk surface with a single dominant action. */
/* FAQ (SEO T-03): plain HTML, readable without JavaScript. */
.landing-faq__list {
    display: grid;
    gap: 1rem;
    max-width: 48rem;
    margin: 1.5rem auto 0;
}
.landing-faq__item {
    padding: 1rem 1.25rem;
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
    background: var(--c-paper);
}
.landing-faq__q {
    margin: 0 0 0.4rem 0;
    font-size: 1.05rem;
    color: var(--c-ink-900);
}
.landing-faq__a {
    margin: 0;
    line-height: 1.55;
    color: var(--c-ink-600);
}

.landing-cta {
    padding: 4.5rem 1.25rem 5rem;
    text-align: center;
    background: var(--c-chalk);
    position: relative;
}

.landing-cta__title {
    margin: 0 0 0.75rem 0;
    font-size: var(--t-section);
    line-height: 1.1;
    letter-spacing: -0.02em;
    font-weight: 800;
    color: var(--c-ink-900);
}

.landing-cta__lede {
    margin: 0 auto 1.75rem auto;
    max-width: 34rem;
    color: var(--c-ink-600);
    font-size: 1.05rem;
}

/* Primary + secondary CTA, centered and wrapping on narrow screens. */
.landing-cta__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.6rem;
}

/* Pricing transparency teaser under the CTA (P2). */
.landing-cta__pricing {
    margin: 1.1rem 0 0 0;
    font-size: 0.92rem;
    color: var(--c-ink-600);
}
.landing-cta__pricing strong { color: var(--c-ink-900); }

/* --------------------------------------------------------------------
   Landing — game-day payoff: wristband mockup (P1.2, wristband half)
   -------------------------------------------------------------------- */
.landing-wristband {
    background: color-mix(in srgb, var(--c-field-900) 4%, #ffffff);
    border-top: 1px solid var(--c-line);
    border-bottom: 1px solid var(--c-line);
    padding: 4rem 1.25rem;
}
.landing-wristband__inner {
    max-width: 1120px;
    margin: 0 auto;
    display: grid;
    gap: 2.5rem;
    align-items: center;
}
.landing-wristband__points {
    margin: 1.25rem 0 0 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: 0.5rem;
}
.landing-wristband__points li {
    position: relative;
    padding-left: 1.4rem;
    color: var(--c-ink-700, var(--c-ink-600));
}
.landing-wristband__points li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.5em;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 2px;
    background: var(--c-endzone);
}
.landing-wristband__visual {
    display: flex;
    justify-content: center;
}

/* The artifact: an elastic band (two straps) framing a laminated play
   sheet. Tilted slightly so it reads as a worn wristband, not a table. */
.wristband {
    width: 300px;
    max-width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    transform: rotate(3deg);
    filter: drop-shadow(0 18px 30px rgba(7, 24, 14, 0.22));
}
.wristband__strap {
    width: 72%;
    height: 22px;
    background: linear-gradient(var(--c-field-700), var(--c-field-900));
    border-radius: 6px;
}
.wristband__sheet {
    width: 100%;
    background: #ffffff;
    border: 1px solid var(--c-line);
    border-radius: 12px;
    padding: 0.85rem;
    margin: -4px 0;
    z-index: 1;
}
.wristband__title {
    font-family: var(--font-display);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-size: 0.74rem;
    color: var(--c-field-900);
    padding-bottom: 0.5rem;
    margin-bottom: 0.55rem;
    border-bottom: 2px solid var(--c-endzone);
}
.wristband__grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.4rem;
}
.wristband__cell {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    border: 1px solid var(--c-line);
    border-radius: 6px;
    padding: 0.3rem 0.35rem;
    background: var(--c-paper);
}
.wristband__num {
    font-family: var(--font-display);
    font-size: 0.92rem;
    color: var(--c-field-700);
    line-height: 1;
}
.wristband__route { width: 22px; height: 24px; display: block; }

@media (min-width: 760px) {
    .landing-wristband__inner { grid-template-columns: 1.05fr 0.95fr; }
}

/* --------------------------------------------------------------------
   7. Playbook list (entry point for 2-tap navigation)
   -------------------------------------------------------------------- */

.playbook-list__header {
    /* Title + "+ New playbook" button on the same row. Stacks on
       narrow screens so the button doesn't crowd the heading. */
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin: 0 0 1.25rem 0;
}

.playbook-list__title {
    margin: 0;
    font-size: clamp(1.5rem, 2vw + 1rem, 2rem);
    letter-spacing: -0.02em;
    font-weight: 800;
}

.playbook-list__new {
    /* Slightly smaller than a hero CTA — utilitarian in-page action. */
    min-height: 40px;
    padding: 0.45rem 1.1rem;
    font-size: 0.95rem;
}

.playbook-list__empty-card {
    margin: 2rem auto;
    padding: 2.5rem 1.75rem;
    max-width: 36rem;
    text-align: center;
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
    box-shadow: var(--shadow-soft);
}

.playbook-list__empty-title {
    margin: 0 0 0.65rem 0;
    font-size: clamp(1.25rem, 1.5vw + 0.8rem, 1.5rem);
    line-height: 1.2;
    letter-spacing: -0.015em;
    font-weight: 800;
    color: var(--c-ink-900);
}

.playbook-list__empty-lede {
    margin: 0 auto 1.5rem auto;
    max-width: 28rem;
    color: var(--c-ink-600);
    font-size: 1rem;
    line-height: 1.5;
}

.playbook-list__empty-actions {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.65rem;
}

.playbook-list__header-actions {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

/* --------------------------------------------------------------------
   Row actions (rename / duplicate / delete on lists + detail header)
   B-016 + B-009
   -------------------------------------------------------------------- */

/* A row of small, low-emphasis action controls. Hangs off the bottom
   of a playbook / play list item, or sits as a divider strip on the
   playbook detail page. Designed so the primary tap target (the link
   to the item / editor) keeps its full clickable area — the row sits
   *below* the link inside the same <li>. */
.row-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    padding: 0.45rem 1rem 0.65rem;
    border-top: 1px solid var(--c-line-soft);
}

.row-actions--playbook,
.row-actions--gameday {
    /* On the detail page we render the actions row as a standalone
       strip, not a footer to a list item; tighten the borders so it
       reads as a divider between the header and the play list.
       --gameday shares the same divider-strip styling as --playbook;
       it's a distinct modifier only so the class name matches the
       group's "Game-day actions" semantics. */
    margin: 0.5rem 0 1.25rem;
    padding: 0.5rem 0;
    border-top: 1px solid var(--c-line);
    border-bottom: 1px solid var(--c-line);
}

.row-actions__form {
    /* The action forms need no padding/margin of their own — the
       container's gap handles spacing. */
    margin: 0;
    display: inline-flex;
}

.row-actions__link,
.row-actions__btn {
    font: inherit;
    font-size: 0.82rem;
    font-weight: 500;
    padding: 0.3rem 0.65rem;
    border-radius: var(--r-pill);
    background: transparent;
    border: 1px solid var(--c-line);
    color: var(--c-ink-600);
    text-decoration: none;
    line-height: 1;
    cursor: pointer;
    transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
}

.row-actions__link:hover,
.row-actions__link:focus,
.row-actions__btn:hover,
.row-actions__btn:focus {
    background: var(--c-field-50);
    border-color: var(--c-field-300);
    color: var(--c-field-700);
    text-decoration: none;
}

.row-actions__link--icon {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}
.row-actions__link--icon svg { display: block; }

.row-actions__btn--danger {
    color: var(--c-error-ink);
    border-color: color-mix(in srgb, var(--c-error-line) 60%, var(--c-line));
}
.row-actions__btn--danger:hover,
.row-actions__btn--danger:focus {
    background: var(--c-error-bg);
    border-color: var(--c-error-line);
    color: var(--c-error-ink);
}

/* --------------------------------------------------------------------
   Public share view (B-007)
   --------------------------------------------------------------------
   Renders one card per play with an inline SVG diagram. Layout is a
   responsive grid: 1 column at mobile, 2 at tablet, 3 at desktop.
*/

.public-playbook__notice {
    background: var(--c-field-50);
    border-left: 3px solid var(--c-field-300);
    padding: 0.55rem 0.9rem;
    color: var(--c-field-900);
    border-radius: var(--r-input);
    margin: 0 0 1rem 0;
    font-size: 0.92rem;
}

.public-playbook__expires {
    margin: 0 0 1.25rem 0;
    color: var(--c-ink-600);
    font-size: 0.85rem;
}

.public-playbook__grid {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 1rem;
}

@media (min-width: 600px) {
    .public-playbook__grid {
        grid-template-columns: repeat(2, 1fr);
    }
}
@media (min-width: 1000px) {
    .public-playbook__grid {
        grid-template-columns: repeat(3, 1fr);
    }
}

.public-play-card {
    display: flex;
    flex-direction: column;
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
    overflow: hidden;
    box-shadow: var(--shadow-soft);
}

.public-play-card__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.6rem 0.85rem;
    border-bottom: 1px solid var(--c-line-soft);
}

.public-play-card__name {
    font-weight: 600;
    color: var(--c-ink-900);
    /* Long play names truncate with an ellipsis so they don't shove
       the kind chip off the card. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.public-play-card__field {
    /* Match the SHARED viewBox the server computed for this playbook, supplied
       inline as ``--share-field-aspect``. This was pinned to 53.33 / 100 — the
       full-field portrait box the renderer used to emit — so the card was a tall
       green rectangle with the play in a thin band across its middle. The server
       now frames tightly (see ``shared_print_view_box`` in app/sharing/routes),
       and the card has to follow that shape or the diagram just letterboxes
       inside the old one. The fallback keeps a sane card if the framing guard
       ever declines. */
    background: var(--c-field-700);
    aspect-ratio: var(--share-field-aspect, 53.33 / 100);
    max-height: 360px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.public-play-card__field svg {
    width: 100%;
    height: 100%;
    display: block;
}

.public-play-card__error {
    margin: 0;
    padding: 1rem;
    color: rgba(255, 255, 255, 0.85);
    font-size: 0.85rem;
    text-align: center;
}

/* --------------------------------------------------------------------
   Settings page (player count + positions + rules)
   -------------------------------------------------------------------- */

.auth-card--wide {
    /* The settings page benefits from more horizontal real estate than
       the auth-card default (440px) — position rows live side by side. */
    max-width: 720px;
}

/* Settings fieldsets (design item #9): drop the dated native
   fieldset border + inset legend in favour of the app's clean section
   styling — a quiet eyebrow label over an open group, matching the
   cards elsewhere. Scoped to the settings form so other auth fieldsets
   (signup plan picker, editor panels) are untouched. */
.settings-form fieldset.auth-field {
    border: 0;
    margin: 0 0 1.5rem;
    padding: 0;
}
.settings-form fieldset.auth-field > legend {
    padding: 0;
    margin-bottom: 0.5rem;
    font-family: var(--font-display);
    font-size: 0.95rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--c-ink-900);
}

.settings-positions {
    display: grid;
    gap: 0.65rem;
    margin-top: 0.5rem;
}

.settings-position {
    display: grid;
    /* FOUR columns: slot letter, Label, Schedule label, colour swatches.
       Was three, and adding the schedule-label field without widening this
       broke the row outright — grid auto-placement wrapped the colour block
       onto row 2 in the 64px slot column, reflowing nine 1.55rem swatches into
       a stack and squeezing the new input to 110px. A layout regression no test
       can see, so it is written down here.

       ``minmax(0, 1fr)`` on the two text columns, not ``1fr``: a bare ``1fr``
       has an auto minimum, so a long placeholder would push the colour column
       off the row instead of letting the input shrink. */
    grid-template-columns: 64px minmax(0, 1fr) minmax(0, 1fr) 110px;
    gap: 0.75rem;
    align-items: end;
    padding: 0.75rem 0.85rem;
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-input);
}

.settings-position--inactive {
    /* Rows beyond ``player_count`` still appear so the user's
       customisations are preserved, but are dimmed to signal they are
       not active on the field. */
    opacity: 0.55;
}

.settings-position__slot {
    font-family: var(--font-display);
    font-size: 1.1rem;
    letter-spacing: 0.08em;
    color: var(--c-field-700);
    align-self: center;
    text-transform: uppercase;
}

.settings-position__field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.settings-position__field label {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--c-ink-600);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.settings-position__field input[type="text"] {
    font: inherit;
    padding: 0.5rem 0.65rem;
    border: 1px solid var(--c-line);
    border-radius: var(--r-input);
    min-height: 38px;
}

/* Marker color: a fixed palette of swatches (radio inputs), not a
   free-form color picker. */
.settings-color__title {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--c-ink-600);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.settings-color__swatches {
    display: flex;
    flex-wrap: wrap;
    gap: 0.45rem;
    align-items: center;
    min-height: 38px;
}
.settings-color__swatch {
    appearance: none;
    -webkit-appearance: none;
    width: 1.55rem;
    height: 1.55rem;
    margin: 0;
    border-radius: 50%;
    background: var(--swatch, var(--c-ink-400));
    cursor: pointer;
    border: 2px solid var(--c-paper);
    box-shadow: 0 0 0 1px var(--c-line);
    transition: box-shadow 0.12s ease, transform 0.12s ease;
}
.settings-color__swatch:hover { transform: scale(1.08); }
.settings-color__swatch:checked {
    box-shadow: 0 0 0 2px var(--c-paper), 0 0 0 4px var(--c-ink-900);
}
.settings-color__swatch:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--c-paper), 0 0 0 4px var(--c-field-300);
}

.settings-rules {
    font: inherit;
    padding: 0.65rem 0.8rem;
    border: 1px solid var(--c-line);
    border-radius: var(--r-input);
    background: var(--c-paper);
    color: var(--c-ink-900);
    width: 100%;
    min-height: 8rem;
    resize: vertical;
}
.settings-rules:focus {
    outline: none;
    border-color: var(--c-field-500);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-field-500) 25%, transparent);
}

/* --------------------------------------------------------------------
   Account / billing page
   -------------------------------------------------------------------- */

.account-details {
    margin: 0.5rem 0 0 0;
    padding: 0;
    display: grid;
    gap: 0.5rem;
}

.account-details__row {
    display: grid;
    grid-template-columns: 11rem 1fr;
    gap: 1rem;
    align-items: baseline;
    margin: 0;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--c-line-soft);
}
.account-details__row:last-child { border-bottom: none; }

.account-details__row dt {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--c-ink-600);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.account-details__row dd {
    margin: 0;
    color: var(--c-ink-900);
    font-size: 1rem;
}

.account-pill {
    display: inline-block;
    padding: 0.2rem 0.6rem;
    border-radius: var(--r-pill);
    font-family: var(--font-display);
    letter-spacing: 0.14em;
    font-size: 0.75rem;
    text-transform: uppercase;
    background: var(--c-line-soft);
    color: var(--c-ink-600);
}
.account-pill--active,
.account-pill--trial {
    background: color-mix(in srgb, var(--c-field-300) 22%, #ffffff);
    color: var(--c-field-700);
}
.account-pill--past_due,
.account-pill--pending {
    background: color-mix(in srgb, var(--c-endzone) 22%, #ffffff);
    color: var(--c-ink-900);
}
.account-pill--suspended,
.account-pill--canceled {
    background: var(--c-error-bg);
    color: var(--c-error-ink);
}

.account-actions {
    margin-top: 1.5rem;
    display: grid;
    gap: 1.5rem;
}
.account-actions form { margin: 0; }
.account-actions .auth-field__hint { margin-top: 0.4rem; }

/* "Activate my plan now" affordance shown on /account while trialing, so
   ending the trial early is discoverable without hitting the credit wall or
   the print lock. */
.account-activate {
    margin-top: 1.5rem;
    padding: 1rem 1.25rem;
    background: var(--c-surface-2, #f8fafc);
    border: 1px solid var(--c-border, #e2e8f0);
    border-radius: var(--r-tile, 0.75rem);
}
.account-activate__lede {
    margin: 0 0 0.85rem;
    font-size: 0.9rem;
    line-height: 1.5;
    color: var(--c-ink-700, #475569);
}
.account-activate form { margin: 0; }

.playbook-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0.65rem;
}

.playbook-list__item {
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
    transition: border-color 140ms ease, box-shadow 140ms ease;
}
.playbook-list__item:hover {
    border-color: var(--c-field-300);
    box-shadow: var(--shadow-soft);
}

.playbook-list__link {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0 0 0.9rem;
    color: inherit;
    min-height: 44px;
    overflow: hidden;
    border-radius: var(--r-card);
}

.playbook-list__link:hover,
.playbook-list__link:focus {
    background: var(--c-field-50);
    text-decoration: none;
}

/* Cover thumbnail (design items #6 / #8): the first play's framed SVG
   gives each playbook card visual identity and fills the list's dead
   space. Landscape tile flush to the card's top edge. */
.playbook-list__cover {
    display: block;
    width: 100%;
    aspect-ratio: 27 / 10;
    /* Match the SVG field fill exactly so the wide banner's letterbox
       bands blend into the grass instead of showing a second green. */
    background: #2e7d32;
    overflow: hidden;
    margin-bottom: 0.6rem;
}
.playbook-list__cover:empty {
    /* No plays yet → a quiet green band instead of a broken-looking gap. */
    aspect-ratio: 27 / 10;
}
.playbook-list__cover svg {
    display: block;
    width: 100%;
    height: 100%;
}
.playbook-list__name,
.playbook-list__meta {
    padding-left: 1.1rem;
    padding-right: 1.1rem;
}

.playbook-list__name {
    font-weight: 600;
    font-size: 1rem;
    color: var(--c-ink-900);
}

.playbook-list__meta {
    color: var(--c-ink-400);
    font-size: var(--t-meta);
}

.playbook-list__empty {
    padding: 1rem;
    color: var(--c-ink-600);
    text-align: center;
}

/* --------------------------------------------------------------------
   8. Playbook detail
   -------------------------------------------------------------------- */

.playbook-detail__breadcrumbs {
    margin: 0 0 0.75rem 0;
    font-size: var(--t-meta);
    color: var(--c-ink-600);
}

.playbook-detail__title {
    margin: 0 0 1rem 0;
    font-size: clamp(1.4rem, 1.8vw + 1rem, 1.75rem);
    letter-spacing: -0.015em;
    font-weight: 800;
}

/* Tiny "5v5" / "6v6" / "7v7" chip next to the playbook title.
   Surfaces the playbook-level player_count so a coach sees the
   roster size at a glance. */
.playbook-detail__player-count {
    display: inline-block;
    margin-left: 0.5rem;
    padding: 0.1rem 0.55rem;
    border-radius: 999px;
    background: var(--c-ink-100, #eef0f3);
    color: var(--c-ink-700, #4b5563);
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    vertical-align: middle;
}

/* Tag filter chips on the playbook detail (B-049 / Req 8.5). */
.play-tag-filter {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin: 0 0 1rem 0;
}
.play-sort {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 1rem 0;
    font-size: 0.85rem;
    color: var(--c-field-700, #475569);
}
.play-sort__label {
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: 0.75rem;
}
.play-sort select {
    padding: 0.3rem 0.55rem;
    border: 1px solid var(--c-line, #cbd5e1);
    border-radius: 0.3rem;
    background: var(--c-paper, #ffffff);
    font-size: 0.85rem;
}

/* Print presets (PM_REVIEW polish). */
.print-presets-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    margin: 0.4rem 0;
}
.print-presets-save {
    display: flex;
    gap: 0.4rem;
    margin-top: 0.5rem;
}
.print-presets-save input[type="text"] {
    flex: 1;
    padding: 0.3rem 0.55rem;
    border: 1px solid var(--c-line, #cbd5e1);
    border-radius: 0.3rem;
    font-size: 0.85rem;
}
.play-tag-chip {
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--c-field-700);
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-pill);
    padding: 0.3rem 0.8rem;
}
.play-tag-chip:hover,
.play-tag-chip:focus {
    border-color: var(--c-field-300);
    text-decoration: none;
}
.play-tag-chip--active {
    background: var(--c-field-700);
    border-color: var(--c-field-700);
    color: #ffffff;
}

.play-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0.5rem;
}

.play-list__item {
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
}

.play-list__link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 1rem 1.1rem;
    color: inherit;
    min-height: 44px;
}

.play-list__link:hover,
.play-list__link:focus {
    background: var(--c-field-50);
    text-decoration: none;
}

.play-list__name { font-weight: 500; }

.play-list__kind {
    font-family: var(--font-display);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--c-field-700);
    background: var(--c-field-50);
    padding: 0.25rem 0.55rem;
    border-radius: 6px;
}

/* P1-4 C (post-save): "Needs review" chip on a saved play whose generated
   note carries an Auto-QA section. Shares the geometry of
   ``.play-list__kind`` so the two sit together as a chip row, but takes a
   warning palette so it reads as an advisory rather than a category label. */
/* READY FOR YOUR INPUT panel — the AI's unresolved flags for one play, shown
   under the editor's transport strip. Same amber "attention, not error" palette
   as the list chip above, so the two read as one feature. */
.editor-review {
    margin: 0.75rem 0 0;
    padding: 0.75rem 0.9rem;
    background: #fff9ef;
    border: 1px solid #f0c268;
    border-left-width: 4px;
    border-radius: 8px;
}
.editor-review__title {
    margin: 0 0 0.4rem;
    font-family: var(--font-display);
    font-size: 0.95rem;
    letter-spacing: 0.02em;
    color: #8a5300;
}
.editor-review__list {
    margin: 0 0 0.6rem;
    padding-left: 1.1rem;
    font-size: 0.88rem;
    line-height: 1.45;
    color: var(--c-ink-900);
}
.editor-review__list li + li { margin-top: 0.2rem; }
.editor-review__form { margin: 0; }
.editor-review__check {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.85rem;
    color: #8a5300;
    cursor: pointer;
}
/* Shown only when the background POST fails. The tick is reverted, so the
   panel and the message have to agree that nothing happened. */
.editor-review__error {
    margin: 0.35rem 0 0;
    font-size: 0.8rem;
    font-weight: 600;
    color: #b91c1c;
}

.play-list__review-chip {
    font-family: var(--font-display);
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: #8a5300;
    background: #fff4e0;
    border: 1px solid #f0c268;
    padding: 0.25rem 0.55rem;
    border-radius: 6px;
    white-space: nowrap;
}

.play-list__empty {
    padding: 1rem;
    color: var(--c-ink-600);
    text-align: center;
}

/* B-027: thumbnail grid variant for the playbook detail.
   Sizing strategy (post-iteration):
   * Desktop: ``minmax(220px, 1fr)`` auto-fills the available width.
     On a typical 1440px monitor that's 5-6 columns; on 1920px+ it
     opens up to 7-8. Combined with ``.app-main--wide`` releasing the
     1120px content cap, a 12-play playbook fills the row(s) with
     no leftover side-margins.
   * Mobile: an explicit ``repeat(1, 1fr)`` rule below (under the
     ``max-width: 599px`` block) forces a single column so each play
     reads as a hero card on a phone screen.

   Tile content stays compact: padded thumbnail, one-line name, and
   the offense/defense kind chip below the name. */
.play-list--thumbnails {
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 0.75rem;
}

.play-list__item--thumb .play-list__link {
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    gap: 0.35rem;
    padding: 0.55rem 0.65rem 0.7rem;
    min-height: 0;
}

.play-list__thumbnail {
    display: block;
    width: 100%;
    /* Landscape tile matching the framed-play aspect (~1.35) from
       render_svg(frame=True), so the cropped play fills the card with
       no letterboxing (design item #6). Field-green background so any
       residual band blends into the grass rather than showing grey.

       ``--tile-aspect`` lets a surface override it when its content is a
       different shape. The FORMATION catalog does: a formation is a nearly-flat
       row of markers, so in a 1.35 tile the players sat in a thin band with big
       green bands above and below. Those tiles set the variable to their
       section's shared box aspect (~2.6). PLAY tiles never set it and keep 27/20,
       so the playbook grid is untouched. */
    aspect-ratio: var(--tile-aspect, 27 / 20);
    overflow: hidden;
    border-radius: 6px;
    background: #2e7d32;
}
.play-list__thumbnail svg {
    width: 100%;
    height: 100%;
    display: block;
}

.play-list__item--thumb .play-list__name {
    font-size: 0.95rem;
    line-height: 1.2;
    font-weight: 600;
    /* All play names render uppercase per the coach-card chalk-talk
       convention. The underlying data stays mixed-case so the API +
       AI prompts read naturally; CSS handles the display. */
    text-transform: uppercase;
    letter-spacing: 0.02em;
    /* Cap to one line so a long play name doesn't push the card taller
       than its siblings (grid auto-rows give every card the same height
       only when the content stays bounded). */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.play-list__item--thumb .play-list__kind {
    align-self: flex-start;
    font-size: 0.65rem;
    padding: 0.1rem 0.45rem;
}

/* Sprint 3.2: per-tile outcome stats strip. Reads "12 reps · 4.3 avg
   · 1 TD" under the play name so a coach can scan game-day
   performance without opening each play. Only renders for plays that
   actually have results recorded. */
.play-list__stats {
    display: block;
    margin-top: 0.25rem;
    font-size: 0.75rem;
    color: #475569;
    letter-spacing: 0.01em;
}

/* Sprint 10: soft-cap upgrade prompt above the play list. Reads as
   a nudge — coaches who hit the cap mid-edit see the path forward
   without losing context. */
.playbook-detail__upgrade {
    margin: 0.75rem 0;
    padding: 0.6rem 0.9rem;
    border: 1px solid #fed7aa;
    background: #fff7ed;
    border-radius: 0.5rem;
    color: #7c2d12;
    font-size: 0.9rem;
}
.playbook-detail__upgrade strong { color: #9a3412; }
.playbook-detail__upgrade a {
    color: #c2410c;
    font-weight: 600;
    text-decoration: underline;
}

/* AI co-coach: picker landing screen. The drawer opens here so the
   coach picks one of the four AI modes before anything else loads. */
.editor-ai__picker {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0.6rem;
    margin: 0.75rem 0 0.5rem;
}
@media (min-width: 540px) {
    .editor-ai__picker { grid-template-columns: 1fr 1fr; }
}
.editor-ai__mode-card {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.3rem;
    padding: 0.85rem 0.95rem;
    border: 1px solid #cbd5e1;
    border-radius: 0.6rem;
    background: #ffffff;
    cursor: pointer;
    text-align: left;
    transition: border-color 0.1s ease, transform 0.08s ease,
        box-shadow 0.1s ease;
}
.editor-ai__mode-card:hover,
.editor-ai__mode-card:focus-visible {
    border-color: #2563eb;
    box-shadow: 0 4px 10px rgba(37, 99, 235, 0.12);
    transform: translateY(-1px);
}
.editor-ai__mode-icon {
    font-size: 1.3rem;
    line-height: 1;
}
.editor-ai__mode-title {
    font-weight: 700;
    font-size: 0.95rem;
    color: #0f172a;
}
.editor-ai__mode-desc {
    color: #475569;
    font-size: 0.83rem;
    line-height: 1.35;
}
.editor-ai__mode-cost {
    margin-top: 0.15rem;
    color: #0d9488;
    font-size: 0.78rem;
    font-weight: 600;
}

/* "Get in-game advice" fills end-zone yellow, matching the .btn--alert CTA on
   the playbook page (owner: red 2026-09-04, yellow 2026-09-06). Keyed on the
   data attribute the TS panel ALREADY uses to route the click rather than a
   new class, so the styling cannot drift away from the card it is meant to
   mark. The whole card takes the fill because the title is a <span> inside it,
   and tinting one line of a white card would read as a state rather than a
   call to action. Every child colour is restated: the cost line is teal by
   default and would vanish on yellow, and the title/desc were white on the red
   fill — kept that way they would be invisible here (see --c-endzone-ink). */
.editor-ai__mode-card[data-editor-ai-mode="advice"] {
    background: var(--c-endzone);
    border-color: var(--c-endzone);
}
.editor-ai__mode-card[data-editor-ai-mode="advice"]:hover,
.editor-ai__mode-card[data-editor-ai-mode="advice"]:focus-visible {
    background: var(--c-endzone-deep);
    border-color: var(--c-endzone-deep);
    /* The shared hover shadow is blue; on yellow that reads as a stray glow. */
    box-shadow: 0 4px 10px rgba(217, 159, 8, 0.35);
}
.editor-ai__mode-card[data-editor-ai-mode="advice"] .editor-ai__mode-title {
    color: var(--c-ink-900);
}
.editor-ai__mode-card[data-editor-ai-mode="advice"] .editor-ai__mode-desc,
.editor-ai__mode-card[data-editor-ai-mode="advice"] .editor-ai__mode-cost {
    color: var(--c-endzone-ink);
}

/* Per-mode panels — exposed by ``data-editor-ai-view`` on the host
   <section>. The TS panel module sets ``hidden`` on the inactive
   panels; this rule is a defensive fallback so a stylesheet load
   without the bundle still hides everything but the picker. */
.editor-ai[data-editor-ai-view="picker"] [data-editor-ai-panel]:not([data-editor-ai-panel="picker"]) { display: none; }
.editor-ai[data-editor-ai-view="modify"] [data-editor-ai-panel="picker"],
.editor-ai[data-editor-ai-view="critique"] [data-editor-ai-panel="picker"],
.editor-ai[data-editor-ai-view="variations"] [data-editor-ai-panel="picker"],
.editor-ai[data-editor-ai-view="advice"] [data-editor-ai-panel="picker"] { display: none; }

.editor-ai__mode-panel {
    margin-top: 0.5rem;
}
.editor-ai__mode-lede {
    color: #475569;
    font-size: 0.9rem;
    margin: 0 0 0.6rem;
}

/* Back-to-menu button in the drawer header. Hidden on the picker
   itself; the TS module flips ``hidden`` when the view changes. */
.editor-ai__back-btn {
    background: none;
    border: none;
    color: #475569;
    cursor: pointer;
    font-size: 0.85rem;
    padding: 0.1rem 0.35rem;
    margin: 0.2rem 0 0;
    border-radius: 0.25rem;
}
.editor-ai__back-btn:hover {
    background: #f1f5f9;
    color: #0f172a;
}

.editor-ai__legacy-advice-panel { display: none !important; }


/* In-game advice — collapsible block inside the AI co-coach drawer.
   The <details>/<summary> shell gives us a native disclosure widget
   so coaches don't see the textarea until they want it. */
.editor-ai__advice {
    margin-top: 0.75rem;
    border-top: 1px dashed #e2e8f0;
    padding-top: 0.75rem;
}
.editor-ai__advice-summary {
    cursor: pointer;
    font-weight: 600;
    color: #0f172a;
    font-size: 0.9rem;
}
.editor-ai__advice-lede {
    color: #475569;
    font-size: 0.85rem;
    margin: 0.5rem 0;
}
.editor-ai__advice-textarea {
    width: 100%;
    border: 1px solid #cbd5e1;
    border-radius: 0.4rem;
    padding: 0.5rem 0.65rem;
    font-family: inherit;
    font-size: 0.9rem;
    resize: vertical;
    min-height: 4rem;
    box-sizing: border-box;
}
.editor-ai__advice-actions {
    margin-top: 0.4rem;
}
.editor-ai__advice-output {
    margin-top: 0.75rem;
    padding: 0.75rem 1rem;
    background: #f0fdfa;
    border: 1px solid #99f6e4;
    border-radius: 0.5rem;
}
.editor-ai__advice-output[hidden] { display: none; }
.editor-ai__advice-summary-text {
    margin: 0 0 0.5rem;
    color: #134e4a;
    font-style: italic;
    font-size: 0.9rem;
}
.editor-ai__advice-heading {
    margin: 0.6rem 0 0.3rem;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #115e59;
    font-weight: 700;
}
.editor-ai__advice-heading:first-of-type { margin-top: 0; }
.editor-ai__advice-list {
    margin: 0;
    padding-left: 1.1rem;
    color: #134e4a;
    font-size: 0.9rem;
    line-height: 1.4;
}
.editor-ai__advice-item + .editor-ai__advice-item {
    margin-top: 0.3rem;
}
.editor-ai__advice-link {
    color: #0d9488;
    font-weight: 600;
    text-decoration: underline;
}
.editor-ai__advice-empty {
    margin: 0;
    color: #475569;
    font-size: 0.9rem;
}

/* Notes panel — collapsible drawer mounted under the editor
   toolbar. Triggered by the "Notes" button next to AI co-coach;
   auto-opens when an AI call stamps a result into it. */
.editor-notes-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}
.editor-notes-toggle__count {
    background: #1d4ed8;
    color: #ffffff;
    border-radius: 999px;
    padding: 0 0.45rem;
    font-size: 0.7rem;
    font-weight: 700;
    min-width: 1.1rem;
    text-align: center;
}
.editor-notes-toggle__count:empty { display: none; }

.editor-notes-panel {
    background: #ffffff;
    border: 1px solid #e2e8f0;
    border-radius: 0.5rem;
    box-shadow: 0 6px 16px rgba(15, 23, 42, 0.07);
    margin: 0.5rem 0 1rem;
    padding: 0.85rem 1rem 1rem;
    max-width: 38rem;
}
.editor-notes-panel[hidden] { display: none; }

/* Ball-movement panel (editor_ball_panel.ts) — shares the notes panel's
   card chrome; only its own rows/form need styling. The football glyph is
   clickable (it opens this panel), so it advertises that. */
g.play-football { cursor: pointer; }
.editor-ball-panel__hint {
    margin: 0 0 0.6rem;
    color: #475569;
    font-size: 0.85rem;
}
.editor-ball-panel__list {
    list-style: none;
    margin: 0 0 0.75rem;
    padding: 0;
}
.editor-ball-panel__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.3rem 0;
    border-bottom: 1px solid #f1f5f9;
    font-size: 0.9rem;
}
.editor-ball-panel__empty {
    color: #64748b;
    font-size: 0.85rem;
    padding: 0.3rem 0;
}
.editor-ball-panel__remove {
    background: none;
    border: 1px solid #e2e8f0;
    border-radius: 0.3rem;
    color: #b91c1c;
    cursor: pointer;
    font-size: 0.78rem;
    padding: 0.1rem 0.5rem;
}
.editor-ball-panel__form {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.5rem;
}
.editor-ball-panel__field {
    display: flex;
    flex-direction: column;
    font-size: 0.75rem;
    color: #475569;
    gap: 0.15rem;
}
.editor-ball-panel__field select {
    min-width: 5.5rem;
    padding: 0.25rem 0.35rem;
    border: 1px solid #cbd5e1;
    border-radius: 0.3rem;
}
.editor-ball-panel__status { margin: 0.5rem 0 0; font-size: 0.82rem; }
.editor-ball-panel__status[data-kind="error"] { color: #b91c1c; }
.editor-notes-panel__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.5rem;
}
.editor-notes-panel__title {
    margin: 0;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #475569;
    font-weight: 600;
}
.editor-notes-panel__close {
    background: none;
    border: none;
    color: #64748b;
    font-size: 1.3rem;
    line-height: 1;
    cursor: pointer;
    padding: 0.15rem 0.4rem;
    border-radius: 0.25rem;
}
.editor-notes-panel__close:hover {
    background: #f1f5f9;
    color: #0f172a;
}
.editor-notes-panel__proposal {
    /* Banner inside the Notes panel that hosts Accept / Reject for a
       staged AI modify proposal. Sits between the header and the
       notes list so the coach sees the actions immediately when the
       Notes panel auto-opens on an AI result. */
    border: 1px solid #c7d2fe;
    background: #eef2ff;
    border-radius: 0.4rem;
    padding: 0.65rem 0.75rem;
    margin: 0 0 0.75rem;
}
.editor-notes-panel__proposal-lede {
    margin: 0 0 0.5rem;
    font-size: 0.85rem;
    color: #1e293b;
    line-height: 1.3;
}
.editor-notes-panel__proposal-actions {
    display: flex;
    gap: 0.4rem;
    flex-wrap: wrap;
}
.editor-notes-panel__proposal-btn {
    font-size: 0.85rem;
    padding: 0.3rem 0.75rem;
}
.editor-notes-panel__proposal-status {
    margin: 0.4rem 0 0;
    font-size: 0.8rem;
    color: #475569;
    min-height: 1em;
}
.editor-notes-panel__list {
    list-style: none;
    margin: 0 0 0.75rem;
    padding: 0;
    max-height: 18rem;
    overflow-y: auto;
}
.editor-notes-panel__item {
    border-bottom: 1px solid #f1f5f9;
    padding: 0.45rem 0;
}
.editor-notes-panel__item:last-child {
    border-bottom: none;
}
.editor-notes-panel__body {
    margin: 0;
    color: #0f172a;
    font-size: 0.9rem;
    line-height: 1.4;
    white-space: pre-wrap;
}
.editor-notes-panel__matchup {
    display: block;
    margin: 0 0 0.4rem;
    padding: 0.4rem 0.55rem;
    border-left: 3px solid #2563eb;
    background: #eff6ff;
    border-radius: 0 4px 4px 0;
}
.editor-notes-panel__matchup-label {
    display: block;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #1d4ed8;
    margin-bottom: 0.12rem;
}
.editor-notes-panel__matchup-value {
    display: block;
    color: #0f172a;
    font-size: 0.9rem;
    line-height: 1.4;
    white-space: pre-wrap;
}
.editor-notes-panel__row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 0.2rem;
}
.editor-notes-panel__time {
    color: #94a3b8;
    font-size: 0.7rem;
}
.editor-notes-panel__delete {
    background: none;
    border: none;
    color: #94a3b8;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    padding: 0.1rem 0.4rem;
    border-radius: 0.25rem;
}
.editor-notes-panel__delete:hover {
    background: #fee2e2;
    color: #dc2626;
}
.editor-notes-panel__empty {
    color: #64748b;
    font-size: 0.85rem;
    padding: 0.5rem 0;
    list-style: none;
}
.editor-notes-panel__textarea {
    width: 100%;
    border: 1px solid #cbd5e1;
    border-radius: 0.4rem;
    padding: 0.5rem 0.65rem;
    font-family: inherit;
    font-size: 0.9rem;
    resize: vertical;
    min-height: 4rem;
    box-sizing: border-box;
}
.editor-notes-panel__textarea:focus {
    outline: 2px solid #2563eb;
    outline-offset: 1px;
    border-color: #2563eb;
}
.editor-notes-panel__actions {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 0.4rem;
}
.editor-notes-panel__status {
    font-size: 0.8rem;
    color: #16a34a;
}
.editor-notes-panel__status[data-kind="error"] {
    color: #dc2626;
}

/* Football marker: smooth transform so the animator's translate
   updates ease the ball between frames rather than snapping. */
.play-football {
    transition: transform 0.04s linear;
    pointer-events: none;
}
.play-football__body { paint-order: stroke fill; }

/* Ball-path indicators (the declared handoff / pitch, drawn by both
   renderers). Emitted AFTER the markers so a tight handoff isn't hidden
   underneath the two circles it connects — which means it sits on top of them
   in the editor, so it must be transparent to the pointer or it would swallow
   the pointerdown that starts a player drag. Same reason .play-football above
   opts out. Colours are inline presentation attributes (brown core over a white
   casing, legible on both the green field and the off-white print sheet), so no
   fill/stroke belongs here. */
.play-ball-path,
.play-exchange {
    pointer-events: none;
}
.play-exchange__head { paint-order: stroke fill; }

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

/* Mobile: single-column hero layout so each play card reads at a
   glance on a phone screen. ``min-width: 600px`` (and the
   thumbnail's auto-fill at 220px) handles every wider viewport. */
@media (max-width: 599px) {
    .play-list--thumbnails {
        grid-template-columns: 1fr;
        gap: 0.6rem;
    }
}

/* Drag-to-reorder UI for the owner-viewed playbook detail. */
.play-list__reorder-hint {
    margin: 0 0 0.5rem;
    font-size: 0.78rem;
    color: var(--c-ink-700, #4b5563);
    font-style: italic;
}

.play-list--sortable .play-list__item {
    position: relative;
    cursor: grab;
}

.play-list--sortable .play-list__item:active {
    cursor: grabbing;
}

.play-list__drag-handle {
    position: absolute;
    /* Drag handle lives in the top-LEFT corner so it doesn't crowd
       the kebab menu button in the top-right. */
    top: 0.15rem;
    left: 0.2rem;
    z-index: 2;
    padding: 0 0.2rem;
    border-radius: 3px;
    background: rgba(255, 255, 255, 0.8);
    color: var(--c-ink-700, #4b5563);
    font-size: 0.65rem;
    line-height: 1;
    letter-spacing: -0.1em;
    opacity: 0;
    transition: opacity 120ms ease;
    pointer-events: none;
    user-select: none;
}

/* Reveal the drag handle only on hover so it doesn't compete with the
   tiny play name for attention at the new tighter tile size. */
.play-list--sortable .play-list__item:hover .play-list__drag-handle,
.play-list--sortable .play-list__item:focus-within .play-list__drag-handle {
    opacity: 1;
}

.play-list__item--dragging {
    opacity: 0.45;
    outline: 2px dashed var(--c-accent-500, #2e7d32);
    outline-offset: -2px;
}

/* Per-play kebab menu (Results / Edit / Rename / Duplicate / Delete).
 * Replaces a 5-button strip that used to take up half each tile. The
 * "⋯" toggle sits in the tile's top-right corner; the popover anchors
 * to it via absolute positioning. One menu open at a time. */
.play-list__menu {
    position: absolute;
    top: 0.15rem;
    right: 0.2rem;
    z-index: 3;
}

.play-list__menu-toggle {
    appearance: none;
    border: none;
    background: rgba(255, 255, 255, 0.88);
    color: var(--c-ink-700, #4b5563);
    width: 22px;
    height: 22px;
    padding: 0;
    border-radius: 4px;
    font-size: 1rem;
    line-height: 1;
    letter-spacing: 0.06em;
    cursor: pointer;
    opacity: 0;
    transition: opacity 120ms ease, background 120ms ease;
}

.play-list__item:hover .play-list__menu-toggle,
.play-list__item:focus-within .play-list__menu-toggle,
.play-list__menu--open .play-list__menu-toggle {
    opacity: 1;
}

.play-list__menu-toggle:hover,
.play-list__menu-toggle:focus-visible {
    background: #ffffff;
    outline: 1px solid var(--c-line, #d4d4d8);
}

.play-list__menu-popover {
    position: absolute;
    top: calc(100% + 0.25rem);
    right: 0;
    min-width: 140px;
    background: #ffffff;
    border: 1px solid var(--c-line, #d4d4d8);
    border-radius: 6px;
    box-shadow: 0 8px 20px rgba(15, 23, 42, 0.16);
    padding: 0.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.05rem;
}

/* The popover starts in the DOM with the HTML ``hidden`` attribute,
   but the ``display: flex`` rule above WINS over the attribute's
   default ``display: none`` — so without this override the popover
   would render OPEN by default and cover the whole tile. The JS
   toggle adds / removes the attribute on click; the CSS rule below
   makes the attribute actually hide the element. */
.play-list__menu-popover[hidden] {
    display: none;
}

/* Inline variant: same kebab + popover, but anchored next to a
   button in a horizontal toolbar (the editor save strip) instead of
   absolute-positioned into a tile corner. The kebab is always
   visible (no hover fade) and matches the height of the surrounding
   buttons. */
.play-list__menu--inline {
    position: relative;
    top: auto;
    right: auto;
}
.play-list__menu--inline .play-list__menu-toggle {
    opacity: 1;
    width: 34px;
    height: 34px;
    background: transparent;
    border: 1px solid var(--c-line, #d4d4d8);
    color: var(--c-ink-700, #4b5563);
    font-size: 1.1rem;
}
.play-list__menu--inline .play-list__menu-toggle:hover,
.play-list__menu--inline .play-list__menu-toggle:focus-visible {
    background: var(--c-field-50, #ecf3ed);
    border-color: var(--c-field-300, #a7c4ac);
}
.play-list__menu--inline .play-list__menu-popover {
    top: calc(100% + 0.35rem);
    /* The save strip is right-aligned on wide viewports — the kebab
       sits near the right edge, so dropping the popover to the right
       of the button can clip off-screen. Anchor it to the right edge
       of the toggle so it grows leftward. */
    right: 0;
    min-width: 160px;
}

.play-list__menu-form {
    margin: 0;
    padding: 0;
    display: flex;
}

.play-list__menu-item {
    appearance: none;
    border: none;
    background: transparent;
    color: var(--c-ink-900, #111827);
    text-decoration: none;
    text-align: left;
    padding: 0.4rem 0.6rem;
    border-radius: 4px;
    font-size: 0.85rem;
    font-weight: 500;
    line-height: 1.2;
    cursor: pointer;
    flex: 1;
    /* Anchor + button siblings should look identical inside the popover. */
    font-family: inherit;
}

.play-list__menu-item:hover,
.play-list__menu-item:focus-visible {
    background: var(--c-field-50, #ecf3ed);
    color: var(--c-field-900, #1f4d29);
    text-decoration: none;
    outline: none;
}

.play-list__menu-item--danger {
    color: var(--c-error-700, #b42318);
}
.play-list__menu-item--danger:hover,
.play-list__menu-item--danger:focus-visible {
    background: #fdecec;
    color: var(--c-error-700, #b42318);
}

/* --------------------------------------------------------------------
   9. Field_Display container
   -------------------------------------------------------------------- */

.play-field-container {
    width: 100%;
    aspect-ratio: 53.33 / 100;
    max-width: 360px;
    margin: 0 auto;
    background: var(--c-field-500);
    border-radius: 8px;
    overflow: hidden;
    touch-action: none;
}

.play-field-container svg.play-field {
    width: 100%;
    height: 100%;
    display: block;
}

/* --------------------------------------------------------------------
   10. Editor mount node
   -------------------------------------------------------------------- */

#teascan-editor-root {
    /* The live canvas frames the play (design item #1): players line up
       across the width and routes develop only ~20-30 yards downfield,
       so a LANDSCAPE box lets the action fill the frame instead of
       floating in a tall, mostly-empty portrait field.

       Vertical-cap rationale: the editor stacks app-header (~4rem) +
       breadcrumb (~2rem) + play-header (~3rem) + transport strip
       (~3.5rem) + save strip (~3.5rem) + gaps (~2rem) above and
       below the field. Reserving ~18rem of viewport chrome lets the
       play name stay visible without scrolling on a typical laptop
       (the previous 9rem cap was too generous — the field grew tall
       enough to push the play header off-screen).

       The container aspect (~4/3) closely matches the framed
       viewBox's target aspect (1.35) so there's no visible
       letterboxing, and ``clientToYards`` maps pointer coords
       through the framed viewBox regardless. */
    aspect-ratio: 4 / 3;
    width: 100%;
    height: auto;
    min-height: 280px;
    max-height: calc(100vh - 18rem);
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    /* Match the SVG field fill so any framing letterbox blends in. */
    background: #2e7d32;
    border-radius: var(--r-card);
    overflow: hidden;
    box-shadow: var(--shadow-soft);
    touch-action: none;
    /* Anchor for the absolutely-positioned inline rename input
       (.field-rename-input) mounted over a player marker. */
    position: relative;
}

#teascan-editor-root[data-state="loading"],
#teascan-editor-root[data-state="error"],
#teascan-editor-root[data-state="no-play"] {
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 1rem;
    color: rgba(255, 255, 255, 0.85);
    background: var(--c-field-900);
}

/* --------------------------------------------------------------------
   Editor play header (B-019)
   -------------------------------------------------------------------- */

.editor-play-header {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    /* Tight vertical margins so the play header + toolbar + field all
       fit in one viewport on a typical laptop. */
    margin: 0.25rem auto 0.5rem auto;
    padding: 0 1rem;
    max-width: 1024px;
    flex-wrap: wrap;
}

.editor-play-header__name {
    margin: 0;
    /* Chalk-talk convention: play names always render uppercase. */
    text-transform: uppercase;
    letter-spacing: 0.015em;
    /* Slightly smaller than the playbook list/detail h1 so it doesn't
       overpower the field. */
    font-size: clamp(1.05rem, 1vw + 0.7rem, 1.35rem);
    font-weight: 800;
    letter-spacing: -0.015em;
    color: var(--c-ink-900);
}

.editor-play-header__kind {
    margin: 0;
}

/* Player-count picker lives in the play header (post-toolbar
   reshuffle). Push it to the far right of the header so it doesn't
   crowd the play name, and shrink the label so it reads as quiet
   metadata rather than a primary control. */
.editor-play-header__player-count {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.85rem;
    color: var(--c-ink-700);
}

.editor-play-header__player-count select {
    padding: 0.15rem 0.4rem;
    font-size: 0.9rem;
}

/* --------------------------------------------------------------------
   Editor toolbar (B-022 + B-001 wiring)
   -------------------------------------------------------------------- */

.editor-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    /* Tight margins keep the toolbar + field together in one viewport. */
    margin: 0.25rem auto 0.5rem auto;
    padding: 0.4rem 0.7rem;
    max-width: 1024px;
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
    box-shadow: var(--shadow-soft);
}

.editor-toolbar__btn {
    /* The toolbar buttons are smaller than the hero CTAs — utilitarian
       in-page action sized for a thumb on phone or a click on desktop. */
    min-height: 34px;
    padding: 0.35rem 0.85rem;
    font-size: 0.88rem;
}

.editor-toolbar__divider {
    align-self: stretch;
    width: 1px;
    background: var(--c-line);
    margin: 0.15rem 0.35rem;
}
/* Pushes everything after it (Undo / Save / status) to the right edge
   so the primary "Draw routes" + animation transport sit left and the
   save controls sit apart on the right (design item #5). */
.editor-toolbar__divider--push {
    margin-left: auto;
    background: transparent;
}

/* Primary creative action: "Draw routes" carries an accent outline at
   rest so it reads as THE tool, distinct from the quiet Undo/Save. The
   filled-green ``[aria-pressed="true"]`` state below still signals when
   the mode is active. */
.editor-toolbar__btn--mode {
    border-color: var(--c-field-700);
    color: var(--c-field-700);
    font-weight: 600;
}
.editor-toolbar__btn--mode:hover {
    background: var(--c-field-50);
    color: var(--c-field-700);
}

/* Football button, ON state. The button both places and removes the ball, but
   with only the 🏈 glyph visible it looked identical either way — so a coach who
   had just added one had no signal that the same button takes it off, and
   reported that a football could not be deleted. The pressed state is the
   signal; ``aria-pressed`` carries the same fact to assistive tech, and the
   toolbar JS rewrites the label to "Remove football". */
.editor-toolbar__btn[aria-pressed="true"],
.editor-toolbar__btn--active {
    background: var(--c-field-50);
    border-color: var(--c-field-700);
    color: var(--c-field-700);
    font-weight: 600;
    /* Inset ring rather than a heavier border so the button does not shift the
       row's metrics when it toggles. */
    box-shadow: inset 0 0 0 2px var(--c-field-700);
}

/* Animation transport grouped on a faint tinted pill so play / pause /
   restart / speed read as one cluster rather than five loose buttons. */
.editor-toolbar__transport {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem;
    padding: 0.2rem 0.35rem;
    background: var(--c-field-50);
    border-radius: var(--r-input);
}

/* Quiet save controls — the editor auto-saves and the status chip
   reports state, so Undo/Save don't need primary weight. */
.editor-toolbar__btn--save {
    border-color: var(--c-line);
    color: var(--c-ink-600);
}
.editor-toolbar__btn--save:hover {
    border-color: var(--c-ink-400);
    background: var(--c-field-50);
    color: var(--c-ink-900);
}

.editor-toolbar__save-status {
    margin-left: 0.5rem;
    font-family: var(--font-display);
    font-size: 0.78rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--c-ink-400);
    line-height: 1;
}
.editor-toolbar__save-status[data-state="dirty"] {
    color: var(--c-endzone-deep);
}
.editor-toolbar__save-status[data-state="saving"] {
    color: var(--c-field-700);
}
.editor-toolbar__save-status[data-state="clean"] {
    color: var(--c-field-300);
}
.editor-toolbar__save-status[data-state="error"] {
    color: var(--c-error-ink);
}

/* Save / Undo strip directly below the field (design item #5). */
/* Edit-controls ribbon (Undo / Clear route / Save / Print) above the
   field. Its own card — matching the animation transport below — with a
   gap to the field so the buttons don't jam against the diagram. */
.editor-save-strip {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    justify-content: center;
    width: 100%;
    max-width: 1024px;
    margin: 0.5rem auto 0.85rem auto;
    padding: 0.4rem 0.7rem;
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
    box-shadow: var(--shadow-soft);
}

/* Active toggle (e.g. "Drawing routes") gets a filled-green pill so
   the user always knows which mode they're in. */
.editor-toolbar__btn--toggle[aria-pressed="true"] {
    background: var(--c-field-700);
    border-color: var(--c-field-700);
    color: #ffffff;
}

/* Animation-speed selector — active speed gets the same filled-green
   pill as the route-mode toggle so the user can tell at a glance which
   speed is in effect (B-034). */
.editor-toolbar__speed {
    display: inline-flex;
    gap: 0.25rem;
}

/* Player-count picker (5/6/7) — sits next to the transport controls
   in the toolbar. Uses native ``<select>`` so it's keyboard +
   touch friendly out of the box. */
.editor-toolbar__player-count {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.85rem;
    color: var(--c-ink-700);
}
.editor-toolbar__player-count select {
    padding: 0.3rem 0.45rem;
    border: 1px solid var(--c-line);
    border-radius: var(--r-input);
    background: var(--c-paper);
    color: var(--c-ink-900);
    font: inherit;
}
.editor-toolbar__speed-btn {
    padding: 0.3rem 0.55rem;
    font-size: 0.85rem;
}
.editor-toolbar__speed-btn[aria-pressed="true"] {
    background: var(--c-field-700);
    border-color: var(--c-field-700);
    color: #ffffff;
}

/* Route line-shape toggle (Curved / Straight) — same segmented look as
   the speed selector; the active shape is filled green. */
.editor-toolbar__line-shape {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.85rem;
    color: var(--c-ink-700);
}
.editor-toolbar__line-btn {
    padding: 0.3rem 0.55rem;
    font-size: 0.85rem;
}
.editor-toolbar__line-btn[aria-pressed="true"] {
    background: var(--c-field-700);
    border-color: var(--c-field-700);
    color: #ffffff;
}

/* Route mode: the SVG cursor switches to a crosshair so the user
   understands clicks add waypoints rather than pan. */
#teascan-editor-root svg.play-field--route-mode,
#teascan-editor-root[data-edit-mode="route"] svg {
    cursor: crosshair;
}

/* Selected marker (route mode): a green halo so the user knows which
   player's route their next click will extend. */
.play-marker--selected .play-circle {
    stroke: var(--c-field-300);
    stroke-width: 0.45;
}
.play-marker--selected .play-label {
    font-weight: 700;
}

/* Selected ball-path leg (owner, 2026-08-29): tapping a brown line SELECTS it
   and the trash button deletes it, so the line has to show that it is the thing
   the trash is pointed at. The casing does the work — it is the wide stroke
   under the hairline, so brightening and thickening it reads as a halo around
   the line rather than as a different notation. The core stroke and the mesh
   dot are left exactly as they are: a selected handoff must still be legible AS
   a handoff. */
.play-exchange--selected .play-exchange__casing,
.play-ball-path--selected .play-exchange__casing {
    stroke: var(--c-field-300);
    stroke-width: 1.1;
}
.play-exchange--selected .play-exchange__dot,
.play-exchange--selected .play-exchange__head {
    stroke: var(--c-field-300);
    stroke-width: 0.3;
}

/* The editor's one destructive button. Ghost until it has something to act on
   — the disabled state is the honest default, since most of the time nothing is
   selected — and it takes the danger colour only when armed, so an idle trash
   icon never looks like a live one. */
.editor-toolbar__btn--danger:not(:disabled) {
    color: var(--c-danger-600, #b42318);
}
.editor-toolbar__btn--danger:not(:disabled):hover {
    background: var(--c-danger-50, #fef3f2);
}
.editor-toolbar__btn--danger:disabled {
    opacity: 0.45;
}
.editor-toolbar__btn--danger svg {
    display: block;
}

/* --------------------------------------------------------------------
   Editor two-column layout
   --------------------------------------------------------------------
   The AI co-coach sits in a fixed-width sidebar on the LEFT; the
   portrait field takes the rest of the width on the right. Stacks
   single-column on narrow viewports so phones still get a usable
   layout (sidebar above the field). */

.editor-layout {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin: 0 auto;
    /* Removed the 1280px cap so the editor's two-column layout (320px
       AI sidebar + flex-grow field column) stretches to the full
       viewport — the field SVG is intrinsically portrait so it caps
       itself at a sensible width via ``.play-field-container``,
       leaving the unused horizontal space to host the sidebar's tools
       at a comfortable width on wide monitors. */
    max-width: none;
    padding: 0 0.5rem;
}

.editor-layout__sidebar {
    /* Stack-on-narrow: full width on mobile. */
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.editor-layout__main {
    /* Holds the editor-root mount + the Save/Undo strip below it, stacked
       and horizontally centred so the portrait field sits in the middle
       of the available space with its controls directly beneath. */
    display: flex;
    flex-direction: column;
    align-items: center;
    flex: 1 1 auto;
    min-width: 0; /* let the flex item shrink under the column width */
    /* On the narrow (stacked) layout the sidebar's four tool panels come
       first in the DOM. Pull the field above them so a coach lands on the
       thing they came to edit instead of scrolling past AI / inspector /
       revisions / defense panels. Reset to source order on desktop, where
       the sidebar sits beside the field. */
    order: -1;
}

@media (min-width: 900px) {
    .editor-layout {
        flex-direction: row;
        align-items: flex-start;
        gap: 1.25rem;
        padding: 0 1rem;
    }
    .editor-layout__sidebar {
        flex: 0 0 320px;
        position: sticky;
        top: 1rem;
        max-height: calc(100vh - 7rem);
        overflow-y: auto;
    }
    .editor-layout__main {
        order: 0; /* sidebar (left) → field (right), source order */
    }
}

/* --------------------------------------------------------------------
   AI chat / modify panel (B-003)
   --------------------------------------------------------------------
   Lives inside the ``.ai-drawer`` slide-in (2026-06-05 restructure).
   The button labelled "AI co-coach" in the editor save strip toggles
   the drawer; the existing ``data-editor-ai-*`` attributes inside the
   ``.editor-ai`` section are wired by ``wireEditorAiPanel`` unchanged. */

.editor-ai {
    padding: 0.85rem 1rem 1rem;
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
    box-shadow: var(--shadow-soft);
}

/* --------------------------------------------------------------------
   Edit-play meta: tags + notes side-by-side on wide viewports
   -------------------------------------------------------------------- */

.edit-play-meta {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.25rem;
    margin: 0.5rem 0 1rem;
}

@media (min-width: 720px) {
    .edit-play-meta {
        grid-template-columns: 1fr 1fr;
    }
}

.edit-play-meta__col {
    margin: 0;
    padding: 0;
    border: none;
}

.edit-play-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
}

/* Visual marker on custom (user-defined) tag checkboxes so a coach
   can spot which entries came from Settings vs. the built-in 7. */
.auth-field--checkbox-custom {
    border-bottom: 1px dotted var(--c-accent-500, #2e7d32);
    padding-bottom: 1px;
}

/* --------------------------------------------------------------------
   AI co-coach toggle button (in the editor save strip)
   --------------------------------------------------------------------
   Icon + label, leading position in the strip. The sparkle icon
   inherits ``currentColor`` so the primary-button white survives the
   ``.btn--primary`` style without manual fill juggling. */

.editor-ai-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    /* Lift slightly so the button reads as the page's primary action.
       The default ``.btn--primary`` already does the heavy lifting; a
       little extra letter-spacing tightens the label against the
       icon. */
    letter-spacing: 0.005em;
}

.editor-ai-btn__icon {
    flex: 0 0 auto;
    /* A subtle pulse on the icon advertises the AI affordance without
       being distracting. Pauses on hover so a coach reading the
       label isn't fighting motion. */
    animation: editor-ai-btn-pulse 2.4s ease-in-out infinite;
    transform-origin: center;
}

.editor-ai-btn:hover .editor-ai-btn__icon,
.editor-ai-btn:focus .editor-ai-btn__icon {
    animation-play-state: paused;
}

@media (prefers-reduced-motion: reduce) {
    .editor-ai-btn__icon {
        animation: none;
    }
}

@keyframes editor-ai-btn-pulse {
    0%, 100% { transform: scale(1); opacity: 1; }
    50%      { transform: scale(1.12); opacity: 0.88; }
}

/* On very narrow viewports, hide the text label and rely on the
   icon + aria-label so the button stays in the strip without
   wrapping the row. */
@media (max-width: 480px) {
    .editor-ai-btn__label { display: none; }
    .editor-ai-btn { padding-left: 0.55rem; padding-right: 0.55rem; }
}

/* --------------------------------------------------------------------
   AI co-coach slide-in drawer (replaces the sidebar AI panel)
   -------------------------------------------------------------------- */

.ai-drawer {
    position: fixed;
    inset: 0;
    z-index: 60;
    pointer-events: none;
    opacity: 0;
    transition: opacity 160ms ease;
}

.ai-drawer--open {
    pointer-events: auto;
    opacity: 1;
}

.ai-drawer__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.35);
    cursor: pointer;
}

.ai-drawer__panel {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(420px, 92vw);
    background: var(--c-paper, #ffffff);
    box-shadow: -8px 0 24px rgba(15, 23, 42, 0.22);
    overflow-y: auto;
    padding: 1rem 1rem 2rem;
    transform: translateX(110%);
    transition: transform 200ms ease;
}

.ai-drawer--open .ai-drawer__panel {
    transform: translateX(0);
}

.ai-drawer__close {
    position: sticky;
    top: 0;
    left: 0;
    margin-bottom: 0.5rem;
    align-self: flex-start;
    background: transparent;
    border: none;
    padding: 0.35rem 0.5rem;
    font-size: 1.05rem;
    line-height: 1;
    cursor: pointer;
}

/* The card-style border + shadow on ``.editor-ai`` reads as a
   nested-card inside the drawer; flatten it so the drawer panel
   itself frames the AI controls. */
.ai-drawer .editor-ai {
    box-shadow: none;
    border: none;
    padding: 0;
}

.editor-ai__head {
    margin-bottom: 0.65rem;
}

.editor-ai__title {
    margin: 0;
    font-size: 1rem;
    font-weight: 700;
    letter-spacing: -0.005em;
    color: var(--c-ink-900);
}

.editor-ai__lede {
    margin: 0.15rem 0 0;
    color: var(--c-ink-600);
    font-size: 0.85rem;
}

/* Example prompt chips (design item #11): low-emphasis, wrap freely,
   tap to seed the prompt box. */
.editor-ai__chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin-bottom: 0.55rem;
}
.editor-ai__chip {
    font: inherit;
    font-size: 0.78rem;
    line-height: 1;
    padding: 0.35rem 0.6rem;
    border: 1px solid var(--c-line);
    border-radius: var(--r-pill);
    background: var(--c-field-50);
    color: var(--c-field-700);
    cursor: pointer;
    transition: background 120ms ease, border-color 120ms ease;
}
.editor-ai__chip:hover {
    background: color-mix(in srgb, var(--c-field-500) 16%, transparent);
    border-color: var(--c-field-300);
}
.editor-ai__chip:disabled {
    opacity: 0.5;
    cursor: default;
}

/* Thinking affordance (design item #12): animated dots so the AI feels
   alive while Claude works. */
.editor-ai__thinking {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.5rem;
    font-size: 0.85rem;
    color: var(--c-field-700);
}
.editor-ai__thinking[hidden] { display: none; }

/* Sprint 2.1: critique output block. Sits above the prompt textarea
   so the coach reads the weaknesses and then types a modification
   addressing them directly. */
.editor-ai__critique {
    margin: 0.75rem 0;
    padding: 0.75rem 1rem;
    background: #fff7ed;
    border: 1px solid #fed7aa;
    border-radius: 0.5rem;
}
.editor-ai__critique[hidden] { display: none; }
.editor-ai__critique-title {
    margin: 0 0 0.5rem;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #c2410c;
    font-weight: 600;
}
.editor-ai__critique-list {
    margin: 0;
    padding-left: 1.1rem;
    color: #1f2937;
    font-size: 0.9rem;
    line-height: 1.4;
}
.editor-ai__critique-item + .editor-ai__critique-item {
    margin-top: 0.3rem;
}
.editor-ai__critique-summary {
    margin: 0.5rem 0 0;
    color: #57534e;
    font-size: 0.85rem;
    font-style: italic;
}
.editor-ai__critique-empty {
    margin: 0;
    color: #57534e;
    font-size: 0.9rem;
}

/* Sprint 2.3: contextual tip card. Sits above the prompt input so
   the coach can read the recommendation and add it without typing. */
.editor-ai__tip {
    margin: 0.5rem 0 0.75rem;
    padding: 0.75rem 1rem;
    background: #eff6ff;
    border: 1px solid #bfdbfe;
    border-radius: 0.5rem;
}
.editor-ai__tip[hidden] { display: none; }
.editor-ai__tip-message {
    margin: 0 0 0.5rem;
    color: #0f172a;
    font-size: 0.9rem;
    line-height: 1.4;
}
.editor-ai__tip-actions {
    display: flex;
    gap: 0.5rem;
}
.editor-ai__tip-btn {
    padding: 0.3rem 0.75rem;
    font-size: 0.85rem;
}

/* Sprint 2.4: voice input. The prompt textarea + mic button share a
   wrapper so the button can position absolute relative to the
   textarea's lower-right corner. */
.editor-ai__prompt-wrap {
    position: relative;
}
.editor-ai__voice-btn {
    position: absolute;
    right: 0.5rem;
    bottom: 0.5rem;
    border: 1px solid #cbd5e1;
    background: #ffffff;
    border-radius: 50%;
    width: 2.25rem;
    height: 2.25rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    color: #0f172a;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
    transition: background 0.15s ease, color 0.15s ease,
        transform 0.08s ease;
}
.editor-ai__voice-btn[hidden] { display: none; }
.editor-ai__voice-btn:hover {
    background: #f1f5f9;
}
.editor-ai__voice-btn--recording {
    background: #dc2626;
    color: #ffffff;
    border-color: #b91c1c;
    animation: editor-ai__voice-pulse 1.2s ease-in-out infinite;
}
@keyframes editor-ai__voice-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.55); }
    50% { box-shadow: 0 0 0 6px rgba(220, 38, 38, 0); }
}
@media (prefers-reduced-motion: reduce) {
    .editor-ai__voice-btn--recording { animation: none; }
}

/* Sprint 2.5: cost preview. Sits between the textarea and the
   submit button so the coach reads "this will cost 1 credit" before
   they tap. Light copy treatment — informational, not a CTA. */
.editor-ai__cost-preview {
    margin: 0.5rem 0;
    font-size: 0.8rem;
    color: #64748b;
    letter-spacing: 0.01em;
}

/* Sprint 2.6: AI proposal diff. Sits below the Accept / Reject row
   inside the review panel so the coach has the visual ghost AND the
   verbal change list in one place. */
.editor-ai__diff {
    margin-top: 0.75rem;
    padding-top: 0.75rem;
    border-top: 1px dashed #e2e8f0;
}
.editor-ai__diff[hidden] { display: none; }
.editor-ai__diff-title {
    margin: 0 0 0.4rem;
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #64748b;
    font-weight: 600;
}
.editor-ai__diff-list {
    margin: 0;
    padding-left: 1.1rem;
    font-size: 0.85rem;
    color: #1f2937;
}
.editor-ai__diff-item + .editor-ai__diff-item {
    margin-top: 0.2rem;
}
.editor-ai__diff-item--route_added::marker,
.editor-ai__diff-item--player_added::marker {
    color: #16a34a;
}
.editor-ai__diff-item--route_removed::marker,
.editor-ai__diff-item--player_removed::marker {
    color: #dc2626;
}
.editor-ai__thinking-dots {
    display: inline-flex;
    gap: 0.22rem;
}
.editor-ai__thinking-dots span {
    width: 0.4rem;
    height: 0.4rem;
    border-radius: 50%;
    background: var(--c-field-500);
    animation: editor-ai-bounce 1s infinite ease-in-out both;
}
.editor-ai__thinking-dots span:nth-child(2) { animation-delay: 0.16s; }
.editor-ai__thinking-dots span:nth-child(3) { animation-delay: 0.32s; }
@keyframes editor-ai-bounce {
    0%, 80%, 100% { transform: scale(0.5); opacity: 0.5; }
    40% { transform: scale(1); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    .editor-ai__thinking-dots span { animation: none; }
}

.editor-ai__prompt {
    font: inherit;
    width: 100%;
    min-height: 4.5rem;
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--c-line);
    border-radius: var(--r-input);
    background: var(--c-paper);
    color: var(--c-ink-900);
    resize: vertical;
    transition: border-color 120ms ease, box-shadow 120ms ease;
}
.editor-ai__prompt:focus {
    outline: none;
    border-color: var(--c-field-500);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-field-500) 25%, transparent);
}
.editor-ai__prompt:disabled {
    background: var(--c-line-soft);
    color: var(--c-ink-400);
}

.editor-ai__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.65rem;
    margin-top: 0.5rem;
}

.editor-ai__status {
    margin: 0;
    font-size: 0.85rem;
    color: var(--c-ink-600);
    line-height: 1.3;
}
.editor-ai__status[data-state="submitting"],
.editor-ai__status[data-state="applying"] {
    color: var(--c-field-700);
}
.editor-ai__status[data-state="proposal-ready"] {
    color: var(--c-endzone-deep);
    font-weight: 600;
}
.editor-ai__status[data-state="error"] {
    color: var(--c-error-ink);
}

.editor-ai__review {
    margin-top: 0.85rem;
    padding-top: 0.85rem;
    border-top: 1px dashed var(--c-line);
}

.editor-ai__review-lede {
    margin: 0 0 0.55rem;
    color: var(--c-ink-600);
    font-size: 0.85rem;
}

.editor-ai__review-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.55rem;
}

/* --------------------------------------------------------------------
   Global AI-error toast (B-028)
   ----------------------------------------------------------------- */

.editor-ai-toast {
    position: fixed;
    z-index: 1000;
    right: 1rem;
    bottom: 1rem;
    max-width: 28rem;
    padding: 0.6rem 0.85rem;
    border-radius: var(--r-input);
    background: var(--c-error-soft, #fde8e8);
    color: var(--c-error-ink, #7a1f1f);
    font-size: 0.9rem;
    box-shadow: var(--shadow-soft);
    border: 1px solid color-mix(in srgb, var(--c-error-ink, #7a1f1f) 25%, transparent);
}
.editor-ai-toast[hidden] { display: none; }
/* Purchase CTA inside the toast (out-of-credits only). Needs to read as a
   button, not as body text: the toast auto-dismisses in 5s, so this is the
   coach's one chance to act on the only AI failure she can actually fix. */
.editor-ai-toast__cta {
    display: inline-block;
    margin-left: 0.5rem;
    padding: 0.15rem 0.55rem;
    border-radius: var(--r-input);
    background: var(--c-error-ink, #7a1f1f);
    color: #ffffff;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}
.editor-ai-toast__cta:hover,
.editor-ai-toast__cta:focus {
    text-decoration: underline;
}
.editor-ai-toast[data-kind="info"] {
    background: var(--c-field-50);
    color: var(--c-field-700);
    border-color: color-mix(in srgb, var(--c-field-700) 25%, transparent);
}

/* --------------------------------------------------------------------
   Defense designer (B-010 / Req 10) — sidebar panel
   ----------------------------------------------------------------- */

.editor-defense {
    padding: 0.85rem 1rem 1rem;
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
    box-shadow: var(--shadow-soft);
}
.editor-defense[hidden] { display: none; }

.editor-defense__head { margin-bottom: 0.5rem; }
.editor-defense__title {
    margin: 0;
    font-size: 1rem;
    font-weight: 700;
    color: var(--c-ink-900);
}
.editor-defense__lede {
    margin: 0.15rem 0 0;
    color: var(--c-ink-600);
    font-size: 0.85rem;
}

.editor-defense__group {
    border: none;
    padding: 0;
    margin: 0 0 0.55rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}
.editor-defense__group legend {
    padding: 0;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--c-ink-600);
}
.editor-defense__group label {
    font-size: 0.9rem;
    color: var(--c-ink-800);
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin-right: 0.75rem;
}

.editor-defense__field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    margin: 0 0 0.55rem;
}
.editor-defense__field label {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--c-ink-600);
}
.editor-defense__field input,
.editor-defense__field textarea {
    font: inherit;
    padding: 0.35rem 0.5rem;
    border: 1px solid var(--c-line);
    border-radius: var(--r-input);
    background: var(--c-paper);
    color: var(--c-ink-900);
    resize: vertical;
}

.editor-defense__actions {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}
.editor-defense__status {
    margin: 0;
    font-size: 0.85rem;
    color: var(--c-ink-600);
    min-height: 1.2em;
}
.editor-defense__status[data-state="submitting"] { color: var(--c-field-700); }
.editor-defense__status[data-state="clean"]      { color: var(--c-field-700); }
.editor-defense__status[data-state="error"]      { color: var(--c-error-ink); }

/* --------------------------------------------------------------------
   Player inspector (route-builder sidebar panel)
   ----------------------------------------------------------------- */

.player-inspector {
    padding: 0.85rem 1rem 1rem;
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
    box-shadow: var(--shadow-soft);
}

.player-inspector[hidden] {
    display: none;
}

.player-inspector__head {
    margin-bottom: 0.65rem;
}

.player-inspector__title {
    margin: 0;
    font-size: 1rem;
    font-weight: 700;
    letter-spacing: -0.005em;
    color: var(--c-ink-900);
}

.player-inspector__lede {
    margin: 0.15rem 0 0;
    color: var(--c-ink-600);
    font-size: 0.85rem;
}

.player-inspector__body {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
}

.player-inspector__selected {
    margin: 0;
    font-size: 0.85rem;
    color: var(--c-ink-700);
}

.player-inspector__group {
    border: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.player-inspector__group legend {
    padding: 0;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--c-ink-600);
}

.player-inspector__choices {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

/* Route-template stamp buttons (Slant / Out / Post / Hitch / Go /
   Wheel / Curl). Compact text pills so seven fit comfortably in the
   inspector column without wrapping awkwardly. */
.player-inspector__templates {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}

.player-inspector__template-btn {
    padding: 0.25rem 0.55rem;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    line-height: 1.1;
}

.player-inspector__choice {
    padding: 0.35rem 0.6rem;
    font-size: 0.85rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.4rem;
}
/* Icon glyph inside a choice button (shape / line / route-end). Inherits
   the button's text color via ``currentColor`` so the pressed state
   inverts to white automatically. */
.player-inspector__choice svg {
    width: 20px;
    height: 20px;
    display: block;
}

.player-inspector__choice[aria-pressed="true"] {
    background: var(--c-field-700);
    color: var(--c-paper);
    border-color: var(--c-field-700);
}

/* Waypoint list (B-037) — one row per route point with a delete button. */
.player-inspector__waypoints {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}
.player-inspector__waypoint {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    font-size: 0.85rem;
    color: var(--c-ink-900);
}
.player-inspector__waypoint-label {
    font-variant-numeric: tabular-nums;
}
.player-inspector__waypoint-delete {
    padding: 0.1rem 0.5rem;
    font-size: 1rem;
    line-height: 1;
}

/* Color swatches — the team's standard colors from Settings (B: replaces
   the native color wheel). One chip per color plus a leading "Auto" chip
   that clears the per-player override. */
.player-inspector__swatches {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.player-inspector__swatch {
    width: 1.9rem;
    height: 1.9rem;
    padding: 0;
    min-width: 0;
    border: 1px solid var(--c-line);
    border-radius: var(--r-input);
    cursor: pointer;
    box-shadow: inset 0 0 0 2px var(--c-paper);
    transition: transform 80ms ease, box-shadow 80ms ease;
}
.player-inspector__swatch:hover {
    transform: scale(1.08);
}
.player-inspector__swatch[aria-pressed="true"] {
    box-shadow:
        inset 0 0 0 2px var(--c-paper),
        0 0 0 2px var(--c-ink-900);
}
.player-inspector__swatch:focus-visible {
    outline: 2px solid var(--c-field-300);
    outline-offset: 1px;
}

/* The "Auto" (use position default) chip reads as text, not a color. */
.player-inspector__swatch--default {
    background: var(--c-paper);
    color: var(--c-ink-600);
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    box-shadow: none;
    width: auto;
    padding: 0 0.5rem;
}
.player-inspector__swatch--default[aria-pressed="true"] {
    box-shadow: 0 0 0 2px var(--c-ink-900);
    color: var(--c-ink-900);
}

/* Inline on-field player rename box (double-click a marker). Anchored
   over the marker by FieldDisplay; the editor host is position:relative
   (set below) so left/top are host-relative. */
.field-rename-input {
    position: absolute;
    transform: translate(-50%, -50%);
    z-index: 5;
    width: 5.5rem;
    padding: 0.15rem 0.3rem;
    font: 600 0.85rem/1.1 inherit;
    text-align: center;
    color: var(--c-ink-900);
    background: var(--c-paper);
    border: 2px solid var(--c-field-700);
    border-radius: var(--r-input);
    box-shadow: 0 2px 10px rgba(15, 29, 23, 0.25);
}

.player-inspector__label-edit {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}
.player-inspector__label-edit input[type="text"] {
    flex: 1 1 6rem;
    min-width: 5rem;
    padding: 0.35rem 0.5rem;
    font: inherit;
    border: 1px solid var(--c-line);
    border-radius: var(--r-input);
    background: var(--c-paper);
    color: var(--c-ink-900);
}
.player-inspector__label-edit input[type="text"]:focus {
    outline: none;
    border-color: var(--c-field-500);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-field-500) 25%, transparent);
}

.play-route-preview {
    pointer-events: none;
}

/* Marquee (rubber-band) multi-select overlay. Drawn in yard/viewBox
   units, so stroke-width is small. Non-interactive so it never steals
   the pointer mid-drag. */
.play-marquee {
    fill: color-mix(in srgb, var(--c-field-300) 14%, transparent);
    stroke: var(--c-field-300);
    stroke-width: 0.3;
    stroke-dasharray: 0.8 0.5;
    pointer-events: none;
}

/* Route handle (Lucidchart/Visio gesture): a small connection tab on
   each player's edge. Dragging it draws a route; dragging the marker
   body moves the player. Subdued at rest (design item #2) so it reads
   as part of the player rather than a second, free-floating dot; hover
   / selection lifts it to full contrast with a heavier ring to invite
   the drag. ``cursor: crosshair`` hints the draw gesture. */
.play-route-handle {
    opacity: 0.45;
    cursor: crosshair;
    transition: opacity 120ms ease, stroke-width 120ms ease;
}
.play-marker:hover .play-route-handle,
.play-marker--selected .play-route-handle {
    opacity: 1;
    stroke-width: 0.42;
}
/* The extend nub lives in a sibling ``<g class="play-route-extends">``
   group (so the animator's per-frame transform on the marker doesn't
   drag it down the route), which means the descendant-selector hover
   rules above can't reach it. The nub is also far from the player
   marker — there's no "free-floating dot next to the body" risk that
   justifies dimming it — so keep it at full prominence at rest. */
.play-route-extend {
    opacity: 1;
}

/* Route legibility (design item #3): a subtle dark casing under every
   route + end glyph so a player's jersey-coloured line stays readable
   against the green field no matter the colour. Cheap SVG filter, no
   extra elements (keeps the one-polyline-per-route invariant intact). */
.play-routes {
    filter: drop-shadow(0 0 0.35px rgba(7, 24, 14, 0.65));
}

/* AI-proposal compare layer (B-040, Req 5.2). The faded baseline
   stays clickable for selection in route mode but the visual weight
   stays clearly behind the live proposal so the coach can compare
   the two states at a glance. */
.play-ghost {
    pointer-events: none;
}
.play-ghost .play-route {
    stroke-dasharray: 0.4,0.4;
}

/* --------------------------------------------------------------------
   Revision history sidebar panel (B-041, Req 5.5 / 5.8)
   ----------------------------------------------------------------- */

.editor-revisions {
    padding: 0.85rem 1rem 1rem;
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
    box-shadow: var(--shadow-soft);
}

.editor-revisions[hidden] {
    display: none;
}

.editor-revisions__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.55rem;
}

.editor-revisions__title {
    margin: 0;
    font-size: 1rem;
    font-weight: 700;
    letter-spacing: -0.005em;
    color: var(--c-ink-900);
}

.editor-revisions__refresh {
    padding: 0.25rem 0.55rem;
    font-size: 0.8rem;
}

.editor-revisions__status {
    margin: 0 0 0.5rem;
    font-size: 0.85rem;
    color: var(--c-ink-600);
    min-height: 1.2em;
}
.editor-revisions__status[data-state="loading"] {
    color: var(--c-field-700);
}
.editor-revisions__status[data-state="error"] {
    color: var(--c-error-ink);
}

.editor-revisions__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    max-height: 320px;
    overflow-y: auto;
}

.editor-revisions__item {
    padding: 0.4rem 0.5rem;
    border: 1px solid var(--c-line);
    border-radius: var(--r-input);
    background: var(--c-paper);
}

.editor-revisions__row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.editor-revisions__label {
    font-weight: 600;
    color: var(--c-ink-900);
    font-size: 0.9rem;
    min-width: 2.5rem;
}

.editor-revisions__source {
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 0.1rem 0.4rem;
    border-radius: 999px;
    background: var(--c-line-soft);
    color: var(--c-ink-700);
}
.editor-revisions__source[data-source="ai"] {
    background: color-mix(in srgb, var(--c-endzone-deep) 18%, transparent);
    color: var(--c-endzone-deep);
}

.editor-revisions__time {
    flex: 1;
    font-size: 0.8rem;
    color: var(--c-ink-600);
}

.editor-revisions__select {
    padding: 0.25rem 0.6rem;
    font-size: 0.8rem;
}

.editor-revisions__empty {
    margin: 0;
    padding: 0.4rem 0;
    font-size: 0.85rem;
    color: var(--c-ink-600);
}

/* --------------------------------------------------------------------
   11. Auth pages (sign-in, sign-up, verify, reset)
   --------------------------------------------------------------------
   The landing has its own bespoke layout (sections 5-6 above). These
   rules style the smaller card-on-page auth surfaces and inherit the
   new tokens automatically.
   ----------------------------------------------------------------- */

.auth-card {
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
    padding: 1.75rem 1.5rem;
    margin: 1.5rem auto;
    max-width: 440px;
    box-shadow: var(--shadow-soft);
    /* B-025: a long email / token in a message must wrap, not overflow,
       at narrow widths (down to 320px). The card is already fluid
       (max-width, not a fixed width). */
    overflow-wrap: break-word;
}

.auth-card--narrow {
    max-width: 380px;
    text-align: center;
}

.auth-card__title {
    margin: 0 0 0.5rem 0;
    font-size: 1.4rem;
    line-height: 1.25;
    font-weight: 800;
    letter-spacing: -0.015em;
}

.auth-card__lede {
    margin: 0 0 1.25rem 0;
    color: var(--c-ink-600);
    font-size: 0.97rem;
}

.auth-card__hint {
    margin: 1rem 0 0 0;
    font-size: var(--t-meta);
    color: var(--c-ink-600);
}

/* Honest email-failure UX (P0-10): the always-on "Didn't get it?" block on
   the sent pages, and the slim degraded-delivery notice above the GET forms. */
.auth-card__fallback {
    margin: 1.25rem 0 0 0;
    padding: 0.9rem 1rem;
    text-align: left;
    border: 1px solid var(--c-line, #e3e9e5);
    border-radius: var(--r-input, 10px);
    background: var(--c-bg, #f7faf8);
}
.auth-card__fallback h2 {
    margin: 0 0 0.5rem 0;
    font-size: 0.95rem;
    font-weight: 700;
    color: var(--c-ink-900);
}
.auth-card__fallback ul {
    margin: 0;
    padding-left: 1.1rem;
    font-size: 0.9rem;
    line-height: 1.5;
    color: var(--c-ink-600);
}
.auth-card__fallback li + li {
    margin-top: 0.35rem;
}

.auth-card__notice {
    margin: 0 0 1.25rem 0;
    padding: 0.7rem 0.85rem;
    text-align: left;
    font-size: 0.9rem;
    line-height: 1.45;
    color: #7a4a05;
    background: #fdf3e0;
    border: 1px solid #f0d9a8;
    border-radius: var(--r-input, 10px);
}

/* Sprint 2: Google + Apple SSO buttons on login + signup. The buttons
   render above the email form and degrade to email-only when no
   provider is configured (no env vars → no DOM at all). */
.auth-sso {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin: 0 0 1.25rem;
}
.auth-sso__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.65rem;
    padding: 0.65rem 0.9rem;
    border-radius: var(--r-input, 0.5rem);
    border: 1px solid var(--c-line, #d1d5db);
    background: var(--c-paper, #fff);
    color: var(--c-ink-900, #0f172a);
    font-size: 0.95rem;
    font-weight: 600;
    text-decoration: none;
    transition: background 0.15s ease, border-color 0.15s ease;
}
.auth-sso__btn:hover {
    background: #f8fafc;
    border-color: #94a3b8;
}
.auth-sso__btn--apple {
    background: #000;
    color: #fff;
    border-color: #000;
}
.auth-sso__btn--apple:hover {
    background: #1a1a1a;
    border-color: #1a1a1a;
}
.auth-sso__divider {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    margin: 0.25rem 0 0;
    color: var(--c-ink-400, #94a3b8);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}
.auth-sso__divider::before,
.auth-sso__divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--c-line, #e5e7eb);
}

/* Helper line under the signup SSO block telling the coach a plan step
   comes next (so "pick plan → Continue with Google" no longer feels like
   it silently dropped the choice). */
.auth-sso__hint {
    margin: 0.5rem 0 0;
    color: var(--c-ink-400, #94a3b8);
    font-size: 0.82rem;
    text-align: center;
}

/* Password field + inline Show/Hide reveal toggle. The input keeps its
   own styling; the toggle button is layered at the trailing edge. The
   button is rendered ``hidden`` and only un-hidden by the page's inline
   script, so there is no dead control when JS is off. */
.auth-field__password {
    position: relative;
    display: flex;
    align-items: stretch;
}
.auth-field__password input {
    flex: 1;
    /* Leave room for the toggle so long passwords don't slide under it. */
    padding-right: 4rem;
}
.auth-field__password-toggle {
    position: absolute;
    top: 50%;
    right: 0.4rem;
    transform: translateY(-50%);
    border: 0;
    background: transparent;
    color: var(--c-accent-700, #1f6f2b);
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    padding: 0.25rem 0.4rem;
}
.auth-field__password-toggle:hover {
    text-decoration: underline;
}

/* Group the "Stay signed in" checkbox with the Sign-in button so the
   two read as one decision row rather than the checkbox floating under
   the field stack. */
.auth-form__submit-row {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.auth-card__actions {
    margin: 1.5rem 0 0 0;
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: center;
}

.auth-form {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.auth-form__error {
    margin: 0;
    padding: 0.7rem 0.9rem;
    background: var(--c-error-bg);
    border: 1px solid var(--c-error-line);
    color: var(--c-error-ink);
    border-radius: var(--r-input);
    font-size: 0.9rem;
}

.auth-field {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.auth-field--row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.75rem;
}

.auth-field--checkbox {
    flex-direction: row;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.9rem;
    color: var(--c-ink-900);
}

.auth-field label {
    font-size: var(--t-meta);
    font-weight: 600;
    color: var(--c-ink-900);
}

.auth-field input[type="email"],
.auth-field input[type="password"],
.auth-field input[type="text"],
.auth-field input[type="tel"] {
    font: inherit;
    padding: 0.65rem 0.8rem;
    border: 1px solid var(--c-line);
    border-radius: var(--r-input);
    background: var(--c-paper);
    color: var(--c-ink-900);
    width: 100%;
    min-height: 44px;
    transition: border-color 120ms ease, box-shadow 120ms ease;
}

.auth-field input:focus {
    outline: none;
    border-color: var(--c-field-500);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--c-field-500) 25%, transparent);
}

.auth-field__hint {
    margin: 0;
    font-size: 0.8rem;
    color: var(--c-ink-600);
    font-weight: 400;
}

/* Honeypot field on public forms (app/auth_pages/human_check.py): off-screen
   rather than display:none, which some form-filling bots skip. */
.form-trap {
    position: absolute;
    left: -10000px;
    top: auto;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.auth-field__error {
    margin: 0;
    color: var(--c-error-ink);
    font-size: 0.85rem;
}

/* Top-of-form error summary. The inline per-field messages are easy to miss
   on a long form after a 400 re-render (the browser returns to scroll-top),
   so every problem is also listed here, above the first control. */
.auth-form-errors {
    margin: 0 0 1.5rem;
    padding: 0.9rem 1.1rem;
    background: var(--c-error-bg);
    color: var(--c-error-ink);
    border: 1px solid var(--c-error-ink);
    border-radius: var(--r-tile, 0.75rem);
}
.auth-form-errors:focus { outline: none; }
.auth-form-errors__title {
    margin: 0 0 0.4rem;
    font-weight: 700;
    font-size: 0.92rem;
}
.auth-form-errors__list {
    margin: 0;
    padding-left: 1.2rem;
    font-size: 0.88rem;
    line-height: 1.5;
}
.auth-form-errors__list a { color: inherit; }

.auth-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: 0.65rem 1.3rem;
    border-radius: var(--r-pill);
    border: 1.5px solid var(--c-line);
    background: var(--c-paper);
    color: var(--c-ink-900);
    font-weight: 600;
    text-decoration: none;
    line-height: 1;
    transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}

.auth-button:hover,
.auth-button:focus {
    background: var(--c-field-50);
    border-color: var(--c-field-300);
    text-decoration: none;
}

.auth-button--primary {
    background: var(--c-field-700);
    border-color: var(--c-field-700);
    color: #ffffff;
}

.auth-button--primary:hover,
.auth-button--primary:focus {
    background: var(--c-field-500);
    border-color: var(--c-field-500);
    color: #ffffff;
}

button.auth-button { cursor: pointer; }

/* --------------------------------------------------------------------
   Signup → Stripe Checkout failure page
   -------------------------------------------------------------------- */

.checkout-failed__retry {
    margin: 1.5rem 0 0;
}

.checkout-failed__retry .btn {
    width: 100%;
    justify-content: center;
}

.checkout-failed__debug {
    /* Dev-only diagnostics — visually muted so it does not compete with
       the user-facing retry button above. ``<details>`` collapses by
       default; expand to read the real exception. */
    margin-top: 1.5rem;
    padding: 0.85rem 1rem;
    border: 1px dashed var(--c-line);
    border-radius: var(--r-card);
    background: var(--c-line-soft);
    color: var(--c-ink-600);
    font-size: 0.88rem;
}

.checkout-failed__debug > summary {
    cursor: pointer;
    font-weight: 600;
    color: var(--c-ink-900);
    list-style: none;
}
.checkout-failed__debug > summary::-webkit-details-marker { display: none; }
.checkout-failed__debug > summary::before {
    content: "▸ ";
    display: inline-block;
    transition: transform 120ms ease;
    color: var(--c-field-700);
}
.checkout-failed__debug[open] > summary::before {
    transform: rotate(90deg);
}

.checkout-failed__debug-hint {
    margin: 0.75rem 0 0.35rem;
}

.checkout-failed__debug-list {
    margin: 0 0 0.85rem 1.1rem;
    padding: 0;
}
.checkout-failed__debug-list li { margin-bottom: 0.3rem; }

.checkout-failed__debug-output {
    margin: 0;
    padding: 0.65rem 0.8rem;
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-input);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.82rem;
    line-height: 1.4;
    color: var(--c-error-ink);
    white-space: pre-wrap;
    word-break: break-word;
    overflow-x: auto;
}

/* --------------------------------------------------------------------
   12. Responsive breakpoints
   -------------------------------------------------------------------- */

@media (min-width: 600px) {
    .playbook-list { grid-template-columns: repeat(2, 1fr); }
    /* Skip the thumbnail variant — it sets its own auto-fill grid
       (~10 columns at the 1120px ``app-main`` width) so the playbook
       detail packs ~12 plays into a single row. Without ``:not()``
       this 2-column rule would override the thumbnail grid because
       both selectors have the same specificity and this one comes
       later in the cascade. */
    .play-list:not(.play-list--thumbnails) { grid-template-columns: repeat(2, 1fr); }
    .play-field-container { max-width: 480px; }

    .step-list   { grid-template-columns: repeat(3, 1fr); }
}

@media (min-width: 768px) {
    .playbook-list { grid-template-columns: repeat(3, 1fr); }
    .play-list:not(.play-list--thumbnails) { grid-template-columns: repeat(2, 1fr); }
    .play-field-container { max-width: 540px; }

    .app-header { padding: 1rem 1.5rem; }
    .app-main { padding: 1.5rem; }

    .landing-section { padding: 5.5rem 2rem; }
    .landing-steps   { padding: 5.5rem 2rem; }
    .landing-cta     { padding: 6rem 2rem; }

}


/* ===================================================================
   Print page (Req 11) — browser-native printing
   ===================================================================
   Two-column "control panel + live preview" on screen; in print the
   panel/chrome drop away and only the paginated sheets remain. The page
   geometry (@page size + per-sheet grid) is injected at runtime into
   ``#print-dynamic`` by the inline script in print/print_page.html. */

.print-shell {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    align-items: flex-start;
}

.print-shell__panel {
    flex: 0 0 320px;
    max-width: 100%;
    display: flex;
    flex-direction: column;
    gap: 1.1rem;
}

.print-shell__title {
    font-size: 1.3rem;
    margin: 0;
    color: var(--c-ink-900);
}

.print-shell__hint {
    margin: -0.4rem 0 0;
    font-size: 0.8rem;
    color: var(--c-ink-400);
}

.print-go { align-self: flex-start; }

.print-field {
    border: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.print-field > legend {
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--c-ink-600);
    padding: 0;
}
.print-field--inline {
    flex-direction: row;
    align-items: center;
    gap: 0.5rem;
}
.print-field--stack > span {
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--c-ink-600);
}
.print-field select,
.print-dims input {
    font: inherit;
    padding: 0.4rem 0.55rem;
    border: 1px solid var(--c-line);
    border-radius: var(--r-input);
    background: var(--c-paper);
    color: var(--c-ink-900);
}

/* Segmented toggle (mode / orientation) */
.print-segment {
    display: inline-flex;
    border: 1px solid var(--c-line);
    border-radius: var(--r-pill);
    overflow: hidden;
    width: fit-content;
}
.print-segment__opt { position: relative; }
.print-segment__opt input {
    position: absolute;
    opacity: 0;
    inset: 0;
    margin: 0;
    cursor: pointer;
}
.print-segment__opt span {
    display: block;
    padding: 0.4rem 0.85rem;
    font-size: 0.85rem;
    color: var(--c-ink-600);
    cursor: pointer;
}
.print-segment__opt input:checked + span {
    background: var(--c-field-700);
    color: #fff;
}
.print-segment__opt input:focus-visible + span {
    outline: 2px solid var(--c-field-300);
    outline-offset: -2px;
}

/* Generic layout mockups (page outline + N placeholder cells) */
.print-mockups {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
}
.print-mockup {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.25rem;
    cursor: pointer;
}
.print-mockup input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
}
.print-mockup__page {
    display: grid;
    grid-template-columns: repeat(var(--cols, 2), 1fr);
    grid-template-rows: repeat(var(--rows, 2), 1fr);
    gap: 2px;
    width: 34px;
    height: 44px;
    padding: 3px;
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: 3px;
    transition: border-color 120ms ease, box-shadow 120ms ease;
}
.print-mockup__page.is-landscape { width: 44px; height: 34px; }
.print-mockup__cell {
    background: var(--c-field-50);
    border: 1px solid var(--c-field-300);
    border-radius: 1px;
}
.print-mockup input:checked + .print-mockup__page {
    border-color: var(--c-field-700);
    box-shadow: 0 0 0 2px var(--c-field-300);
}
.print-mockup input:focus-visible + .print-mockup__page {
    box-shadow: 0 0 0 2px var(--c-field-300);
}
.print-mockup__label {
    font-size: 0.78rem;
    color: var(--c-ink-600);
}

/* M2: prominent "paper vs. wristband" chooser at the top of the panel.
   Two large labeled cards, each with a generic mockup of the output, so
   the wristband option is discoverable up front rather than reading as a
   small toggle. */
.print-mode-choice { margin-bottom: 0.25rem; }
.print-mode-cards {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.6rem;
}
.print-mode-card { position: relative; cursor: pointer; }
.print-mode-card input {
    position: absolute;
    opacity: 0;
    inset: 0;
    margin: 0;
    cursor: pointer;
}
.print-mode-card__body {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    height: 100%;
    padding: 0.75rem 0.6rem;
    text-align: center;
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-input);
    transition: border-color 120ms ease, box-shadow 120ms ease;
}
.print-mode-card input:checked + .print-mode-card__body {
    border-color: var(--c-field-700);
    box-shadow: 0 0 0 2px var(--c-field-300);
}
.print-mode-card input:focus-visible + .print-mode-card__body {
    box-shadow: 0 0 0 2px var(--c-field-300);
}
.print-mode-card__title {
    display: block;
    font-size: 0.9rem;
    font-weight: 700;
    color: var(--c-ink-900);
}
.print-mode-card__hint {
    display: block;
    margin-top: 0.15rem;
    font-size: 0.76rem;
    color: var(--c-ink-600);
}
/* Generic output mockups — a paper sheet of cells vs. a wide wristband
   strip of cells. Purely decorative (aria-hidden in the markup). */
.print-mode-card__mock {
    display: grid;
    gap: 2px;
    padding: 3px;
    background: var(--c-field-900);
    border-radius: 3px;
}
.print-mode-card__mock span {
    background: var(--c-field-50);
    border-radius: 1px;
}
.print-mode-card__mock--sheet {
    width: 34px;
    height: 44px;
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: repeat(2, 1fr);
}
.print-mode-card__mock--band {
    width: 56px;
    height: 22px;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, 1fr);
}

.print-presets {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}
.print-preset { font-size: 0.78rem; padding: 0.3rem 0.6rem; }
.print-dims {
    display: flex;
    gap: 0.6rem;
}
.print-dims label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.78rem;
    color: var(--c-ink-600);
}
.print-dims input { width: 5rem; }

.print-bulk { display: flex; gap: 0.4rem; }
.print-play-list {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 16rem;
    overflow-y: auto;
    border: 1px solid var(--c-line);
    border-radius: var(--r-input);
}
.print-play-list li { border-bottom: 1px solid var(--c-line-soft); }
.print-play-list li:last-child { border-bottom: none; }
.print-play-list label {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    padding: 0.4rem 0.6rem;
    font-size: 0.88rem;
    cursor: pointer;
}

/* Live preview pane + the printable sheets themselves */
.print-preview {
    flex: 1 1 360px;
    min-width: 0;
    background: var(--c-field-50);
    border-radius: var(--r-card);
    padding: 1.25rem;
    overflow: auto;
}
.print-pages {
    display: flex;
    flex-direction: column;
    /* NOT ``center``. The page box is sized in INCHES by #print-dynamic (11in
       wide in landscape), which can exceed the preview panel. Centering an
       over-wide child inside an ``overflow: auto`` parent pushes the overflow
       to BOTH sides and makes the leading edge unscrollable — coaches saw the
       left and right columns clipped with only the middle column reachable.
       Aligning to the start keeps every column scrollable; ``width:
       max-content`` lets this flex container size to the page so the scroll
       range covers it. The zoom-to-fit below normally avoids scrolling at all;
       this is the correctness floor for when it can't. */
    align-items: flex-start;
    gap: 1.25rem;
}

/* SCREEN ONLY, and PLAYBOOK MODE ONLY. ``max-content`` makes the scroll range
   cover an over-wide sheet in the preview, but it must reach neither of two
   other cases:
     * in PRINT it stopped the wristband flex row from ever wrapping at the page
       edge — the third band ran off the paper (hence @media screen);
     * on SCREEN in WRISTBAND mode it broke the wrap just as thoroughly. A
       wrapping flex container sized to ``max-content`` resolves to its
       SINGLE-LINE sum, so every band laid out in one long overflowing row while
       the paper showed a grid — the preview contradicted the print. The
       wristband container gets a definite sheet width from #print-dynamic
       instead (see applyLayout).
   Hence the :not() — playbook mode still gets its scrollable over-wide sheet. */
@media screen {
    .print-shell:not([data-mode="wristband"]) .print-pages {
        width: max-content;
        min-width: 100%;
    }
}

/* WRISTBAND CARD: title + diagram, diagram dominant — matching the reference
   sheet coaches actually use. The call number is documented as the one token
   that must survive on every band (PRINT-NUM), so it stays, but as a small
   corner stamp instead of its own full-width block line: as a block it plus the
   bold left-aligned title plus the matchup left the play diagram a few pixels
   tall. Centring the title mirrors the reference and reads better on a sleeve. */
.print-shell[data-mode="wristband"] .play-card {
    position: relative;
}
.print-shell[data-mode="wristband"] .play-card__num {
    position: absolute;
    top: 0;
    left: 0;
    padding: 0 0.12rem;
    font-size: 0.42rem;
    font-weight: 700;
    line-height: 1.2;
    color: #6b7280;
}
/* WB-TITLE: a FIXED-height title strip. Reserving the height (rather than
   letting the strip size to its content) is what makes the diagrams uniform:
   a two-line name like "DOUBLE BACK REVERSE RIGHT" otherwise stole a line's
   worth of card from its own diagram, so that card's play drew visibly smaller
   than its neighbours on the same band -- the "lack of uniformity" again, at
   card scale. Deliberately NOT ellipsis/nowrap: the shrink-to-fit pass (BN-1)
   already steps --title-fs down until the whole name fits, so reserving the
   strip keeps every character AND a constant diagram area. Mixed case at a
   modest weight mirrors the reference sleeve. */
.print-shell[data-mode="wristband"] .play-card__title {
    height: 1.15rem;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    /* Cap the shrink-to-fit seed: the playbook-sheet size is far too heavy on
       a card a couple of inches wide. */
    font-size: min(var(--title-fs, 0.7rem), 0.46rem);
    font-weight: 600;
    text-transform: none;
    letter-spacing: 0.01em;
    line-height: 1.12;
    padding: 0 0.1rem 0 0.62rem;
    border-bottom: 0;
    overflow: hidden;
}
/* WB-FIELD: match the reference sleeve -- a WHITE card carrying a single
   line of scrimmage, not an off-white field striped with yard lines. At
   wristband scale the 10-yard hash marks are noise a coach reads past; the
   LOS is the one line that carries meaning (it tells them which way the
   play goes). The renderer emits nine hash lines at y=10..90, so the fifth
   is the y=50 line the plays are centred on -- see
   test_wristband_los_is_fifth_hashmark, which pins that contract so this
   nth-child selector can't silently start dimming the wrong line. */
.print-shell[data-mode="wristband"] .play-field-bg {
    fill: #ffffff;
}
.print-shell[data-mode="wristband"] .play-field-hashmarks line {
    display: none;
}
.print-shell[data-mode="wristband"] .play-field-hashmarks line:nth-child(5) {
    display: inline;
    stroke: #9ca3af;
    stroke-opacity: 1;
    stroke-width: 0.16;
}
/* Playbook sheets get the same clean card as the wristband (owner-approved
   mockup, 2026-10-04, against resources/Spring2026_Plays.jpg): white field,
   ONE light line of scrimmage, no other yard lines. Same nth-child(5) rule
   as above: the renderer emits nine hash lines and plays centre on y=50. */
.print-shell[data-mode="playbook"] .play-field-bg {
    fill: #ffffff;
}
.print-shell[data-mode="playbook"] .play-field-hashmarks line {
    display: none;
}
.print-shell[data-mode="playbook"] .play-field-hashmarks line:nth-child(5) {
    display: inline;
    stroke: #c7cdd4;
    stroke-opacity: 1;
    stroke-width: 0.14;
}
/* Bold, narrow letters in the player marks: they read at wristband size
   where the regular weight looked thin. Slightly lighter route lines keep
   the marks the strongest thing on the card. Print sheets only. */
.print-shell .play-label {
    font-family: "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
    font-weight: 800;
    letter-spacing: -0.02em;
}
.print-shell .play-route {
    stroke-width: 0.32;
}
/* The diagram takes every remaining pixel. */
.print-shell[data-mode="wristband"] .play-card__svg {
    flex: 1 1 auto;
    min-height: 0;
}

/* Wristband bands tile on SCREEN too, so the preview matches the sheet. The
   playbook mode keeps its single-column stack of full sheets. */
.print-shell[data-mode="wristband"] .print-pages {
    flex-direction: row;
    flex-wrap: wrap;
    align-content: flex-start;
    align-items: flex-start;
}
.print-shell[data-mode="wristband"] .print-page {
    flex: 0 0 auto;
    outline: 1px dashed #9aa39d;
}

/* SCREEN-ONLY zoom-to-fit for the preview. ``--preview-zoom`` is set from JS
   (panel width / declared SHEET width) after the page box is sized. ``zoom`` is
   used rather than ``transform: scale()`` because it also shrinks the LAID-OUT
   box, so the panel doesn't keep a full-size scroll area with the artwork
   floating in it. Inside ``@media screen`` so the real print output is
   untouched.

   ON THE CONTAINER, not on each page. Because ``zoom`` really does affect
   layout, zooming each BAND changed where the flex row wraps: at zoom 0.35 six
   4in bands each occupied ~136px, so all six fitted one 960px row and the
   preview showed 6-across-by-1-down where the paper prints 2-across-by-3-down.
   Zooming the container scales the whole tiled sheet as one unit, so the bands
   wrap at their true inch positions and the preview matches the print.
   Playbook mode is unaffected — scaling the column scales the sheets in it. */
@media screen {
    .print-preview .print-pages {
        zoom: var(--preview-zoom, 1);
    }
}
.print-page {
    display: grid;
    background: var(--c-paper);
    box-shadow: 0 2px 12px rgba(15, 29, 23, 0.12);
    /* width / height / grid-template / gap come from #print-dynamic */
}
.print-empty { color: var(--c-ink-400); }

.play-card {
    border: 1px solid #444;
    border-radius: 4px;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    min-height: 0;
    min-width: 0;
    break-inside: avoid;
}
.play-card__title {
    /* BN-1: the title font-size is the SHRINK-TO-FIT floor seed. The
       inline applyLayout() pass in print_page.html steps a per-card
       --title-fs custom property down from this value until the full
       name fits its cell — so we drive font-size off that variable and
       fall back to 0.7rem when the JS hasn't run yet (or is disabled). */
    font-size: var(--title-fs, 0.7rem);
    font-weight: 700;
    /* Print + preview play names render uppercase to match the
       playbook detail tiles + the editor header. */
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 0.15rem 0.35rem 0.22rem;
    border-bottom: 1px solid #ccc;
    /* P1b(a): wrap names at WORD boundaries only — never split inside a
       word. The shrink-to-fit pass drops the font toward its floor so the
       full name fits; if a word still can't fit the cell width it wraps
       whole (overflow-wrap:normal / word-break:normal / hyphens:none), so
       a token like "VERTICALS" can never shear to "VERTIC ALS". */
    white-space: normal;
    overflow-wrap: normal;
    word-break: normal;
    hyphens: none;
    line-height: 1.15;
    /* P1b(b,c): CAP the title block so a long name can never consume the
       whole tile and kill the diagram. The block is capped at ~38% of the
       card (--title-cap-h, set per card by applyLayout()); the
       shrink-to-fit JS first steps --title-fs down to fit that cap, and
       only if the FULL name still won't fit at the floor does the block
       ellipsize at the cap boundary with a real "…". The extra bottom
       padding (above) keeps the last visible line whole, not half-cut. */
    flex: 0 0 auto;
    max-height: var(--title-cap-h, none);
    overflow: hidden;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: var(--title-lines, 3);
    line-clamp: var(--title-lines, 3);
    text-overflow: ellipsis;
}
/* PRINT-NUM / PRINT-1: the call number is the primary scan token a coach
   reads under pressure, so it is heavy and LARGE (sized off the
   shrink-to-fit --title-fs so it scales with the card, with a 0.95rem
   floor so the densest bands stay legible) and tinted field-green so it
   reads as a label. It is its OWN row on the card — NOT nested in the
   title — so the dense-band auto-hide (.play-card--noname) drops the
   name/matchup for legibility but the number ALWAYS survives. Always
   present on a real play (plan call-order ordinal, else the 1..N
   play-order fallback). */
.play-card__num {
    flex: 0 0 auto;
    font-weight: 900;
    font-size: max(0.95rem, calc(var(--title-fs, 0.7rem) * 1.35));
    line-height: 1.05;
    color: #15803d;
    padding: 0.1rem 0.35rem 0.05rem;
    font-variant-numeric: tabular-nums;
}
/* PRINT-1: auto-hide drops the NAME (and matchup) on a too-dense cell for
   legibility, but never the call number — the number row is excluded. */
.play-card--noname .play-card__title { display: none; }
.play-card__svg { flex: 1 1 auto; min-height: 0; }
.play-card__svg svg { width: 100%; height: 100%; display: block; }
/* Defensive Matchup line — a muted, smaller-than-title note under the
   diagram so a coach reading a wristband sees "what's this good against"
   at a glance. The value is self-describing ("Best against …") so no
   label is printed. Hidden when the "Show defensive matchup" toggle is
   off (.play-card--nomatchup). */
.play-card__matchup {
    font-size: 0.6rem;
    line-height: 1.25;
    color: #555;
    padding: 0.12rem 0.35rem;
    border-top: 1px solid #ddd;
    white-space: normal;
    overflow: hidden;
    /* BN-2: never clip a matchup line mid-word. Clamp to at most two
       lines (line-clamp ellipsizes cleanly at the 2-line boundary
       instead of shearing a third line), and reserve enough row height
       (min-height ≈ 2 lines + the cell's vertical padding) so the
       descenders on the last visible line aren't sheared by the card's
       overflow:hidden. When the matchup cannot fit at all the row is
       dropped wholesale by applyLayout() adding .play-card--nomatchup —
       so a coach sees a complete line or no line, never a half-cut one. */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    min-height: calc(2 * 0.6rem * 1.25 + 0.24rem);
}
.play-card--nomatchup .play-card__matchup { display: none; }

/* Print field colors.
 *
 * The on-screen editor uses a dark-green field (chalk-talk realism)
 * with white hash marks. That contrast is great on a monitor but
 * reads as a heavy ink block on paper — the white hash marks
 * disappear into the green and route lines sit on a busy background.
 *
 * For both the live print preview (``.print-preview`` panel on the
 * print page) AND the printed sheet (``@media print``) we swap the
 * field to off-white and the hash marks to mid-grey. SVG ``fill`` /
 * ``stroke`` *attributes* are presentation defaults; the CSS rules
 * below win without touching the renderer.
 */
.print-preview .play-field-bg {
    fill: #f5f5f5;
}
.print-preview .play-field-hashmarks line {
    stroke: #c0c0c0;
    stroke-opacity: 1;
}
/* The preview shows what the paper will: no route casing (see the
   ``@media print`` rule below for why). */
.print-preview .play-routes {
    filter: none;
}
.print-preview .play-route--qb-drop {
    display: none;
}

@media (max-width: 720px) {
    .print-shell__panel { flex-basis: 100%; }
}

/* --------------------------------------------------------------------
    Print — every page: no header, white sheet, no shell padding
   -------------------------------------------------------------------- */
@media print {
    .app-header,
    .app-shell > .app-header,
    .print-screen-only,
    #teascan-offline-banner { display: none !important; }
    body { background: #fff; }
    .app-shell,
    .app-main { margin: 0 !important; padding: 0 !important; max-width: none !important; box-shadow: none !important; }
    /* Routes print as VECTOR lines. The on-screen casing below
       (``.play-routes { filter: drop-shadow(...) }``) makes Chrome flatten
       the whole route layer to a 300 ppi bitmap with a blurred halo, which
       is what made printed plays look soft next to other programs. On
       white paper the casing has nothing to separate the line from. */
    .play-routes { filter: none !important; }
    /* The QB's short dropback set (``play-route--qb-drop``, see
       QB_DROP_MAX_YARDS in app/field/renderer.py) printed as a stray nub
       under the QB. Not printed (owner, 2026-10-04). */
    .play-route--qb-drop { display: none !important; }
}

/* --------------------------------------------------------------------
    Print page (Req 11) — print-media layout
   -------------------------------------------------------------------- */
@media print {
    .print-shell { display: block; }
    .print-preview {
        background: #fff;
        padding: 0;
        margin: 0;
        overflow: visible;
        border-radius: 0;
    }
    .print-pages { display: block; }
    .print-page {
        box-shadow: none;
        margin: 0;
        break-after: page;
        page-break-after: always;
    }
    .print-page:last-child { break-after: auto; page-break-after: auto; }

    /* WRISTBAND MODE: bands TILE onto the sheet instead of taking one sheet
       each. A wristband used to be the @page itself, so a coach printing bands
       for a squad got one band per sheet and a stack of mostly-blank paper.
       The page is now real paper (set by #print-dynamic), each ``.print-page``
       is one band sized in inches, and the forced per-band break is removed so
       the browser packs bands and breaks only when the sheet is full.
       ``break-inside: avoid`` keeps a band from being split across the fold —
       half a wristband is useless. The dashed outline is the cut guide. */
    .print-shell[data-mode="wristband"] .print-pages {
        display: flex;
        flex-direction: row;
        flex-wrap: wrap;
        align-content: flex-start;
        align-items: flex-start;
    }
    .print-shell[data-mode="wristband"] .print-page {
        break-after: auto;
        page-break-after: auto;
        break-inside: avoid;
        page-break-inside: avoid;
        outline: 1px dashed #9aa39d;
        outline-offset: 0;
        flex: 0 0 auto;
    }

    /* Off-white field + mid-grey hash marks while printing — same
       overrides applied in the on-screen preview above, repeated here
       so a coach who saves the page as PDF (which bypasses the
       ``.print-preview`` host) still gets the legible field. */
    .play-field-bg { fill: #f5f5f5; }
    .play-field-hashmarks line {
        stroke: #c0c0c0;
        stroke-opacity: 1;
    }
}

/* ============================================================
   Sprint 1.4 — Keyboard shortcuts cheat-sheet modal
   ============================================================ */
.editor-shortcuts-modal {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.55);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    padding: 1rem;
}
.editor-shortcuts-modal[hidden] {
    display: none;
}
.editor-shortcuts-modal__panel {
    background: #ffffff;
    border-radius: 0.75rem;
    box-shadow: 0 24px 48px rgba(15, 23, 42, 0.25);
    max-width: 32rem;
    width: 100%;
    max-height: 80vh;
    overflow-y: auto;
    padding: 1.5rem;
    color: #0f172a;
}
.editor-shortcuts-modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 1rem;
}
.editor-shortcuts-modal__title {
    margin: 0;
    font-size: 1.25rem;
    font-weight: 600;
}
.editor-shortcuts-modal__close {
    background: none;
    border: none;
    color: #64748b;
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
    padding: 0.25rem 0.5rem;
    border-radius: 0.25rem;
}
.editor-shortcuts-modal__close:hover {
    background: #f1f5f9;
    color: #0f172a;
}
.editor-shortcuts-modal__section + .editor-shortcuts-modal__section {
    margin-top: 1.25rem;
}
.editor-shortcuts-modal__section-title {
    margin: 0 0 0.5rem;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #64748b;
    font-weight: 600;
}
.editor-shortcuts-modal__list {
    list-style: none;
    padding: 0;
    margin: 0;
}
.editor-shortcuts-modal__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.35rem 0;
    border-bottom: 1px solid #f1f5f9;
}
.editor-shortcuts-modal__row:last-child {
    border-bottom: none;
}
.editor-shortcuts-modal__label {
    color: #0f172a;
}
.editor-shortcuts-modal__combo {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
}
.editor-shortcuts-modal__key {
    background: #f1f5f9;
    border: 1px solid #e2e8f0;
    border-radius: 0.25rem;
    padding: 0.1rem 0.45rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.8rem;
    color: #0f172a;
    box-shadow: inset 0 -1px 0 #cbd5e1;
}
.editor-shortcuts-modal__plus {
    color: #94a3b8;
    font-size: 0.75rem;
}
.editor-shortcuts-modal__footer {
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px solid #f1f5f9;
    color: #64748b;
    font-size: 0.85rem;
}

.editor-toolbar__btn--shortcut {
    min-width: 2rem;
    padding-left: 0.5rem;
    padding-right: 0.5rem;
    font-weight: 600;
}

/* ============================================================
   Sprint 2.2 — Variations picker modal
   ============================================================ */
.editor-variations-modal {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.55);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    padding: 1rem;
}
.editor-variations-modal[hidden] { display: none; }
.editor-variations-modal__panel {
    background: #ffffff;
    border-radius: 0.75rem;
    box-shadow: 0 24px 48px rgba(15, 23, 42, 0.25);
    max-width: 56rem;
    width: 100%;
    max-height: 86vh;
    overflow-y: auto;
    padding: 1.5rem;
    color: #0f172a;
}
.editor-variations-modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.5rem;
}
.editor-variations-modal__title {
    margin: 0;
    font-size: 1.25rem;
    font-weight: 600;
}
.editor-variations-modal__close {
    background: none;
    border: none;
    color: #64748b;
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
    padding: 0.25rem 0.5rem;
    border-radius: 0.25rem;
}
.editor-variations-modal__close:hover {
    background: #f1f5f9;
    color: #0f172a;
}
.editor-variations-modal__lede {
    color: #64748b;
    font-size: 0.9rem;
    margin: 0 0 1rem;
}
.editor-variations-modal__empty {
    color: #64748b;
    margin: 1rem 0;
}
.editor-variations-modal__grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
}
@media (min-width: 768px) {
    .editor-variations-modal__grid {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}
.editor-variations-modal__tile {
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 0.5rem;
    padding: 0.75rem;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.5rem;
    cursor: pointer;
    transition: transform 0.08s ease, border-color 0.08s ease,
        box-shadow 0.08s ease;
}
.editor-variations-modal__tile:hover {
    border-color: #2563eb;
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(37, 99, 235, 0.15);
}
.editor-variations-modal__thumb {
    display: block;
    width: 100%;
    aspect-ratio: 27 / 20;
    overflow: hidden;
    border-radius: 0.375rem;
    background: #2e7d32;
}
.editor-variations-modal__thumb svg {
    width: 100%;
    height: 100%;
    display: block;
}
.editor-variations-modal__label {
    font-size: 0.9rem;
    font-weight: 600;
    color: #0f172a;
    text-align: center;
}
.editor-variations-modal__rationale {
    display: block;
    margin-top: 0.25rem;
    color: #475569;
    font-size: 0.78rem;
    line-height: 1.3;
    text-align: left;
}

/* --------------------------------------------------------------------------
   Global footer (legal links on every page) + legal page body.
   -------------------------------------------------------------------------- */
.app-footer {
    margin-top: 3rem;
    padding: 1.5rem 1.25rem 2rem;
    border-top: 1px solid #e2e8f0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    color: #64748b;
    font-size: 0.85rem;
}
.app-footer__links {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 1.25rem;
}
.app-footer__links a {
    color: #475569;
    text-decoration: none;
}
.app-footer__links a:hover {
    text-decoration: underline;
}
.app-footer__copy {
    margin: 0;
    color: var(--c-ink-400);
}

/* Legal pages (Privacy / Terms): readable long-form column. */
.legal-body {
    max-width: 70ch;
    margin: 1.5rem auto 0;
    color: #334155;
    line-height: 1.6;
}
.legal-body h2 {
    margin: 1.75rem 0 0.5rem;
    font-size: 1.05rem;
    color: #0f172a;
}
.legal-body ul {
    margin: 0.5rem 0 0.5rem 1.25rem;
    padding: 0;
}
.legal-body li {
    margin: 0.35rem 0;
}
/* Content pages (SEO T-02: /learn, /flag-football-plays): the legal-body
   prose plus tables, play diagrams and a breadcrumb. */
.content-body h3 {
    margin: 1.5rem 0 0.4rem;
    font-size: 1rem;
    color: #0f172a;
}
.content-body table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
    border-collapse: collapse;
    margin: 1rem 0;
    font-size: 0.9rem;
}
.content-body th,
.content-body td {
    border: 1px solid #e2e8f0;
    padding: 0.45rem 0.6rem;
    text-align: left;
    vertical-align: top;
}
.content-body th {
    background: #f8fafc;
    color: #0f172a;
}
.content-play {
    margin: 1rem 0 1.25rem;
}
/* Content pages sit inside .landing, whose hero animation hides and redraws
   every .play-route; a diagram here is a static, fully drawn play. */
.landing .content-play .play-route {
    stroke-dasharray: none;
    stroke-dashoffset: 0;
    animation: none;
}
.landing .content-play .play-arrow {
    opacity: 1;
    animation: none;
}
/* The QB's short dropback set (``play-route--qb-drop``) ends under the QB's
   own marker and shows only as a nub; a public diagram leaves it out, as
   print does. */
.content-play .play-route--qb-drop {
    display: none;
}
.content-play__field svg {
    display: block;
    width: 100%;
    max-width: 32rem;
    height: auto;
    border-radius: 0.5rem;
    border: 1px solid #e2e8f0;
}
/* The guide diagrams use the PRINTED card's look (owner, 2026-10-16: "the
   printed versions look more polished"): white field, one light line of
   scrimmage, bold narrow labels, the lighter route line. Same rules as the
   print sheet's .print-shell block; nth-child(5) is the line of scrimmage
   (the renderer emits nine yard lines and plays centre on y=50). */
.content-play .play-field-bg {
    fill: #ffffff;
}
.content-play .play-field-hashmarks line {
    display: none;
}
.content-play .play-field-hashmarks line:nth-child(5) {
    display: inline;
    stroke: #c7cdd4;
    stroke-opacity: 1;
    stroke-width: 0.14;
}
.content-play .play-label {
    font-family: "Arial Narrow", "Helvetica Neue", Arial, sans-serif;
    font-weight: 800;
    letter-spacing: -0.02em;
}
.content-play .play-route {
    stroke-width: 0.32;
}
/* Smaller player marks. These plays sit on a 25-yard field, so at full
   size the marks touched (QB and RB are 3 yd apart) and covered most of a
   4-yard route. Each shape and its letter shrink around their own centre,
   so positions and routes stay exactly where the play puts them. */
.content-play .play-marker > * {
    transform-box: fill-box;
    transform-origin: center;
    transform: scale(0.72);
}
/* The guides index (/learn). */
.content-guides {
    list-style: none;
    margin: 1.5rem 0 0;
    padding: 0;
    display: grid;
    gap: 1.25rem;
    max-width: 44rem;
}
.content-guides__item {
    padding: 1rem 1.25rem;
    background: #ffffff;
    border: 1px solid #e2e8f0;
    border-radius: 0.75rem;
}
.content-guides__title {
    margin: 0 0 0.35rem;
    font-size: 1.15rem;
}
.content-guides__item p {
    margin: 0;
    color: #475569;
}
/* The home page's line to the guides, under "Three steps". */
.landing-steps__guides {
    margin: 1.5rem auto 0;
    max-width: 44rem;
    text-align: center;
}
/* A wide figure (the route tree's row of receivers) gets more room. */
.content-figure__wide svg {
    max-width: 44rem;
}
.content-play figcaption {
    margin-top: 0.4rem;
    font-size: 0.85rem;
    color: #475569;
}
.content-crumbs {
    margin: 0 0 0.75rem;
    font-size: 0.85rem;
    color: #475569;
}
.content-facts dt {
    font-weight: 700;
    color: #0f172a;
}
.content-facts dd {
    margin: 0 0 0.6rem;
}
.content-hub {
    max-width: 70ch;
    margin: 1.5rem auto 0;
}

/* Roster game-day check-in (Feature 1). Sits above the player list
   on the roster detail page so the coach can flip the whole roster
   at once, then mark individual absences. The "OUT" badge greys
   out a player row without removing it. */
.roster-checkin {
    margin: 0.75rem 0 1rem;
    padding: 0.7rem 1rem;
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 0.5rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: center;
    justify-content: space-between;
}
.roster-checkin__head {
    display: flex;
    align-items: baseline;
    gap: 0.65rem;
}
.roster-checkin__title {
    margin: 0;
    font-size: 0.9rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #475569;
    font-weight: 700;
}
.roster-checkin__count {
    margin: 0;
    color: #0f172a;
    font-size: 0.9rem;
}
.roster-checkin__count strong {
    font-weight: 700;
    color: #16a34a;
}
.roster-checkin__bulk {
    display: flex;
    gap: 0.5rem;
}
/* The player list takes its own full-width row under the header and bulk
   buttons; as a flex sibling it shrank to a narrow two-column strip on the
   right with a large empty area beside it. */
.roster-checkin > .play-list {
    flex: 1 1 100%;
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
}
.roster-checkin .play-list__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding-right: 0.6rem;
}
.roster-checkin .play-list__link {
    padding: 0.6rem 0.9rem;
}
.roster-checkin__out-badge {
    background: #94a3b8;
    color: #ffffff;
    font-size: 0.65rem;
    padding: 0.05rem 0.35rem;
    border-radius: 0.2rem;
    margin-left: 0.45rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    vertical-align: middle;
}
.play-list__item--unavailable .play-list__name {
    color: #94a3b8;
    text-decoration: line-through;
    text-decoration-color: #cbd5e1;
}

/* Quarter scheduler grid (Roster / Schedule). Rows are quarters,
   columns are position slots. Wrapped in an overflow container so
   a 9v9 schedule with 8 quarters can scroll horizontally on a
   phone without crowding the cells. */
.schedule-grid-wrap {
    overflow-x: auto;
    margin: 0.75rem 0;
    border: 1px solid #e2e8f0;
    border-radius: 0.5rem;
    background: #ffffff;
}
.schedule-grid {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
}
.schedule-grid th,
.schedule-grid td {
    padding: 0.5rem 0.6rem;
    border-bottom: 1px solid #e2e8f0;
    text-align: left;
}
.schedule-grid thead th {
    background: #f1f5f9;
    color: #0f172a;
    font-weight: 700;
    text-transform: uppercase;
    font-size: 0.75rem;
    letter-spacing: 0.04em;
}
.schedule-grid tbody th {
    background: #f8fafc;
    font-weight: 700;
    color: #1e3a8a;
    white-space: nowrap;
}
.schedule-grid select {
    width: 100%;
    min-width: 9rem;
    padding: 0.3rem 0.4rem;
    border: 1px solid #cbd5e1;
    border-radius: 0.3rem;
    background: #ffffff;
    font-size: 0.85rem;
}
.schedule-grid select.schedule-cell--conflict {
    /* Two cells in the same quarter point to the same player —
       the server will reject this on save (422). Paint the cell so
       the coach sees the issue before submitting. */
    border-color: #dc2626;
    background: #fef2f2;
    color: #b91c1c;
    box-shadow: 0 0 0 1px #dc2626 inset;
}

/* First-run editor tour (S1). Modal overlay that walks a new coach
   through the four core moves on initial editor load; self-gates
   via localStorage so it never reappears. */
.editor-tour {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.55);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    padding: 1rem;
}
.editor-tour__card {
    background: #ffffff;
    border-radius: 0.75rem;
    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.35);
    padding: 1.5rem;
    max-width: 28rem;
    width: 100%;
}
.editor-tour__title {
    margin: 0 0 0.5rem;
    font-size: 1.25rem;
    color: #0f172a;
    font-weight: 700;
}
.editor-tour__body {
    margin: 0 0 0.85rem;
    color: #475569;
    font-size: 0.9rem;
    line-height: 1.5;
}
.editor-tour__progress {
    margin: 0 0 1.1rem;
    color: #94a3b8;
    font-size: 0.75rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}
.editor-tour__actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.6rem;
}
.editor-tour__btn {
    padding: 0.55rem 1rem;
    border-radius: 0.4rem;
    border: 1px solid transparent;
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
}
.editor-tour__btn--ghost {
    background: transparent;
    color: #475569;
    border-color: #cbd5e1;
}
.editor-tour__btn--ghost:hover {
    background: #f1f5f9;
}
.editor-tour__btn--primary {
    background: #1e3a8a;
    color: #ffffff;
}
.editor-tour__btn--primary:hover {
    background: #1e40af;
}

/* Schedule v2: transposed grid (slots-as-rows / quarters-as-columns),
   bench row, settings disclosure, print layout. */
.schedule-settings {
    margin: 0.75rem 0;
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 0.5rem;
    padding: 0.5rem 0.85rem;
}
.schedule-settings__summary {
    cursor: pointer;
    font-weight: 600;
    color: #1e3a8a;
}
.schedule-settings__form { margin-top: 0.75rem; }
.schedule-settings__labels {
    margin: 0.75rem 0;
    padding: 0.5rem 0.85rem;
    border: 1px solid #cbd5e1;
    border-radius: 0.5rem;
}
.schedule-settings__labels legend {
    padding: 0 0.4rem;
    font-weight: 600;
    color: #475569;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.schedule-bench-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin-right: 0.5rem;
    font-size: 0.9rem;
    color: #475569;
    cursor: pointer;
}
.schedule-grid__bench th,
.schedule-grid__bench td {
    background: #fef3c7;
    font-size: 0.8rem;
    color: #78350f;
}

/* Wristband Q-card (item B). One card per quarter; the outer
   .wristband-sheet + .wristband-card containers come from the
   playbook wristband template so the cut/bleed pattern matches. */
.schedule-q-card__inner {
    display: flex;
    flex-direction: column;
    height: 100%;
    padding: 0.2in;
}
.schedule-q-card__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    border-bottom: 1px solid #cbd5e1;
    padding-bottom: 0.1in;
    margin-bottom: 0.1in;
}
.schedule-q-card__title {
    font-size: 1.1rem;
    font-weight: 800;
    color: #1e3a8a;
    letter-spacing: 0.04em;
}
.schedule-q-card__sub {
    font-size: 0.65rem;
    color: #475569;
}
.schedule-q-card__lineup {
    list-style: none;
    padding: 0;
    margin: 0;
    flex: 1;
}
.schedule-q-card__row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.05in 0;
    border-bottom: 1px dotted #e2e8f0;
    font-size: 0.8rem;
}
.schedule-q-card__slot {
    font-weight: 700;
    color: #0f172a;
}
.schedule-q-card__player {
    color: #1e3a8a;
}

/* Print: default to landscape so a 4-quarter / 9v9 grid fits a
   page without scaling. Hide all interactive chrome — only the
   title, summary line, and the grid (including bench if toggled
   on) should render on paper. */
@media print {
    @page {
        size: landscape;
        margin: 0.4in;
    }
    body { background: #ffffff; }
    .schedule-screen-only { display: none !important; }
    .schedule-page--print .schedule-screen-only { display: none !important; }
    .schedule-grid select {
        border: none;
        background: transparent;
        appearance: none;
        -webkit-appearance: none;
        padding: 0.1rem 0.2rem;
        min-width: 0;
        font-size: 0.85rem;
    }
    .schedule-grid-wrap { border: none; overflow: visible; }
    .schedule-grid th, .schedule-grid td { padding: 0.3rem 0.4rem; }
    /* Hide the global nav / header so paper doesn't waste space on
       links the coach can't tap. */
    nav, header.app-header { display: none !important; }
    /* ...and the global footer, for a stronger reason than wasted space: it
       is a block AFTER the last band, so once the bands fill a sheet it gets
       pushed onto a sheet of its OWN. A wristband print came out as
       "bands + one page holding nothing but '(c) Teascan LLC'" — a whole
       wasted sheet on every print. The legal links it carries are a
       requirement of the public SITE, not of a printed wristband. */
    footer.app-footer { display: none !important; }
    /* Keep the position chip's FILL when printing. Browsers drop backgrounds
       by default, which would silently return the printed schedule to the
       plain text the chip replaced.

       Lives in THIS block rather than a new trailing ``@media print`` of its
       own: ``tests/test_print_pages.py`` reads the LAST top-level print block,
       so appending one displaced the footer rule and failed the check that
       stops the site footer claiming its own sheet. One print block is also
       simply easier to reason about. */
    .slot-chip {
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }

    /* ---- Keep each side's grid WHOLE ------------------------------------
       A grid split across a page fold is unusable on a sideline. Both sections
       share a sheet when they fit; the defense takes its own when they do not.
       ``break-inside`` is the modern property and ``page-break-inside`` the one
       older Chrome honours — both, because this only matters on paper and a
       silent no-op there is invisible until a coach prints. */
    .schedule-sheet {
        break-inside: avoid;
        page-break-inside: avoid;
    }
    /* No forced break BETWEEN them: that would always cost two sheets, and the
       owner asked for one sheet where one is enough. */

    /* ---- Per-sheet header ---------------------------------------------- */
    .schedule-print-head {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: 0.6rem;
        margin: 0 0 0.35rem;
        padding-bottom: 0.2rem;
        border-bottom: 1px solid #111827;
        font-size: 0.8rem;
        color: #111827;
    }
    .schedule-print-head__left,
    .schedule-print-head__right {
        display: flex;
        align-items: baseline;
        gap: 0.5rem;
        min-width: 0;
    }
    .schedule-print-head__name { font-weight: 700; }
    /* The size sits BESIDE the date, per the owner, so one glance answers
       "which game, and how many a side". */
    .schedule-print-head__meta { white-space: nowrap; }
    /* Far right corner: the team, then the sheet index. */
    .schedule-print-head__team { font-weight: 700; }
    .schedule-print-head__page { font-variant-numeric: tabular-nums; }
    /* Set by the page when both grids share ONE sheet: a second header on the
       same sheet is the visible bug. A class rather than an inline style so the
       rule above can still turn the header on. */
    .schedule-print-head--dup { display: none !important; }

    /* The page title and summary line are redundant once every sheet carries
       its own header, and they cost the first sheet two lines of grid. */
    .playbook-detail__title, .game-day__sub { display: none !important; }

    /* ---- GRID MARKS (opt-in) -------------------------------------------
       Off: the clean read-only sheet this has always printed. On: every cell
       is ruled so a coach can write in it on the field. Scoped to a class the
       page sets from the checkbox, so it cannot change the screen. */
    .schedule-grid-marks .schedule-grid,
    .schedule-grid-marks .schedule-grid th,
    .schedule-grid-marks .schedule-grid td {
        border: 1px solid #111827 !important;
        border-collapse: collapse;
    }
    .schedule-grid-marks .schedule-grid { border-width: 2px !important; }
    /* Give a written-in cell room, and keep the borderless <select> from
       hiding the rule it now sits inside. */
    .schedule-grid-marks .schedule-grid td { padding: 0.45rem 0.4rem; }
    .schedule-grid-marks .schedule-grid-wrap { border: none; }
}

/* --------------------------------------------------------------------
   Landing — THE AI EDGE (three differentiators on midnight turf)
   -------------------------------------------------------------------- */
.ai-card-grid{display:grid;gap:2.25rem;grid-template-columns:1fr;margin-top:1rem}
.ai-card{position:relative}
/* Step number in scoreboard type: the three are done in this order. */
.ai-card__index{display:block;margin-bottom:.9rem;font-family:var(--font-display);font-size:3.5rem;line-height:.9;color:var(--c-endzone)}
.ai-card__motif{width:120px;height:96px;margin-bottom:1rem}
.ai-card__motif .ai-motif{width:120px;height:96px;display:block}
.ai-card__title{margin:0 0 .5rem 0;font-size:1.3rem;font-weight:700;letter-spacing:-.01em;color:#fff}
.ai-card__body{margin:0;color:rgba(255,255,255,.8);line-height:1.55;font-size:.95rem}
.ai-motif__prompt{fill:rgba(255,255,255,.1);stroke:rgba(255,255,255,.22);stroke-width:1}
.ai-motif__prompt-text{stroke:rgba(255,255,255,.55);stroke-width:2;stroke-linecap:round}
.ai-motif__route{stroke:var(--c-endzone);stroke-width:3;filter:drop-shadow(0 0 4px rgba(245,193,22,.5))}
.ai-motif__route--weak{stroke:rgba(255,255,255,.32);filter:none}
.ai-motif__arrow{fill:var(--c-endzone)}
.ai-motif__dot{fill:#fff}
.ai-motif__flag{fill:none;stroke:var(--c-endzone);stroke-width:2;opacity:.9}
.ai-motif__flag-mark{stroke:var(--c-endzone);stroke-width:2.4;stroke-linecap:round}
.ai-motif__zone{stroke-width:1}
.ai-motif__zone--beats{fill:color-mix(in srgb,var(--c-field-300) 30%,transparent);stroke:color-mix(in srgb,var(--c-field-300) 60%,transparent)}
.ai-motif__zone--stuffs{fill:rgba(214,39,40,.22);stroke:rgba(214,39,40,.5)}
.ai-motif__tag{font-family:var(--font-display);font-size:8px;letter-spacing:.12em;text-anchor:middle}
.ai-motif__tag--beats{fill:var(--c-field-300)}
.ai-motif__tag--stuffs{fill:#f0a3a3}
@media (prefers-reduced-motion:no-preference){.ai-motif__flag{animation:ai-flag-pulse 2.4s ease-in-out infinite}}
@keyframes ai-flag-pulse{0%,100%{opacity:.45;transform:scale(.92);transform-origin:96px 58px}50%{opacity:1;transform:scale(1.08);transform-origin:96px 58px}}
@media (min-width:760px){.ai-card-grid{grid-template-columns:repeat(3,1fr);gap:3rem}}

/* --------------------------------------------------------------------
   Landing — PLANS TEASER (compact 4-tier summary)
   -------------------------------------------------------------------- */
.plan-mini-grid{list-style:none;margin:0 0 1.4rem 0;padding:0;display:grid;gap:.85rem;grid-template-columns:repeat(2,1fr)}
.plan-mini{position:relative;background:var(--c-paper);border:1px solid var(--c-line);border-radius:var(--r-card);box-shadow:var(--shadow-soft);padding:1.1rem 1.1rem 1.15rem;display:flex;flex-direction:column;gap:.3rem}
.plan-mini--featured{border-color:var(--c-field-700);border-width:2px;box-shadow:var(--shadow-card)}
.plan-mini__name{font-family:var(--font-display);font-size:1.25rem;letter-spacing:.02em;color:var(--c-ink-900)}
.plan-mini__price{font-size:1.5rem;font-weight:800;letter-spacing:-.02em;color:var(--c-ink-900);line-height:1}
.plan-mini__price small{font-size:.8rem;font-weight:600;color:var(--c-ink-400);letter-spacing:0}
.plan-mini__ai{font-size:.82rem;font-weight:600;color:var(--c-field-700)}
.plan-mini__ai--off{color:var(--c-ink-400);font-weight:500}
/* Trial eligibility, per teaser card. Quieter than the credit line above it —
   it qualifies the price rather than selling the plan — and the --off variant
   is the same muted ink as .plan-mini__ai--off so "no AI" and "no trial" read
   as the same kind of statement. */
.plan-mini__trial{font-size:.78rem;font-weight:500;color:var(--c-ink-600)}
.plan-mini__trial--off{color:var(--c-ink-400)}
/* Game-plan verdict chips. Without these both states rendered in the
   identical .play-list__kind palette, so "Worked" and "Didn't work" were
   indistinguishable at a glance — on a sheet whose whole job is being read at
   a glance. Green/amber on the same muted weight as the Called chip. */
.play-list__kind[data-state="worked"]{color:var(--c-field-700);font-weight:600}
.play-list__kind[data-state="failed"]{color:#7a4b00;font-weight:600}
.plans-teaser__addon{margin:0 0 1.5rem 0;color:var(--c-ink-600);font-size:.95rem}
.plans-teaser__addon strong{color:var(--c-ink-900)}
.plans-teaser__cta{margin:0}
@media (min-width:760px){.plan-mini-grid{grid-template-columns:repeat(4,1fr)}}

/* --------------------------------------------------------------------
   Pricing — five-card grid + billing toggle
   -------------------------------------------------------------------- */
.pricing-grid--five{grid-template-columns:repeat(auto-fit,minmax(210px,1fr));max-width:1180px;align-items:stretch}
.price-card--free{background:var(--c-chalk)}
.price-card__feature--off{color:var(--c-ink-400)}
.price-card__save{margin:-.4rem 0 1rem 0;font-size:.78rem;font-weight:700;letter-spacing:.02em;color:var(--c-field-700)}
.pricing-grid.is-monthly .price-card__save{visibility:hidden}
@media (min-width:1024px){.price-card--featured{transform:translateY(-10px)}}
.billing-toggle{display:flex;align-items:center;justify-content:center;gap:.85rem;margin:.5rem auto 0}
.billing-toggle__label{font-family:var(--font-display);font-size:var(--t-eyebrow);letter-spacing:.14em;text-transform:uppercase;color:var(--c-ink-400)}
.billing-toggle__switch{display:inline-flex;background:var(--c-paper);border:1px solid var(--c-line);border-radius:var(--r-pill);padding:3px;gap:2px}
.billing-toggle__opt{margin:0;cursor:pointer}
.billing-toggle__input{position:absolute;opacity:0;width:1px;height:1px;pointer-events:none}
.billing-toggle__pill{display:inline-flex;align-items:center;gap:.4rem;padding:.45rem 1rem;border-radius:var(--r-pill);font-weight:600;font-size:.9rem;color:var(--c-ink-600);transition:background 140ms ease,color 140ms ease}
.billing-toggle__input:checked + .billing-toggle__pill{background:var(--c-field-700);color:#fff}
.billing-toggle__save{font-style:normal;font-size:.72rem;font-weight:700;padding:.08rem .4rem;border-radius:var(--r-pill);background:var(--c-endzone);color:var(--c-ink-900)}
.billing-toggle__input:focus-visible + .billing-toggle__pill{outline:3px solid var(--c-endzone-deep);outline-offset:2px}
.pricing__addon{margin:1.75rem auto 0;max-width:40rem;text-align:center;color:var(--c-ink-600);font-size:.95rem}
.pricing__addon strong{color:var(--c-ink-900)}
.pricing__tax-note{margin:1rem auto 0;max-width:40rem;text-align:center;color:var(--c-ink-600);font-size:.85rem}
@media (max-width:759px){.price-card--featured{order:-1}}
@media (min-width:760px){.price-card--featured{order:0}}

/* New-play page: two-column form-left / live-formation-preview-right.
   flex-wrap stacks on narrow widths; the explicit column rule below the
   720px breakpoint guarantees full-width stacking on phones (form first
   so the autofocused name field stays first in tab/visual order). The
   .auth-card margin override is SCOPED to this layout so login/signup
   etc. keep their centered card. */
.new-play-layout {
    display: flex;
    gap: 1.5rem;
    align-items: flex-start;
    flex-wrap: wrap;
}
.new-play-layout > .auth-card {
    margin: 1.5rem 0;
    flex: 1 1 380px;
    min-width: 0;
}
.formation-preview {
    flex: 0 1 300px;
    align-self: stretch;
    position: sticky;
    top: 1rem;
    background: var(--c-paper);
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
    padding: 1rem;
    box-shadow: var(--shadow-soft);
    margin: 1.5rem 0;
}
.formation-preview__title {
    margin: 0 0 0.75rem;
    font-size: 1rem;
    color: var(--c-ink-600);
}
.formation-preview__svg {
    max-width: 300px;
}
/* "Preview unavailable" fallback: the .play-list__thumbnail box is
   field-green (#2e7d32), so the message needs a light color to read. */
.formation-preview__placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    max-width: 300px;
    color: var(--c-paper);
    font-size: 0.9rem;
    text-align: center;
}
.formation-preview__caption {
    margin: 0.75rem 0 0;
    color: var(--c-ink-600);
    font-size: 0.95rem;
}

@media (max-width: 720px) {
    .new-play-layout { flex-direction: column; }
    .formation-preview {
        flex-basis: auto;
        width: 100%;
        position: static;
    }
}

/* Optional league-rule toggles on the starter form. A wrapped row of checkboxes
   above the free-text box; ticking one appends its sentence there so the coach
   can see what the AI is told. */
.starter-field__rule-toggles {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 1rem;
    margin: 0 0 0.6rem;
}
.starter-field__rule-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.88rem;
    color: var(--c-ink-900);
    cursor: pointer;
}

/* League-ruleset chooser: which of the four flag codes the AI designs under.
   Shared by BOTH AI entry points — the starter form and the single-play "new
   play with AI" form — so the class names are deliberately page-neutral rather
   than living in either page's ``starter-field__`` / ``auth-field__`` namespace.
   Change them with both surfaces in view.

   Stacked rather than a wrapped row (unlike the rule toggles above): each
   choice carries a second line naming its rulebook, and four two-line cards in
   a flex row wrap into unreadable ragged columns on a phone. */
.ruleset-choices {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin: 0 0 0.6rem;
}
.ruleset-choice {
    display: grid;
    /* The radio sits in column 1 and spans nothing; both text rows live in
       column 2, so the hint aligns under the label rather than under the
       control. */
    grid-template-columns: auto 1fr;
    gap: 0.1rem 0.55rem;
    align-items: baseline;
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--c-line);
    border-radius: var(--r-input);
    cursor: pointer;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}
.ruleset-choice:hover { border-color: var(--c-field-300); }
/* Keyboard focus has to land on the CARD, not just the native radio ring:
   the whole row is the click target, so the visible focus must match it. */
.ruleset-choice:focus-within {
    border-color: var(--c-field-300);
    box-shadow: 0 0 0 3px rgba(63, 181, 107, 0.22);
}
/* Selected state, decorative only — the native radio is what actually
   communicates the choice, so a browser without ``:has()`` support loses a
   tint and nothing else. */
.ruleset-choice:has(input:checked) {
    border-color: var(--c-field-300);
    background: var(--c-field-50);
}
.ruleset-choice input { margin: 0; }
.ruleset-choice__label {
    font-size: 0.92rem;
    font-weight: 600;
    color: var(--c-ink-900);
}
.ruleset-choice__blurb {
    grid-column: 2;
    font-size: 0.8rem;
    line-height: 1.45;
    color: var(--c-ink-600);
}

/* Founder-supplied AI disclaimer in the Terms (Section 7). Boxed and tinted so
   it reads as a standing caveat a reader should not skim past, without shouting
   like an error banner. */
.legal-disclaimer {
    margin: 0 0 1rem;
    padding: 0.85rem 1rem;
    border-left: 4px solid var(--c-field-700, #2F6F3E);
    border-radius: 4px;
    background: var(--c-field-50, #F1F7F2);
}

/* SLOT CHIP — a position label wearing the colour the coach chose for it.
   The schedule grid printed positions as plain text, so the colour that ties a
   row to the marker on the field diagram lived only in the drawing.

   ``background`` and ``color`` are set INLINE by the template, not here: they
   are the coach's data, and the ink is computed per background
   (``position_names.readable_ink_for``) because the palette spans both ends —
   #FF7F0E needs black, #1F77B4 needs white, and a fixed choice here would make
   half the chips unreadable. Everything that is not data lives in this rule. */
.slot-chip {
    display: inline-block;
    min-width: 2.1rem;
    padding: 0.12rem 0.42rem;
    border-radius: 0.28rem;
    font-weight: 700;
    font-size: 0.82rem;
    line-height: 1.35;
    text-align: center;
    letter-spacing: 0.01em;
    /* A hairline of the ink itself, so a chip whose colour is close to the page
       background still reads as a chip rather than as floating text. */
    box-shadow: inset 0 0 0 1px currentColor;
}

/* Inline hint under a settings field — used by the schedule label to say what
   an EMPTY box will fall back to, so "leave it blank" reads as a choice rather
   than an omission. */
/* Narrow screens: four columns in ~340px of width is unusable, so the row
   becomes slot + a stack. The colour swatches keep their own line, which is
   what they need anyway (nine of them wrap regardless below ~420px). */
@media (max-width: 720px) {
    .settings-position {
        grid-template-columns: 64px minmax(0, 1fr);
    }
    .settings-position__field--color {
        grid-column: 1 / -1;
    }
}

.settings-position__hint {
    margin: 0.18rem 0 0;
    font-size: 0.72rem;
    line-height: 1.3;
    color: #6b7280;
}

/* SIT-OUTS on the schedule bench. A coach's own DECISION ("Devon rests the
   4th") has to look different from a gap she has not filled yet — in a flat
   list of names the two are indistinguishable, and deciding first is the whole
   point of the control. */
/* Bench groups (owner, 2026-10-02): "Sitting out:" holds the coach's
   chosen rests (with x), "Not assigned:" the players with no spot yet. */
.bench-group { display: block; }
.bench-group + .bench-group { margin-top: 0.15rem; }
.bench-group__label {
    margin-right: 0.25rem;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: #6b7280;
}
.bench-group__label[hidden] { display: none; }
.bench-sit {
    display: inline-flex;
    align-items: center;
    gap: 0.15rem;
    margin: 0 0.2rem 0.15rem 0;
    padding: 0.05rem 0.2rem 0.05rem 0.35rem;
    border-radius: 0.7rem;
    background: #e5e7eb;
    color: #111827;
    font-size: 0.8rem;
    font-weight: 600;
}
.bench-sit__undo {
    border: 0;
    background: transparent;
    padding: 0 0.15rem;
    font-size: 0.9rem;
    line-height: 1;
    color: #6b7280;
    cursor: pointer;
}
.bench-sit__undo:hover,
.bench-sit__undo:focus-visible { color: #b91c1c; }
.bench-sit-picker {
    margin-left: 0.2rem;
    font-size: 0.75rem;
    padding: 0.05rem 0.15rem;
}
/* Print: the picker and the undo buttons are controls, not information. The
   NAMES stay — a printed schedule should still say who is resting. */
@media print {
    .bench-sit-picker,
    .bench-sit__undo { display: none !important; }
}

/* DEFENSIVE grid row headers. The header cell is the naming control: the coach
   looking at the grid is the one who knows what the row is, so she types it
   there rather than in a settings form somewhere else. The number stays
   visible so an unnamed row is still something you can point at.

   The flex lives on an inner DIV, not on the ``<th>``: ``display: flex`` on a
   table cell takes it out of the table formatting context, and the column
   widths stop agreeing with the header row above. */
.defense-row-head {
    display: flex;
    align-items: center;
    gap: 0.35rem;
}
.defense-row-head__num {
    flex: 0 0 auto;
    min-width: 1.1rem;
    font-size: 0.75rem;
    font-weight: 700;
    color: #9ca3af;
    text-align: right;
}
.defense-row-head__input {
    flex: 1 1 auto;
    min-width: 0;
    width: 100%;
    padding: 0.15rem 0.3rem;
    border: 1px solid #d1d5db;
    border-radius: 0.25rem;
    font: inherit;
    font-size: 0.85rem;
    background: #fff;
    color: #111827;
}
.defense-row-head__input::placeholder {
    color: #9ca3af;
    font-style: italic;
    font-weight: 400;
}
/* Print: an empty input is a control, not information — but a NAMED row must
   still print its name, so the border goes and the text stays. */
@media print {
    .defense-row-head__num { color: #111827; }
    .defense-row-head__input {
        border: 0;
        padding: 0;
        background: transparent;
        font-weight: 600;
    }
    .defense-row-head__input::placeholder { color: transparent; }
}

/* The Playing-time tally. Its own class because the obvious-looking
   ``play-list`` is a grid container for the <ul> lists elsewhere, and applying
   it to a <table> silently un-tables it. */
.schedule-tally th,
.schedule-tally td {
    padding: 0.3rem 0.4rem;
    border-bottom: 1px solid var(--c-line);
}
.schedule-tally thead th {
    font-size: 0.75rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #6b7280;
}

/* --------------------------------------------------------------------
   SCHEDULE PRINT LAYOUT (owner, 2026-08-28)
   Grid marks, offense/defense kept whole, and a per-sheet header.
   -------------------------------------------------------------------- */
/* The per-sheet header is PAPER ONLY. On screen the page already has a title
   and a summary line; a second copy above each grid is noise.
   
   Wrapped in ``@media screen`` deliberately. As a bare rule it sits AFTER the
   print block in this file, so it won out at print time and the header did not
   render at all — caught by printing to PDF and looking, not by reading the
   CSS. Scoping it to screen means the two rules never compete. */
@media screen {
    .schedule-print-head { display: none; }
}


/* --------------------------------------------------------------------
   AI-content acknowledgement (owner, 2026-08-30)

   A full-page overlay, because the ask is that the coach agrees BEFORE
   viewing the content — a banner beside it would be a notice, not an
   acknowledgement. ``position: fixed`` with a high z-index so it covers the
   editor's own toolbars and panels; the panel itself is centred and capped so
   the copy stays readable on a phone.

   Deliberately styleable without scripting: the accept control is a link, so
   there is no disabled/enabled state to manage and nothing to unhide. */
.ai-review-gate {
    position: fixed;
    inset: 0;
    z-index: 9000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.25rem;
    background: rgba(15, 23, 42, 0.72);
}
.ai-review-gate__panel {
    max-width: 32rem;
    width: 100%;
    background: var(--c-surface, #fff);
    border-radius: 0.75rem;
    padding: 1.5rem;
    box-shadow: 0 20px 45px rgba(15, 23, 42, 0.35);
}
.ai-review-gate__title {
    margin: 0 0 0.5rem;
    font-size: 1.15rem;
    line-height: 1.3;
}
.ai-review-gate__body {
    margin: 0 0 1.25rem;
    color: var(--c-text-600, #475467);
}
.ai-review-gate__ack {
    width: 100%;
    text-align: center;
}

/* --- Best-of-two pick page (Phase 2) --------------------------------------
   Two candidate plays side by side, and the layout is part of the
   experiment's validity: the cards are IDENTICAL in every visual respect so
   neither draws the eye. No accent on one, no ordering cue, no badge. A
   coach who can tell the candidates apart is voting on the difference she
   can see rather than on the play. Order is randomised server-side. */
.pick-play__options {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
    gap: 1.5rem;
    margin: 1.5rem 0;
}
.pick-play__option {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0.6rem;
    padding: 1rem;
    border: 1px solid var(--c-line);
    border-radius: var(--r-card);
    background: var(--c-paper);
}
.pick-play__diagram svg {
    display: block;
    width: 100%;
    height: auto;
}
.pick-play__name {
    margin: 0;
    font-weight: 600;
    color: var(--c-ink-900);
}
.pick-play__meta {
    margin: 0;
    font-size: var(--t-meta);
    color: var(--c-ink-600);
}
.pick-play__decline {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin-top: 1rem;
}
.pick-play__decline-note {
    font-size: var(--t-meta);
    color: var(--c-ink-400);
}
