/* ============================================================
   File:        members.css
   Project:     StudioFlow (Grace Aerobics & Fitness Center)
   Developer:   Janvi Vakhariya — V Techno Labs
   Created:     2026-09-02  (updated 2026-09-07)
   Purpose:     Design system for Members - five screens, one family:
                1) Members list  2) Member Details (hub)
                3) Add Member  4) Edit Member  5) Renew.
                ONE list pattern (.sf-member-row), ONE detail pattern
                (.sf-member-sec cards + .sf-member-actions grid) and ONE
                form pattern (.sf-member-form-card) shared by screens 3-5.
   Copyright:   © 2026 V Techno Labs. All rights reserved.
                Proprietary software developed by V Techno Labs.
   ============================================================

   Same "Sunset" language as the other final screens: white glass cards with
   soft pink shadows, 20px radius, Archivo, the wing gradient as a ribbon.

   THE SCANNING PROBLEM - 200 names, four states, some owing money: activity is
   a coloured spine plus a tinted chip (the same teal/orange/violet trio as
   Plans and Batches); state is one pill in a fixed position; dues are a red
   chip that appears ONLY when money is owed. Three signals, three fixed places,
   no reading required.

   Prefix: sf-member- throughout. Never sf-dash- / sf-idash- / sf-dev- /
   sf-user- / sf-plan- / sf-batch-.

   ============================================================
   Five things this file does differently from the supplied design, all of them
   because it lives inside the StudioFlow app shell:

   1. IT PAINTS NO BACKGROUND. The design gave .sf-member its own striped-wall
      ground. site.css now paints ONE background on .sf-app for every signed-in
      page, so a second one here would double the wash and put this sheet back
      into the state that had Plans tiling and Users blank. The photo variable is
      kept below, documented and unused, so the design's intent is still legible.

   2. The bar variables are bound to the shell's real fixed bars (--appbar-h
      56px, --tabbar-h 64px, from site.css :root) and then zeroed inside .sf-app,
      which already pads the body past both. The bottom bar carries four tabs for
      an instructor and five for the owner; its HEIGHT is the same either way,
      so one variable is right for both.

   3. Cards are solid white rather than translucent. The shared background sits
      behind every page now, and a card that let a bright mural through is the
      readability problem that background work already settled.

   4. container-type: inline-size is not declared. The design set it, but no
      @@container query in this file reads it.

   5. The <select> keeps its native caret. The design hid it with
      appearance: none and slotted a 16x16 chevron in its place, but
      wwwroot/icons has no chevron - hiding the arrow with nothing to put back
      would leave a dropdown that does not look like one. Same decision as
      batches.css.

   ============================================================
   IMPORTANT - two of these class names ALREADY EXIST in site.css, left over
   from the members screens this file replaces. site.css loads first, so every
   property it sets on them is re-declared here and this file wins. No view
   carries either class any more except the ones restructured with this design,
   so overriding them is safe:

     .sf-member       site.css gives it display: block. This file makes it a
                      flex column, so display is re-declared below.
     .sf-member-head  site.css gives it border-top: 4px solid var(--primary).
                      That is a leftover card rule, and the new page header wore
                      it as a teal line above the title. Cancelled with
                      border-top: 0 below.
   ============================================================

   Icons: every .sf-member-ico is a slot for an icon that EXISTS in
   wwwroot/icons. The design also asked for add, search, scale, edit and freeze
   glyphs, which this app does not have; those slots are left out and the text
   stands on its own rather than an icon being invented.
   ============================================================ */

