/* ============================================================================
   dpt.v3.css — the APP BRAND KIT layer. Additive; loads AFTER dpt.v1.css and
   dpt.v2.css, which stay untouched.

   WHAT THIS IS
   The DPT App Brand Kit v1.0 (approved 2026-08-23) rendered as tokens and
   components the apps can actually consume. v1 owns the palette and the
   user-toggled theme. v2 owns the "Scoreboard, floating" surfaces. v3 owns
   ROLE-BASED mode, the app type scale, the rounded app shape, the charged
   accent, and the composite data-display components.

   THE ONE IDEA THAT MATTERS
   v1 and v2 flip with the USER's choice (data-theme + light-dark()). v3 flips
   with the SURFACE's job: athlete data is dark, money and forms are light,
   and neither asks the visitor. A screen declares .dpt-dark or .dpt-light on
   its root and every v1/v2 token underneath it is pinned to that pole with a
   plain value — not light-dark() — so the mode is identical on Safari 16 and
   Chrome 130. color-scheme is set on the same element so native controls,
   scrollbars and form widgets follow.

   That pinning is what lets an app adopt this kit without rewriting the rules
   it already has: existing CSS keeps reading --dp-surface / --dp2-card and
   simply gets the fixed-mode value.

   PUT THE ROLE CLASS ON <html>, NOT <body>.
   Every app here declares its alias block on :root — `--card: var(--dp-
   surface-raised)`. A custom property is substituted at the element where it
   is DECLARED, so those aliases are resolved on <html>. Put the role class on
   <body> and :root never sees the pinned value; the alias computes to the
   literal token stream `light-dark(#FFFFFF, #232D3B)`, which then happens to
   resolve correctly further down only because color-scheme is dark there.
   Measured:

       role on <body>   --card = light-dark(#FFFFFF, #232D3B)
       role on <html>   --card = #232D3B

   Both LOOK identical in a current browser, which is what makes this worth
   writing down. But the whole point of AMENDMENT 5 is to stop depending on
   light-dark() resolving, and the <body> placement quietly keeps that
   dependency for every aliased token. It also breaks outright for any value
   light-dark() cannot carry — a shadow, a border shorthand.

   Put it on <html>. Add it to <body> as well only if the app has body-level
   rules that need to outrank the kit.

   THE OTHER HALF OF THE SAME RULE: do not alias a --dp3-* token on :root.
   The mode tokens below exist ONLY inside .dpt-dark / .dpt-light. Aliasing
   one at :root when the role class is lower makes it invalid at computed-
   value time and it inherits as nothing — panels and borders disappear while
   the rest of the page still looks right.

   ROLE MAP (App Brand Kit section 07)
     .dpt-dark    portal stats/testing/progress, staff dashboard, roster,
                  ops, testing app
     .dpt-light   /book/, checkout, forms, account and billing, wp-admin hubs

   RULES
     1. Components consume --dp3-* / --dp-* / --dp2-* semantic tokens. Never a
        raw hex in an app rule.
     2. Electric green (--dp3-electric) is DARK SURFACES ONLY, and only on
        hero numerals, rings, active indicators and primary fills. Never on
        light. Never as body text.
     3. One glowing element per screen, maximum. Glow is dark-only.
     4. Status and score always ship a dot or icon PLUS a word. Never colour
        alone.
     5. Spacing here is --dp3-sp-*, not --sp-*. The WordPress child theme
        already defines --sp-1..6 on :root with DIFFERENT values (16/24/48/96
        against this kit's 12/16/24/32). An unprefixed scale would silently
        repaint the marketing site the moment /book/ loaded this file. Same
        collision class as --dp-green in wordpress.mjs; same fix.
     6. Changes land as numbered amendments in README.md, continuing the
        existing sequence. There is only one ledger.

   Fonts: Oswald (display + numerals) and Hanken Grotesk (UI text) for app
   surfaces. Roboto stays for anything still on the v1/v2 body stack and for
   the marketing site. All three are self-hosted variable woff2 in fonts/.
   ========================================================================= */

/* ---------------------------------------------------------------- font ---
   Hanken Grotesk, OFL-1.1, self-hosted for the same reason as Oswald and
   Roboto: the type must not depend on a third party being reachable from a
   gym wifi. One variable file covers 400-800. Same Google "latin" subset as
   the other two, so a non-latin glyph falls back to the system stack rather
   than pulling a second file.
   -------------------------------------------------------------------------*/
