@charset "utf-8";
/* ==========================================================================
   RACQUET — shared design system
   assets/racquet.css · single source of truth for tokens and components.

   Loaded by every page with one tag, before the page's own <style>:
       <link rel="stylesheet" href="../assets/racquet.css">

   WHY A CASCADE LAYER
   Everything below sits inside @layer racquet.*. Unlayered CSS always beats
   layered CSS regardless of specificity, so a page's remaining local <style>
   block wins automatically — no !important, no specificity arms race, and no
   need to rename a single class when a page adopts this file. Delete the
   duplicated rules, keep the page-specific ones, done.
   (The one inversion: for !important declarations layer order is reversed, so
   the [hidden] and prefers-reduced-motion rules here deliberately win over a
   page. Both of those should always win.)

   NO BUILD STEP. NO DEPENDENCY. The only external resource is the Google
   Fonts stylesheet the pages already load.

   RTL: logical properties only. There is no `left:` or `right:` positional
   property anywhere in this file — inset-inline-start/end, margin-inline,
   padding-inline, border-inline, text-align:start/end.

   HEBREW TYPOGRAPHY
   - No monospace stack is defined. A mono stack resolves to SF Mono on iOS,
     which contains no Hebrew glyphs, and Hebrew set in it renders as tofu.
   - No letter-spacing on Hebrew. The `:lang(he)` guard in §3b enforces that
     mechanically: any element whose computed language is Hebrew gets
     letter-spacing:normal, no matter what a page sets — importantly, even
     over a page's own !important. The Latin wordmark carries lang="en" and is
     therefore exempt by construction, not by exception.

   CONTRAST
   Every text/background pair in this system is measured; the full table is in
   docs/DESIGN_SYSTEM.md. Six values in the palette exist only because a
   measured pair failed — they are marked "CONTRAST FIX" below and must not be
   reverted to the artboard value.
   ========================================================================== */

@layer racquet.base, racquet.components, racquet.utilities;

