/* CrossFitness trainer console — same palette as the app: lime on near-black. */

:root {
  --lime: #CCFF00;
  --lime-press: #B8E600;
  --lime-tint: rgba(204, 255, 0, 0.11);
  --lime-edge: rgba(204, 255, 0, 0.24);

  --ink: #050505;
  --panel: rgba(255, 255, 255, 0.04);
  --panel-lift: rgba(255, 255, 255, 0.065);
  --panel-solid: #0d0d0f;
  --hairline: rgba(255, 255, 255, 0.08);
  --hairline-lift: rgba(255, 255, 255, 0.16);

  /* Off-white, not #fff. Pure white against near-black has no headroom left: every
     other tier has to be an alpha of it, so "bright" and "brightest" collapse into
     the same value and the hierarchy flattens. */
  --text: #F2F4EE;
  --dim: rgba(242, 244, 238, 0.52);
  --faint: rgba(242, 244, 238, 0.34);

  --danger: #FF7A7A;
  --danger-fill: rgba(255, 122, 122, 0.10);
  --danger-edge: rgba(255, 122, 122, 0.30);

  /* ── RADIUS: one rule, radius follows surface size ──
     There were eleven values in this file - 26, 24, 18, 14, 13, 12, 11, 10 and three
     more - assigned per element as each was written. Nothing was wrong individually
     and the page still read as slightly unresolved, because a 13px thumbnail sat
     beside an 11px button inside a 18px card. Four steps, applied by what a thing IS:

       pill  every control: buttons, chips, tabs, badges, switches
       lg    full surfaces: the login card, the large avatar
       md    cards, rows, banners, previews
       sm    things you type into, and small imagery

     AND NESTING TIGHTENS IT. Anything sitting INSIDE a card takes the step below
     its container. A 46px avatar at the row's own 14px looked stuck onto the row
     rather than set into it, because concentric corners only read as concentric
     when the inner one is tighter. */
  --r-pill: 999px;
  --r-lg: 20px;
  --r-md: 14px;
  --r-sm: 10px;

  /* Kept: app.js and older rules still name these. */
  --r-card: var(--r-md);
  --r-field: var(--r-sm);

  /* Space. A 4px base, so gaps relate instead of being typed one at a time. */
  --s1: 4px;  --s2: 8px;  --s3: 12px;
  --s4: 16px; --s5: 24px; --s6: 34px;

  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

* { box-sizing: border-box; }

/*
 * The hidden attribute has to beat every display rule below it.
 *
 * hidden works through the UA stylesheet's [hidden] { display: none }, which loses to any
 * class selector setting display — so .busy { display: grid } stayed on screen even with
 * hidden set, covering the page and swallowing every click. The page looked loaded and
 * dead at the same time, because it was.
 */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font-family: Geist, -apple-system, "Segoe UI", system-ui, sans-serif;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
}

/* Form controls do not inherit font or colour — they take the browser's own, which on a
   dark page means black text in a black box. textarea and select need this as much as
   input does. */
button, input, textarea, select { font: inherit; color: inherit; }
h1, h2 { margin: 0; letter-spacing: -0.03em; font-weight: 600; }

/* ─── shared bits ─── */
.eyebrow {
  display: inline-block; padding: 4px 11px; border-radius: var(--r-pill);
  border: 1px solid rgba(255,255,255,0.12); background: rgba(255,255,255,0.03);
  font-size: 10px; font-weight: 600; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--dim);
}
.field-label {
  display: block; font-size: 10px; font-weight: 600; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--faint);
}
.muted { color: var(--dim); font-size: 13px; }
.mono { font-family: "Geist Mono", ui-monospace, SFMono-Regular, monospace;
        font-variant-numeric: tabular-nums; color: var(--faint); font-size: 13px; margin: 3px 0 0; }
.error { color: var(--danger); font-size: 13px; margin: 12px 0 0; }
.empty { text-align: center; color: var(--dim); padding: 60px 0; }

.field {
  display: flex; align-items: center; gap: 10px;
  height: 48px; padding: 0 15px; margin-top: 7px;
  background: var(--panel); border: 1px solid var(--hairline);
  border-radius: var(--r-field);
}
.field input { flex: 1; min-width: 0; background: none; border: 0; outline: none; font-size: 15px; }
.field input::placeholder { color: var(--faint); }
.field .cc { color: var(--dim); font-size: 15px; }
.field.phone .cc { padding-inline-end: 12px; border-inline-end: 1px solid var(--hairline); }

button.primary {
  width: 100%; height: 52px; border: 0; border-radius: var(--r-pill);
  background: var(--lime); color: var(--ink);
  font-size: 15px; font-weight: 700; cursor: pointer;
  transition: background .15s ease, transform .1s ease;
}
button.primary:hover { background: var(--lime-press); }
button.primary:active { transform: scale(0.99); }
button.primary:disabled { opacity: .5; cursor: default; }
button.primary.small { width: auto; height: 38px; padding: 0 18px; font-size: 13px; }