@font-face {
  font-family: 'Hanken Grotesk';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('fonts/hanken.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* --- type ---------------------------------------------------------------
     Oswald keeps the display job it already has. Hanken replaces Roboto for
     UI text on app surfaces only: it is a touch wider and rounder at small
     sizes, which is the difference between a 13px status chip being readable
     across a court and not. --dp-font-body is untouched so nothing that has
     not opted in moves. */
  --dp3-font-display: 'Oswald', 'Roboto Condensed', 'Arial Narrow', sans-serif;
  --dp3-font-ui: 'Hanken Grotesk', -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;

  /* Fixed px, not the clamp() scale in v1. The v1 sizes are built for a
     marketing page that spans a phone to a 27in monitor; these are app
     chrome, where a status chip is 13px on every device and growing it with
     the viewport just makes a dense table wrap. The hero numeral is the one
     exception and keeps a clamp. */
  --dp3-t-score-hero: clamp(56px, 14vw, 88px);  /* Oswald 600, line-height 1 */
  --dp3-t-score: 40px;                          /* Oswald 600, stat tiles */
  --dp3-t-h1: clamp(26px, 5vw, 32px);           /* Oswald 700, uppercase */
  --dp3-t-h2: 22px;                             /* Oswald 600 */
  --dp3-t-h3: 17px;                             /* Hanken 700 */
  --dp3-t-body: 16px;                           /* Hanken 400, 1.55 */
  --dp3-t-sub: 14px;                            /* Hanken 400-500 */
  --dp3-t-micro: 11px;                          /* Hanken 700 CAPS, +.08em */

  /* --- shape --------------------------------------------------------------
     The app scale is rounder than the site's 8px. v1's --dp-radius stays for
     anything not on this kit. */
  --dp3-r-sm: 8px;      /* inline elements, small tags */
  --dp3-r-md: 12px;     /* list rows, slot chips */
  --dp3-r-lg: 16px;     /* cards */
  --dp3-r-xl: 20px;     /* hero cards, sheets, modals */
  --dp3-r-pill: 999px;  /* buttons, chips, segmented controls */
  /* AMENDMENT 22: fields leave r-md. 12px inside a 16px card is optically the
     same curve, so the scale read as "pill + mush" instead of three steps
     (verified against 40 Mobbin references + M3's 4-8px field band, Jon
     approved 2026-08-28). Fields get their own step: 8 -> 16 -> pill, each
     visibly a step. List rows and slots STAY on r-md - only the thing you
     type into tightens. */
  --dp3-r-field: 8px;   /* text inputs, selects, textareas */

  /* --- space --- see RULE 5 above for why these are prefixed --- */
  --dp3-sp-1: 4px;  --dp3-sp-2: 8px;  --dp3-sp-3: 12px; --dp3-sp-4: 16px;
  --dp3-sp-5: 24px; --dp3-sp-6: 32px; --dp3-sp-7: 40px; --dp3-sp-8: 48px;

  /* --- controls ---
     Button heights come from v1 (--dp-btn-h 48 / --dp-btn-h-sm 44 /
     --dp-btn-h-desk 40, BREAK 2). Only the app field height is new. */
  --dp3-field-h: 48px;

  /* --- motion --- */
  --dp3-ease: cubic-bezier(.2, .7, .3, 1);
  --dp3-t-fast: 150ms;  /* hover, press, chip toggle */
  --dp3-t-med: 220ms;   /* card, reveal */
  --dp3-t-slow: 320ms;  /* sheet, panel, count-up */

  /* --- the charged accent -------------------------------------------------
     #00E676 measures 11.34:1 on the #0D1117 page — brighter than the
     #2BE07B action step (10.9:1) it sits beside, so it reads as "charged"
     without being the thing you click.

     A solid electric fill carries INK, never white: white on #00E676 is
     1.67:1 and unreadable. That is not a departure from AMENDMENT 3, which
     already scopes white-on-green to the --dp2-action pair and says in as
     many words that white does not carry on the bright dark-mode step.
     Electric is a brighter step again, so the same reasoning applies harder.

     Declared here rather than in a mode block because it never flips — there
     is simply no light-surface use for it (RULE 2). */
  --dp3-electric: #00E676;
  --dp3-electric-ink: #06120B;

  /* Glow. Dark surfaces only, one element per screen. */
  --dp3-glow-text: 0 0 18px rgba(0, 230, 118, .40);
  --dp3-glow-ring: drop-shadow(0 0 10px rgba(0, 230, 118, .45));
  --dp3-glow-dot: 0 0 8px rgba(0, 230, 118, .60);
}

/* ==========================================================================
   ROLE: DARK — athlete data
   Pins v1 + v2 to their dark pole with plain values. See "THE ONE IDEA".
   ========================================================================== */
.dpt-dark {
  color-scheme: dark;

  /* v1 surfaces, text, line */
  --dp-surface: #0D1117;
  --dp-surface-raised: #232D3B;
  --dp-surface-sunken: #080B0F;
  --dp-chrome: #06090D;
  --dp-chrome-ink: #E8EBEE;
  --dp-text: #E8EBEE;
  --dp-text-muted: #A2ADB6;
  --dp-line: #465468;
  /* v1 action and status */
  --dp-action-t: #2BE07B;
  --dp-action-t-ink: #06120B;
  --dp-action-t-hover: #5CE99A;
  --dp-green-text: #2BE07B;
  --dp-good-t: #2BE07B;      --dp-good-wash-t: #0E2A1C;
  --dp-warn-t: #F0B45A;      --dp-warn-wash-t: #2A2413;
  --dp-crit-t: #FF8078;      --dp-crit-wash-t: #2B1614;
  --dp-shadow-t: 0 4px 18px rgba(0, 0, 0, .5);

  /* v2 */
  --dp2-page: #0B0D10;
  --dp2-card: #232D3B;           /* AMENDMENT 15: aliased to the one dark card. Was #14181D. */
  --dp2-well: #0B0D10;
  --dp2-glass: rgba(255, 255, 255, .045);
  --dp2-hairline: #242B33;
  --dp2-hairline-soft: #1B222B;
  --dp2-card-border-c: transparent;
  --dp2-text: #EDF1F4;
  --dp2-text-2: #93A0AB;
  --dp2-label: #7A8794;
  --dp2-good: #2BE07B;
  --dp2-warn: #F0B45A;
  --dp2-crit: #FF8078;
  --dp2-crit-chip-bg: #3A1E1C;
  --dp2-accent-text: #2BE07B;
  --dp2-action-glow: 0 10px 30px rgba(0, 166, 81, .25);
  --dp2-nav-bg: rgba(23, 27, 33, .9);
  --dp2-nav-border: rgba(255, 255, 255, .06);
  --dp2-nav-item: #7A8794;
  --dp2-nav-active: #2BE07B;
  --dp2-nav-shadow: 0 10px 30px rgba(0, 0, 0, 0);
  --dp2-streak-on: #2BE07B;

  /* v3 kit semantics */
  --dp3-bg: #0D1117;
  /* AMENDMENT 15 SHIPPED 2026-08-27. One dark card: #232D3B. Was #1A222C
     (1.18:1 on #0D1117 — cards vanished in daylight). #232D3B is 1.36:1
     on the page. Foreground on this card, measured: text #F2F5F7 12.70:1,
     muted #98A6B3 5.59:1, text-3 #6B7885 3.08:1 (micro only; fails AA as
     16px body), line-strong .38 3.34:1. */
  --dp3-card: #232D3B;
  /* Raised-2 was #232E3A. On the new card that is 1.009:1 — the step
     collapsed, so --dp3-card-2 is a documented alias of --dp3-card. */
  --dp3-card-2: #232D3B;
  --dp3-line: rgba(255, 255, 255, .10);
  /* .22 was 2.06:1 on the old #1A222C card and failed WCAG 1.4.11. See
     AMENDMENT 13 — every use of this token is a CONTROL boundary, which
     has a 3:1 floor. .38 measures 3.56 on the page, 3.34 on the card.
     card-2 is an alias of the card, so the third number is the same. */
  --dp3-line-strong: rgba(255, 255, 255, .38);
  --dp3-text: #F2F5F7;
  --dp3-text-2: #98A6B3;
  --dp3-text-3: #6B7885;
  --dp3-accent: #00A651;            /* structural brand accent */
  --dp3-accent-hot: var(--dp3-electric);
  --dp3-accent-hot-ink: var(--dp3-electric-ink);
  --dp3-link: #4ADE80;
  --dp3-link-hover: #86EFAC;
  --dp3-focus: #4ADE80;
  --dp3-ok: #4ADE80;      --dp3-ok-tint: rgba(74, 222, 128, .14);
  --dp3-warn: #FFA94D;    --dp3-warn-tint: rgba(255, 169, 77, .14);
  --dp3-danger: #FF6B6B;  --dp3-danger-tint: rgba(255, 107, 107, .14);
  --dp3-info: #6FB1FF;    --dp3-info-tint: rgba(111, 177, 255, .14);
  --dp3-score-optimal: var(--dp3-electric);
  --dp3-score-fair: #FFA94D;
  --dp3-score-low: #FF6B6B;
  --dp3-score-none: #6B7885;
  --dp3-shadow-card: none;
  --dp3-shadow-float: 0 12px 32px rgba(0, 0, 0, .45);

  /* Charts — categorical, FIXED order, CVD-safe, validated against #0D1117.
     A 6th series folds into --dp3-chart-other labelled "Other". Never cycle
     hues; a repeated colour is a lie about the data. Re-run the dataviz
     palette validator before changing any value here. */
  --dp3-chart-1: #00B25C;   /* green — the DPT series is always first */
  --dp3-chart-2: #4C8DFF;   /* blue, 5.9:1 */
  --dp3-chart-3: #EA580C;   /* orange */
  --dp3-chart-4: #8B5CF6;   /* violet */
  --dp3-chart-5: #EC4899;   /* pink */
  --dp3-chart-other: #6B7885;
  --dp3-chart-grid: rgba(255, 255, 255, .08);
  --dp3-chart-axis: #98A6B3;
  /* Sequential (single-hue magnitude, e.g. a heatmap). Monotonic lightness. */
  --dp3-seq-1: #12301F; --dp3-seq-2: #0F5230; --dp3-seq-3: #008C47;
  --dp3-seq-4: #00B25C; --dp3-seq-5: #3ADF8A;
  /* Diverging (improved vs declined) */
  --dp3-div-pos: #00B25C; --dp3-div-mid: #8B97A1; --dp3-div-neg: #FF6B6B;
}

/* ==========================================================================
   ROLE: LIGHT — money, forms, parent admin
   ========================================================================== */
.dpt-light {
  color-scheme: light;

  /* v1 */
  --dp-surface: #F4F4F4;
  --dp-surface-raised: #FFFFFF;
  --dp-surface-sunken: #ECEEF0;
  --dp-chrome: #0D1117;
  --dp-chrome-ink: #FFFFFF;
  --dp-text: #161208;            /* AMENDMENT 21: warm ink. Was #141A21. */
  --dp-text-muted: #55606B;
  --dp-line: #D7DBD4;
  --dp-action-t: #00A651;
  --dp-action-t-ink: #0D1117;
  --dp-action-t-hover: #00B357;
  --dp-green-text: #00753C;
  --dp-good-t: #00753C;      --dp-good-wash-t: #E6F6EE;
  --dp-warn-t: #96540A;      --dp-warn-wash-t: #FBEFD8;
  --dp-crit-t: #C42115;      --dp-crit-wash-t: #FDECEA;
  --dp-shadow-t: 0 4px 18px rgba(13, 17, 23, .14);

  /* v2 */
  --dp2-page: #F4F4F4;
  --dp2-card: #FFFFFF;
  --dp2-well: #ECEEF0;
  --dp2-glass: #FFFFFF;
  --dp2-hairline: #D7DBD4;
  --dp2-hairline-soft: #E3E6E4;
  --dp2-card-border-c: #D7DBD4;
  --dp2-text: #161208;           /* AMENDMENT 21 */
  --dp2-text-2: #55606B;
  --dp2-label: #55606B;
  --dp2-good: #00753C;
  --dp2-warn: #96540A;
  --dp2-crit: #C42115;
  --dp2-crit-chip-bg: #FDECEA;
  --dp2-accent-text: #00753C;
  --dp2-action-glow: 0 10px 30px rgba(0, 166, 81, .3);
  --dp2-nav-bg: rgba(255, 255, 255, .94);
  --dp2-nav-border: #D7DBD4;
  --dp2-nav-item: #55606B;
  --dp2-nav-active: #00753C;
  --dp2-nav-shadow: 0 10px 30px rgba(13, 17, 23, .12);
  --dp2-streak-on: #00A651;

  /* v3 kit semantics.
     --dp3-accent-hot is the DEEP green here, not electric: RULE 2. It is
     aliased rather than absent so a component can write var(--dp3-accent-hot)
     once and be correct in both modes. */
  --dp3-bg: #F4F4F4;
  --dp3-card: #FFFFFF;
  --dp3-card-2: #FAFBFB;
  --dp3-line: #E4E7EA;
  /* #C9CFD5 was 1.57:1 on white and failed WCAG 1.4.11. See AMENDMENT 13 -
     this token is only ever a CONTROL boundary (field, chip, slot, ghost
     button, week mark), which has a 3:1 floor. #828B94 measures 3.15 on the
     page, 3.46 on the card, 3.34 on card-2. */
  --dp3-line-strong: #828B94;
  --dp3-text: #161208;           /* AMENDMENT 21: warm ink. Was #141A21. */
  --dp3-text-2: #55606B;
  --dp3-text-3: #8A95A0;
  --dp3-accent: #00753C;
  --dp3-accent-hot: #00753C;
  --dp3-accent-hot-ink: #FFFFFF;
  --dp3-link: #00753C;
  --dp3-link-hover: #005E30;
  --dp3-focus: #00753C;
  /* warn and danger are v1's audited steps, NOT the kit spec's #B45309 /
     #DC2626. Those two measure 4.48:1 and 4.17:1 on their own tints - both
     under AA for normal text, and these tokens are used as WORDS (a status
     chip, a "late" cell, a validation message), not just as marks. The v1
     steps were contrast-audited when AMENDMENT 1 split the status set and
     come in at 5.17:1 and 5.14:1 on the same grounds. See AMENDMENT 12.
     Dark mode is unaffected - #FFA94D and #FF6B6B clear AA on #0D1117. */
  --dp3-ok: #00753C;      --dp3-ok-tint: #E6F6EE;
  --dp3-warn: #96540A;    --dp3-warn-tint: #FBEFD8;
  --dp3-danger: #C42115;  --dp3-danger-tint: #FDECEA;
  --dp3-info: #1D4ED8;    --dp3-info-tint: #EAF0FD;
  --dp3-score-optimal: #00753C;
  --dp3-score-fair: #96540A;
  --dp3-score-low: #C42115;
  --dp3-score-none: #8A95A0;
  --dp3-shadow-card: 0 1px 2px rgba(13, 17, 23, .06);
  --dp3-shadow-float: 0 8px 24px rgba(13, 17, 23, .12);

  /* Charts — validated against #FFFFFF */
  --dp3-chart-1: #00753C;
  --dp3-chart-2: #2563EB;
  --dp3-chart-3: #C2570B;
  --dp3-chart-4: #7C3AED;
  --dp3-chart-5: #DB2777;
  --dp3-chart-other: #8A95A0;
  --dp3-chart-grid: #ECEFF1;
  --dp3-chart-axis: #55606B;
  --dp3-seq-1: #E6F6EE; --dp3-seq-2: #9FDFBE; --dp3-seq-3: #4CBF87;
  --dp3-seq-4: #00A651; --dp3-seq-5: #00753C;
  --dp3-div-pos: #00753C; --dp3-div-mid: #8B97A1; --dp3-div-neg: #DC2626;
}

/* A screen root paints itself and sets the app type. Both classes are listed
   so a nested .dpt-light island inside a .dpt-dark page (a checkout sheet over
   a stats screen) still gets its own ground rather than inheriting through. */
.dpt-dark, .dpt-light {
  background: var(--dp3-bg);
  color: var(--dp3-text);
  font-family: var(--dp3-font-ui);
  font-size: var(--dp3-t-body);
  line-height: 1.55;
}

/* Focus. v1 already sets outline-color globally from --dp-action-t, which the
   role blocks above pin correctly — this adds the width and offset the kit
   asks for without fighting v1 for the colour.

   It deliberately does NOT set border-radius. The kit spec listed one, and the
   first draft of this file honoured it — but `.dpt-dark :focus-visible` is
   (0,2,0) and outranks an app's own field rule, so every input in the testing
   app snapped 12px -> 8px the instant it was focused: a shape jump under a
   coach's thumb, on the control they are actively using. It is also
   unnecessary. An outline already follows the element's own border-radius per
   spec, so the only thing declaring one here can do is override a shape the
   app got right. */
.dpt-dark :focus-visible, .dpt-light :focus-visible {
  outline: 2px solid var(--dp3-focus);
  outline-offset: 2px;
}

/* ==========================================================================
   COMPONENTS
   Opt-in, like the .dp2-* set. Nothing below applies until an app writes the
   class. Every rule reads a semantic token; there is no raw hex past this
   line except where a comment says why.
   ========================================================================== */

/* --- type helpers --- */
.dp3-h1 {
  font-family: var(--dp3-font-display); font-weight: 700;
  font-size: var(--dp3-t-h1); line-height: 1.1;
  text-transform: uppercase; letter-spacing: .02em;
}
.dp3-h2 {
  font-family: var(--dp3-font-display); font-weight: 600;
  font-size: var(--dp3-t-h2); line-height: 1.2;
}
.dp3-h3 { font-weight: 700; font-size: var(--dp3-t-h3); line-height: 1.3; }
.dp3-micro {
  font-size: var(--dp3-t-micro); font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--dp3-text-2);
}
/* Any figure a coach reads at a glance. Tabular so a column of times does not
   jitter as the digits change. */