@layer racquet.base {

/* ==========================================================================
   1. TOKENS
   ========================================================================== */
:root {

  /* ---- 1.1 Brand surfaces --------------------------------------------- */
  --navy:            #071a3a;   /* header, hero, footer, focus ring on light */
  --navy-2:          #0d2450;   /* sub-navigation strip                      */
  --navy-3:          #0c2246;   /* raised block on a navy page               */
  --deep-navy:       #0d1526;   /* pressed state of a navy button            */
  --page:            #f4f7fb;   /* the light page ground                     */
  --white:           #ffffff;   /* card ground                               */

  /* ---- 1.2 Ink on LIGHT ------------------------------------------------ */
  --ink:             #17213a;   /* body copy            14.86:1 on --page     */
  --ink-2:           #536078;   /* secondary copy        5.90:1 on --page     */
  --ink-3:           #65728a;   /* hints, placeholders   4.52:1 on --page     */
  --ghost:           #748ab6;   /* CONTRAST FIX 1 — large decorative numerals
                                   only (>=24px/700). 3.23:1 on --page, which
                                   clears the 3:1 large-text threshold and
                                   nothing else. Never use for body copy.     */

  /* ---- 1.3 Ink on DARK -------------------------------------------------
     Named by surface, not by shade, because the old --muted meant #b6c6de on
     the landing page and #536078 in the app — the same token, opposite ends
     of the scale. That collision is the reason these are prefixed on-dark.  */
  --on-dark:         #ffffff;   /* 17.24:1 on --navy */
  --on-dark-2:       #dbe7f7;   /* 13.78:1 */
  --on-dark-3:       #cddcf0;   /* 12.39:1 */
  --on-dark-4:       #b6c6de;   /*  9.95:1 — CONTRAST FIX 2, replaced #6c85a6
                                   in the footer legal row (was 4.55:1)      */
  --on-dark-5:       #8fa5c4;   /*  6.85:1 — smallest supporting text        */

  /* ---- 1.4 Accent ------------------------------------------------------ */
  --lime:            #a8e64a;
  --lime-hover:      #c4f279;
  --on-lime:         #234117;   /*  7.65:1 on --lime */
  --on-lime-2:       #2c4a1c;   /*  6.27:1 on --lime */
  --amber:           #ffb15c;
  --on-amber:        #5f4218;   /* CONTRAST FIX 3 — was #6b4a1c at 4.46:1,
                                   now 5.13:1 on --amber                     */
  --green:           #3d7410;   /*  5.28:1 on --page — eyebrows, badges       */
  --green-bg:        #eef8dd;
  --link:            #2f6f00;   /*  5.76:1 on --page, 6.19:1 on --white       */

  /* ---- 1.5 Lines ------------------------------------------------------- */
  --line:            #dce4ef;   /* decorative card edge on light             */
  --line-strong:     #7f90a8;   /* CONTRAST FIX 4 — the boundary of a form
                                   control. Was #c3cede at 1.48:1 on --page,
                                   which fails WCAG 1.4.11 (3:1) and made the
                                   inputs read as flat panels. Now 3.03:1 on
                                   --page, 3.25:1 on --white.                */
  --line-checked:    #5f9016;   /* CONTRAST FIX 5 — selected radio card edge.
                                   Was #7fbf1f at 2.04:1 on --ok-bg; now
                                   3.48:1, so the selected state is visible
                                   without relying on the fill alone.        */
  --line-dark:       #16305c;   /* decorative rule on --navy                 */
  --line-dark-2:     #2b4a7d;   /* decorative divider on --navy              */
  --line-dark-3:     #4d74b0;   /* CONTRAST FIX 6 — the boundary of an
                                   INTERACTIVE control on --navy. #2b4a7d is
                                   1.95:1 there; this is 3.65:1.             */
  --hairline:        rgba(255,255,255,.15);
  --veil:            rgba(255,255,255,.05);
  --veil-2:          rgba(255,255,255,.08);

  /* ---- 1.6 Status ------------------------------------------------------ */
  --ok-bg:           #eef8dd;
  --ok-ink:          #234117;   /* 10.38:1 on --ok-bg */
  --ok-line:         #cbe89a;
  --bad-bg:          #fff1e2;
  --bad-ink:         #5f4218;   /*  8.31:1 on --bad-bg */
  --bad-line:        #ffb15c;
  --chip-bg:         #e6ecf6;   /* neutral chip; --navy on it is 14.53:1     */

  /* ---- 1.7 Type --------------------------------------------------------
     Every fallback has full Hebrew coverage on its own platform:
     Assistant/Rubik from the CDN, Noto Sans Hebrew (Android/Linux),
     Segoe UI (Windows), -apple-system + Arial Hebrew (iOS/macOS).
     Never add a weight-named face ("Segoe UI Semibold") to a chain: the
     browser falls through per text run, so one Hebrew word inside a heading
     comes back a different weight from the words beside it.                */
  --font-body:    "Assistant","Noto Sans Hebrew","Segoe UI",-apple-system,BlinkMacSystemFont,"Arial Hebrew",Arial,system-ui,sans-serif;
  --font-display: "Rubik","Assistant","Noto Sans Hebrew","Segoe UI",-apple-system,BlinkMacSystemFont,"Arial Hebrew",Arial,system-ui,sans-serif;

  /* rem-based so the reader's browser font-size setting is honoured.
     Root stays at the browser default; nothing here sets html{font-size}. */
  --text-2xs:  0.75rem;    /* 12px — chips, kickers                    */
  --text-xs:   0.8125rem;  /* 13px — tags, fine print                  */
  --text-sm:   0.875rem;   /* 14px — hints, meta, table body           */
  --text-md:   0.9375rem;  /* 15px — dense body                        */
  --text-base: 1rem;       /* 16px — body. Never smaller on mobile.    */
  --text-lg:   1.0625rem;  /* 17px — lede, sub-heading                 */
  --text-xl:   1.25rem;    /* 20px — h2 in app screens                 */
  --text-2xl:  clamp(1.3125rem, 3.4vw, 1.5625rem);  /* 21 → 25  h3/h2  */
  --text-3xl:  clamp(1.4375rem, 5vw, 1.9375rem);    /* 23 → 31  h1 app */
  --text-4xl:  clamp(1.875rem, 6vw, 2.75rem);       /* 30 → 44  h1 doc */
  --text-display: clamp(2.375rem, 8vw, 4.75rem);    /* 38 → 76  hero   */

  --leading-tight: 1.25;
  --leading-snug:  1.4;
  --leading-body:  1.6;
  --leading-loose: 1.85;   /* long-form prose, e.g. the accessibility page */

  /* ---- 1.8 Spacing — a 4px scale. Nothing off-scale.  ------------------ */
  --space-1:  0.25rem;   /*  4 */
  --space-2:  0.5rem;    /*  8 */
  --space-3:  0.75rem;   /* 12 */
  --space-4:  1rem;      /* 16 */
  --space-5:  1.25rem;   /* 20 */
  --space-6:  1.5rem;    /* 24 */
  --space-8:  2rem;      /* 32 */
  --space-10: 2.5rem;    /* 40 */
  --space-12: 3rem;      /* 48 */
  --space-16: 4rem;      /* 64 */

  /* ---- 1.9 Radii ------------------------------------------------------- */
  --radius-xs:   8px;
  --radius-sm:  10px;
  --radius-md:  12px;
  --radius-lg:  14px;
  --radius-xl:  18px;
  --radius-2xl: 22px;
  --radius-pill: 999px;

  /* ---- 1.10 Shadows ---------------------------------------------------- */
  --shadow-sm: 0 1px 2px rgba(7,26,58,.05);
  --shadow-md: 0 7px 24px rgba(23,33,58,.06);
  --shadow-lg: 0 30px 70px rgba(7,26,58,.35);

  /* ---- 1.11 Layout ----------------------------------------------------- */
  --gutter: clamp(1rem, 4vw, 1.5rem);   /* 16 → 24 */
  --shell:  1000px;                     /* default: an app screen  */
  --bar-height: 3.5rem;                 /* 56px minimum header row */

  /* ---- 1.12 Motion ----------------------------------------------------- */
  --ease: cubic-bezier(.2,.7,.3,1);
  --dur:  .18s;

  /* ---- 1.13 Focus ------------------------------------------------------
     Recoloured per surface rather than fixed, so the ring always clears 3:1
     against whatever it sits on. A dark block sets these two and every focus
     ring inside it follows. See .on-dark below.                            */
  --focus:      var(--navy);
  --focus-mate: var(--white);

  /* ---- 1.14 Z-index — a scale, not ad-hoc numbers --------------------- */
  --z-sticky: 20;
  --z-header: 50;
  --z-skip:   200;

  /* ---- 1.15 Colour scheme ---------------------------------------------
     DELIBERATE: light only. See docs/DESIGN_SYSTEM.md §"Why there is no dark
     mode". Declaring it explicitly is the point — without it a UA in dark
     mode repaints native form controls, the datalist and the scrollbars dark
     against our light surfaces, which is the half-done state we are avoiding. */
  color-scheme: light;
}

/* ==========================================================================
   2. BREAKPOINTS
   Custom properties cannot be used in a media query, so the scale lives here
   as a comment and the literal values below must match it. Four, and only
   four. The pages today use eight (600/640/700/900/940/960/1000/1080), which
   is not a scale, it is eight separate decisions.

     --bp-sm :  480px   large phone — two-up chips
     --bp-md :  640px   small tablet — a form goes two-column
     --bp-lg :  960px   the nav becomes a row; the shell gets a real gutter
     --bp-xl : 1280px   the marketing shell reaches full width
   ========================================================================== */

/* ==========================================================================
   3. RESET + ELEMENT BASE
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-body);
  /* anywhere, not word-break:break-all — the latter chops Latin tokens
     mid-word, e.g. an email address split as "bittoneliran19 / 70@gmail.com" */
  overflow-wrap: anywhere;
}

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