button.ghost {
  height: 40px; padding: 0 16px; border-radius: var(--r-pill); cursor: pointer;
  background: transparent; border: 1px solid var(--hairline); color: var(--dim);
  font-size: 13px; font-weight: 600;
}
button.ghost:hover { color: var(--text); border-color: rgba(255,255,255,0.2); }
button.ghost.danger { color: var(--danger); border-color: var(--danger-edge); }

.link {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-size: 12px; font-weight: 700; color: var(--lime);
}
.link.danger { color: var(--danger); }
.actions { display: inline-flex; gap: 14px; align-items: center; }

/* ─── language ─── */
.lang {
  display: inline-flex; gap: 2px; padding: 3px;
  background: var(--panel); border: 1px solid var(--hairline); border-radius: var(--r-pill);
}
.lang button {
  padding: 5px 12px; border: 0; border-radius: var(--r-pill); background: none;
  color: var(--faint); font-size: 12px; font-weight: 600; cursor: pointer;
}
.lang button.on { background: rgba(255,255,255,0.10); color: var(--text); }
/* In flow on its own line rather than floated into the corner, where it sat on top of
   the eyebrow. */
.login-card { display: flex; flex-direction: column; align-items: stretch; }
.login-card .lang { align-self: flex-end; margin-bottom: 16px; }
/* The eyebrow is a pill sized to its text — stretching it across the card turns it
   into a bar. */
.login-card .eyebrow { align-self: flex-start; }

/* ─── login ─── */
.login { min-height: 100dvh; display: grid; place-items: center; position: relative; overflow: hidden; }
.glow {
  position: absolute; top: -260px; left: -160px; width: 700px; height: 700px; border-radius: 50%;
  background: radial-gradient(circle, rgba(204,255,0,0.22) 0%, rgba(204,255,0,0.05) 45%, transparent 70%);
  pointer-events: none;
}
.login-card {
  position: relative; width: min(400px, calc(100vw - 40px));
  padding: 34px 30px; border-radius: var(--r-lg);
  background: rgba(13,13,15,0.72); border: 1px solid var(--hairline);
  backdrop-filter: blur(20px);
}
.login-card h1 { font-size: 34px; margin: 18px 0 0; }
.login-card .sub { color: var(--dim); font-size: 14px; margin: 8px 0 26px; }
.login-card .field-label { margin-top: 16px; }
.login-card button.primary { margin-top: 24px; }

/* ═══ CONSOLE CHROME ═══════════════════════════════════════════════════════
   The language here is the rack room, not a dark SaaS panel. The app already has
   a real idea in it - the member's week is drawn as a loaded barbell, plate
   diameter set by the day's volume - and the console shared none of it. So:
   steel surfaces, knurl, colour-coded collars down the list, and the lime used
   the way a gym uses it, as marking paint on equipment rather than as a glow.
   ═══════════════════════════════════════════════════════════════════════════ */

/* THE RAIL. Chrome used to sit in a topbar, sharing a line with the brand, the
   language pill and sign-out; under 620px all of it wrapped, the filters wrapped
   under that, and the list started a third of the way down a phone. Moving
   navigation to the side takes it out of the vertical budget completely and gives
   the list the full height, which is the thing a trainer looks at all day. */
.rail {
  position: fixed; inset-block: 0; inset-inline-start: 0; z-index: 5;
  width: 216px; padding: var(--s5) var(--s4);
  display: flex; flex-direction: column; gap: var(--s6);
  background: #0a0a0b;
  border-inline-end: 1px solid var(--hairline);
}
/* Knurl. The diamond cut on a barbell shaft, at the alpha where you register it as
   texture rather than see it as stripes. One surface only: repeat it and it stops
   being a detail and becomes a pattern. */