.dp3-num { font-variant-numeric: tabular-nums; }

/* --- buttons -------------------------------------------------------------
   --primary is the action button and follows the role: electric on dark
   (carrying ink, see the --dp3-electric note), deep green on light (carrying
   white). --gold is the MONEY button and is deliberately separate.

   BREAK 1 still stands: inside the staff tools gold means ATTENTION, not
   money, and those marks keep using --dp2-gold / --dp-warn-t as they do
   today. .dp3-btn--gold is for family-facing purchase surfaces only — the
   credits screen, checkout, the receipt CTA — where the kit's money-only rule
   applies and there is at most one per view. Do not put it in ops, the staff
   dashboard, roster or the hubs. */
.dp3-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: var(--dp-btn-h, 48px); padding: 0 26px;
  border-radius: var(--dp3-r-pill);
  font-family: var(--dp3-font-ui); font-size: 15px; font-weight: 700;
  border: 1.5px solid transparent; cursor: pointer;
  white-space: nowrap; text-decoration: none;
  transition: background var(--dp3-t-fast) var(--dp3-ease),
              border-color var(--dp3-t-fast) var(--dp3-ease);
}
.dp3-btn--sm { min-height: var(--dp-btn-h-sm, 44px); padding: 0 20px; font-size: 14px; }
.dp3-btn--primary { background: var(--dp3-accent-hot); color: var(--dp3-accent-hot-ink); }
.dpt-dark .dp3-btn--primary:hover { background: #5CE99A; }
.dpt-light .dp3-btn--primary:hover { background: #005E30; }
/* Gold never flips: #FFC72C is 11.97:1 under warm ink #161208 on both grounds.
   AMENDMENT 21 — was cool ink #141A21. */
.dp3-btn--gold { background: #FFC72C; color: #161208; }
.dp3-btn--gold:hover { background: #D69E06; }
.dp3-btn--ghost { background: transparent; border-color: var(--dp3-line-strong); color: var(--dp3-text); }
.dpt-dark .dp3-btn--ghost { background: rgba(255, 255, 255, .06); border-color: rgba(255, 255, 255, .28); }
.dp3-btn--danger { background: transparent; border-color: var(--dp3-danger); color: var(--dp3-danger); }
.dp3-btn[disabled], .dp3-btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
.dp3-link {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--dp3-link); font-weight: 700; font-size: 15px; text-decoration: none;
}
.dp3-link:hover { color: var(--dp3-link-hover); text-decoration: underline; }

/* --- ring + hero numeral -------------------------------------------------
   The composite status display: a numeral you read first and an arc that
   tells you where it sits without a second glance. This is the screen's ONE
   glowing element (RULE 3) — if a screen has a ring, nothing else glows.
   The glow is applied to the arc and the numeral only, never the track. */
.dp3-ring { position: relative; display: inline-block; }
.dp3-ring svg { transform: rotate(-90deg); display: block; }
.dp3-ring .dp3-ring-track { fill: none; stroke: var(--dp3-line); stroke-width: 10; }
.dp3-ring .dp3-ring-prog {
  fill: none; stroke: var(--dp3-accent-hot); stroke-width: 10;
  stroke-linecap: round; filter: var(--dp3-glow-ring);
}
.dpt-light .dp3-ring .dp3-ring-prog { filter: none; }  /* RULE 3: dark only */
.dp3-ring .dp3-ring-center {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2px; text-align: center;
}
.dp3-ring .dp3-ring-num {
  font-family: var(--dp3-font-display); font-weight: 600; line-height: 1;
  font-size: var(--dp3-t-score-hero); color: var(--dp3-text);
  font-variant-numeric: tabular-nums; text-shadow: var(--dp3-glow-text);
}
.dpt-light .dp3-ring .dp3-ring-num { text-shadow: none; }
.dp3-ring .dp3-ring-label {
  font-size: var(--dp3-t-micro); font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--dp3-text-2);
}

/* --- score band ---
   RULE 4: the dot is not decoration, it is the redundant channel that makes
   this readable without colour vision. Never ship the chip without its word. */
.dp3-scorechip {
  display: inline-flex; align-items: center; gap: 6px;
  border-radius: var(--dp3-r-pill); padding: 4px 12px;
  font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
}
.dp3-scorechip .dp3-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }
.dp3-scorechip--optimal { color: var(--dp3-score-optimal); background: var(--dp3-ok-tint); }
.dp3-scorechip--fair { color: var(--dp3-score-fair); background: var(--dp3-warn-tint); }
.dp3-scorechip--low { color: var(--dp3-score-low); background: var(--dp3-danger-tint); }
.dp3-scorechip--none { color: var(--dp3-score-none); background: var(--dp3-card-2); }

/* --- stat tile + delta --- */
.dp3-tile {
  background: var(--dp3-card); border: 1px solid var(--dp3-line);
  border-radius: var(--dp3-r-lg); padding: var(--dp3-sp-4);
  box-shadow: var(--dp3-shadow-card);
  display: flex; flex-direction: column; gap: var(--dp3-sp-1); min-width: 0;
}
.dp3-tile .dp3-tile-val {
  font-family: var(--dp3-font-display); font-weight: 600;
  font-size: var(--dp3-t-score); line-height: 1.05;
  color: var(--dp3-text); font-variant-numeric: tabular-nums;
}
.dp3-tile .dp3-tile-val small { font-size: 16px; font-weight: 500; color: var(--dp3-text-2); margin-left: 2px; }
.dp3-delta { display: inline-flex; align-items: center; gap: 4px; font-size: 12.5px; font-weight: 700; }
.dp3-delta--up { color: var(--dp3-ok); }
.dp3-delta--down { color: var(--dp3-danger); }
.dp3-delta--flat { color: var(--dp3-text-2); }

/* --- card + row --- */
.dp3-card {
  background: var(--dp3-card); border: 1px solid var(--dp3-line);
  border-radius: var(--dp3-r-lg); padding: var(--dp3-sp-4);
  box-shadow: var(--dp3-shadow-card);
}
.dp3-card--hero { border-radius: var(--dp3-r-xl); padding: var(--dp3-sp-5); }
.dp3-row {
  display: flex; align-items: center; gap: var(--dp3-sp-3);
  min-height: 52px; padding: var(--dp3-sp-2) 0;
  border-bottom: 1px solid var(--dp3-line);
}
.dp3-row:last-child { border-bottom: 0; }
.dp3-row .dp3-row-grow { flex: 1; min-width: 0; }
.dp3-row .dp3-row-t { font-weight: 600; color: var(--dp3-text); font-size: 15px; }
.dp3-row .dp3-row-s { font-size: 13px; color: var(--dp3-text-2); }
.dp3-row .dp3-row-v { font-weight: 700; color: var(--dp3-text); font-variant-numeric: tabular-nums; }

/* --- week strip ---
   Consistency at a glance. "today" is an outline, not a fill, so a day that
   has not happened yet never reads as a day that was missed. */
.dp3-week { display: flex; gap: 9px; align-items: center; }
.dp3-week .dp3-week-day {
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  font-size: 10.5px; font-weight: 600; color: var(--dp3-text-3);
}
.dp3-week .dp3-week-mark {
  width: 30px; height: 30px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  border: 1.5px solid var(--dp3-line-strong); color: transparent;
  font-size: 13px; font-weight: 700;
}
.dp3-week .is-done { background: var(--dp3-accent); border-color: var(--dp3-accent); color: #FFFFFF; }
.dp3-week .is-today { border-color: var(--dp3-accent-hot); color: var(--dp3-accent-hot); }

/* --- chips, segmented control, slots --- */
.dp3-chip {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 36px; padding: 0 15px; border-radius: var(--dp3-r-pill);
  border: 1.5px solid var(--dp3-line-strong); background: transparent;
  color: var(--dp3-text); font-family: var(--dp3-font-ui);
  font-size: 13.5px; font-weight: 600; white-space: nowrap; cursor: pointer;
}
.dp3-chip.is-on {
  background: var(--dp3-accent-hot); border-color: var(--dp3-accent-hot);
  color: var(--dp3-accent-hot-ink);
}
.dp3-seg {
  display: inline-flex; background: var(--dp3-card); border: 1px solid var(--dp3-line);
  border-radius: var(--dp3-r-pill); padding: 4px; gap: 2px;
}
.dpt-light .dp3-seg { background: #EDF0F2; border-color: transparent; }
.dp3-seg > * {
  min-height: 34px; padding: 0 16px; border-radius: var(--dp3-r-pill);
  color: var(--dp3-text-2); font-weight: 600; font-size: 13.5px;
  display: inline-flex; align-items: center; border: 0; background: transparent; cursor: pointer;
}
.dp3-seg > .is-on { background: var(--dp3-card-2); color: var(--dp3-text); }
.dpt-light .dp3-seg > .is-on { background: #FFFFFF; box-shadow: 0 1px 2px rgba(13, 17, 23, .08); }
.dp3-slots { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--dp3-sp-2); }
.dp3-slot {
  min-height: var(--dp-btn-h-sm, 44px); border-radius: var(--dp3-r-md);
  border: 1.5px solid var(--dp3-line-strong); background: var(--dp3-card);
  color: var(--dp3-text); font-weight: 600; font-size: 14px;
  display: flex; align-items: center; justify-content: center;
  font-variant-numeric: tabular-nums; cursor: pointer;
}
.dp3-slot.is-sel { background: var(--dp3-accent-hot); border-color: var(--dp3-accent-hot); color: var(--dp3-accent-hot-ink); }
.dp3-slot.is-off {
  color: var(--dp3-text-3); background: var(--dp3-card-2);
  border-color: var(--dp3-line); text-decoration: line-through; cursor: not-allowed;
}

/* --- badges + alerts --- RULE 4 applies: dot or icon, plus a word. --- */
.dp3-badge {
  display: inline-flex; align-items: center; gap: 6px;
  border-radius: var(--dp3-r-pill); padding: 3px 10px;
  font-size: 12px; font-weight: 700; white-space: nowrap;
}
.dp3-badge .dp3-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }
.dp3-badge--ok { color: var(--dp3-ok); background: var(--dp3-ok-tint); }
.dp3-badge--warn { color: var(--dp3-warn); background: var(--dp3-warn-tint); }
.dp3-badge--danger { color: var(--dp3-danger); background: var(--dp3-danger-tint); }
.dp3-badge--info { color: var(--dp3-info); background: var(--dp3-info-tint); }
.dp3-badge--neutral { color: var(--dp3-text-2); background: var(--dp3-card-2); }
.dpt-light .dp3-badge--neutral { background: #EDF0F2; }
.dp3-alert {
  display: flex; gap: var(--dp3-sp-3); align-items: flex-start;
  border-radius: var(--dp3-r-md); padding: 13px 15px;
  font-size: 13.5px; border: 1px solid; color: var(--dp3-text);
}
.dp3-alert .dp3-alert-ic {
  flex: 0 0 auto; width: 21px; height: 21px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 800;
}
.dp3-alert b { display: block; margin-bottom: 1px; }
.dp3-alert--warn { background: var(--dp3-warn-tint); border-color: var(--dp3-warn); }
.dp3-alert--warn .dp3-alert-ic { background: var(--dp3-warn); color: #161208; }
.dp3-alert--info { background: var(--dp3-info-tint); border-color: var(--dp3-info); }
.dp3-alert--info .dp3-alert-ic { background: var(--dp3-info); color: #FFFFFF; }
.dp3-alert--danger { background: var(--dp3-danger-tint); border-color: var(--dp3-danger); }
.dp3-alert--danger .dp3-alert-ic { background: var(--dp3-danger); color: #FFFFFF; }

/* --- avatar ---
   Initials only. Outside a family's own view a minor is first name + last
   initial, so this carries at most two characters by design. */
.dp3-avatar {
  width: 38px; height: 38px; border-radius: 50%; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  background: var(--dp3-ok-tint); color: var(--dp3-accent);
  font-weight: 700; font-size: 14px;
}
.dpt-dark .dp3-avatar { color: var(--dp3-link); }
.dp3-avatar--coach { box-shadow: 0 0 0 2px var(--dp3-accent); }

/* --- form field --- */
.dp3-field { display: flex; flex-direction: column; gap: 6px; }
.dp3-field label { font-size: var(--dp3-t-sub); font-weight: 600; color: var(--dp3-text); }
.dp3-field input, .dp3-field select, .dp3-field textarea {
  min-height: var(--dp3-field-h, 48px); border-radius: var(--dp3-r-field, 8px); /* AMENDMENT 22 */
  border: 1.5px solid var(--dp3-line-strong); background: var(--dp3-card);
  color: var(--dp3-text); font: inherit; padding: 0 14px; width: 100%;
}
.dp3-field textarea { padding: 12px 14px; min-height: 96px; }
.dp3-field .dp3-field-err { font-size: 13px; font-weight: 600; color: var(--dp3-danger); }

/* --- app bar + tab bar --- */
.dp3-appbar { display: flex; align-items: center; gap: var(--dp3-sp-3); min-height: 52px; }
.dp3-appbar .dp3-appbar-title {
  font-family: var(--dp3-font-display); font-weight: 600; font-size: 18px;
  text-transform: uppercase; letter-spacing: .04em; flex: 1; text-align: center;
}
.dp3-iconbtn {
  width: var(--dp-btn-h-sm, 44px); height: var(--dp-btn-h-sm, 44px);
  border-radius: 50%; border: 0; background: var(--dp3-card);
  color: var(--dp3-text); display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.dp3-tabbar {
  display: flex; justify-content: space-around;
  background: var(--dp3-card); border-top: 1px solid var(--dp3-line);
  padding: 6px 4px calc(6px + env(safe-area-inset-bottom, 12px));
}
.dp3-tab {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px;
  min-height: var(--dp-btn-h-sm, 44px); padding-top: 7px;
  color: var(--dp3-text-3); font-size: 10.5px; font-weight: 600;
  position: relative; text-decoration: none;
}
.dp3-tab svg { width: 22px; height: 22px; }
/* The active indicator is bright but does NOT glow, and that is deliberate.

   RULE 3 allows one glowing element per screen. Glow was originally attached
   to the ring AND to this indicator, which meant any screen with both — the
   portal progress screen, which is the kit's own reference mockup — broke the
   rule the moment it rendered. A rule you can violate by using two components
   as documented is not a rule, it is a warning.

   So glow belongs to the hero element only: the ring and the hero numeral,
   which appear at most once per screen by construction. Persistent chrome
   never glows. A screen whose hero genuinely IS a chrome element can opt in
   by adding .dp3-glow to that one element. */
.dp3-tab.is-on { color: var(--dp3-accent-hot); }
.dp3-tab.is-on::after {
  content: ""; position: absolute; top: 0;
  width: 16px; height: 3px; border-radius: 2px;
  background: var(--dp3-accent-hot);
}
.dpt-dark .dp3-glow { box-shadow: var(--dp3-glow-dot); }

/* --- chart hooks ---
   Paint a series with style="fill:var(--dp3-chart-1)", not
   fill="var(--dp3-chart-1)".

   Current Chrome DOES resolve var() in an SVG presentation attribute —
   measured, so the existing ring and bar code is not broken today. But
   presentation attributes are the lowest-priority layer of the cascade and
   support is not uniform; the style= form is the one that has always worked
   everywhere. Use it in new code and leave working old code alone. */
.dp3-chart svg { width: 100%; height: auto; display: block; }
.dp3-chart text { font-family: var(--dp3-font-ui); font-size: 11px; fill: var(--dp3-chart-axis); }
.dp3-chart .dp3-chart-grid { stroke: var(--dp3-chart-grid); stroke-width: 1; }
.dp3-chart .dp3-chart-label { font-weight: 700; font-size: 12px; fill: var(--dp3-text); }

/* Reduced motion. v1 already covers its own animations; this covers v3's, and
   a count-up must render its final value rather than animating to it. */
@media (prefers-reduced-motion: reduce) {
  .dpt-dark *, .dpt-light * {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