h1, h2, h3, h4 {
  font-family: var(--font-display);
  margin: 0;
  line-height: var(--leading-tight);
  text-wrap: balance;
}
h1 { font-weight: 900; font-size: var(--text-3xl); }
h2 { font-weight: 700; font-size: var(--text-xl); }
h3 { font-weight: 700; font-size: var(--text-lg); }
h4 { font-weight: 700; font-size: var(--text-base); }

p { margin: 0; }
p + p { margin-top: var(--space-3); }

a { color: var(--link); text-decoration: underline; text-underline-offset: 3px; }
a:hover { color: var(--navy); }

/* Native controls inherit the page face, not the OS face. Without this, a
   <select> on Windows falls back to Segoe UI at a different size and the row
   of fields stops lining up. */
input, select, textarea, button { font-family: inherit; }

/* ---- Focus ------------------------------------------------------------
   :focus-visible only, so a mouse click on a card does not paint a ring, but
   the ring is never removed for the keyboard. The double ring (outline plus
   a box-shadow in the mate colour) guarantees visibility on both a light and
   a dark surface with one rule. */
:focus { outline: none; }
:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
  border-radius: var(--radius-xs);
  box-shadow: 0 0 0 6px var(--focus-mate);
}

/* Any dark block. Sets the focus pair once for everything inside it. */
.on-dark {
  --focus: var(--lime);
  --focus-mate: var(--navy);
  color: var(--on-dark-3);
}