.rail::after {
  content: ""; position: absolute; inset-block: 0; inset-inline-end: 0; width: 5px;
  background:
    repeating-linear-gradient(45deg,  rgba(242,244,238,.05) 0 1px, transparent 1px 4px),
    repeating-linear-gradient(-45deg, rgba(242,244,238,.05) 0 1px, transparent 1px 4px);
  pointer-events: none;
}
.rail-brand { display: flex; align-items: center; gap: var(--s3); }
.rail-brand strong { font-size: 13px; font-weight: 600; color: var(--dim); min-width: 0;
                     overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The mark is a plate on a bar, end on: lime ring, dark core, the shaft through it.
   Two gradients, no asset, and it scales with the font. */
.mark {
  width: 26px; height: 26px; flex: 0 0 auto; border-radius: 50%;
  background: radial-gradient(circle, var(--ink) 0 34%, var(--lime) 34% 100%);
  position: relative;
}
.mark::after { content: ""; position: absolute; inset-inline: -4px; top: 50%;
               height: 3px; transform: translateY(-50%); border-radius: 2px;
               background: var(--hairline-lift); }

.tabs { display: flex; flex-direction: column; gap: 2px; }
.tab {
  position: relative; padding: 11px 14px; border: 0; border-radius: var(--r-sm);
  background: none; color: var(--faint); cursor: pointer;
  font-size: 13.5px; font-weight: 600; text-align: start;
}
.tab:hover { color: var(--text); background: var(--panel); }
/* The lit tab is marked at the edge like a rack position, not filled like a button. */
.tab.on { background: var(--panel-lift); color: var(--text); }
.tab.on::before {
  content: ""; position: absolute; inset-inline-start: 0; top: 22%; bottom: 22%;
  width: 3px; border-radius: 2px; background: var(--lime);
}
a.tab { text-decoration: none; display: block; }

.rail-foot { margin-block-start: auto; display: flex; flex-direction: column; gap: var(--s3); }
.rail-foot .lang { justify-content: center; }
.rail-foot .ghost { width: 100%; }

.shell { margin-inline-start: 216px; }

.view { max-width: 900px; margin: 0 auto; padding: var(--s6) var(--s5) 80px; }
/* The console had no page heading at all: the only way to tell Students from Shop
   was which pill was lit. */
.view-title { font-size: 30px; font-weight: 600; letter-spacing: -0.035em; margin: 0 0 var(--s5); }

.filters { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; margin-bottom: var(--s5); }
.chip { padding: 9px 15px; border-radius: var(--r-pill); cursor: pointer;
        background: transparent; border: 1px solid var(--hairline); color: var(--faint);
        font-size: 13px; font-weight: 600; }
.chip.on { background: var(--panel-lift); color: var(--text); border-color: var(--hairline-lift); }
.chip b { color: var(--lime); margin-inline-start: 6px; font-variant-numeric: tabular-nums; }
.chip[data-status="expired"] b { color: var(--danger); }
.search { flex: 1; min-width: 200px; height: 38px; padding: 0 15px;
          background: var(--panel); border: 1px solid var(--hairline);
          border-radius: var(--r-pill); outline: none; font-size: 13px; }
.count { color: var(--dim); font-size: 13px; margin-inline-end: auto; }

.licence {
  max-width: 900px; margin: var(--s5) auto -4px; padding: 13px 16px;
  background: var(--danger-fill); border: 1px solid var(--danger-edge);
  border-radius: var(--r-sm); color: var(--danger); font-size: 13px; line-height: 1.5;
}

/* ─── ROWS: the plate rack ───
   Every row carries a COLLAR at its inline start, colour-coded by state, so the whole
   list is scannable down one edge without reading a word. It replaces tinting the
   entire expired row red, which shouted at the one thing a trainer least needs
   shouted at - they already know Mahmoud lapsed, they are looking for Rivka.

   Semantic state is the one case a colour marker earns its place; it is not
   decoration, and there is no second one anywhere on the page. */
.rows { display: flex; flex-direction: column; gap: var(--s2); }
.row {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: var(--s4);
  padding: 14px 16px 14px 20px;
  background: var(--panel); border: 1px solid var(--hairline);
  border-radius: var(--r-md); cursor: pointer;
}
.row::before {
  content: ""; position: absolute; inset-block: 0; inset-inline-start: 0;
  width: 4px; background: var(--lime);
}
.row.expired::before { background: var(--danger); }
.row.hidden-item::before { background: var(--hairline-lift); }
.row:has(.pill.waiting)::before { background: var(--dim); }
/* The tint stays, at a quarter of its old strength: enough to find the row, not
   enough to make it the loudest thing on screen. */
.row.expired { background: rgba(255,122,122,0.035); border-color: rgba(255,122,122,0.16); }

.row .who { flex: 1; min-width: 0; }
.row .who strong { display: block; font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
.row .who span { font-size: 12.5px; color: var(--faint); }
.row .days { text-align: end; line-height: 1.05; }
/* Stamped, like the weight on a plate: mono, tabular, and big enough to read at a
   glance from a standing position at the desk. */
.row .days b {
  display: block;
  font-family: "Geist Mono", ui-monospace, SFMono-Regular, monospace;
  font-size: 21px; font-weight: 500; font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em; color: var(--lime);
}
.row.expired .days b { color: var(--danger); }
.row .days span { font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--faint); }

.avatar {
  width: 46px; height: 46px; border-radius: var(--r-sm); flex: 0 0 auto;
  display: grid; place-items: center; overflow: hidden;
  background: #111a05; border: 1px solid var(--lime-edge);
  color: var(--lime); font-weight: 600; font-size: 15px;
  background-size: cover; background-position: center;
}
.row.expired .avatar { background-color: rgba(255,122,122,0.14); border-color: var(--danger-edge); color: var(--danger); }
.avatar.lg { width: 78px; height: 78px; border-radius: var(--r-lg); font-size: 26px; }

/* Placeholder artwork. Sized as a share of its well so one drawing serves a 46px row
   avatar and a 150px preview without a second asset. */
.ph { width: 52%; height: 52%; fill: var(--lime); opacity: .5; }
.avatar .ph { width: 56%; height: 56%; }
.row.expired .avatar .ph { fill: var(--danger); }
.preview .ph, .thumb .ph { width: 40%; height: 40%; }
.thumb, .preview { display: grid; place-items: center; }

.thumb { width: 62px; height: 62px; border-radius: var(--r-sm); flex: 0 0 auto;
         background: #111a05 center/cover no-repeat; border: 1px solid var(--hairline); }
.price { font-size: 15px; font-weight: 700; color: var(--lime); }
.was { font-size: 12.5px; color: var(--faint); text-decoration: line-through; margin-inline-start: 8px; }
.badge { font-size: 10px; font-weight: 700; padding: 3px 8px; border-radius: var(--r-pill);
         background: var(--lime-tint); color: var(--lime); margin-inline-start: 8px; }
.badge.out { background: rgba(255,122,122,0.16); color: var(--danger); }
.state { font-size: 10px; color: var(--faint); text-align: end; }
.row.hidden-item { opacity: .45; }

.pill { display: inline-block; margin-top: 8px; padding: 3px 10px; border-radius: var(--r-pill);
        font-size: 10.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
        background: var(--lime-tint); color: var(--lime); border: 1px solid var(--lime-edge); }
.pill.expired { background: rgba(255,122,122,0.14); color: var(--danger); border-color: var(--danger-edge); }
.pill.waiting { background: rgba(255,255,255,0.07); color: var(--dim); border-color: var(--hairline); }

/* ─── drawers ─── */
.scrim { position: fixed; inset: 0; background: rgba(0,0,0,0.6); z-index: 10; }
.panel { position: fixed; top: 0; inset-inline-end: 0; bottom: 0; width: min(460px, 100vw);
         background: var(--panel-solid); border-inline-start: 1px solid var(--hairline);
         z-index: 11; overflow-y: auto; }
.panel-inner { padding: 26px; display: flex; flex-direction: column; gap: 14px; }
.close { position: absolute; top: 18px; inset-inline-end: 20px; width: 34px; height: 34px;
         border: 0; border-radius: var(--r-pill); background: rgba(255,255,255,0.06);
         color: var(--dim); font-size: 20px; cursor: pointer; }
.identity { display: flex; gap: 15px; align-items: center; margin-top: 8px; }
.identity h2 { font-size: 22px; }

.card { padding: 16px; background: var(--panel); border: 1px solid var(--hairline);
        border-radius: var(--r-card); display: flex; flex-direction: column; gap: 12px; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.ends { display: flex; align-items: baseline; gap: 10px; }
.ends strong { font-size: 25px; font-weight: 600; }
.ends span { font-size: 13px; font-weight: 600; color: var(--lime); }
.ends span.lapsed { color: var(--danger); }
.shortcuts { display: grid; grid-template-columns: repeat(4, 1fr); gap: 7px; }
.shortcuts button { height: 38px; border-radius: var(--r-sm); cursor: pointer;
                    background: var(--lime-tint); border: 1px solid var(--lime-edge);
                    color: var(--lime); font-size: 12.5px; font-weight: 700; }
input[type=date] { background: var(--panel); border: 1px solid var(--hairline);
                   border-radius: var(--r-sm); padding: 7px 11px; font-size: 13px; color-scheme: dark; }

.preview { height: 150px; border-radius: var(--r-sm); background: #111a05 center/cover no-repeat;
           border: 1px solid var(--hairline); display: grid; place-items: center; color: var(--faint); font-size: 13px; }
.preview.light { background-color: #fff; background-size: contain; }
.plan { display: flex; align-items: center; gap: 12px; }
.plan a { color: var(--lime); font-size: 13px; font-weight: 600; }

.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.final { display: flex; align-items: center; justify-content: space-between;
         padding: 13px 15px; border-radius: var(--r-sm);
         background: var(--lime-tint); border: 1px solid var(--lime-edge); }
.final strong { font-size: 20px; color: var(--lime); }
.final s { color: var(--faint); font-size: 12.5px; margin-inline-end: 8px; }

.toggle small { display: block; color: var(--faint); font-size: 11.5px; margin-top: 2px; }
.switch { position: relative; width: 46px; height: 27px; flex: 0 0 auto; }
.switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.switch span { position: absolute; inset: 0; border-radius: var(--r-pill);
               background: rgba(255,255,255,0.14); transition: background .15s ease; }
.switch span::after { content: ""; position: absolute; top: 3px; inset-inline-start: 3px;
                      width: 21px; height: 21px; border-radius: 50%; background: #fff;
                      transition: transform .15s ease; }
.switch input:checked + span { background: var(--lime); }
.switch.red input:checked + span { background: var(--danger); }
.switch input:checked + span::after { transform: translateX(19px); }
/* The knob has to travel the other way when the box is mirrored. */
[dir="rtl"] .switch input:checked + span::after { transform: translateX(-19px); }

/* Phone numbers read left to right in every language. Digits never mirror, and neither
   does the + on a country code — in an RTL run "+972" renders as "972+" unless the span
   is given its own direction. */
/* unicode-bidi: plaintext decides direction from the text itself. A phone number has no
   strongly-directional characters, so it resolves to LTR and the + stays in front —
   while the element keeps the page's alignment, so it still sits on the start side. */
.mono, .row .who span, .tel { unicode-bidi: plaintext; }
[dir="ltr"] { direction: ltr; }
.field .cc { direction: ltr; unicode-bidi: isolate; }
.field.phone input { direction: ltr; text-align: start; }

/* The glow belongs to the screen, not to the reading order, so it stays put. */

textarea {
  margin-top: 7px; padding: 11px 14px; min-height: 78px; resize: vertical;
  background: var(--panel); border: 1px solid var(--hairline);
  border-radius: var(--r-field); outline: none; font-size: 14px; line-height: 1.5;
}
textarea::placeholder { color: var(--faint); }
.field-label small { text-transform: none; letter-spacing: 0; font-weight: 500; color: var(--faint); margin-inline-start: 6px; }
select {
  background: var(--panel); border: 1px solid var(--hairline); border-radius: var(--r-sm);
  padding: 8px 11px; font-size: 13px; outline: none;
}
.field select { border: 0; background: none; flex: 1; padding: 0; }
.zone-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.zone-actions button { height: 42px; }
.danger-zone small { display: block; line-height: 1.5; }
.frozen-note { margin: 0; font-size: 12.5px; color: var(--dim); }
.chip[data-status="frozen"] b { color: var(--dim); }
.chip[data-status="archived"] b { color: var(--faint); }

.panel-actions { display: flex; flex-direction: column; gap: 10px; margin-top: 6px; }
.panel-actions button.ghost { height: 46px; }

.busy { position: fixed; inset: 0; display: grid; place-items: center;
        background: rgba(5,5,5,0.5); z-index: 20; }
.busy span { width: 34px; height: 34px; border-radius: 50%;
             border: 3px solid rgba(204,255,0,0.25); border-top-color: var(--lime);
             animation: spin .7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

@media (max-width: 820px) {
  /* The rail lies down and docks to the bottom, where a thumb is. Fixed, so the list
     scrolls under it rather than the navigation scrolling away. */
  .rail {
    inset: auto 0 0 0; width: auto; flex-direction: row; align-items: center;
    gap: var(--s3); padding: var(--s2) var(--s3);
    padding-bottom: calc(var(--s2) + env(safe-area-inset-bottom));
    border-inline-end: 0; border-block-start: 1px solid var(--hairline);
  }
  .rail::after { inset: 0 0 auto 0; width: auto; height: 4px; }
  .rail-brand { display: none; }

  /* TWO ROWS, not one. Squeezed onto a single line at 375px the three tabs, the
     language pill and sign-out do not fit: "Check-in" broke across two lines and
     "Sign out" was clipped by the screen edge. Wrapping is the honest fix - the bar
     is 40px taller and nothing is cut off or hyphenated. */
  .rail { flex-wrap: wrap; }
  .tabs { flex-direction: row; flex: 1 0 100%; gap: var(--s1); }
  .rail-foot { flex: 1 0 100%; justify-content: center; }
  .tab { flex: 1; text-align: center; padding: 10px 8px; font-size: 12.5px;
         white-space: nowrap; }
  /* The marker moves to the top edge, because a vertical bar has nowhere to sit in a
     horizontal tab. */
  .tab.on::before { inset: 0 22% auto 22%; width: auto; height: 3px; }
  .rail-foot { margin: 0; flex-direction: row; gap: var(--s2); }
  .rail-foot .lang { flex: 0 0 auto; }
  .rail-foot .ghost { width: auto; }

  .shell { margin-inline-start: 0; }
  /* Clears the two-row bar so the last row is never trapped under it. */
  .view { padding: var(--s5) var(--s4) 132px; }
  .view-title { font-size: 24px; }
  .pair { grid-template-columns: 1fr; }
  .panel { width: 100vw; }
}


/* App Store and Play both want a reachable policy URL, and a member who goes looking
   should find one without being told where. */
.legal-links { display: flex; gap: 18px; justify-content: center; padding: 28px 0 34px;
               font-size: 12.5px; }
.legal-links a { color: var(--faint); text-decoration: none; }
.legal-links a:hover { color: var(--text); }

/* ─────────── Waiting-for-approval banner ───────────
   A member in this state cannot sign in AT ALL, so on their panel approving is the
   only action that matters. It gets the lime treatment reserved for the primary
   path, sits directly under the identity where it is read first, and carries the
   button itself — the approve control used to be a ghost below the fold, next to a
   primary Save that did not approve anybody. */
.approve-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  margin: 0 0 16px;
  padding: 14px 16px;
  background: var(--lime-tint);
  border: 1px solid var(--lime-edge);
  border-radius: var(--r-md);
}
.approve-copy { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.approve-copy strong { color: var(--lime); font-size: 14px; letter-spacing: .01em; }
.approve-copy small  { color: var(--faint); font-size: 12.5px; line-height: 1.45; }
.approve-banner .primary { flex: 0 0 auto; }

/* ═══════════════════════════════════════════════════════════════════════════
   INTERACTION LAYER
   Motion here is feedback and hierarchy, never decoration. Four things move,
   each for a stated reason, and all four stop under prefers-reduced-motion.
   ═══════════════════════════════════════════════════════════════════════════ */

/* 1. FOCUS. There was no focus style in this file at all, so a trainer driving the
      console from the keyboard - which is what you do at a desk with a scanner in
      one hand - could not see where they were. The ring is the lime, offset so it
      reads on a dark panel and never sits under the element's own border. */
:where(button, input, textarea, select, a, [tabindex]):focus-visible {
  outline: 2px solid var(--lime);
  outline-offset: 2px;
  border-radius: inherit;
}
.row:focus-visible { outline-offset: 3px; }

/* 2. PRESS. Every control dips. A button that does nothing under the finger reads as
      broken on a touchscreen at the desk, and the console is used on a tablet. */
button, .chip, .tab, .row { transition:
  background .18s var(--ease), border-color .18s var(--ease),
  color .18s var(--ease), transform .09s var(--ease); }
button:active, .chip:active, .tab:active { transform: scale(0.975); }
.row:active { transform: scale(0.995); }

/* 3. ROWS LIFT ON HOVER. The list is the console's main surface and every row is a
      target; a border change alone is too quiet to say "this is the one". */
.row:hover { border-color: var(--hairline-lift); background: var(--panel-lift); }

/* 4. THE LIST ARRIVES TOP-FIRST. A stagger on render puts the eye at the top of the
      list rather than everywhere at once, and it re-runs on filter so a changed
      result set is visibly a NEW result set. Capped at ten so a long list is not a
      two-second cascade, and short enough that it never delays a click. */
@media (prefers-reduced-motion: no-preference) {
  .rows .row { animation: row-in .26s var(--ease) backwards; }
  .rows .row:nth-child(1) { animation-delay: 0ms; }
  .rows .row:nth-child(2) { animation-delay: 22ms; }
  .rows .row:nth-child(3) { animation-delay: 44ms; }
  .rows .row:nth-child(4) { animation-delay: 66ms; }
  .rows .row:nth-child(5) { animation-delay: 88ms; }
  .rows .row:nth-child(6) { animation-delay: 110ms; }
  .rows .row:nth-child(7) { animation-delay: 132ms; }
  .rows .row:nth-child(8) { animation-delay: 154ms; }
  .rows .row:nth-child(9) { animation-delay: 176ms; }
  .rows .row:nth-child(n+10) { animation-delay: 198ms; }

  /* The drawer comes in from the edge it lives on, so closing it obviously sends it
     back there. inset-inline-end means this mirrors correctly in Hebrew and Arabic
     without a second rule. */
  .panel { animation: panel-in .3s var(--ease); }
  .scrim { animation: fade-in .22s var(--ease); }
}
@keyframes row-in   { from { opacity: 0; transform: translateY(6px); } }
@keyframes panel-in { from { transform: translateX(24px); opacity: 0; } }
@keyframes fade-in  { from { opacity: 0; } }
[dir="rtl"] .panel { animation-name: panel-in-rtl; }
@keyframes panel-in-rtl { from { transform: translateX(-24px); opacity: 0; } }

/* THE OVERRIDE. Everything above collapses to static. The spinner keeps turning
   because a frozen spinner reads as a hung page, but it slows right down. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .busy span { animation: spin 1.6s linear infinite !important; }
  button:active, .chip:active, .tab:active, .row:active { transform: none; }
}

/* ─── Empty state ───
   It used to be one grey line of text. An empty list is the most common thing a new
   gym sees, and "nothing here" should say what to do about it. */
.empty { display: grid; place-items: center; gap: var(--s2); padding: 72px 24px; }
.empty strong { color: var(--text); font-size: 15px; font-weight: 600; }
.empty small { color: var(--faint); font-size: 13px; max-width: 34ch; line-height: 1.55; text-align: center; }

/* ─── Skeletons ───
   For the first load, where a spinner would say "something is happening" and a
   skeleton says "a list is coming, this is its shape". */
.skeleton { position: relative; overflow: hidden; background: var(--panel);
            border: 1px solid var(--hairline); border-radius: var(--r-md); height: 74px; }
.skeleton::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.045), transparent);
  animation: shimmer 1.3s infinite;
}
@keyframes shimmer { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .skeleton::after { animation: none; } }

/* The wrapper around a price and its struck-through original. It had no rule at all,
   so the two sat on the text baseline with no relationship to each other. */
.prices { display: inline-flex; align-items: baseline; gap: var(--s2); }

/* ─── Which plan a member is on ───
   A list of rungs, not a dropdown: a trainer needs to see the whole ladder and
   where this member sits on it, and the order IS the meaning here. */
.levels { display: flex; flex-direction: column; gap: 6px; }
.level {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 11px 13px; text-align: start; cursor: pointer;
  background: var(--panel); border: 1px solid var(--hairline);
  border-radius: var(--r-sm); color: var(--dim);
  font-size: 13.5px; font-weight: 600;
}
.level:hover { color: var(--text); border-color: var(--hairline-lift); }
.level.on {
  background: var(--lime-tint); border-color: var(--lime-edge); color: var(--lime);
}
.level small { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; opacity: .8; }

/* ═══ CHECK-INS DASHBOARD ══════════════════════════════════════════════════
   One series throughout, so there is no legend anywhere: each title names its
   own measure. The fill is a stepped-down lime rather than the brand one -
   #CCFF00 sits at OKLCH L 0.93, which is an asset on a hero number and far too
   hot spread across thirty bar fills. Brand lime is kept for the hovered bar and
   the headline figures, where the area is small.
   ═══════════════════════════════════════════════════════════════════════════ */
:root {
  --series: #7A9900;        /* validated: in the dark band, 6.20:1 on ink */
  --series-hot: var(--lime);
}

/* Stat tiles. Three numbers with no shape to compare are numbers, not a chart. */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
         gap: var(--s3); margin-bottom: var(--s5); }
.tile { padding: var(--s4); background: var(--panel); border: 1px solid var(--hairline);
        border-radius: var(--r-md); display: flex; flex-direction: column; gap: 2px; }
.tile-label { font-size: 10px; font-weight: 600; letter-spacing: .12em;
              text-transform: uppercase; color: var(--faint); }
/* Proportional figures, not tabular: equal-width digits make a large standalone
   number look loose. tabular-nums belongs in the tables and the axes. */
.tile b { font-size: 30px; font-weight: 600; letter-spacing: -0.03em; color: var(--text);
          font-variant-numeric: proportional-nums; }
.tile small { font-size: 11px; color: var(--faint); }
#tile-refused b { color: var(--danger); }

.chart-card { padding: var(--s4) var(--s4) var(--s3); background: var(--panel);
              border: 1px solid var(--hairline); border-radius: var(--r-md);
              margin-bottom: var(--s3); }
.chart-title { font-size: 14px; font-weight: 600; margin: 0 0 2px; letter-spacing: -0.01em; }
.chart-sub { font-size: 12.5px; color: var(--faint); margin: 0 0 var(--s4); }
.chart-title + .plot { margin-top: var(--s4); }

/* The container includes the axis band, so the card never grows a nested scrollbar. */
.plot { display: flex; align-items: flex-end; gap: 2px; height: 150px;
        border-bottom: 1px solid var(--hairline); }
.plot.short { height: 110px; }
.bar-col { flex: 1; min-width: 0; height: 100%; position: relative;
           display: flex; align-items: flex-end; cursor: default; }
/* The hit area is the whole column, so a one-check-in day is still hoverable. */
.bar { width: 100%; min-height: 2px; background: var(--series);
       border-radius: 4px 4px 0 0; transition: background .12s ease; }
.bar-col:hover .bar, .bar-col:focus-visible .bar { background: var(--series-hot); }
.bar.peak { background: var(--series-hot); }
.bar-peak { position: absolute; top: -16px; inset-inline: 0; text-align: center;
            font-family: "Geist Mono", ui-monospace, monospace; font-size: 10px;
            color: var(--lime); font-variant-numeric: tabular-nums; }
.bar.zero { min-height: 0; background: none; }
.bar-col:focus-visible { outline: none; }

.axis { display: flex; padding-top: 6px; font-family: "Geist Mono", ui-monospace, monospace;
        font-size: 10px; color: var(--faint); font-variant-numeric: tabular-nums; }
.axis span { flex: 1; }

table.data { width: 100%; border-collapse: collapse; margin-top: var(--s3); }
table.data td { padding: 9px 0; border-bottom: 1px solid var(--edge, rgba(255,255,255,0.05)); }
table.data tr:last-child td { border-bottom: 0; }
table.data strong { display: block; font-size: 13.5px; font-weight: 600; }
table.data .mono { display: block; font-size: 11.5px; margin: 1px 0 0; }
table.data td.num { text-align: end; white-space: nowrap; color: var(--dim);
                    font-family: "Geist Mono", ui-monospace, monospace; font-size: 12.5px;
                    font-variant-numeric: tabular-nums; }
.tag-refused { margin-inline-start: 8px; padding: 1px 7px; border-radius: var(--r-pill);
               font-size: 9.5px; font-weight: 700; letter-spacing: .06em;
               text-transform: uppercase; vertical-align: middle;
               background: rgba(255,122,122,0.14); color: var(--danger);
               border: 1px solid var(--danger-edge); }

.tip { position: absolute; z-index: 30; pointer-events: none;
       padding: 6px 10px; border-radius: var(--r-sm);
       background: var(--panel-solid); border: 1px solid var(--hairline-lift);
       font-size: 12px; color: var(--text); white-space: nowrap;
       box-shadow: 0 8px 24px rgba(0,0,0,.5); }

@media (max-width: 820px) { .tiles { grid-template-columns: repeat(2, 1fr); } }

/* ═══ WORKOUT EDITOR ═══════════════════════════════════════════════════════
   A ladder on one side and one week on the other. The ladder's ORDER is the
   meaning - a member carries an index into it - so it is a list with explicit
   move controls rather than anything that could be reordered by accident.
   ═══════════════════════════════════════════════════════════════════════════ */
.view.wide { max-width: 1120px; }
.wk-grid { display: grid; grid-template-columns: 300px 1fr; gap: var(--s5); align-items: start; }
.wk-plans { display: flex; flex-direction: column; gap: var(--s2);
            position: sticky; top: var(--s5); }
.wk-hint { line-height: 1.5; margin-top: var(--s2); font-size: 11.5px; }

.wk-plan { display: flex; align-items: stretch; gap: 4px; }
.wk-plan-open {
  flex: 1; min-width: 0; text-align: start; cursor: pointer;
  padding: 11px 13px; background: var(--panel); border: 1px solid var(--hairline);
  border-radius: var(--r-sm); color: var(--dim);
}
.wk-plan-open b { display: block; font-size: 13.5px; font-weight: 600; color: var(--text); }
.wk-plan-open span { font-size: 11.5px; color: var(--faint);
                     font-family: "Geist Mono", ui-monospace, monospace; }
.wk-plan.on .wk-plan-open { background: var(--lime-tint); border-color: var(--lime-edge); }
.wk-plan.on .wk-plan-open b { color: var(--lime); }
.wk-moves { display: flex; flex-direction: column; gap: 2px; }
.wk-moves button {
  width: 26px; flex: 1; cursor: pointer; border-radius: var(--r-sm);
  background: var(--panel); border: 1px solid var(--hairline); color: var(--dim);
  font-size: 11px; line-height: 1;
}
.wk-moves button:disabled { opacity: .3; cursor: default; }

.wk-week { display: flex; flex-direction: column; gap: var(--s3); }
.wk-day { gap: var(--s3); }
.wk-day.rest { opacity: .62; }
.wk-rest { display: flex; align-items: center; gap: var(--s2); }
.wk-rest small { font-size: 11px; color: var(--faint); }
.wk-add { width: 100%; }

.wk-ex { border: 1px solid var(--hairline); border-radius: var(--r-sm);
         background: rgba(255,255,255,0.02); overflow: hidden; }
.wk-ex-head {
  width: 100%; text-align: start; cursor: pointer; padding: 10px 12px;
  background: none; border: 0; display: flex; align-items: baseline;
  justify-content: space-between; gap: var(--s3);
}
.wk-ex-head b { font-size: 13.5px; font-weight: 600; color: var(--text); }
.wk-ex-head span { font-size: 11.5px; color: var(--faint); white-space: nowrap;
                   font-family: "Geist Mono", ui-monospace, monospace;
                   font-variant-numeric: tabular-nums; }
.wk-ex-body { padding: 0 12px 12px; display: flex; flex-direction: column; gap: var(--s2);
              border-top: 1px solid var(--hairline); padding-top: var(--s3); }
.wk-field { display: block; }
.wk-field .field { height: 40px; margin-top: 5px; }
.wk-field input { flex: 1; min-width: 0; background: none; border: 0; outline: none; font-size: 14px; }
.wk-trio { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s2); }
.wk-ex-tools { display: flex; gap: var(--s4); padding-top: 2px; }