.sf-member {
  /* NO PAGE BACKGROUND IMAGE HERE. site.css declares the one background the
     whole signed-in app uses, on .sf-frame. A second photo in this sheet is
     how the app ended up with a different ground on every screen. */
  --sf-member-wing: linear-gradient(90deg,#43a047,#d4b91e,#f57c00,#e53957,#c2185b,#7e57c2,#5c6f91);

  /* Muted deepened from #6d6b58, which measured 4.05:1 against the darkest
     part of the shared ball-pit ground -- under the 4.5:1 minimum for any
     muted line that sits on the ground rather than inside a card. #615F4E
     measures 4.85:1 there and 6.45:1 on white. Deepening the ink was the fix,
     not a panel behind the text. */
  --sf-member-ink: #2b2a1f; --sf-member-muted: #615F4E;
  --sf-member-pink: #c2185b; --sf-member-pink-deep: #98123f;
  --sf-member-card: #ffffff;                 /* solid - see note 3 */
  --sf-member-edge: rgba(43,42,31,.10);

  /* Gone with the fades it existed for. It was the colour the A-Z strip's end
     gradients faded INTO, hard-coded to white -- and the day this screen got a
     photographic ground (the shared one in site.css) that white stopped matching
     what was behind it and painted a smear over the last key instead. The
     strip wraps now and needs no fade at all, so the variable has no reader.
     Left recorded here rather than silently dropped, because the lesson is the
     point: a fade has to end in the colour actually behind it, and this one
     could not know what that was.

  --sf-member-bg: #fff;
  */
  --sf-member-line: rgba(43,42,31,.12);
  --sf-member-good: #1d7a3f; --sf-member-good-tint: #e3f3e8;   /* active */
  --sf-member-warn: #9a5b00; --sf-member-warn-tint: #fdf0d7;   /* expiring soon */
  --sf-member-bad: #b3261e;  --sf-member-bad-deep: #8c1d17; --sf-member-bad-tint: #fdecea;
  --sf-member-ice: #46587e;  --sf-member-ice-tint: #e7ecf5;    /* frozen */

  --sf-member-aer: #0f766e; --sf-member-aer-mid: #17a89a; --sf-member-aer-tint: #dff1ef;
  --sf-member-yog: #c96a00; --sf-member-yog-mid: #f08a1d; --sf-member-yog-tint: #fdeedd;
  --sf-member-dan: #6d28d9; --sf-member-dan-mid: #9d76e8; --sf-member-dan-tint: #efe8fa;

  --sf-member-radius: 20px; --sf-member-tap: 48px;
  --sf-member-top-bar: var(--appbar-h, 56px); --sf-member-bottom-bar: var(--tabbar-h, 64px);

  box-sizing: border-box; max-width: 640px; margin: 0 auto;
  display: flex; flex-direction: column; gap: 16px;
  padding: calc(var(--sf-member-top-bar) + 14px) 16px calc(var(--sf-member-bottom-bar) + 26px + env(safe-area-inset-bottom, 0px));
  color: var(--sf-member-ink);
  font-family: "Archivo", system-ui, sans-serif; font-size: 15px; line-height: 1.5;
  -webkit-tap-highlight-color: transparent;
}

/* Inside the shell the layout has already done all of this: .sf-app pads the
   body past both fixed bars and paints the background, and .sf-content supplies
   the gutter and caps the column at 640px. Repeating any of it would double it. */
.sf-app .sf-member {
  --sf-member-top-bar: 0px;
  --sf-member-bottom-bar: 0px;
  padding-left: 0; padding-right: 0;
  background: none;
}

.sf-member *, .sf-member *::before, .sf-member *::after { box-sizing: border-box; }
/* A real selector, not :where(). The design used :where(.sf-member a), which
   has ZERO specificity, so site.css's own link colour and underline won every
   time and every member name, group summary and expiry date rendered as an
   underlined blue link. This is the same rule with weight behind it. */
.sf-member a { color: inherit; text-decoration: none; }
.sf-member :focus-visible { outline: 2px solid var(--sf-member-pink); outline-offset: 2px; }
.sf-member-ico { display: inline-block; width: 20px; height: 20px; flex: none; }
.sf-member-go { display: inline-block; width: 16px; height: 16px; flex: none; opacity: .7; }

/* ---------- shared chrome ---------- */
.sf-member-intro { margin: 0 2px; }
.sf-member-title { margin: 0; font-size: 22px; font-weight: 800; letter-spacing: -.01em; }
.sf-member-sub { margin: 2px 0 0; font-size: 14px; color: var(--sf-member-muted); text-wrap: pretty; }
.sf-member-sub strong { color: var(--sf-member-ink); font-weight: 700; }

.sf-member-head {
  display: flex; align-items: center; gap: 10px;
  border-top: 0;                  /* cancels site.css's leftover teal rule */
}
.sf-member-head img { flex: none; border-radius: 7px; display: block; }
.sf-member-head .sf-member-intro { flex: 1; min-width: 0; }

.sf-member-card {
  background: var(--sf-member-card);
  border: 1.5px solid var(--sf-member-edge);
  border-radius: var(--sf-member-radius);
  box-shadow: 0 14px 30px -16px rgba(194,24,91,.4);
}

.sf-member-addbtn {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  width: 100%; min-height: 52px; border: 0; border-radius: 16px; cursor: pointer;
  background: linear-gradient(155deg, #e0447a, var(--sf-member-pink-deep));
  color: #fff; font-family: inherit; font-size: 15px; font-weight: 800;
  box-shadow: 0 12px 24px -12px rgba(194,24,91,.7);
}
.sf-member-addbtn:active { transform: translateY(1px); filter: brightness(.95); }

/* ============================================================
   SCREEN 1 - LIST
   ============================================================ */
.sf-member-search { display: flex; gap: 8px; }
.sf-member-search input {
  flex: 1; min-width: 0; min-height: 52px; padding: 0 14px;
  border: 1.5px solid var(--sf-member-edge); border-radius: 16px;
  background: var(--sf-member-card);
  color: var(--sf-member-ink); font-family: inherit; font-size: 16px; font-weight: 600;
}
.sf-member-search input::placeholder { color: rgba(43,42,31,.4); font-weight: 400; }
.sf-member-search input:focus { border-color: var(--sf-member-pink); outline: none; box-shadow: 0 0 0 3px rgba(194,24,91,.15); }
.sf-member-search button {
  flex: none; min-height: 52px; padding: 0 16px; border-radius: 16px; cursor: pointer;
  border: 1.5px solid var(--sf-member-edge); background: var(--sf-member-card);
  color: var(--sf-member-ink); font-family: inherit; font-size: 14px; font-weight: 800;
}

/* state filter chips - tinted in their own state colour when on */
.sf-member-filters { display: flex; flex-wrap: wrap; gap: 8px; }
.sf-member-filter {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 40px; padding: 0 13px; border-radius: 999px; cursor: pointer;
  border: 1.5px solid var(--sf-member-edge);
  background: var(--sf-member-card);
  color: var(--sf-member-ink); font-family: inherit; font-size: 13.5px; font-weight: 800; white-space: nowrap;
}
.sf-member-filter small { font-weight: 700; color: var(--sf-member-muted); }
.sf-member-filter.is-on { border-color: transparent; color: #fff; background: linear-gradient(155deg, #e0447a, var(--sf-member-pink-deep)); }
.sf-member-filter.is-on small { color: rgba(255,255,255,.85); }
.sf-member-filter--good.is-on { background: var(--sf-member-good); }
.sf-member-filter--warn.is-on { background: var(--sf-member-warn); }
.sf-member-filter--bad.is-on  { background: var(--sf-member-bad); }
.sf-member-filter--ice.is-on  { background: var(--sf-member-ice); }

/* ---------- activity filter row -------------------------------------------
   All / Aerobics / Yoga / Dance, above the state chips. One line that scrolls
   sideways on its own so the header cannot grow a third row of wrapping chips
   and push the list off a 360px screen -- the same trick the A-Z strip below
   uses, and for the same reason.

   Each button wears its activity's own colour when it is on, taken from the
   tokens the rows, chips and spines already use, so the filter and the list it
   produces are the same colour. */
.sf-member-actfilters {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding-bottom: 2px;
  scrollbar-width: none;
  /* The row scrolls; the page must not. */
  max-width: 100%;
}
.sf-member-actfilters::-webkit-scrollbar { display: none; }

.sf-member-actfilter {
  flex: none;
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 44px;                 /* a full tap target, not a 40px chip */
  padding: 0 13px; border-radius: 999px;
  border: 1.5px solid var(--sf-member-edge);
  background: var(--sf-member-card);
  color: var(--sf-member-ink);
  font-family: inherit; font-size: 13.5px; font-weight: 800; white-space: nowrap;
}
.sf-member-actfilter img { width: 20px; height: 20px; border-radius: 5px; display: block; }
.sf-member-actfilter small { font-weight: 700; color: var(--sf-member-muted); font-variant-numeric: tabular-nums; }

/* The selection has to be obvious at a glance: filled, in the activity's own
   colour, with the count carried through in white. */
.sf-member-actfilter.is-on { border-color: transparent; color: #fff; background: linear-gradient(155deg, #e0447a, var(--sf-member-pink-deep)); }
.sf-member-actfilter.is-on small { color: rgba(255,255,255,.85); }
.sf-member-actfilter--aer.is-on { background: var(--sf-member-aer); }
.sf-member-actfilter--yog.is-on { background: var(--sf-member-yog); }
.sf-member-actfilter--dan.is-on { background: var(--sf-member-dan); }

/* A-Z strip: one key per letter, the same control as the group headers below.

   IT WRAPS. It used to be one row that scrolled sideways, and that is the bug
   this replaced. Nothing clipped it and nothing blocked the drag -- the row was
   a real scroller -- but the only thing SAYING so was a pair of 26px fades
   painted in a hard-coded #fff, and the signed-in pages carry a photographic
   ground (the shared one in site.css). So the fade rendered as a white smear
   across the last key instead of dissolving into what was behind it, and with
   the scrollbar hidden on both engines there was nothing else to go on. The
   strip read as ending at I, and K and M -- the two letters that actually had
   members -- were never found.

   A grid, not flex-wrap. Flex wrapping sizes each key to its own content, so a
   "#" key and a "W" key come out different widths and the rows stop lining up
   into columns; a scanning eye loses the alphabet. Nine equal columns give a
   clean block AND divide the 27 keys (All + A-Z) into exactly three full rows,
   with no orphan key dangling alone on a fourth. Extra keys -- the digit and
   "#" groups StripKeys adds for names like "24Seven" -- simply begin a fourth
   row when they exist.

   THE HEIGHT TRADE, stated plainly because it is a real one. Twenty-seven keys
   at the 40px width a thumb wants need 4 rows at 360px, which is 194px of
   header above the list. Nine columns instead give ~31px per key at 360px
   (~37px including its share of the gaps) and 144px total. Below the 40px
   floor on the narrowest phone, above it from about 430px up -- and the keys
   stay a full 44px tall throughout, which is the dimension a thumb actually
   misses on. Three rows of nine was judged the better trade than a fourth row
   pushing the member list off the screen. */
.sf-member-az {
  display: grid;
  grid-template-columns: repeat(9, minmax(0, 1fr));
  gap: 6px;
}

.sf-member-azkey {
  display: grid; place-items: center;

  /* min-width: 0, and it matters. The key used to carry min-width: 40px, which
     was right for a scrolling row and is actively wrong in a grid: a 40px item
     in a 31px track does not shrink, it overflows, and nine of them would push
     the whole page into the horizontal scroll this fix exists to remove. The
     track decides the width now; the key fills it.

     44px tall is kept, and that is the dimension that matters -- a thumb misses
     vertically far more often than it misses a letter's width. */
  min-width: 0; height: 44px; padding: 0 2px; border-radius: 12px;
  border: 1.5px solid var(--sf-member-edge); background: var(--sf-member-card);
  font-size: 14px; font-weight: 800; color: var(--sf-member-ink);
}
.sf-member-azkey.is-on { border-color: transparent; color: #fff; background: linear-gradient(155deg, #e0447a, var(--sf-member-pink-deep)); }
/* Nothing behind this letter under the filters that are on.
   Opacity alone read as a rendering fault -- the key looked like the others,
   only fainter. This is a different KIND of thing: no border, no card, just a
   grey character sitting in the row. Every one of them carries a title and an
   aria-label saying which filters emptied it, so the state explains itself
   instead of looking broken. */
.sf-member-azkey--empty {
  border-color: transparent;
  background: transparent;
  color: var(--sf-member-muted);
  opacity: .55;
  font-weight: 700;
  cursor: default;
}

/* A-Z group.
   A link plus a body, not a <details>. The open state lives in the URL, so it
   survives a reload, can be bookmarked, and still works when the PWA is running
   from cache. A <details> would lose all three, and the server would no longer
   be able to send only the rows that are actually open. */
.sf-member-group { padding: 0 14px; overflow: hidden; }
.sf-member-groupsum {
  display: flex; align-items: center; gap: 10px;
  min-height: 52px; margin: 0 -14px; padding: 0 16px;
}
.sf-member-groupsum-letter {
  flex: none; display: grid; place-items: center;
  width: 34px; height: 34px; border-radius: 11px;
  background: linear-gradient(155deg, #e0447a, var(--sf-member-pink-deep));
  color: #fff; font-size: 15px; font-weight: 800;
}
.sf-member-groupsum-count { flex: 1; min-width: 0; font-size: 13.5px; font-weight: 700; color: var(--sf-member-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sf-member-chev { flex: none; color: var(--sf-member-muted); transition: transform .15s; }
.sf-member-group.is-open .sf-member-chev { transform: rotate(180deg); }
.sf-member-group.is-open .sf-member-groupsum { border-bottom: 1px solid var(--sf-member-line); }

/* one member row - the whole row is the tap target */
.sf-member-row {
  display: grid; grid-template-columns: minmax(0,1fr) auto;
  align-items: center; column-gap: 10px; row-gap: 2px;
  padding: 12px 0 12px 12px; min-height: 64px;
  border-top: 1px solid var(--sf-member-line);
  position: relative;
}
.sf-member-rowwrap:first-of-type .sf-member-row { border-top: 0; }
.sf-member-row:active { background: rgba(43,42,31,.04); }
.sf-member-row::before { content: ""; position: absolute; left: 0; top: 12px; bottom: 12px; width: 4px; border-radius: 999px; background: var(--sf-member-spine, var(--sf-member-pink)); }
.sf-member-rowwrap--aer { --sf-member-spine: var(--sf-member-aer-mid); }
.sf-member-rowwrap--yog { --sf-member-spine: var(--sf-member-yog-mid); }
.sf-member-rowwrap--dan { --sf-member-spine: var(--sf-member-dan-mid); }

.sf-member-row-name { margin: 0; grid-column: 1; grid-row: 1; display: flex; align-items: center; gap: 8px; min-width: 0; font-size: 15px; font-weight: 800; text-transform: capitalize; }
.sf-member-row-name-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sf-member-row-sub { margin: 0; grid-column: 1; grid-row: 2; font-size: 12.5px; color: var(--sf-member-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums; }
.sf-member-row-sub .sf-member-act-chip { margin-right: 2px; }

.sf-member-row-side { grid-column: 2; grid-row: 1 / span 2; display: flex; flex-direction: column; align-items: flex-end; gap: 3px; }
.sf-member-row-exp { font-size: 12px; color: var(--sf-member-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.sf-member-row-exp strong { color: var(--sf-member-ink); font-weight: 700; }

/* state pill - one per row, always top-right */
.sf-member-pill { display: inline-flex; align-items: center; gap: 5px; min-height: 24px; padding: 0 9px; border-radius: 999px; font-size: 11px; font-weight: 800; letter-spacing: .03em; text-transform: uppercase; white-space: nowrap; }
.sf-member-pill--good { background: var(--sf-member-good-tint); color: var(--sf-member-good); }
.sf-member-pill--warn { background: var(--sf-member-warn-tint); color: var(--sf-member-warn); }
.sf-member-pill--bad  { background: var(--sf-member-bad-tint);  color: var(--sf-member-bad); }
.sf-member-pill--ice  { background: var(--sf-member-ice-tint);  color: var(--sf-member-ice); }

/* activity chip - tiny, tinted. The icon is the app's own finished activity
   tile, placed at 12px and never masked or recoloured. */
.sf-member-act-chip { display: inline-flex; align-items: center; gap: 4px; min-height: 20px; padding: 0 7px; border-radius: 999px; font-size: 10px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; white-space: nowrap; vertical-align: -3px; }
.sf-member-act-chip img { width: 12px; height: 12px; border-radius: 3px; display: block; }
.sf-member-act-chip--aer { background: var(--sf-member-aer-tint); color: var(--sf-member-aer); }
.sf-member-act-chip--yog { background: var(--sf-member-yog-tint); color: var(--sf-member-yog); }
.sf-member-act-chip--dan { background: var(--sf-member-dan-tint); color: var(--sf-member-dan); }
.sf-member-act-chip--plain { background: rgba(43,42,31,.07); color: var(--sf-member-muted); }

/* dues chip - appears only when money is owed */
.sf-member-due { flex: none; display: inline-flex; align-items: center; min-height: 22px; padding: 0 8px; border-radius: 999px; background: var(--sf-member-bad); color: #fff; font-size: 11.5px; font-weight: 800; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* frozen rows read stood-down */
.sf-member-rowwrap--frozen .sf-member-row-name-text { color: var(--sf-member-muted); }
.sf-member-rowwrap--frozen .sf-member-row::before { background: transparent; border: 1.5px dashed var(--sf-member-spine, var(--sf-member-muted)); width: 2.5px; }

/* ---------- quick actions -------------------------------------------------
   A bar of small square buttons under each row: Renew, Add weight, and
   Freeze/Resume for the owner. Siblings of the row link, never nested inside
   it, because a link cannot legally sit inside another link -- which is also
   what keeps a tap on a button from opening the member.

   Two sizes, on purpose. The <a> is 44px square: that is the tap target, and it
   is the minimum a thumb can hit reliably. The face inside it is 38px: that is
   what is drawn. Wrapping the small square in the larger target is what lets
   the row stay compact without making the buttons hard to hit -- the extra 6px
   is transparent and overlaps nothing.

   The bar adds 44px to a row, so 25 rows grow by about a screen and a half.
   That is the cost of the feature; the row itself is unchanged. */
.sf-member-rowwrap { position: relative; }

/* The old Renew pill. The members list no longer renders one -- the quick
   actions replaced it -- but Member Details still dresses its "Remove from
   batch" button in this class, so the rule stays. */
.sf-member-row-renew {
  display: inline-flex; align-items: center; min-height: 36px; padding: 0 12px;
  margin: 0 0 10px 12px; border-radius: 999px;
  border: 1.5px solid rgba(194,24,91,.35);
  color: var(--sf-member-pink); font-size: 12.5px; font-weight: 800;
}

.sf-member-quick {
  display: flex;
  align-items: center;
  gap: 6px;
  /* Aligned with the name above it: 12px is the row's own left padding, less
     the 3px the 44px target carries around its 38px face.

     Pulled up by 4px into the row's own bottom padding. The tap target keeps
     its full 44px; only the empty space above it is reclaimed, which is worth
     doing once it is multiplied by twenty-five rows. */
  margin-top: -4px;
  padding: 0 12px 6px 9px;
}

.sf-member-quick-btn {
  display: grid;
  place-items: center;
  width: 44px; height: 44px;
  flex: none;
  border-radius: 14px;
  color: var(--sf-member-pink);
  -webkit-tap-highlight-color: transparent;
}

/* The square that is actually drawn, in the same rounded-tile language as the
   activity chips and the action grid on Member Details. */
.sf-member-quick-face {
  display: grid;
  place-items: center;
  width: 38px; height: 38px;
  border-radius: 12px;
  border: 1.5px solid rgba(194,24,91,.35);
  background: #fff;
  font-size: 14px; font-weight: 800; letter-spacing: .01em;
}

.sf-member-quick-btn:active .sf-member-quick-face { background: rgba(194,24,91,.08); }

/* Resume, in the app's own frozen colour: the one button whose meaning changes
   with the row state says so without a second glance. */
.sf-member-quick-btn--ice { color: var(--sf-member-ice); }
.sf-member-quick-btn--ice .sf-member-quick-face { border-color: rgba(70,88,126,.4); background: var(--sf-member-ice-tint); }

/* Freeze on a membership that cannot take one. Present so the row keeps its
   shape, plainly out of action, and carrying the reason in its title. */
.sf-member-quick-btn--off { color: var(--sf-member-muted); cursor: not-allowed; }
.sf-member-quick-btn--off .sf-member-quick-face {
  border-color: var(--sf-member-line);
  border-style: dashed;
  background: rgba(43,42,31,.04);
}

.sf-member-empty {
  background: var(--sf-member-card);
  border: 2px dashed rgba(43,42,31,.22); border-radius: var(--sf-member-radius);
  padding: 28px 18px; text-align: center;
}
.sf-member-empty h2 { margin: 0 0 6px; font-size: 16px; font-weight: 800; }
.sf-member-empty p { margin: 0; font-size: 13.5px; color: var(--sf-member-muted); text-wrap: pretty; }
.sf-member-hint { margin: 0; font-size: 12.5px; color: var(--sf-member-muted); text-wrap: pretty; }

/* ============================================================
   SCREEN 2 - MEMBER DETAILS (the hub)
   ============================================================ */
.sf-member-id { padding: 16px; position: relative; overflow: hidden; }
.sf-member-id::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 5px; background: var(--sf-member-wing); }
.sf-member-id-top { display: flex; align-items: flex-start; gap: 10px; margin-top: 2px; }
.sf-member-id-who { flex: 1; min-width: 0; }
.sf-member-id-name { margin: 0; font-size: 20px; font-weight: 800; text-transform: capitalize; line-height: 1.25; }
.sf-member-id-mobile { margin: 2px 0 0; font-size: 14px; color: var(--sf-member-muted); font-variant-numeric: tabular-nums; }
.sf-member-id-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.sf-member-id-dates { display: flex; gap: 10px; margin-top: 12px; }
.sf-member-id-date { flex: 1; border-radius: 14px; padding: 8px 12px; background: rgba(43,42,31,.05); }
.sf-member-id-date small { display: block; font-size: 10.5px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: var(--sf-member-muted); }
.sf-member-id-date strong { font-size: 14.5px; font-weight: 800; font-variant-numeric: tabular-nums; }
.sf-member-id-date--warn { background: var(--sf-member-warn-tint); }
.sf-member-id-date--warn strong { color: var(--sf-member-warn); }
.sf-member-id-date--bad { background: var(--sf-member-bad-tint); }
.sf-member-id-date--bad strong { color: var(--sf-member-bad); }

/* generic section card */
.sf-member-sec { padding: 14px 16px; }
.sf-member-sec-title { margin: 0 0 8px; display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: var(--sf-member-muted); }
.sf-member-sec-title .sf-member-side { margin-left: auto; font-size: 12.5px; font-weight: 700; text-transform: none; letter-spacing: 0; }

/* ---- a history section that folds -----------------------------------------
   The four history sections on Member Details -- weight, attendance, payments,
   membership -- open collapsed, so the page starts at the things she came for:
   who this is, what they owe, and what she can do about it.

   Native <details>/<summary>, deliberately, and this is the one place this
   module differs from the A-Z groups on the list. There, a closed group is not
   sent to the page at all, because a letter can hide two hundred rows and the
   saving is real. Here the whole record is a handful of rows the controller has
   already read, so a round trip per tap would be latency bought for nothing.
   <details> opens instantly, works with no script at all, and is focusable and
   operable from the keyboard without anything being wired up.

   The summary line on the closed header is the point of the whole exercise: it
   answers the question most of the time, so the section never needs opening. */
.sf-member-fold { padding: 0 16px; overflow: hidden; }

.sf-member-foldsum {
  display: flex; align-items: center; gap: 10px;
  min-height: 48px;                       /* a real tap target */
  margin: 0 -16px; padding: 4px 16px;
  cursor: pointer; list-style: none;
}
/* Both spellings: WebKit still wants its own pseudo-element, and the standard
   one is what everything else reads. Without them the browser paints its own
   triangle beside the chevron this design draws. */
.sf-member-foldsum::-webkit-details-marker { display: none; }
.sf-member-foldsum::marker { content: ""; }
.sf-member-foldsum:active { background: rgba(43,42,31,.04); }
.sf-member-foldsum:focus-visible { outline: 2px solid var(--sf-member-pink); outline-offset: -2px; }

/* The section's name, in the same small caps as every other card title, so a
   folded section and an open one read as the same furniture. */
.sf-member-foldsum-title {
  flex: none;
  font-size: 13px; font-weight: 800; letter-spacing: .07em;
  text-transform: uppercase; color: var(--sf-member-muted);
}

/* The answer. Takes the slack and truncates, so a long line can never push the
   chevron off a 360px screen. */
.sf-member-foldsum-note {
  flex: 1; min-width: 0; text-align: right;
  font-size: 12.5px; font-weight: 700; color: var(--sf-member-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* Money still owed is the one figure on a closed header that has to interrupt. */
.sf-member-foldsum-note .sf-member-foldsum-due { color: var(--sf-member-bad); }
/* Nothing recorded yet: the header still appears, in the same place, faded. */
.sf-member-foldsum-note--empty { color: var(--sf-member-muted); font-weight: 600; }

.sf-member-fold .sf-member-chev { flex: none; color: var(--sf-member-muted); transition: transform .15s; }
.sf-member-fold[open] .sf-member-chev { transform: rotate(180deg); }
.sf-member-fold[open] .sf-member-foldsum { border-bottom: 1px solid var(--sf-member-line); }

/* The body. The negative margins above let the header rule run the full width
   of the card; the body keeps the card's own padding. */
.sf-member-foldbody { padding: 12px 0 14px; }
.sf-member-foldbody > :first-child { margin-top: 0; }
.sf-member-foldbody > :last-child { margin-bottom: 0; }

@media (prefers-reduced-motion: reduce) {
  .sf-member-fold .sf-member-chev { transition: none; }
}

/* key/value rows */
.sf-member-kvlist { margin: 0; }
.sf-member-kv { display: flex; justify-content: space-between; gap: 12px; padding: 8px 0; border-top: 1px solid var(--sf-member-line); font-size: 14px; }
.sf-member-kv:first-of-type { border-top: 0; }
.sf-member-kv dt { color: var(--sf-member-muted); font-weight: 600; }
.sf-member-kv dd { margin: 0; font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; text-wrap: pretty; }

/* money rows - pending is the loud one, and reads green when nothing is owed */
.sf-member-kv--pend dd { color: var(--sf-member-bad); font-size: 16px; font-weight: 800; }
.sf-member-kv--pend-zero dd { color: var(--sf-member-good); font-size: 16px; font-weight: 800; }
.sf-member-kv--strong dt, .sf-member-kv--strong dd { font-weight: 800; color: var(--sf-member-ink); font-size: 15px; }

/* ---- the action grid: two columns, always deliberate ----
   Renew always spans the full width, and so does the last action. An owner sees
   seven and an instructor four; both land on a clean block because of those two
   rules, without either role needing a layout of its own. */
.sf-member-actions { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 10px; }
.sf-member-action {
  display: flex; align-items: center; justify-content: center; gap: 7px;
  min-height: var(--sf-member-tap); padding: 0 12px; border-radius: 14px; cursor: pointer;
  border: 1.5px solid var(--sf-member-edge);
  background: var(--sf-member-card);
  color: var(--sf-member-ink); font-family: inherit; font-size: 14px; font-weight: 800; white-space: nowrap;
}
.sf-member-action img { flex: none; border-radius: 4px; display: block; }
.sf-member-action:active { filter: brightness(.95); }
.sf-member-action--wide { grid-column: 1 / -1; }
.sf-member-action--primary {
  border: 0; min-height: 52px;
  background: linear-gradient(155deg, #e0447a, var(--sf-member-pink-deep));
  color: #fff; font-size: 15px;
  box-shadow: 0 12px 24px -12px rgba(194,24,91,.7);
}
.sf-member-action--primary:active { transform: translateY(1px); }
.sf-member-action--ice { border-color: rgba(70,88,126,.4); color: var(--sf-member-ice); }
.sf-member-action--danger { border-color: rgba(179,38,30,.35); color: var(--sf-member-bad); }
.sf-member-action--danger:active { background: var(--sf-member-bad-tint); }

/* a POST action arrives wrapped in a form carrying the antiforgery token; the
   form shrinks to the button inside it so the grid still reads as equal cells */
.sf-member-actform { margin: 0; display: grid; }
.sf-member-actform--wide { grid-column: 1 / -1; }

/* payments / weight / history rows */
.sf-member-payrow { display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: center; row-gap: 1px; padding: 10px 0; border-top: 1px solid var(--sf-member-line); }
.sf-member-payrow:first-of-type { border-top: 0; }
.sf-member-pay-amt { font-size: 15px; font-weight: 800; font-variant-numeric: tabular-nums; }
.sf-member-pay-amt .sf-member-mode { margin-left: 6px; display: inline-flex; align-items: center; min-height: 20px; padding: 0 7px; border-radius: 999px; background: rgba(43,42,31,.07); color: var(--sf-member-muted); font-size: 10.5px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; vertical-align: 2px; }
.sf-member-pay-sub { grid-column: 1; font-size: 12.5px; color: var(--sf-member-muted); font-variant-numeric: tabular-nums; }

.sf-member-weight { display: flex; align-items: center; gap: 14px; }
.sf-member-weight-col small { display: block; font-size: 10.5px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: var(--sf-member-muted); }
.sf-member-weight-col strong { font-size: 19px; font-weight: 800; font-variant-numeric: tabular-nums; }
.sf-member-weight-arrow { color: var(--sf-member-muted); font-weight: 800; }
.sf-member-delta { margin-left: auto; display: inline-flex; align-items: center; min-height: 26px; padding: 0 10px; border-radius: 999px; font-size: 12.5px; font-weight: 800; font-variant-numeric: tabular-nums; }
.sf-member-delta--down { background: var(--sf-member-good-tint); color: var(--sf-member-good); }
.sf-member-delta--up { background: var(--sf-member-warn-tint); color: var(--sf-member-warn); }

/* ============================================================
   FORM PATTERN - screens 3, 4, 5
   ============================================================ */
.sf-member-form-card {
  background: var(--sf-member-card);
  border: 1.5px solid var(--sf-member-edge);
  border-radius: var(--sf-member-radius);
  box-shadow: 0 14px 30px -16px rgba(194,24,91,.4);
  padding: 16px; position: relative; overflow: hidden;
  display: flex; flex-direction: column; gap: 14px;
}
.sf-member-form-card::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 5px; background: var(--sf-member-wing); }

/* group header inside the long form */
.sf-member-formsec { margin: 6px 0 -4px; padding-top: 14px; border-top: 2px solid var(--sf-member-line); }
.sf-member-formsec:first-child { margin-top: 2px; padding-top: 0; border-top: 0; }
.sf-member-formsec h2 { margin: 0; font-size: 15px; font-weight: 800; }
.sf-member-formsec p { margin: 1px 0 0; font-size: 12.5px; color: var(--sf-member-muted); text-wrap: pretty; }

.sf-member-field { display: flex; flex-direction: column; gap: 6px; }
.sf-member-label { font-size: 13px; font-weight: 800; letter-spacing: .02em; }
.sf-member-label small { font-weight: 600; color: var(--sf-member-muted); }
.sf-member-input {
  width: 100%; min-height: 52px; padding: 0 14px;
  border: 1.5px solid rgba(43,42,31,.2); border-radius: 14px;
  background: #fff; color: var(--sf-member-ink);
  font-family: inherit; font-size: 16px; font-weight: 600;
}
.sf-member-input::placeholder { color: rgba(43,42,31,.35); font-weight: 400; }
.sf-member-input:focus { border-color: var(--sf-member-pink); outline: none; box-shadow: 0 0 0 3px rgba(194,24,91,.15); }
textarea.sf-member-input { min-height: 84px; padding: 12px 14px; resize: vertical; line-height: 1.45; }
select.sf-member-input { padding-right: 40px; }
.sf-member-pair { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 10px; }
.sf-member-affix { position: relative; }
.sf-member-affix .sf-member-input { padding-left: 34px; }
.sf-member-affix-unit { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); font-size: 15px; font-weight: 700; color: var(--sf-member-muted); pointer-events: none; }

/* the reassurance callout: the expiry computed from the plan and the join date */
.sf-member-runs {
  border: 1.5px solid rgba(29,122,63,.35); border-left-width: 5px;
  border-radius: 14px; background: var(--sf-member-good-tint);
  padding: 10px 14px;
}
.sf-member-runs small { display: block; font-size: 10.5px; font-weight: 800; letter-spacing: .07em; text-transform: uppercase; color: var(--sf-member-good); }
.sf-member-runs strong { font-size: 16px; font-weight: 800; color: var(--sf-member-good); font-variant-numeric: tabular-nums; }
.sf-member-runs span { display: block; font-size: 12.5px; color: #1d5c33; }

/* cash / UPI segmented radios */
.sf-member-seg { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 10px; }
.sf-member-segopt { position: relative; display: block; min-width: 0; }
.sf-member-segopt input { position: absolute; opacity: 0; inset: 0; pointer-events: none; }
.sf-member-segopt-card {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 52px; padding: 0 10px; border-radius: 14px; cursor: pointer;
  border: 1.5px solid rgba(43,42,31,.18); background: #fff;
  font-size: 14.5px; font-weight: 800;
}
.sf-member-segopt input:checked + .sf-member-segopt-card { border-color: var(--sf-member-pink); background: #fce8f0; color: var(--sf-member-pink); box-shadow: 0 0 0 3px rgba(194,24,91,.15); }
.sf-member-segopt input:focus-visible + .sf-member-segopt-card { outline: 2px solid var(--sf-member-pink); outline-offset: 2px; }

/* quick discount chips. .sf-chip-on is what site.js writes on the chosen chip;
   .is-on is the design's own name for the same state, so both are matched
   rather than a shared script being rewritten. */
.sf-member-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.sf-member-chip {
  min-height: 40px; padding: 0 13px; border-radius: 999px; cursor: pointer;
  border: 1.5px solid rgba(43,42,31,.2); background: #fff;
  font-family: inherit; font-size: 13.5px; font-weight: 800; color: var(--sf-member-ink);
}
.sf-member-chip:active, .sf-member-chip.is-on, .sf-member-chip.sf-chip-on { border-color: var(--sf-member-pink); background: #fce8f0; color: var(--sf-member-pink); }

/* active toggle row (Edit) */
.sf-member-toggle { display: flex; align-items: flex-start; gap: 10px; padding: 12px 14px; border: 1.5px solid rgba(43,42,31,.16); border-radius: 14px; background: #fff; }
.sf-member-toggle input { width: 22px; height: 22px; margin: 1px 0 0; accent-color: var(--sf-member-pink); flex: none; }
.sf-member-toggle strong { display: block; font-size: 14.5px; font-weight: 800; }
.sf-member-toggle span span { display: block; font-size: 12.5px; color: var(--sf-member-muted); text-wrap: pretty; }

/* whom card (Renew / Freeze / Resume / payment) - who, and which period */
.sf-member-whom { display: flex; align-items: center; gap: 12px; padding: 12px; border-radius: 16px; background: #fce8f0; border: 1.5px solid rgba(194,24,91,.25); }
.sf-member-whom-badge { flex: none; display: grid; place-items: center; min-width: 54px; min-height: 54px; border-radius: 14px; background: #fff; color: var(--sf-member-pink); font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; text-align: center; line-height: 1.3; padding: 4px 8px; }
.sf-member-whom-name { margin: 0; font-size: 16px; font-weight: 800; text-transform: capitalize; }
.sf-member-whom-sub { margin: 0; font-size: 13px; color: var(--sf-member-muted); }

/* renewal before-and-after summary */
.sf-member-sum { border: 1.5px solid rgba(43,42,31,.16); border-radius: 14px; background: rgba(43,42,31,.04); padding: 4px 14px; }

.sf-member-submit {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  width: 100%; min-height: 52px; margin-top: 2px;
  border: 0; border-radius: 16px; cursor: pointer;
  background: linear-gradient(155deg, #e0447a, var(--sf-member-pink-deep));
  color: #fff; font-family: inherit; font-size: 16px; font-weight: 800;
  box-shadow: 0 12px 24px -12px rgba(194,24,91,.7);
}
.sf-member-submit:active { transform: translateY(1px); filter: brightness(.95); }
.sf-member-cancel {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  width: 100%; min-height: var(--sf-member-tap);
  border: 1.5px solid var(--sf-member-edge); border-radius: 14px; cursor: pointer;
  background: var(--sf-member-card);
  color: var(--sf-member-ink); font-family: inherit; font-size: 15px; font-weight: 700;
}
.sf-member-cancel:active { filter: brightness(.96); }

/* ---------- validation ----------
   The summary the view prints when the server rejects a post, and the per-field
   message the asp-validation-for span carries. The span keeps its own
   .sf-field-error class from site.css and wears .sf-member-error as well, so the
   tag helper markup is untouched and this file only recolours it. */
.sf-member-alert {
  display: flex; flex-direction: column; gap: 4px;
  margin: 0; padding: 12px 14px; border-radius: 14px;
  background: var(--sf-member-bad-tint); color: var(--sf-member-bad-deep);
  border: 1.5px solid rgba(179,38,30,.3);
  font-size: 13.5px; font-weight: 600; text-wrap: pretty;
}
/* A NOTICE, not an alert. The early-renewal message on Renew is good news --
   the member keeps days they paid for -- so it reads in the calm blue-grey of
   information rather than the red of .sf-member-alert above, which is reserved
   for money owed and for days about to be thrown away. */
.sf-member-note {
  display: flex; flex-direction: column; gap: 4px;
  margin: 0; padding: 12px 14px; border-radius: 14px;
  background: var(--sf-member-ice-tint, #eef2f7); color: var(--sf-member-ink);
  border: 1.5px solid rgba(92,111,145,.28);
  font-size: 13.5px; text-wrap: pretty;
}
.sf-member-note .sf-member-label { color: var(--sf-member-muted); }
.sf-member-note .sf-member-hint { color: inherit; }

/* The confirmation that has to be ticked before a renewal may discard paid
   days. Sized as a real tap target and given the same weight as the warning it
   sits inside, so it cannot be missed on a phone held one-handed at the desk. */
.sf-member-check {
  display: flex; align-items: center; gap: 12px;
  min-height: 48px; margin-top: 6px; padding: 8px 12px;
  border: 1.5px solid rgba(179,38,30,.35); border-radius: 12px;
  background: #fff; cursor: pointer;
  font-size: 14px; font-weight: 800; color: var(--sf-member-bad-deep);
}
.sf-member-check input { flex: none; width: 22px; height: 22px; accent-color: var(--sf-member-bad); cursor: pointer; }
.sf-member-check:has(input:checked) { border-color: var(--sf-member-bad); background: var(--sf-member-bad-tint); }

.sf-member-error { font-size: 12.5px; font-weight: 700; color: var(--sf-member-bad); }
.sf-member-error:empty { display: none; }
.sf-member-input.input-validation-error { border-color: var(--sf-member-bad); background: #fffafa; }

/* ---------- footer ----------
   The rainbow rule only. The tagline and the credit under it come from the
   shared _SiteFooter partial the layout already renders; repeating them here
   would say "Music on. Chaos off." twice on one screen. */
.sf-member-foot { text-align: center; padding: 2px 2px 0; }
.sf-member-wingbar { height: 4px; border: 0; width: 132px; margin: 0 auto 12px; border-radius: 999px; background: var(--sf-member-wing); }

/* ---------------------------------------------------------------- fee block
   The three figures on Add and Edit that must always reconcile:
       TOTAL_FEE = PLAN_PRICE - DISCOUNT_AMOUNT
   Laid out as a small statement rather than a form row, because none of it is
   typed: the price comes from the plan, the total is arithmetic, and only the
   discount above it is an input. The discount line is drawn in the warning
   colour and is present in the markup even when hidden, so it can appear as
   she types without the block jumping height on first keystroke. */
.sf-member-fee {
    margin: 14px 0 4px;
    padding: 14px 16px;
    border: 1px solid var(--sf-member-line);
    border-radius: 14px;
    background: var(--sf-member-tint, #F7F8FA);
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.sf-member-fee-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    font-size: 14px;
    color: var(--sf-member-muted);
}

.sf-member-fee-row strong {
    font-size: 15px;
    font-weight: 700;
    color: var(--sf-member-ink);
    font-variant-numeric: tabular-nums;
}

/* The reason rides inside the label, so a long one wraps under the words
   rather than pushing the amount off the row. */
.sf-member-fee-row--off,
.sf-member-fee-row--off strong { color: var(--sf-member-warn, #B45309); }
.sf-member-fee-row--off span span { font-style: italic; }

.sf-member-fee-row--total {
    margin-top: 2px;
    padding-top: 10px;
    border-top: 1px solid var(--sf-member-line);
    font-size: 15px;
    color: var(--sf-member-ink);
    font-weight: 600;
}

.sf-member-fee-row--total strong { font-size: 18px; }

/* ------------------------------------------------------------- audit lines
   Who added a member, who took a payment, who froze and who resumed. Audit
   information, not a headline: one step smaller than the sub-line it follows,
   in the muted ink, and never bold. It sits on its own line so a long name
   cannot push the amount or the date out of shape. */
.sf-member-audit {
    display: block;
    margin-top: 2px;
    font-size: 11px;
    line-height: 1.45;
    font-weight: 400;
    color: var(--sf-member-muted);
}

/* Inside a membership period, whose label is bold and whose current row is
   bolder still. Stated rather than inherited, so the audit line stays secondary
   to the period it belongs to however the row above it is emphasised. */
.sf-member-kv dt .sf-member-audit {
    margin-top: 3px;
    font-size: 11px;
    font-weight: 400;
    color: var(--sf-member-muted);
}

/* The dates keep their line and the name wraps instead.
   Both halves of a kv row can shrink by default, so adding a second line of
   text on the left pulled the right-hand column narrow enough to break
   "14 Sep - 14 Oct 2026" across two lines. Scoped to the period rows: the money
   rows above have long values against short labels and need the opposite. */
.sf-member-kv--period dd { flex: 0 0 auto; }
.sf-member-kv--period dt { min-width: 0; }

/* Inside a payment or freeze row, which is a two-column grid: the line has to
   claim the whole width or auto-placement drops it into the right-hand column
   beside the date, squeezing both until the date breaks one word per line. */
.sf-member-payrow .sf-member-audit { grid-column: 1 / -1; }

/* The pair at the foot of the page, set apart from the last card. */
.sf-member-auditbox {
    margin: 4px 2px 0;
    padding: 0 2px;
    display: flex;
    flex-direction: column;
    gap: 1px;
}