/* ==========================================================================
   3b. HEBREW GUARD — mechanical, not a convention

   Any element whose computed language is Hebrew gets letter-spacing:normal
   and text-transform:none, whatever a page or a future component sets.
   Hebrew has no letter-spacing tradition and spaced Hebrew reads as broken
   text; text-transform:uppercase on Hebrew is a silent no-op that only ever
   proves the rule was written for Latin and never re-read.

   WHY !important AND WHY IN THE FIRST LAYER
   Everywhere else in this file the intent is that a page's own CSS wins. Not
   here. For !important declarations the cascade reverses layer order and
   ranks UNLAYERED author styles lowest of all, so an !important in the FIRST
   layer beats every page rule — important or not. That is deliberate: this is
   the one rule a page must not be able to override by accident.

   The Latin wordmark keeps its tracking because it carries lang="en" and
   therefore does not match. A page that genuinely needs spaced Latin inside
   Hebrew prose marks that run lang="en" or wraps it in <bdi lang="en"> —
   which is what it should already be doing for bidi.
   ========================================================================== */
:lang(he) {
  letter-spacing: normal !important;
  text-transform: none !important;
}

} /* @layer racquet.base */


@layer racquet.components {

/* ==========================================================================
   4. LAYOUT
   ========================================================================== */
.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.shell-sm { --shell: 760px; }    /* a single form — change request        */
.shell-md { --shell: 860px; }    /* a reading page — public profile       */
.shell-lg { --shell: 1000px; }   /* an app screen                         */
.shell-xl { --shell: 1280px; --gutter: clamp(1.25rem, 5vw, 4rem); }  /* marketing */

main.shell { padding-block: clamp(var(--space-5), 5vw, var(--space-10)); }

.stack > * + * { margin-top: var(--space-4); }
.stack-sm > * + * { margin-top: var(--space-2); }
.stack-lg > * + * { margin-top: var(--space-6); }

.divider {
  border: 0;
  border-top: 1px solid var(--line);
  margin-block: var(--space-6) var(--space-5);
}

/* ==========================================================================
   5. SKIP LINK
   Off-canvas via inset-inline-start, never display:none — a hidden link is
   not focusable and the whole point is that Tab reaches it first.
   ========================================================================== */
.skip-link {
  position: absolute;
  inset-inline-start: -9999px;
  inset-block-start: 0;
  z-index: var(--z-skip);
  background: var(--lime);
  color: var(--navy);
  font-weight: 700;
  padding: var(--space-3) var(--space-5);
  text-decoration: none;
  border-end-end-radius: var(--radius-sm);
}
.skip-link:focus { inset-inline-start: var(--space-3); inset-block-start: var(--space-3); }

/* ==========================================================================
   6. PAGE HEADER + BRAND
   ========================================================================== */
.site-header {
  background: var(--navy);
  color: var(--on-dark);
  border-bottom: 1px solid var(--line-dark);
  position: relative;
  z-index: var(--z-header);
  --focus: var(--lime);
  --focus-mate: var(--navy);
}
.site-header-sticky { position: sticky; inset-block-start: 0; }

.bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--bar-height);
  padding-block: var(--space-2);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  text-decoration: none;
  flex: none;
  order: 1;
}
.brand:hover { color: inherit; }
.mark {
  width: 2rem; height: 2rem; flex: none;
  display: block;
  object-fit: cover;
  border-radius: var(--radius-xs);
}
/* The wordmark is the Latin string RACQUET and must carry lang="en" in the
   markup. That is what keeps it out of the :lang(he) letter-spacing guard at
   the end of this file — the guard is not an exception list, it is a rule. */