@media (max-width: 900px) {
  .wk-grid { grid-template-columns: 1fr; }
  .wk-plans { position: static; }
  .wk-trio { grid-template-columns: 1fr 1fr; }
}

/* A movement's photograph, the same well the member photo uses. */
.wk-img { display: flex; flex-direction: column; gap: 6px; }
.wk-well { height: 120px; border-radius: var(--r-sm); }

/* Sort control, sitting in the filters row with the status chips and the search. */
.sortby {
  height: 38px; padding: 0 12px; cursor: pointer;
  background: var(--panel); border: 1px solid var(--hairline);
  border-radius: var(--r-pill); color: var(--dim);
  font-size: 13px; font-weight: 600; outline: none;
}
.sortby:hover { color: var(--text); border-color: var(--hairline-lift); }

/* Says the search bypassed the lit chip, so a list that disagrees with the
   selected filter is explained rather than merely odd. */
.search-note { margin: 0 0 var(--s3); font-size: 12.5px; color: var(--faint); }

/* ─────────── Picture credit ───────────
   The exercise drawings are CC BY-SA, and that licence wants the author named
   wherever the work is shown. This is the attribution, not decoration: it is the
   condition on which the gym is allowed to use these at all, so it does not get
   removed to tidy the page up. Same line lives in the app. */
.credit { margin: 26px 0 8px; font-size: 12px; color: var(--faint); }
.credit a { color: var(--faint); text-decoration: underline; }
.credit a:hover { color: var(--text); }

/* Reset password. One field and one button on a line; .zone-actions is a two-up grid
   for button pairs and would leave this button at half width with nothing beside it. */
.pass-row { display: flex; gap: 10px; margin-top: 7px; }
.pass-row .field { flex: 1; margin-top: 0; }
.pass-row button { height: 48px; padding: 0 16px; white-space: nowrap; }
#card-password small { display: block; line-height: 1.5; margin-top: 10px; }
#card-password small.done { color: var(--text); }

/* A multi-line field — the exercise description. .field is a fixed 48px row built for
   one-line inputs; this one grows with its text instead. */
.field.area { height: auto; padding: 10px 15px; align-items: stretch; }
.field.area textarea { flex: 1; min-width: 0; min-height: 76px; resize: vertical;
                       background: none; border: 0; outline: none; color: inherit;
                       font: inherit; font-size: 14.5px; line-height: 1.45; }