.word {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 1.1875rem;   /* 19px */
  letter-spacing: .1em;
  color: var(--on-dark);
}

/* Signed-in identity block, pushed to the inline end of the bar. */
.who {
  order: 2;
  margin-inline-start: auto;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--on-dark-3);
}

/* ==========================================================================
   7. NAVIGATION — two patterns, neither of them a horizontal scroller

   WHAT WAS THERE
   The app screen used `nav ul { display:flex; overflow-x:auto }`. That is the
   weakest option available, for five reasons:
     1. Items past the edge are invisible with no affordance; on touch there
        is no scrollbar at all, so a user cannot know the fourth tab exists.
     2. In RTL the initial scroll position of an overflow container is the one
        place browsers still genuinely disagree, so which tabs are on screen
        at first paint is not deterministic across engines.
     3. The container has no tabindex, so it cannot be scrolled from the
        keyboard (WCAG 2.1.1); Tab jumps focus into an off-screen control and
        the view lurches.
     4. It creates horizontal scrolling inside a vertically scrolling page,
        which is what WCAG 1.4.10 Reflow exists to prevent.
     5. The targets were ~43px tall at 4px gaps. Minimum is 44px.

   WHAT REPLACES IT
   (a) .site-nav  — a DISCLOSURE, for a site nav with many links that is
       secondary to the content. Below 960px the list collapses behind a
       labelled 44x44 toggle and opens as full-width rows; at 960px and up the
       CSS shows the list and hides the toggle, so the menu can never be
       trapped closed if the script fails to load.
   (b) .site-subnav — a WRAPPING tab strip, for an app's primary view switcher
       where every destination must stay visible. It wraps to a second row on a
       narrow phone. Two rows of tabs is not elegant; a tab nobody can find is
       worse, and this needs no JavaScript at all.
   ========================================================================== */

/* ---- 7a. Disclosure nav ------------------------------------------------ */
.nav-toggle {
  width: 2.75rem; height: 2.75rem;   /* 44x44 */
  flex: none;
  order: 3;
  margin-inline-start: auto;
  display: grid; place-items: center;
  background: transparent;
  border: 1px solid var(--line-dark-3);
  border-radius: var(--radius-sm);
  color: var(--on-dark);
  cursor: pointer;
}
.who ~ .nav-toggle { margin-inline-start: 0; }

.site-nav {
  display: none;
  flex-basis: 100%;
  order: 5;
}
.site-nav.is-open { display: block; }

.nav-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
  padding: var(--space-2) 0 var(--space-3);
  list-style: none;
}
.nav-list a {
  /* flex + min-height rather than block + padding, so the 44px target is
     guaranteed at every width instead of falling out of the line-height. */
  display: flex;
  align-items: center;
  min-height: 2.75rem;
  padding-block: var(--space-2);
  padding-inline: var(--space-1);
  font-size: var(--text-base);
  color: var(--on-dark-4);
  text-decoration: none;
}
.nav-list a:hover { color: var(--lime); }
.nav-list a[aria-current] {
  color: var(--lime);
  font-weight: 700;
  border-inline-start: 3px solid var(--lime);
  padding-inline-start: var(--space-3);
}

/* An optional CTA that rides with the menu on a phone and sits in the bar on
   a wide screen. */
.nav-cta { display: none; flex-basis: 100%; order: 6; padding-bottom: var(--space-3); }
.nav-cta.is-open { display: block; }
.nav-cta .btn { width: 100%; }

@media (min-width: 960px) {
  .bar { flex-wrap: nowrap; gap: var(--space-6); min-height: 4.25rem; }
  .nav-toggle { display: none; }
  .site-nav, .site-nav.is-open { display: block; flex-basis: auto; order: 2; }
  .nav-list { flex-direction: row; gap: var(--space-5); padding: 0; }
  .nav-list a { font-size: var(--text-md); }
  .nav-list a[aria-current] {
    border-inline-start: 0;
    padding-inline-start: var(--space-1);
    border-bottom: 3px solid var(--lime);
  }
  .nav-cta, .nav-cta.is-open { display: block; flex-basis: auto; order: 3; padding: 0; margin-inline-start: auto; }
  .nav-cta .btn { width: auto; }
  .who { order: 4; margin-inline-start: 0; }
}

/* ---- 7b. Wrapping tab strip ------------------------------------------- */
.site-subnav {
  background: var(--navy-2);
  border-top: 1px solid var(--line-dark);
  --focus: var(--lime);
  --focus-mate: var(--navy-2);
}
.site-subnav ul {
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
  list-style: none;
  margin-block: 0;
  display: flex;
  flex-wrap: wrap;            /* never overflow-x */
  gap: var(--space-1);
}
.site-subnav a,
.site-subnav button {
  display: flex;
  align-items: center;
  min-height: 2.75rem;        /* 44px */
  padding-inline: var(--space-3);
  color: var(--on-dark-3);    /* 10.89:1 on --navy-2 */
  text-decoration: none;
  font: inherit;
  font-weight: 600;
  background: none;
  border: 0;
  border-bottom: 3px solid transparent;
  cursor: pointer;
}
.site-subnav a:hover,
.site-subnav button:hover { color: var(--on-dark); }
/* The current tab is marked three ways — colour, weight and the rule beneath
   — because colour alone is not an indicator (WCAG 1.4.1). */
.site-subnav [aria-current="page"] {
  color: var(--lime);
  font-weight: 700;
  border-bottom-color: var(--lime);
}

/* ==========================================================================
   8. CARD
   ========================================================================== */
.card {
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: clamp(var(--space-5), 4vw, var(--space-6));
  margin-bottom: var(--space-5);
  box-shadow: var(--shadow-sm);
}
.card:last-child { margin-bottom: 0; }
.card-flat { box-shadow: none; }

/* A panel inside a card: sunken, so it reads as content, not as a control. */
.panel {
  background: var(--page);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: var(--space-4);
}

/* ==========================================================================
   9. TYPOGRAPHY HELPERS
   ========================================================================== */
.lede { color: var(--ink-3); font-size: var(--text-lg); margin-bottom: var(--space-6); max-width: 60ch; }
.hint { color: var(--ink-3); font-size: var(--text-sm); margin: 0 0 var(--space-2); }
.note { color: var(--ink-3); font-size: var(--text-sm); line-height: var(--leading-loose); }
.sub  { color: var(--ink-2); font-size: var(--text-sm); }
.eyebrow {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-xs);
  color: var(--green);
  /* No letter-spacing. The artboard had 2px here; Hebrew has no
     letter-spacing tradition and it reads as broken text. */
}
.prose { max-width: 65ch; line-height: var(--leading-loose); }

/* An amber advisory box. Distinct from .note, which is fine print. */
.callout {
  background: var(--bad-bg);
  color: var(--bad-ink);
  border: 1px solid var(--bad-line);
  border-inline-start: 4px solid var(--amber);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  font-weight: 600;
  margin: 0 0 var(--space-4);
}

/* ==========================================================================
   10. FORMS
   ========================================================================== */
.field { margin-bottom: var(--space-5); }
.field:last-child { margin-bottom: 0; }

label, .label {
  display: block;
  font-weight: 700;
  margin-bottom: var(--space-2);
}

input, select, textarea {
  width: 100%;
  font: inherit;
  color: inherit;
  background: var(--white);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  min-height: 2.75rem;           /* 44px target */
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
input::placeholder, textarea::placeholder { color: var(--ink-3); opacity: 1; }
input:hover, select:hover, textarea:hover { border-color: var(--navy); }
textarea { min-height: 7rem; resize: vertical; line-height: var(--leading-body); }
input[disabled], select[disabled], textarea[disabled] { opacity: .55; cursor: not-allowed; }
input[type="checkbox"], input[type="radio"] { width: auto; min-height: 0; }
input[type="file"] {
  width: auto;
  max-width: 100%;
  background: var(--white);
  padding: var(--space-3);
}

fieldset { border: 0; margin: 0 0 var(--space-5); padding: 0; min-width: 0; }
legend { font-weight: 700; margin-bottom: var(--space-2); padding: 0; }

/* Two fields side by side above --bp-md. Named field-row, not row: .row
   already means a 2-column grid on one page and a service card on another. */
.field-row { display: grid; gap: var(--space-5); }
@media (min-width: 640px) { .field-row { grid-template-columns: 1fr 1fr; } }

.field-grid { display: grid; gap: 0 var(--space-5); grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); }

/* A Latin-only value — email, URL, username, a numeric code. Kept LTR so bidi
   cannot reorder it, and flush to the RTL start edge. `end` on a direction:ltr
   element resolves to the right edge, so this stays a logical property. */
.ltr { direction: ltr; text-align: end; }

/* A radio or checkbox card carrying an explanation. */
.choices { display: grid; gap: var(--space-3); }
.choice {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--page);
  cursor: pointer;
  margin-bottom: 0;
  font-weight: 400;
}
.choice:has(input:checked) { border-color: var(--line-checked); background: var(--ok-bg); }
.choice input { margin: 5px 0 0; flex: none; }
.choice .desc { display: block; color: var(--ink-2); font-size: var(--text-sm); margin-top: 2px; }

/* ==========================================================================
   11. BUTTONS
   ========================================================================== */
.btn, button.primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-base);
  line-height: 1.2;
  min-height: 2.75rem;                       /* 44px */
  padding: var(--space-3) var(--space-6);
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  background: var(--lime);
  color: var(--navy);                        /* 11.56:1 */
  transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.btn:hover, button.primary:hover { background: var(--lime-hover); color: var(--navy); }
.btn[disabled], button.primary[disabled] { opacity: .6; cursor: not-allowed; }
.btn[aria-busy="true"], button.primary[aria-busy="true"] { cursor: progress; }

.btn-navy  { background: var(--navy); color: var(--on-dark); border-color: var(--navy); }
.btn-navy:hover { background: var(--deep-navy); color: var(--on-dark); }
.btn-ghost { background: transparent; color: var(--on-dark); border-color: var(--line-dark-3); }
.btn-ghost:hover { background: var(--veil-2); color: var(--on-dark); }
.btn-quiet { background: var(--white); color: var(--navy); border-color: var(--line-strong); }
.btn-quiet:hover { background: var(--page); }
.btn-small, button.small { padding: var(--space-2) var(--space-4); font-size: var(--text-sm); }
.btn-block { width: 100%; }

/* A button that reads and behaves as a link. min-height 1.5rem meets the 24px
   AA target floor of WCAG 2.5.8 without breaking the inline flow of a
   sentence, which is where these actually sit. */
.btn-link, button.link {
  display: inline-flex;
  align-items: center;
  min-height: 1.5rem;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-weight: 700;
  color: var(--link);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.btn-link:hover, button.link:hover { color: var(--navy); }

/* ==========================================================================
   12. STATUS MESSAGE
   The node must carry role="status" and aria-live="polite" in the markup.

   The idle state uses NEITHER display:none NOR visibility:hidden — both of
   those take the node out of the accessibility tree, and a live region that
   is not in the tree when the text arrives is a live region that never
   announces. This is the bug in the pages today: .status starts at
   display:none and say() adds .show and writes the text in the same tick, so
   whether it is spoken depends on the screen reader's timing.

   Instead the region is always rendered and always empty when idle: zero
   height, no padding, no border, nothing to see, but present in the tree.
   RACQUET.say() sets the class first and writes the text on the next frame,
   so the region is already laid out when the announcement fires.
   ========================================================================== */
.status {
  margin: 0;
  padding: 0;
  border: 0 solid transparent;
  border-radius: var(--radius-sm);
  font-weight: 600;
  max-height: 0;
  overflow: hidden;
}
.status.show {
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-width: 1px;
  max-height: none;
  overflow: visible;
}
.status.ok  { background: var(--ok-bg);  color: var(--ok-ink);  border-color: var(--ok-line); }
.status.bad { background: var(--bad-bg); color: var(--bad-ink); border-color: var(--bad-line); }

/* ==========================================================================
   13. CHIP / TAG
   ========================================================================== */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-2xs);
  font-weight: 700;
  border-radius: var(--radius-pill);
  padding: var(--space-1) var(--space-3);
  background: var(--chip-bg);
  color: var(--navy);            /* 14.53:1 */
  border: 1px solid transparent;
}
.chip-draft     { background: var(--amber);  color: var(--navy);    }  /*  9.60:1 */
.chip-published { background: var(--ok-bg);  color: var(--ok-ink);  border-color: var(--ok-line); }
.chip-suspended { background: var(--bad-bg); color: var(--bad-ink); border-color: var(--bad-line); }
.chip-archived  { background: var(--chip-bg); color: var(--ink-2);  }  /*  5.34:1 */

.chips { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }

/* A chip that carries an action, e.g. a removable role. */
.tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  font-weight: 700;
  background: var(--chip-bg);
  color: var(--navy);
  border-radius: var(--radius-pill);
  padding: var(--space-1) var(--space-3);
}
.tags { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }

/* On a dark surface a chip inverts. */
.on-dark .chip, .site-header .chip {
  background: var(--veil);
  border-color: var(--hairline);
  color: var(--on-dark-2);
}

/* ==========================================================================
   14. AVATAR
   ========================================================================== */
.avatar {
  display: block;
  width: 6.5rem; height: 6.5rem;
  object-fit: cover;
  border-radius: var(--radius-xl);
  border: 1px solid var(--line);
  background: var(--page);
  flex: none;
}
.avatar-sm { width: 2.75rem; height: 2.75rem; border-radius: 50%; }
.avatar-fallback {
  width: 6.5rem; height: 6.5rem;
  border-radius: var(--radius-xl);
  display: block;
  object-fit: cover;             /* the logo tile, where a photo would be */
  background: var(--navy);
  flex: none;
}
.avatar-sm.avatar-fallback { width: 2.75rem; height: 2.75rem; border-radius: 50%; }

/* ==========================================================================
   15. EMPTY STATE
   Not an error and not a status — a neutral statement that a list is empty.
   ========================================================================== */
.empty {
  color: var(--ink-3);
  margin: 0;
  padding: var(--space-6) 0;
  text-align: center;
}
.empty-inline { padding: 0; text-align: start; }

/* ==========================================================================
   16. DESCRIPTION LIST (key/value)
   ========================================================================== */
.kv { display: grid; grid-template-columns: auto 1fr; gap: var(--space-2) var(--space-4); font-size: var(--text-md); margin: 0; }
.kv dt { color: var(--ink-3); }
.kv dd { margin: 0; font-weight: 600; }

/* ==========================================================================
   17. FOOTER
   ========================================================================== */
.site-footer {
  background: var(--navy);
  color: var(--on-dark-4);
  padding-block: var(--space-6);
  font-size: var(--text-sm);
  --focus: var(--lime);
  --focus-mate: var(--navy);
}
.site-footer a { color: var(--on-dark-4); text-decoration: none; }
.site-footer a:hover { color: var(--lime); text-decoration: underline; }

} /* @layer racquet.components */


@layer racquet.utilities {

/* ==========================================================================
   18. UTILITIES
   ========================================================================== */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
.text-start { text-align: start; }
.text-center { text-align: center; }
.pre-wrap { white-space: pre-wrap; }
.push-end { margin-inline-start: auto; }
.no-wrap { white-space: nowrap; }

/* A table that cannot shrink further gets its own scroll container rather
   than pushing the page sideways, and is focusable so it can be scrolled from
   the keyboard (WCAG 2.1.1). */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table-scroll:focus-visible { outline: 3px solid var(--focus); }

[hidden] { display: none !important; }

/* ==========================================================================
   19. MOTION
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   20. PRINT
   Colour here is information — status messages, chips. Without
   print-color-adjust every browser drops the backgrounds and an "ok" and a
   "bad" message print identically.
   ========================================================================== */
@media print {
  * { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }
  html, body { background: var(--white); }
  body { font-size: 11pt; }
  @page { margin: 14mm 12mm; }
  .skip-link, .nav-toggle, .site-subnav { display: none !important; }
  .card, .panel, table { box-shadow: none; break-inside: avoid; }
  h1, h2, h3 { break-after: avoid; }
}

} /* @layer racquet.utilities */
