/* Admin Panel Styles - RTL layout (Hebrew) */

/* ── Login ────────────────────────────────────────── */

.admin-login {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 80vh;
  direction: rtl;
}

.login-card {
  background: rgba(255, 255, 255, 0.78);
  -webkit-backdrop-filter: blur(18px) saturate(1.5);
  backdrop-filter: blur(18px) saturate(1.5);
  border: 1px solid rgba(255, 255, 255, 0.65);
  border-radius: 16px;
  padding: 2.5rem;
  width: 380px;
  max-width: calc(100vw - 32px);
  text-align: center;
  box-shadow: 0 8px 32px rgba(0, 60, 130, 0.08);
}

.login-card h1 {
  font-size: 1.5rem;
  font-weight: 600;
  color: #333;
  margin-bottom: 0.25rem;
}

.login-subtitle {
  color: #888;
  font-size: 0.9rem;
  margin-bottom: 1.5rem;
}

.login-input-wrapper {
  position: relative;
  display: flex;
  align-items: center;
}

.login-input {
  width: 100%;
  padding: 0.75rem 5.5rem 0.75rem 1rem;
  border: 1px solid #ddd;
  border-radius: 8px;
  font-size: 1rem;
  outline: none;
  transition: border-color 0.2s;
  direction: rtl;
}

.login-input:focus {
  border-color: #0071E3;
}

.login-input-action {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: #aaa;
  font-size: 0.85rem;
  cursor: pointer;
  transition: color 0.15s, background 0.15s;
}

.login-input-action:hover {
  color: #555;
  background: rgba(0, 0, 0, 0.04);
}

.login-eye-btn {
  left: 8px;
}

.login-clear-btn {
  left: 38px;
  font-size: 0.78rem;
}

.login-error {
  color: #dc3545;
  font-size: 0.85rem;
  margin: 0.5rem 0 0;
}

.login-btn {
  width: 100%;
  padding: 0.75rem;
  margin-top: 1rem;
  background: #0071E3;
  color: #fff;
  border: none;
  border-radius: 8px;
  font-size: 1rem;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.2s;
}

.login-btn:hover {
  background: #0077ED;
}

/* ── Layout (Glass Campus shell) ──────────────────── */

/* Viewport-locked shell: the sidebar and .admin-main scroll independently,
   so the side menu never scrolls off-screen with long page content.
   Glass Campus: the layout paints the ambient brand canvas; the sidebar and
   mobile bar are frosted-glass surfaces floating over it. */
.admin-layout {
  /* Shell design tokens (Glass Campus) */
  --adm-ink: #191A23;
  --adm-ink-2: #5A5B6A;
  --adm-ink-3: #6E7180;
  --adm-line: rgba(25, 26, 35, 0.08);
  --adm-accent: #0071E3;
  --adm-accent-ink: #0B5CC4;
  --adm-glass: rgba(255, 255, 255, 0.72);
  --adm-glass-strong: rgba(255, 255, 255, 0.85);
  --adm-glass-border: rgba(255, 255, 255, 0.65);
  --adm-glass-blur: blur(18px) saturate(1.5);
  /* Near-opaque stand-in for glass on phone/tablet chrome (decision F5):
     sticky/fixed surfaces inside <=1024px blocks use this fill and drop
     their backdrop-filter, because blur composited over a scrolling layer
     is the classic WebKit repaint flicker. Keep borders/shadows/radii so
     the Glass Campus family resemblance holds. */
  --adm-surface-solid: rgba(255, 255, 255, 0.97);
  --adm-glass-shadow: 0 8px 32px rgba(0, 60, 130, 0.08);
  --adm-grad-v: linear-gradient(180deg, #0894FF 0%, #BD32D6 50%, #FFA304 100%);
  /* The two phone-chrome heights, each stated ONCE. The sticky top bar takes
     --adm-topbar-h as an explicit height (it used to fall out of its padding),
     and the bottom tab bar's min-height plus BOTH .admin-main clearance calcs
     read --adm-tabbar-h - those three used to be a hand-synced 56px triple.
     The phone block overrides --adm-topbar-h where the bar's padding shrinks. */
  --adm-topbar-h: 58px;
  --adm-tabbar-h: 56px;

  display: flex;
  height: 100vh;
  height: 100dvh;
  direction: rtl;
  text-align: right;
  /* A thumb-scroll that reaches the edge of an inner pane must stop here, not
     chain on into the document: the document CAN scroll by the URL-bar delta
     on phones, and that scroll collapses/expands the browser chrome and
     relayouts this dvh-sized shell (flicker mechanism N6). */
  overscroll-behavior-y: contain;
  background:
    radial-gradient(ellipse at 85% 8%, rgba(8, 148, 255, 0.07) 0%, transparent 55%),
    radial-gradient(ellipse at 12% 60%, rgba(105, 98, 234, 0.06) 0%, transparent 60%),
    #F5F9FF;
}

/* global.css gives #app a 100vh min-height floor for the public pages. Inside
   the admin it is the one thing that makes the DOCUMENT scrollable: 100vh
   exceeds 100dvh by the URL-bar delta whenever mobile browser chrome is
   visible, so the page gains exactly that much scroll slack, thumb-scrolls
   chain into it, the chrome collapses, dvh re-resolves and the whole shell
   relayouts. Scoped with :has() so global.css and every public page are
   untouched; the shell above is the single dvh-sized element and the document
   has nothing left to scroll. */
#app:has(.admin-layout) {
  min-height: auto;
}

.admin-sidebar {
  width: 264px;
  background: var(--adm-glass);
  -webkit-backdrop-filter: var(--adm-glass-blur);
  backdrop-filter: var(--adm-glass-blur);
  border-inline-end: 1px solid var(--adm-line);
  box-shadow: var(--adm-glass-shadow);
  color: var(--adm-ink-2);
  display: flex;
  flex-direction: column;
  padding: 1.25rem 0 0.75rem;
  flex-shrink: 0;
  overscroll-behavior: contain;
}

.admin-brand {
  padding: 0 1.25rem 1rem;
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}

.brand-link {
  font-family: "Helvetica Neue", sans-serif;
  font-weight: 200;
  font-size: 1.6rem;
  color: var(--adm-ink);
  text-decoration: none;
  direction: ltr;
}

.brand-label {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  color: var(--adm-ink-3);
  text-transform: uppercase;
  letter-spacing: 0.14em;
}

/* ── Grouped nav: hubs -> sub-pages ───────────────── */

.admin-nav {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 0 0.75rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.admin-nav-group {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-bottom: 1rem;
}

.admin-nav-group__title {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 400;
  color: var(--adm-ink-3);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  padding: 0.4rem 0.6rem 0.35rem;
}

.admin-nav-link {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.5rem 0.85rem;
  color: var(--adm-ink-2);
  text-decoration: none;
  border-radius: 10px;
  font-size: 0.85rem;
  line-height: 1.3;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;
}

.admin-nav-link:hover {
  color: var(--adm-ink);
  background: rgba(255, 255, 255, 0.7);
}

.admin-nav-link.active {
  color: var(--adm-accent-ink);
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 60, 130, 0.06), 0 1px 2px rgba(0, 60, 130, 0.04);
}

/* The single brand-gradient moment of the shell: a slim vertical accent bar
   on the active nav item. Keep it unique - no other gradient in the chrome. */
.admin-nav-link.active::before {
  content: '';
  position: absolute;
  inset-inline-start: 0;
  top: 8px;
  bottom: 8px;
  width: 3px;
  border-radius: 2px;
  background: var(--adm-grad-v);
}

.admin-nav-link i {
  width: 18px;
  text-align: center;
  font-size: 0.8rem;
  color: #8E8F9E;
  transition: color 0.15s;
}

.admin-nav-link:hover i {
  color: var(--adm-ink-2);
}

.admin-nav-link.active i {
  color: var(--adm-accent);
}

.admin-nav-link__label {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── User identity card (bottom of the side menu) ─── */

.admin-user {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  margin: 0.5rem 0.75rem 0.25rem;
  padding: 0.6rem 0.7rem;
  background: rgba(255, 255, 255, 0.7);
  border: 1px solid var(--adm-line);
  border-radius: 14px;
}

.admin-user__avatar {
  position: relative;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 50%;
}

.admin-user__avatar img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  background: #E8EEF8;
}

/* Presence dot, notched into the avatar corner */
.admin-user__presence {
  position: absolute;
  inset-inline-end: -1px;
  bottom: -1px;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: #3fb950;
  border: 2px solid #fff;
}

.admin-user__meta {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 0.1rem;
}

.admin-user__name {
  max-width: 100%;
  color: var(--adm-ink);
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.15;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.admin-user__role {
  color: var(--adm-ink-3);
  font-family: var(--font-mono);
  font-size: 0.58rem;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.admin-logout {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  margin-inline-start: auto;
  flex-shrink: 0;
  color: #8E8F9E;
  background: none;
  border: none;
  border-radius: 9px;
  font-size: 0.85rem;
  cursor: pointer;
  /* Unconditional and inert on its own - it only anchors the <=1024px
     invisible hit area declared with the other touch floors below. */
  position: relative;
  transition: color 0.15s, background 0.15s;
}

.admin-logout:hover {
  color: #dc3545;
  background: rgba(220, 53, 69, 0.08);
}

.admin-main {
  flex: 1;
  /* min-width: 0 keeps wide tables from blowing the flex layout out past the
     viewport (where #app's overflow-x: clip would cut them off with no
     scrollbar); overflow: auto makes this pane the global scroller on both
     axes instead. */
  min-width: 0;
  /* Transparent so the layout's ambient canvas shows behind scrolling
     content - the glass surfaces need it to read as glass. */
  background: transparent;
  padding: 2rem;
  overflow: auto;
  /* The ONLY intended scroller. Contain on both axes so hitting its edge
     never chains the gesture into the document (see the shell note above). */
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  /* Admin content is English. Force LTR so bidi doesn't reorder
     mixed-script strings (numbers + Latin) inside an RTL ancestor. */
  direction: ltr;
  text-align: left;
}

/* ── Desktop sidebar rail (collapsed side menu) ───── */

/* The toggle only exists where the rail exists. At or below 1024px the sidebar
   is an off-canvas drawer whose full width IS the point of it, so a button
   offering to narrow it would either do nothing or produce a 64px sliver of
   glyphs sliding over the page. It is opted back in inside the guard below,
   which is also the only place it is ever laid out. */
.admin-rail-toggle {
  display: none;
}

/* Everything about the rail is fenced inside this one query, and the fence is
   load-bearing rather than tidy.

   `.admin-layout--rail .admin-sidebar { width: 64px }` scores (0,2,0), the same
   as the drawer's own `.admin-sidebar { width: 292px }` inside the
   `max-width: 1024px` block below - so whichever is authored LATER in the file
   wins wherever both apply. Unguarded, this rule would beat the drawer at every
   width under 1025px and the mobile menu would open as a 64px strip of
   unlabelled icons. The guard makes the two mutually exclusive by media rather
   than by source order, so the file stays safe to reorder.

   It is also the ONLY thing that decides where the rail is real: the class is
   set unconditionally by the route in AdminLayout.vue, with no viewport logic
   in JavaScript (S35). A tablet at exactly 1024px therefore keeps the drawer
   and never gains a rail - deliberate, since a rail is a pointer affordance and
   a 1024px iPad is being touched, not moused.

   No width transition on purpose. `.admin-sidebar` is a flex sibling of
   `.admin-main`, so animating its width relayouts the entire page - including
   the wide admin tables - on every frame, which is exactly the jank the
   viewport-locked shell exists to avoid. The change is deliberate and rare; it
   snaps. */
@media (min-width: 1025px) {
  .admin-rail-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    /* .admin-brand aligns its children on the BASELINE so the wordmark and its
       label sit on one line; a 44px box on that baseline hangs below the row. */
    align-self: center;
    /* Pushes the button to the inline END of the brand row (the left edge in
       this RTL shell), the same lever .admin-logout uses one card down. */
    margin-inline-start: auto;
    flex-shrink: 0;
    /* 44px even though this control can never render on a phone: iPads and
       touch laptops are both above this breakpoint, and the pointer type is not
       something the layout gets to assume. */
    width: 44px;
    height: 44px;
    padding: 0;
    color: #8E8F9E;
    background: none;
    border: none;
    border-radius: 10px;
    font-size: 0.8rem;
    cursor: pointer;
    transition: color 0.15s, background 0.15s;
  }

  .admin-rail-toggle:hover {
    color: var(--adm-ink);
    background: rgba(255, 255, 255, 0.7);
  }

  /* NO `:disabled` RULE, ON PURPOSE (D5, 2026-08-09). A dimmed variant used to
     sit right here, for the Person page, which forces the rail for the length
     of the visit. The page still forces it - but the button now acts there too,
     moving a per-visit override instead of the stored preference, so it is
     never inert and has nothing to dim. The old greyed-out state was honest
     about the mechanism and, to the person looking at it, indistinguishable
     from a broken control. See AdminLayout.vue for the state it now moves. */

  .admin-rail-toggle:focus-visible {
    outline: 2px solid var(--adm-accent);
    outline-offset: 2px;
  }

  .admin-layout--rail .admin-sidebar {
    width: 64px;
  }

  /* ── The peek: a collapsed menu opened by resting the pointer on it ──────

     `admin-layout--peek` REPLACES `admin-layout--rail` on the shell while the
     pointer dwells on the rail (AdminLayout.vue), it does not join it. Every
     rule above simply stops matching, so the panel is the ordinary 264px menu
     again - byte-identical to a genuinely expanded sidebar, with nothing to
     "restore". Undoing the rail with `:not()` or with overrides instead would
     be ten edits, and the four hidden labels could not be brought back at all:
     `revert` rolls a declaration back to the BROWSER default, which answers
     `inline` for all four, not to the rule underneath.

     IT FLOATS OVER THE CONTENT, and this one negative margin is the whole
     mechanism. Flexbox sizes and places an item by its MARGIN box, so 264px of
     width with -200px of end margin still consumes exactly the 64px track the
     rail consumed: `.admin-main` keeps the identical width and origin it had a
     moment ago, and the extra 200px is simply painted on top of it. Nothing on
     the page reflows, which is the point - the alternative moves every column
     of a wide table sideways whenever a pointer crosses the menu.

     `inline-end` is the physical LEFT here, because the shell is RTL and the
     sidebar is its first flex item. Stated logically rather than as
     `margin-left` so the overlay stays on the correct side the day anybody
     debugs this shell in LTR - the same lever `.admin-rail-toggle` uses above.

     z-index 1016 sits one rung above the bars that dock over content (1015)
     and one below `.modal-overlay` (1018), so an open modal correctly dims and
     blocks a peeked menu rather than being poked through by it.

     The glass and the shadow are the DRAWER's, copied from the `max-width`
     block below and not inherited from the sidebar's own: the standing 0.72
     glass is too transparent to read over a table, and the base
     `--adm-glass-shadow` points DOWN, where there is nothing to cast onto. The
     drawer's leftward `-8px 0 32px` is what makes this read as a layer rather
     than as a rendering fault.

     No transition and no animation, for the reason at the top of this block
     plus one more: animating the width would have to animate the margin in
     exact lockstep or the content reflows on every frame - which is the one
     thing the float exists to prevent. */
  .admin-layout--peek .admin-sidebar {
    margin-inline-end: -200px;
    position: relative;
    z-index: 1016;
    background: var(--adm-glass-strong, rgba(255, 255, 255, 0.85));
    -webkit-backdrop-filter: blur(24px) saturate(1.5);
    backdrop-filter: blur(24px) saturate(1.5);
    box-shadow: -8px 0 32px rgba(0, 60, 130, 0.16);
  }

  /* One rule retires every text run the rail has no room for. Each of these
     already lived in its own element, so collapsing the menu is a hide, never a
     reflow of the markup. The links keep their names through `title` +
     `aria-label` in the template. */
  .admin-layout--rail .admin-nav-link__label,
  .admin-layout--rail .admin-nav-group__title,
  .admin-layout--rail .brand-label,
  .admin-layout--rail .admin-user__meta {
    display: none;
  }

  /* Wordmark over toggle. Side by side they need ~90px inside a 64px rail, and
     `margin-inline-start: auto` does nothing useful in a column. */
  .admin-layout--rail .admin-brand {
    flex-direction: column;
    align-items: center;
    gap: 0.35rem;
    padding-inline: 0;
    padding-bottom: 0.75rem;
  }

  .admin-layout--rail .admin-rail-toggle {
    margin-inline-start: 0;
  }

  /* The nav's own 0.75rem gutters plus each link's 0.85rem ones leave about
     13px of content box for an 18px glyph, so both shrink and the icon centres
     in what is left. The active item's gradient bar is pinned to
     `inset-inline-start: 0` and needs no adjustment. */
  .admin-layout--rail .admin-nav {
    padding-inline: 0.5rem;
  }

  .admin-layout--rail .admin-nav-link {
    justify-content: center;
    padding-inline: 0.35rem;
  }

  /* The identity card keeps BOTH controls, stacked. In a row they measure
     40px avatar + 10px gap + 32px logout = 82px, which overflows a 64px rail
     and clips the logout button off the edge of the shell - the one control in
     the sidebar a user must always be able to reach. */
  .admin-layout--rail .admin-user {
    flex-direction: column;
    gap: 0.4rem;
    margin-inline: 0.35rem;
    padding-inline: 0.25rem;
  }

  .admin-layout--rail .admin-logout {
    margin-inline-start: 0;
  }
}

/* ── Shared Admin Components ──────────────────────── */

.admin-page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1.5rem;
}

.admin-page-title {
  font-size: 1.5rem;
  font-weight: 600;
  color: #333;
  margin: 0;
}

.admin-section {
  margin-bottom: 2.5rem;
}

.admin-section-title {
  font-size: 1rem;
  font-weight: 600;
  color: #333;
  margin: 0 0 1rem 0;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid #e5e5e5;
}

.admin-toolbar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: nowrap;
}

.admin-search {
  position: relative;
  display: flex;
  align-items: center;
}

.admin-search .fa-search {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  color: #888;
  font-size: 0.8rem;
  pointer-events: none;
}

.admin-search input {
  padding-left: 30px;
  padding-right: 28px;
  width: 220px;
}

.search-clear {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: none;
  cursor: pointer;
  color: #999;
  font-size: 0.8rem;
  padding: 2px 4px;
  line-height: 1;
}

.search-clear:hover {
  color: #333;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  border: 1px solid #ddd;
  border-radius: 6px;
  font-size: 0.85rem;
  cursor: pointer;
  transition: all 0.2s;
  background: #fff;
  color: #333;
}

.btn:hover {
  border-color: #bbb;
}

.btn-primary {
  background: #0071E3;
  color: #fff;
  border-color: #0071E3;
}

.btn-primary:hover {
  background: #005bb5;
}

.btn-danger {
  color: #dc3545;
  border-color: #dc3545;
}

.btn-danger:hover {
  background: #dc3545;
  color: #fff;
}

.btn-sm {
  padding: 0.3rem 0.6rem;
  font-size: 0.8rem;
}

.btn-icon {
  /* Every OTHER user of this class is a <button>, whose UA default is
     inline-block - min-height/min-width apply there and the touch floors
     below take effect. AdminContracts' PDF-download control is an <a>
     instead (a real download link, not a click handler), and a non-replaced
     inline element ignores min-height/min-width entirely per spec: the
     .ct-page floor and the panel-wide X6 floor were both already declared
     and both already inert on it. inline-flex gives it the same box model
     the buttons get for free, so both floors start applying. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.35rem 0.5rem;
  border: none;
  background: none;
  cursor: pointer;
  color: #888;
  border-radius: 4px;
  transition: all 0.15s;
}

.btn-icon:hover {
  background: #eee;
  color: #333;
}

.btn-icon.danger:hover {
  background: #fee;
  color: #dc3545;
}

/* Permanent (hard) delete - always red so it reads as more dangerous
   than the restorable soft-delete trash. */
.btn-icon.reg-perm-delete {
  color: #dc2626;
}

.btn-icon.reg-perm-delete:hover {
  background: #dc2626;
  color: #fff;
}

a.btn-icon.reg-whatsapp {
  color: #25d366;
  text-decoration: none;
}

a.btn-icon.reg-whatsapp:hover {
  background: #e6f9ee;
  color: #128c4a;
}

/* ── Table ────────────────────────────────────────── */

.admin-table {
  width: 100%;
  background: #fff;
  border-radius: 8px;
  border: 1px solid #e8e8e8;
  border-collapse: collapse;
  font-size: 0.9rem;
}

/* The generic wide-table scroller (mobile revamp, frozen interface): views
   wrap any table that can outgrow its column in <div class="adm-table-scroll">
   so the TABLE pans inside the wrapper instead of the page root becoming the
   scroller. Replaces the per-page-root overflow-x pattern - the island group
   near the end of this file keeps only the pages not yet migrated. Contained
   on x so an edge-hit pan never becomes a history swipe. */
.adm-table-scroll {
  max-inline-size: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}

.admin-table th {
  padding: 0.75rem 1rem;
  text-align: right;
  font-weight: 600;
  color: #666;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  border-bottom: 2px solid #eee;
  background: #fafafa;
}

.admin-table th:first-child {
  border-radius: 0 8px 0 0;
}

.admin-table th:last-child {
  border-radius: 8px 0 0 0;
}

.admin-table td {
  padding: 0.65rem 1rem;
  border-bottom: 1px solid #f0f0f0;
  color: #444;
  vertical-align: middle;
}

.admin-table tr:last-child td {
  border-bottom: none;
}

.admin-table tr:hover td {
  background: #f8f9ff;
}

/* ── Toggle Switch ────────────────────────────────── */

.toggle {
  /* Must be inline-block (or block) for width/height to apply - a bare
     <label> is inline, which silently drops the sizing and collapses the
     slider pill to a 0-width sliver. */
  display: inline-block;
  position: relative;
  width: 36px;
  height: 20px;
  flex: none;
  cursor: pointer;
  vertical-align: middle;
}

.toggle input {
  opacity: 0;
  width: 0;
  height: 0;
}

.toggle-slider {
  position: absolute;
  inset: 0;
  background: #d0d5dd;
  border: 1px solid #a8b0bd;
  border-radius: 20px;
  transition: background 0.2s, border-color 0.2s;
  box-sizing: border-box;
}

.toggle-slider::before {
  content: '';
  position: absolute;
  width: 14px;
  height: 14px;
  left: 2px;
  bottom: 2px;
  background: #fff;
  border-radius: 50%;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
  transition: transform 0.2s;
}

.toggle input:checked + .toggle-slider {
  background: #0071E3;
  border-color: #0061c4;
}

.toggle input:checked + .toggle-slider::before {
  transform: translateX(16px);
}

/* Keyboard focus outline - without this, keyboard users can't tell
   which switch is focused. */
.toggle input:focus-visible + .toggle-slider {
  outline: 2px solid #0071E3;
  outline-offset: 2px;
}

/* ── Badge ────────────────────────────────────────── */

.badge {
  display: inline-block;
  padding: 0.15rem 0.5rem;
  border-radius: 4px;
  font-size: 0.75rem;
  font-weight: 500;
}

.badge-published {
  background: #e8f5e9;
  color: #2e7d32;
}

.badge-draft {
  background: #fff3e0;
  color: #e65100;
}

.badge-course {
  font-weight: 600;
  letter-spacing: 0.02em;
}

.badge-course-developer {
  background: #e3f2fd;
  color: #0d47a1;
}

.badge-course-architect {
  background: #f3e5f5;
  color: #6a1b9a;
}

/* ── Course filter tabs (Admin Curriculum) ───────── */

.course-filter-tabs {
  display: inline-flex;
  align-items: center;
  background: #f3f4f6;
  border: 1px solid #e2e4e8;
  border-radius: 8px;
  padding: 3px;
  gap: 2px;
}

.course-filter-tab {
  border: none;
  background: transparent;
  padding: 0.4rem 0.85rem;
  border-radius: 6px;
  font-size: 0.85rem;
  font-weight: 500;
  cursor: pointer;
  color: #555;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  transition: background 0.15s, color 0.15s, box-shadow 0.15s;
}

.course-filter-tab:hover {
  color: #1a1a1a;
}

.course-filter-tab.is-active {
  background: #fff;
  color: #1a1a1a;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}

.course-filter-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.6em;
  padding: 0 0.35rem;
  font-size: 0.7rem;
  font-weight: 600;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.06);
  color: #666;
}

.course-filter-tab.is-active .course-filter-count {
  background: #e3f2fd;
  color: #0d47a1;
}

/* ── Modal ────────────────────────────────────────── */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  /* 1018, not 1000: the phone bottom bars (tab bar, leads bulk bar) sit at
     1015 and paint over anything lower - a modal footer behind the tab bar
     had its buttons swallowed by nav links. Modals must cover the bars but
     stay under the nav scrim (1020) / drawer (1030) / sheets (1040-1050). */
  z-index: 1018;
}

.modal {
  background: #fff;
  border-radius: 12px;
  width: 600px;
  max-width: 90vw;
  max-height: 85vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.25rem 1.5rem;
  border-bottom: 1px solid #eee;
}

.modal-header h2 {
  font-size: 1.15rem;
  font-weight: 600;
  color: #333;
  margin: 0;
}

.modal-body {
  padding: 1.5rem;
}

.modal-footer {
  display: flex;
  justify-content: flex-start;
  gap: 0.5rem;
  padding: 1rem 1.5rem;
  border-top: 1px solid #eee;
}

/* ── Form ─────────────────────────────────────────── */

.form-group {
  margin-bottom: 1rem;
}

.form-label {
  display: block;
  font-size: 0.85rem;
  font-weight: 500;
  color: #555;
  margin-bottom: 0.35rem;
}

.form-input,
.form-select,
.form-textarea {
  width: 100%;
  padding: 0.55rem 0.75rem;
  border: 1px solid #ddd;
  border-radius: 6px;
  font-size: 0.9rem;
  outline: none;
  transition: border-color 0.2s;
  font-family: inherit;
  direction: rtl;
  text-align: right;
}

.form-input:focus,
.form-select:focus,
.form-textarea:focus {
  border-color: #0071E3;
}

.form-textarea {
  resize: vertical;
  min-height: 80px;
}

.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}

/* ── Metadata List Editor ─────────────────────────── */

.meta-list {
  border: 1px solid #eee;
  border-radius: 8px;
  padding: 0.75rem;
}

.meta-list-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.5rem;
}

.meta-list-title {
  font-size: 0.85rem;
  font-weight: 600;
  color: #666;
}

.meta-item {
  background: #fafafa;
  border: 1px solid #eee;
  border-radius: 6px;
  padding: 0.75rem;
  margin-bottom: 0.5rem;
  position: relative;
}

.meta-item:last-child {
  margin-bottom: 0;
}

.meta-item-remove {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
}

.meta-item .form-group {
  margin-bottom: 0.5rem;
}

.meta-item .form-group:last-child {
  margin-bottom: 0;
}

.exclusive-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  padding-top: 0.25rem;
  border-top: 1px dashed #eee;
}

.exclusive-hint {
  font-size: 0.75rem;
  color: #888;
}

.option-text-placeholder {
  flex-basis: 100%;
  margin-top: 0.25rem;
}

/* ── Scale Per-step Editor ────────────────────────── */

.scale-steps-hint {
  font-size: 0.75rem;
  color: #888;
  font-weight: 400;
}

.scale-steps-hint code {
  background: #f0f0f0;
  padding: 0.05rem 0.3rem;
  border-radius: 3px;
  font-family: var(--font-mono, monospace);
  font-size: 0.7rem;
}

.scale-steps-empty {
  font-size: 0.85rem;
  color: #888;
  padding: 0.75rem;
  background: #fafafa;
  border: 1px dashed #ddd;
  border-radius: 6px;
  text-align: center;
}

.scale-steps-table {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.scale-steps-head,
.scale-steps-row {
  display: grid;
  grid-template-columns: 60px 1.2fr 1.3fr 2fr;
  gap: 0.5rem;
  align-items: start;
}

.scale-steps-head {
  font-size: 0.75rem;
  font-weight: 600;
  color: #666;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 0 0.25rem;
}

.scale-steps-row {
  background: #fafafa;
  border: 1px solid #eee;
  border-radius: 6px;
  padding: 0.5rem;
}

.scale-steps-row .form-input,
.scale-steps-row .form-textarea {
  width: 100%;
}

.scale-step-value-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2rem;
  height: 2rem;
  padding: 0 0.5rem;
  background: #fff;
  border: 1px solid #e0e0e0;
  border-radius: 6px;
  font-weight: 600;
  color: var(--primary-color, #0071E3);
  font-variant-numeric: tabular-nums;
}

.scale-step-icon-wrap {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.scale-step-icon-preview {
  font-size: 1.25rem;
  color: #555;
  flex-shrink: 0;
}

@media (max-width: 768px) {
  .scale-steps-head {
    display: none;
  }
  .scale-steps-row {
    grid-template-columns: 1fr;
    gap: 0.4rem;
  }
}

/* ── Curriculum Tree ──────────────────────────────── */

.tree-node {
  background: #fff;
  border: 1px solid #e8e8e8;
  border-radius: 8px;
  margin-bottom: 0.5rem;
  overflow: hidden;
  /* Curriculum content is Hebrew - lay the tree rows out RTL so the
     order number and drag handle sit on the right, content reads R-to-L.
     (The rest of admin.css is LTR; this is scoped to the curriculum tree.) */
  direction: rtl;
}

.tree-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.65rem 1rem;
  cursor: pointer;
  transition: background 0.15s;
}

.tree-header:hover {
  background: #f8f9ff;
}

.tree-expand {
  color: #bbb;
  transition: transform 0.2s;
  font-size: 0.75rem;
  width: 20px;
  text-align: center;
}

.tree-expand.open {
  transform: rotate(-90deg);
  color: #0071E3;
}

.tree-title {
  /* Shrink to content so the title hugs the order number on the right
     (RTL); right-align the text rather than inheriting the LTR base. */
  flex: 0 1 auto;
  min-width: 0;
  text-align: right;
  font-weight: 500;
  color: #333;
  font-size: 0.9rem;
}

/* Title + hours pill form the right-hand group; everything after the hours
   pill (badges, chapter id, subject count, actions) is pushed to the left. */
.tree-hours + * {
  margin-inline-start: auto;
}

.tree-actions {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  opacity: 0;
  transition: opacity 0.15s;
}

.tree-header:hover .tree-actions {
  opacity: 1;
}

.tree-children {
  padding: 0.5rem 2rem 0.75rem 0.75rem;
  border-top: 1px solid #f0f0f0;
}

.tree-children--subjects {
  padding-right: 3.5rem;
  margin-right: 0.5rem;
  border-right: 2px solid #e8e8e8;
  border-top: none;
}

/* Large module index sitting at the far-right edge of each module row,
   beside the drag handle - a quick visual anchor for the module's order. */
.tree-module-num {
  flex-shrink: 0;
  min-width: 1.4em;
  font-size: 1.7rem;
  font-weight: 700;
  line-height: 1;
  color: #aeb4bf;
  text-align: center;
  font-variant-numeric: tabular-nums;
  direction: ltr;
}

.tree-order {
  color: #bbb;
  font-size: 0.8rem;
  font-weight: 600;
  min-width: fit-content;
  white-space: nowrap;
  text-align: center;
  /* Keep the English type label + number reading "Module 1" (not "1 Module")
     even though the row around it is RTL. */
  direction: ltr;
}

.tree-type-label {
  color: #888;
  font-weight: 400;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.tree-description {
  margin: 0 0.75rem 0.5rem;
  padding: 0.35rem 0.75rem;
  font-size: 0.79rem;
  color: #555;
  line-height: 1.5;
  background: #f5f6f8;
  border-radius: 4px;
}

.tree-description:has(.inline-edit-desc) {
  padding: 0;
  margin: 0;
  background: none;
}

/* ── Filter bar ───────────────────────────────────── */

.filter-bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1rem;
  padding: 0.75rem 1rem;
  background: #fff;
  border: 1px solid #e8e8e8;
  border-radius: 8px;
}

.filter-label {
  font-size: 0.85rem;
  color: #888;
  font-weight: 500;
}

/* ── Page picker (sections editor step 1) ─────────── */

.page-picker-intro {
  color: #666;
  margin: 0 0 1rem;
  font-size: 0.95rem;
}

.page-picker-table tbody tr.page-picker-row {
  cursor: pointer;
  transition: background-color 0.12s;
}

.page-picker-table tbody tr.page-picker-row:hover td {
  background: #f3f8ff;
}

.page-picker-table td.th-count,
.page-picker-table th.th-count {
  width: 120px;
  text-align: center;
  color: #555;
}

.page-picker-row-all td {
  background: #fafbff;
}

.page-picker-chevron {
  width: 32px;
  color: #bbb;
  text-align: center;
}

.page-picker-row:hover .page-picker-chevron {
  color: var(--primary-color);
}

.admin-page-title-page {
  color: #888;
  font-weight: 400;
  font-size: 0.85em;
  margin-inline-start: 0.25rem;
}

.page-picker-back {
  margin-inline-end: 0.5rem;
  color: #666;
}

.page-picker-back:hover {
  color: var(--primary-color);
}

/* ── Empty state ──────────────────────────────────── */

.empty-state {
  text-align: center;
  padding: 3rem;
  color: #aaa;
}

/* ── Toast / flash messages ───────────────────────── */

.admin-readonly-banner {
  background: #fff3cd;
  color: #856404;
  border: 1px solid #ffc107;
  border-radius: 8px;
  padding: 0.75rem 1.25rem;
  margin-bottom: 1rem;
  font-size: 0.9rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.admin-readonly-banner i {
  font-size: 1.1rem;
}

.admin-deploy-banner {
  background: #e8f1fb;
  color: #0d3b66;
  border: 1px solid var(--primary-color, #0071E3);
  border-radius: 8px;
  padding: 0.75rem 1.25rem;
  margin-bottom: 1rem;
  font-size: 0.9rem;
  line-height: 1.5;
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
}

.admin-deploy-banner > i {
  font-size: 1.15rem;
  margin-top: 0.15rem;
  flex-shrink: 0;
  color: var(--primary-color, #0071E3);
}

.admin-deploy-banner-body {
  flex: 1;
}

.admin-deploy-banner-body strong {
  font-weight: 600;
  margin-right: 0.25rem;
}

.admin-deploy-banner code {
  background: rgba(0, 113, 227, 0.1);
  padding: 0.1em 0.45em;
  border-radius: 4px;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 0.85em;
  white-space: nowrap;
}

.toast {
  position: fixed;
  bottom: 1.5rem;
  left: 1.5rem;
  padding: 0.75rem 1.25rem;
  background: #333;
  color: #fff;
  border-radius: 8px;
  font-size: 0.9rem;
  z-index: 2000;
  animation: fadeInUp 0.25s ease;
  /* Admin toasts are English. Pin direction so bidi doesn't move
     leading numbers to the end of the line under an RTL ancestor. */
  direction: ltr;
  text-align: left;
}

.toast.success {
  background: #2e7d32;
}

.toast.error {
  background: #dc3545;
}

.toast.warning {
  background: #b26a00;
}

@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ── Drag and Drop ───────────────────────────────── */

.drag-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  cursor: grab;
  color: #ccc;
  font-size: 0.85rem;
  transition: color 0.15s;
  flex-shrink: 0;
  user-select: none;
}

.drag-handle:hover {
  color: #666;
}

.drag-handle:active {
  cursor: grabbing;
}

/* Active drag item highlight */
.tree-header.drag-active,
tr.drag-active {
  opacity: 0.6;
  background: rgba(0, 113, 227, 0.08);
  box-shadow: 0 0 0 1px rgba(0, 113, 227, 0.3);
  border-radius: 4px;
}

/* Drop position indicator - a 2px blue line along the leading edge of the
   hovered target, showing whether the dragged item will land before or
   after it. Uses box-shadow rather than border so it doesn't affect layout
   and can sit cleanly on top of the existing header. */
.tree-header.drop-above {
  box-shadow: 0 -2px 0 0 #0071E3;
}
.tree-header.drop-below {
  box-shadow: 0 2px 0 0 #0071E3;
}

/* Smooth reorder animation via Vue TransitionGroup */
.drag-reorder-move {
  transition: transform 0.2s ease;
}

/* ── Inline Editing ──────────────────────────────── */

.inline-edit {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  flex: 1;
  min-width: 0;
}

.inline-edit-input {
  flex: 1;
  min-width: 0;
  padding: 0.2rem 0.5rem;
  border: 1px solid #0071E3;
  border-radius: 4px;
  font-size: 0.9rem;
  font-family: inherit;
  font-weight: 500;
  color: #333;
  outline: none;
  background: #fff;
}

.inline-edit-input:focus {
  box-shadow: 0 0 0 2px rgba(0, 113, 227, 0.2);
}

.inline-edit-confirm {
  color: #2e7d32 !important;
  flex-shrink: 0;
}

.inline-edit-confirm:hover {
  background: #e8f5e9 !important;
  color: #1b5e20 !important;
}

.inline-edit-cancel {
  color: #dc3545 !important;
  flex-shrink: 0;
}

.inline-edit-cancel:hover {
  background: #fee !important;
  color: #b71c1c !important;
}

.inline-edit-desc {
  display: flex;
  gap: 0.35rem;
  align-items: flex-start;
  width: 100%;
}

.inline-edit-desc-input {
  flex: 1;
  min-width: 0;
  padding: 0.3rem 0.5rem;
  border: 1px solid #0071E3;
  border-radius: 4px;
  font-size: 0.79rem;
  font-family: inherit;
  color: #333;
  outline: none;
  background: #fff;
  resize: vertical;
  line-height: 1.5;
}

.inline-edit-desc-input:focus {
  box-shadow: 0 0 0 2px rgba(0, 113, 227, 0.2);
}

.inline-edit-desc-actions {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  flex-shrink: 0;
}

/* ── Rich Text Editor ────────────────────────────── */

.rich-editor {
  border: 1px solid #ddd;
  border-radius: 6px;
  overflow: hidden;
  transition: border-color 0.2s;
}

.rich-editor:focus-within {
  border-color: #0071E3;
}

.rich-editor-toolbar {
  display: flex;
  gap: 2px;
  padding: 0.3rem 0.5rem;
  background: #fafafa;
  border-bottom: 1px solid #eee;
}

.rich-editor-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: 4px;
  background: none;
  color: #666;
  font-size: 0.8rem;
  cursor: pointer;
  transition: all 0.15s;
}

.rich-editor-btn:hover {
  background: #e8e8e8;
  color: #333;
}

.rich-editor-btn.active {
  background: #0071E3;
  color: #fff;
}

.rich-editor-content {
  padding: 0.55rem 0.75rem;
  font-size: 0.9rem;
  font-family: inherit;
  line-height: 1.5;
  color: #333;
  outline: none;
  direction: rtl;
  text-align: right;
  overflow-y: auto;
  overscroll-behavior: contain;
  max-height: 200px;
}

.rich-editor-source {
  display: block;
  width: 100%;
  padding: 0.55rem 0.75rem;
  font-family: 'SF Mono', Menlo, Consolas, monospace;
  font-size: 0.8rem;
  line-height: 1.5;
  color: #222;
  background: #fafafa;
  border: 0;
  outline: none;
  resize: vertical;
  min-height: 8em;
  max-height: 320px;
}

.rich-editor-content:empty::before {
  content: '';
  color: #aaa;
}

.rich-editor-content ul {
  margin: 0.25rem 0;
  padding-right: 1.5rem;
  padding-left: 0;
}

.rich-editor-content li {
  margin-bottom: 0.15rem;
}

.rich-editor-content a {
  color: #0071E3;
  text-decoration: underline;
}

.rich-editor-sep {
  width: 1px;
  height: 18px;
  background: #ddd;
  align-self: center;
  margin: 0 4px;
}

.rich-editor-btn-logo {
  font-size: 0.65rem;
  font-weight: 800;
  font-family: 'Helvetica Neue', sans-serif;
  letter-spacing: -0.02em;
  background: linear-gradient(90deg, #0894FF 0%, #BD32D6 35%, #6462EA 50%, #0894FF 70%, #FFA304 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.rich-editor-btn-logo:hover {
  background: linear-gradient(90deg, #0894FF 0%, #BD32D6 35%, #6462EA 50%, #0894FF 70%, #FFA304 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  opacity: 0.8;
}

.rich-editor-btn-bluebold {
  color: var(--primary-color);
  font-weight: 700;
}

.rich-editor-anchor-select {
  height: 28px;
  padding: 0 22px 0 8px;
  border: 1px solid #d0d7de;
  border-radius: 4px;
  background: #fff;
  font-size: 0.75rem;
  color: #24292f;
  cursor: pointer;
  max-width: 220px;
  direction: rtl;
}

.rich-editor-anchor-select optgroup {
  font-weight: 700;
  color: #0969da;
}

.rich-editor-anchor-select option {
  font-weight: 400;
  color: #24292f;
}

/* Disabled (isActive=false) open-day dates in any date-select dropdown: struck
   through + muted so cancelled/inactive dates read as unavailable. */
option.od-opt-disabled {
  text-decoration: line-through;
  color: #9ca3af;
}

.rich-editor-anchor-select:hover {
  border-color: #0969da;
}

.rich-editor-anchor-select:focus {
  outline: none;
  border-color: #0969da;
  box-shadow: 0 0 0 3px rgba(9, 105, 218, 0.15);
}

/* ── Mobile top bar (hidden on desktop) ──────────── */
/* Mobile navigation is the hamburger + off-canvas sidebar only - there is no
   floating dock. */

.admin-mobile-bar {
  display: none;
}

.admin-sidebar-overlay {
  display: none;
}

/* ══════════════════════════════════════════════════════
   ADMIN RESPONSIVE - Tablet (768px - 1024px)
   ══════════════════════════════════════════════════════ */

@media (max-width: 1024px) {
  .admin-mobile-bar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    /* Explicit height (the token, not the padding, is the truth now) so any
       clearance math has one number to read. 58px is what the old padding
       rendered at this breakpoint: 38px hamburger + 2 x 0.6rem + 1px border. */
    height: var(--adm-topbar-h);
    padding: 0.6rem 1rem;
    /* Solid on phone/tablet chrome (F5): blur composited over a scrolling
       layer is the classic WebKit repaint flicker, so the sticky bar trades
       the glass fill for the near-opaque solid and drops its filter. Border,
       shadow and radius language stay, so the family resemblance holds. */
    background: var(--adm-surface-solid, rgba(255, 255, 255, 0.97));
    border-bottom: 1px solid rgba(25, 26, 35, 0.08);
    position: sticky;
    top: 0;
    z-index: 1010;
  }

  .admin-mobile-bar .brand-link {
    font-family: "Helvetica Neue", sans-serif;
    font-weight: 200;
    font-size: 1.35rem;
    color: #191A23;
    text-decoration: none;
    direction: ltr;
  }

  .admin-mobile-bar .brand-label {
    font-family: var(--font-mono);
    font-size: 0.64rem;
    color: #6E7180;
    text-transform: uppercase;
    letter-spacing: 0.14em;
  }

  .admin-hamburger {
    background: none;
    border: none;
    color: #5A5B6A;
    font-size: 1.15rem;
    cursor: pointer;
    width: 38px;
    height: 38px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    /* position: relative anchors the invisible hit area below - the visible
       38px box stays exactly what --adm-topbar-h's own arithmetic expects. */
    position: relative;
    transition: color 0.15s, background 0.15s;
  }

  .admin-hamburger:hover {
    color: #191A23;
    background: rgba(255, 255, 255, 0.7);
  }

  /* 38px measured against the 44px floor. The visible box cannot grow - the
     top bar's height token is derived FROM this exact box (see the comment on
     .admin-mobile-bar above) - so the touch target grows around it instead,
     the same invisible-halo pattern .toggle::after uses at E2. */
  .admin-hamburger::after {
    content: '';
    position: absolute;
    inset: -3px;
  }

  .admin-layout {
    flex-direction: column;
  }

  .admin-sidebar-overlay {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.3);
    z-index: 1020;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.3s ease;
  }

  .admin-sidebar-overlay.is-open {
    opacity: 1;
    pointer-events: auto;
  }

  /* Off-canvas drawer, sliding in from the RIGHT (RTL start side). Solid
     fill instead of glass at this width (F5) - a blur over the page it is
     sliding across repaints the whole backdrop on every frame of the slide.
     The slide itself is transform + opacity, never `right`: animating an
     inset is a LAYOUT property and relayouts on every frame, while a
     transform stays on the compositor. `visibility` flips at the transition
     ends so the closed drawer is out of the tab order and cannot be focused
     into from behind the overlay. */
  .admin-sidebar {
    position: fixed;
    top: 0;
    right: 0;
    left: auto;
    width: 292px;
    height: 100vh;
    height: 100dvh;
    z-index: 1030;
    background: var(--adm-surface-solid, rgba(255, 255, 255, 0.97));
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    border-inline-end: none;
    border-inline-start: 1px solid rgba(25, 26, 35, 0.08);
    transform: translateX(100%);
    opacity: 0;
    visibility: hidden;
    transition:
      transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
      opacity 0.3s ease,
      visibility 0s linear 0.3s;
    box-shadow: none;
  }

  .admin-sidebar.is-open {
    transform: translateX(0);
    opacity: 1;
    visibility: visible;
    transition:
      transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
      opacity 0.3s ease,
      visibility 0s;
    box-shadow: -8px 0 32px rgba(0, 60, 130, 0.16);
  }

  .admin-main {
    padding: 1.5rem;
    /* Column layout: allow shrinking below content height so this pane
       scrolls internally instead of stretching the viewport-locked shell. */
    min-height: 0;
  }

  .admin-page-header {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.75rem;
  }

  .admin-toolbar {
    flex-wrap: wrap;
    width: 100%;
  }

  .admin-search input {
    width: 180px;
  }

  /* Tables: horizontal scroll. Contained on x so a pan that hits the table's
     edge does not hand the gesture to the browser's history swipe. */
  .admin-table {
    display: block;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
  }

  .admin-table thead,
  .admin-table tbody,
  .admin-table tr {
    display: table;
    width: 100%;
    table-layout: fixed;
  }

  .admin-table th,
  .admin-table td {
    padding: 0.55rem 0.75rem;
    font-size: 0.82rem;
  }

  /* Modal adjustments. dvh with a vh fallback: a vh-sized cap ignores mobile
     browser chrome, so the modal footer sat under the URL bar whenever the
     chrome was expanded. */
  .modal {
    width: 90vw;
    max-height: 90vh;
    max-height: 90dvh;
  }

  .modal-body {
    padding: 1rem;
  }

  .form-row {
    grid-template-columns: 1fr;
  }

  /* Tree adjustments */
  .tree-children {
    padding: 0.5rem 1.25rem 0.75rem 0.5rem;
  }

  .tree-children--subjects {
    padding-right: 2rem;
  }

  /* Filter bar */
  .filter-bar {
    flex-wrap: wrap;
  }

  /* Toast */
  .toast {
    left: 1rem;
    right: 1rem;
    bottom: 1rem;
    text-align: center;
  }
}

/* The drawer's slide and the overlay's fade are pure motion, so they answer
   the OS-level setting the same way .adm-sheet's transitions do. The drawer
   still opens and closes - it just appears and disappears in place, and the
   closed state's visibility delay goes with the fade. Top-level rather than
   nested in the tablet block: above the breakpoint neither element carries a
   transition, so the wider scope changes nothing and every CSS parser in the
   build agrees on it. */
@media (prefers-reduced-motion: reduce) {
  .admin-sidebar,
  .admin-sidebar-overlay {
    transition: none;
  }
}

/* ══════════════════════════════════════════════════════
   ADMIN RESPONSIVE - Phone (< 768px)
   ══════════════════════════════════════════════════════ */

@media (max-width: 767px) {
  /* The top bar's padding shrinks here, so its height token shrinks with it:
     38px hamburger + 2 x 0.5rem + 1px border. On the shell, not the bar,
     because clearance math elsewhere reads the token off the shell scope. */
  .admin-layout {
    --adm-topbar-h: 55px;
  }

  .admin-mobile-bar {
    padding: 0.5rem 0.75rem;
  }

  /* Narrower drawer only - the off-screen position is the transform in the
     tablet block above, which needs no per-width numbers. */
  .admin-sidebar {
    width: 280px;
  }

  .admin-main {
    padding: 1rem 0.75rem calc(1.25rem + env(safe-area-inset-bottom, 0px));
  }

  .admin-page-title {
    font-size: 1.2rem;
  }

  .admin-toolbar {
    gap: 0.5rem;
  }

  .admin-search input {
    width: 100%;
  }

  .admin-search {
    width: 100%;
  }

  .btn {
    padding: 0.4rem 0.75rem;
    font-size: 0.8rem;
  }

  .btn-sm {
    padding: 0.25rem 0.45rem;
    font-size: 0.75rem;
  }

  /* Tables: card layout for mobile */
  .admin-table {
    font-size: 0.8rem;
  }

  .admin-table th,
  .admin-table td {
    padding: 0.45rem 0.6rem;
    font-size: 0.78rem;
  }

  /* Modal: full screen on phone. dvh with a vh fallback - 100vh overshoots
     the visible viewport by the URL-bar delta, hiding the footer buttons. */
  .modal {
    width: 100vw;
    max-width: 100vw;
    max-height: 100vh;
    max-height: 100dvh;
    border-radius: 0;
    margin: 0;
  }

  .modal-header {
    padding: 1rem;
  }

  .modal-header h2 {
    font-size: 1rem;
  }

  .modal-body {
    padding: 0.75rem;
  }

  .modal-footer {
    padding: 0.75rem;
  }

  .form-group {
    margin-bottom: 0.75rem;
  }

  .form-label {
    font-size: 0.8rem;
  }

  .form-input,
  .form-select,
  .form-textarea {
    /* 16px, never less: iOS zooms the page onto any focused control under
       16px computed, and the zoom drags the viewport-locked shell with it.
       The consolidated floor at the end of this file holds the same line
       for every text-entry control; this one also compacts the padding. */
    font-size: 16px;
    padding: 0.5rem 0.65rem;
  }

  /* Login */
  .login-card {
    width: 100%;
    max-width: calc(100vw - 32px);
    padding: 1.75rem;
    margin: 0 1rem;
  }

  .login-card h1 {
    font-size: 1.3rem;
  }

  /* Tree: stack each row on phone. The title is often a long RTL phrase;
     without wrapping it gets crushed to one word per line by the nowrap
     hours pill + course/status badges, and the row overflows the viewport.
     Let the header wrap, give the title (and its inline editor) a full-width
     line of its own, and let the meta pills/badges/actions wrap below it. */
  .tree-header {
    flex-wrap: wrap;
    align-items: center;
    column-gap: 0.5rem;
    row-gap: 0.4rem;
    padding: 0.6rem 0.7rem;
  }

  .tree-title,
  .tree-header .inline-edit {
    flex: 1 1 100%;
    min-width: 0;
    font-size: 0.85rem;
  }

  .tree-children {
    padding: 0.35rem 1rem 0.5rem 0.35rem;
  }

  .tree-children--subjects {
    padding-right: 1.25rem;
  }

  .tree-description {
    margin: 0 0.5rem 0.35rem;
    padding: 0.25rem 0.5rem;
    font-size: 0.75rem;
  }

  .tree-actions {
    opacity: 1;
  }

  .drag-handle {
    font-size: 0.75rem;
  }

  /* Meta list */
  .meta-item {
    padding: 0.55rem;
  }

  /* Rich editor */
  .rich-editor-toolbar {
    flex-wrap: wrap;
  }

  .rich-editor-btn {
    width: 26px;
    height: 26px;
    font-size: 0.75rem;
  }

  .rich-editor-content {
    /* A contenteditable is a text-entry surface: same 16px iOS-zoom floor
       as the form kit (see the consolidated floor at the end of the file). */
    font-size: 16px;
    padding: 0.45rem 0.6rem;
  }

  /* Empty state */
  .empty-state {
    padding: 2rem 1rem;
    font-size: 0.9rem;
  }
}

/* ── Inline-style replacements ───────────────────── */
/* (moved from inline styles for Chrome Workspaces)  */

.select-auto { width: auto; min-width: 140px; }
.th-order { width: 60px; }
.th-visible { width: 70px; }
.th-actions { width: 140px; }
.order-cell { display: flex; align-items: center; gap: 2px; }
.icon-sm { font-size: 0.7rem; }
.section-title-display { cursor: default; }
.section-id-display { font-size: 0.78rem; color: #999; }
.meta-list-spaced { margin-bottom: 0.75rem; }
.checkbox-label-inline { display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: #666; }

/* Section-editor modal toggle row - three labelled .toggle widgets.
   Same switch style as the table columns so authors get consistent UI. */
.form-toggles {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
  margin: 0.25rem 0 0.75rem;
  padding: 0.75rem 1rem;
  background: #f6f7fb;
  border: 1px solid #e0e3ea;
  border-radius: 8px;
}
.toggle-field {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.88rem;
  color: #333;
  cursor: pointer;
  user-select: none;
}
.toggle-field__label { font-weight: 500; }
.form-group-row { display: flex; align-items: flex-end; }
.checkbox-label-admin { display: flex; align-items: center; gap: 0.5rem; font-size: 0.9rem; color: #555; padding-bottom: 0.55rem; }
.tree-actions-visible { opacity: 1; }
.chapter-subject-count { color: #aaa; font-size: 0.78rem; }
.tree-id {
  font-family: var(--font-mono, monospace);
  font-size: 0.72rem;
  color: #777;
  background: #f1f1f4;
  border: 1px solid #e2e2e8;
  border-radius: 4px;
  padding: 0.1rem 0.4rem;
  letter-spacing: 0.02em;
}

/* ── Curriculum admin stats banner ─────────────────────────────────
   Quick at-a-glance totals (hours, sessions, weeks, counts) above the
   module tree. Internal-only view, so it's information-dense but visually
   restrained - thin border, subtle gradient, no marketing flourish. */
/* Curriculum totals banner - mirrors the public site's "hours-banner"
   (public/css/hours-view.css): radial-gradient glass card, a 2px brand-
   gradient hairline on top, light-weight gradient numerals, hairline
   gradient dividers, and a mono eyebrow. */
.admin-stats-banner {
  position: relative;
  overflow: hidden;
  margin: 0 0 1.5rem;
  padding: 1.1rem 1.75rem 1.35rem;
  background:
    radial-gradient(120% 140% at 15% 0%, rgba(8, 148, 255, 0.07) 0%, transparent 55%),
    radial-gradient(120% 140% at 95% 100%, rgba(189, 50, 214, 0.06) 0%, transparent 55%),
    #fff;
  border: 1px solid rgba(0, 60, 130, 0.08);
  border-radius: var(--radius-xl, 20px);
  box-shadow: var(--shadow-md);
  direction: rtl;
}

.admin-stats-banner::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: linear-gradient(90deg, #0894FF 0%, #BD32D6 50%, #FFA304 100%);
  opacity: 0.9;
}

.admin-stats-eyebrow {
  font-family: var(--font-mono, monospace);
  font-size: 0.68rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-tertiary, #98a0ac);
  text-align: center;
  margin-bottom: 0.95rem;
}

.admin-stats-grid {
  display: flex;
  align-items: stretch;
  justify-content: center;
  gap: 0.5rem;
}

.admin-stat {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 0 0.5rem;
  text-align: center;
}

/* Gradient sits on weight-300 numerals only - per the brand rule, the
   gradient never touches medium/bold text. */
.admin-stat-num {
  font-family: 'Noto Sans Hebrew', 'Helvetica Neue', var(--font-display);
  font-size: clamp(1.7rem, 2.4vw, 2.5rem);
  font-weight: 300;
  letter-spacing: -0.02em;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  background: linear-gradient(90deg, #0894FF 0%, #BD32D6 60%, #FFA304 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}

.admin-stat-label {
  font-size: 0.78rem;
  color: var(--text-secondary, #5b6470);
  line-height: 1.35;
}

.admin-stat-sublabel {
  display: block;
  font-size: 0.66rem;
  color: var(--text-tertiary, #98a0ac);
  margin-top: 2px;
}

.admin-stat-divider {
  width: 1px;
  align-self: stretch;
  margin: 0.35rem 0;
  flex-shrink: 0;
  background: linear-gradient(180deg, transparent 0%, rgba(0, 60, 130, 0.15) 50%, transparent 100%);
}

/* Phone: 7 figures can't share one row (the last count gets clipped
   off-screen). Reflow into a 3-up grid and drop the vertical dividers.
   This block lives after the base rules on purpose - equal specificity,
   so it must win on source order. */
@media (max-width: 767px) {
  .admin-stats-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem 0;
  }

  .admin-stat {
    padding: 0 0.35rem;
  }

  .admin-stat-divider {
    display: none;
  }

  .admin-stat-num {
    font-size: 1.5rem;
  }

  .admin-stat-label {
    font-size: 0.7rem;
  }

  .admin-stat-sublabel {
    font-size: 0.6rem;
  }
}

/* ── Hours / sessions pill in tree rows ────────────────────────────
   Compact inline display of "X שעות · Y מפגשים" on each row.
   - .tree-hours--derived: read-only totals on modules + chapters
   - .tree-hours--subject: clickable editable hours on subjects
   - .tree-hours--editing:  active inline editor (input + sessions preview)
   Hebrew copy is RTL inside the otherwise LTR admin panel. */
.tree-hours {
  display: inline-flex;
  align-items: baseline;
  gap: 0.45rem;
  padding: 0.22rem 0.7rem;
  border-radius: 999px;
  font-size: 0.76rem;
  line-height: 1.4;
  white-space: nowrap;
  direction: rtl;
  font-feature-settings: 'tnum';
  border: 1px solid transparent;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}

/* Visually push the `·` separator out a touch so the two label-number pairs
   feel like distinct groups rather than a single run-on phrase. */
.tree-hours-sep {
  margin-inline: 0.15rem;
}

.tree-hours-num {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.tree-hours-unit {
  font-weight: 400;
  opacity: 0.85;
}

.tree-hours-sep {
  color: #c4c4c4;
  margin: 0 0.1rem;
}

/* Module / chapter derived totals - quieter, neutral background to read as
   "this is a computed summary, not directly editable". */
.tree-hours--derived {
  background: #f3f4f6;
  color: #5b6470;
  border-color: #e6e8eb;
}

.tree-hours--derived .tree-hours-num {
  color: #2d3640;
}

/* Subject hours - the editable source-of-truth. Brand-tinted so it reads as
   the actionable affordance. Empty state shows a clock icon prompting input. */
.tree-hours--subject {
  background: rgba(0, 113, 227, 0.08);
  color: #0071e3;
  border-color: rgba(0, 113, 227, 0.18);
  cursor: pointer;
}

.tree-hours--subject:hover {
  background: rgba(0, 113, 227, 0.16);
  border-color: rgba(0, 113, 227, 0.32);
}

.tree-hours--subject.is-empty {
  background: transparent;
  border-style: dashed;
  border-color: #cfd3d9;
  color: #8b94a0;
}

.tree-hours--subject.is-empty:hover {
  background: rgba(0, 113, 227, 0.06);
  border-color: rgba(0, 113, 227, 0.32);
  color: #0071e3;
}

.tree-hours--subject.is-readonly {
  cursor: default;
  background: #f3f4f6;
  color: #5b6470;
  border-color: #e6e8eb;
}

.tree-hours-empty-icon {
  font-size: 0.7rem;
}

.tree-hours-empty-label {
  font-weight: 500;
}

/* Editing state - sits inline, input is narrow + numeric. Confirm/cancel
   buttons mirror the existing inline-edit pattern. */
.tree-hours--editing {
  background: #fff;
  border-color: #0071e3;
  box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.12);
  padding: 0.12rem 0.4rem;
  cursor: default;
}

.inline-edit-hours-input {
  width: 4rem;
  padding: 0.15rem 0.35rem;
  border: 1px solid #d6d9dd;
  border-radius: 4px;
  font-size: 0.78rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: center;
  background: #fff;
  color: #2d3640;
  direction: ltr;
  -moz-appearance: textfield;
}

.inline-edit-hours-input::-webkit-outer-spin-button,
.inline-edit-hours-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.inline-edit-hours-input:focus {
  outline: none;
  border-color: #0071e3;
  box-shadow: 0 0 0 2px rgba(0, 113, 227, 0.15);
}

.tree-hours--editing .btn-icon {
  width: 22px;
  height: 22px;
  font-size: 0.7rem;
}

/* Export: select-all label */
.export-select-all { font-size: 0.82rem; color: #888; display: flex; align-items: center; gap: 0.4rem; cursor: pointer; }
/* Export: preview subtitle/org */
.preview-subtitle { color: #888; }
.preview-org { color: #aaa; font-size: 0.8rem; }
.preview-chapter-count { color: #aaa; }
/* Export: gdoc result/setup boxes */
.gdoc-success { margin-top: 1rem; padding: 1rem; background: #e8f5e9; border-radius: 8px; }
.gdoc-setup { margin-top: 1rem; background: #fff8e1; }
.gdoc-setup-text { font-size: 0.9rem; color: #666; margin-bottom: 0.75rem; }
.gdoc-setup-list { font-size: 0.85rem; color: #555; margin: 0; padding-right: 1.25rem; }
.gdoc-setup-label { font-size: 0.85rem; color: #888; margin-top: 0.75rem; }

/* ── Export component (moved from scoped styles) ─── */

.export-card {
  background: #fff;
  border: 1px solid #e8e8e8;
  border-radius: 8px;
  padding: 1.25rem;
  margin-bottom: 1rem;
}

.export-card-title {
  font-size: 1rem;
  font-weight: 600;
  color: #333;
  margin: 0 0 0.75rem;
}

.export-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.75rem;
}

.export-card-header .export-card-title {
  margin: 0;
}

.export-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}

.export-checklist {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.export-check-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.5rem;
  border-radius: 4px;
  cursor: pointer;
  font-size: 0.88rem;
  color: #444;
  transition: background 0.15s;
}

.export-check-item:hover {
  background: #f8f9ff;
}

.export-check-type {
  margin-left: auto;
  font-size: 0.75rem;
  color: #aaa;
}

.export-preview {
  border: 1px dashed #ddd;
  border-radius: 6px;
  padding: 1rem;
  background: #fafafa;
  font-size: 0.85rem;
}

.preview-cover {
  text-align: center;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid #eee;
  margin-bottom: 0.75rem;
}

.preview-block {
  margin-bottom: 0.75rem;
}

.preview-heading {
  font-weight: 600;
  color: #555;
  margin-bottom: 0.35rem;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.preview-item {
  padding: 0.2rem 0 0.2rem 0.75rem;
  color: #666;
  font-size: 0.82rem;
  border-left: 2px solid #e0e0e0;
}

.export-actions {
  display: flex;
  gap: 0.75rem;
  margin-top: 1rem;
}

/* ══════════════════════════════════════════════════════
   Open Day Registrations - admin table view
   ══════════════════════════════════════════════════════ */

/* ── Date tabs ─────────────────────────────────────────
   One tab per open-day date + an "all dates" overview. The
   active tab scopes the table AND the stats panel to a single
   date; the strip scrolls horizontally when dates overflow. */

.reg-tabs {
  display: flex;
  align-items: stretch;
  gap: 0.15rem;
  margin-bottom: 0.85rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: thin;
  border-bottom: 1px solid #e2e6ec;
}

.reg-tab {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.58rem 0.95rem;
  border: none;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: #5b6470;
  font-size: 0.85rem;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: color 0.12s, border-color 0.12s, background 0.12s;
}

.reg-tab:hover {
  color: #1a1a1a;
  background: #f4f7fb;
}

.reg-tab--active {
  color: #0071E3;
  border-bottom-color: #0071E3;
}

.reg-tab--active:hover {
  background: transparent;
}

.reg-tab--past {
  color: #98a0ac;
}

.reg-tab--past.reg-tab--active {
  color: #0071E3;
}

.reg-tab-icon {
  font-size: 0.8em;
  opacity: 0.85;
}

.reg-tab-count {
  padding: 0.05rem 0.44rem;
  border-radius: 999px;
  background: #eef2f7;
  color: #5b6470;
  font-size: 0.7rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.reg-tab--active .reg-tab-count {
  background: #e3f2fd;
  color: #0d47a1;
}

/* Disabled (isActive=false) open-day date tab: strike through the label so a
   cancelled/inactive date reads as unavailable (mirrors option.od-opt-disabled).
   Only the label is struck - the count badge stays legible. */
.reg-tab-label--disabled {
  text-decoration: line-through;
}

/* Scope chip in the stats header: which date the figures below reflect. */
.reg-stats-scope {
  margin-inline-start: 0.5rem;
  padding: 0.1rem 0.5rem;
  border-radius: 999px;
  background: #e3f2fd;
  color: #0d47a1;
  font-size: 0.72rem;
  font-weight: 700;
  white-space: nowrap;
}

/* ── Filter bar ──────────────────────────────────── */

.reg-filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
  padding: 0.85rem 1rem;
  background: #fff;
  border: 1px solid #e8e8e8;
  border-radius: 10px;
}

.reg-filter-field {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 180px;
}

.reg-filter-field--grow {
  flex: 1;
  min-width: 220px;
}

/* Wide enough for the full "attendance-confirmation status for this open day"
   label to stay on one line above its select. */
.reg-filter-field--status {
  min-width: 15rem;
}

/* ── Date filter pills ────────────────────────────────
   Multi-selectable pills, one per open-day date; the strip
   scrolls horizontally when the dates outgrow the bar. */

.reg-filter-field--dates {
  flex: 1 1 100%;
  min-width: 0;
}

.reg-date-pills {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  padding-bottom: 0.25rem;
  scrollbar-width: thin;
}

.reg-date-pill {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.34rem 0.8rem;
  border: 1px solid #dfe4ec;
  border-radius: 999px;
  background: #fff;
  color: #47505c;
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}

.reg-date-pill:hover {
  background: #f4f7fb;
  border-color: #b9c6d8;
}

.reg-date-pill--active {
  background: #0071E3;
  border-color: #0071E3;
  color: #fff;
}

.reg-date-pill--active:hover {
  background: #0077ED;
  border-color: #0077ED;
}

.reg-date-pill--past {
  color: #98a0ac;
  border-style: dashed;
}

.reg-date-pill--past.reg-date-pill--active {
  color: #fff;
  border-style: solid;
}

.reg-date-pill-count {
  padding: 0.05rem 0.42rem;
  border-radius: 999px;
  background: #eef2f7;
  color: #5b6470;
  font-size: 0.7rem;
  font-weight: 700;
}

.reg-date-pill--active .reg-date-pill-count {
  background: rgba(255, 255, 255, 0.22);
  color: #fff;
}

.reg-filter-field .filter-label {
  font-size: 0.72rem;
  color: #8e8f9e;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.reg-filter-field .form-select,
.reg-filter-field .form-input {
  direction: ltr;
  text-align: left;
  padding: 0.45rem 0.7rem;
  font-size: 0.88rem;
}

.reg-filter-field .admin-search {
  width: 100%;
}

.reg-filter-field .admin-search input {
  width: 100%;
}

.reg-filter-clear {
  align-self: flex-end;
  margin-bottom: 1px;
  color: #666;
}

/* ── The open day's calendar event ────────────────────
   The first of the date-level strips under the filter bar since the capacity
   strip was removed (owner, 2026-08-20) - it now opens that stack rather than
   sitting under it. Quiet on purpose. It is a status line with a few actions,
   never a KPI. */

.reg-cal {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.7rem 1.1rem;
  margin-bottom: 0.75rem;
  background: #fff;
  border: 1px solid #e8eef8;
  border-radius: 10px;
  box-shadow: 0 1px 2px rgba(0, 60, 130, 0.04);
}

.reg-cal-main {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  flex-wrap: wrap;
  min-width: 0;
}

.reg-cal-icon {
  font-size: 0.9rem;
  color: #98a0ac;
}

.reg-cal-label {
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--text-secondary);
  letter-spacing: 0.01em;
}

.reg-cal-state {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  flex-wrap: wrap;
  font-size: 0.86rem;
  color: var(--text-color);
}

/* "Not created" and "switched off" are both absences of an event, and neither
   is a problem - so they read as the same quiet grey rather than as a warning. */
.reg-cal-state--muted {
  color: #8a93a3;
}

/* A read that did not come back IS a problem, and the retry beside it is the
   only thing that can clear it. */
.reg-cal-state--error {
  color: #b45309;
  font-weight: 600;
}

.reg-cal-created {
  font-weight: 700;
  color: #047857;
}

.reg-cal-count {
  font-weight: 600;
}

.reg-cal-count--accepted {
  color: #047857;
}

.reg-cal-sep {
  color: #c3c9d2;
}

.reg-cal-actions {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex-wrap: wrap;
}

/* "Open in the calendar" is an <a> - it leaves the panel, so it has to be a real
   link a middle click can open - and `.btn` does not clear an anchor's
   underline. Left alone it reads as a stray link that wandered into a row of
   buttons. Same fix `.reg-action` already carries for the person-card link. */
.reg-cal-actions a.btn {
  text-decoration: none;
}

/* ── Status chips (summary counts) ───────────────── */

.reg-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  font-size: 0.76rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  border: 1px solid transparent;
}

/* The total is the anchor the status chips break down, so it is filled rather
   than tinted - it must not read as a seventh status. */
.reg-chip--total     { background: var(--primary-color); color: #fff; border-color: var(--primary-color); }
.reg-chip--pending   { background: #f3f4f6; color: #4b5563; border-color: #e5e7eb; }
.reg-chip--approved  { background: #ecfdf5; color: #047857; border-color: #a7f3d0; }
.reg-chip--rejected  { background: #fef2f2; color: #b91c1c; border-color: #fecaca; }
.reg-chip--no_answer { background: #f5f3ff; color: #6d28d9; border-color: #ddd6fe; }
.reg-chip--deleted   { background: #1f2937; color: #f3f4f6; border-color: #374151; }

/* ── Course-of-interest chips (in-row) ───────────── */

.reg-course-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  border: 1px solid transparent;
  white-space: nowrap;
}

.reg-course-chip--developer { background: #e3f2fd; color: #0d47a1; border-color: #bbdefb; }
.reg-course-chip--architect { background: #f3e5f5; color: #6a1b9a; border-color: #e1bee7; }
.reg-course-chip--bundle    { background: #ede7f6; color: #4527a0; border-color: #d1c4e9; }
.reg-course-chip--undecided { background: #f5f5f5; color: #666;    border-color: #e0e0e0; }

/* ── Statistics overview panel ───────────────────────
   Global, filter-independent snapshot above the filters: KPI cards, a per-date
   x status breakdown matrix, and a course-interest split. */
.reg-stats {
  background: #fff;
  border: 1px solid #e8eef8;
  border-radius: 12px;
  box-shadow: 0 1px 3px rgba(0, 60, 130, 0.05);
  margin-bottom: 1rem;
  overflow: hidden;
}

.reg-stats-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 1rem;
  border-bottom: 1px solid #eef2f8;
}

.reg-stats-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  color: #191A23;
}

.reg-stats-toggle i {
  color: #8e8f9e;
  font-size: 0.78rem;
  width: 0.8rem;
}

.reg-stats-title {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: #5A5B6A;
}

.reg-stats-headcount {
  font-size: 0.85rem;
  color: #5A5B6A;
}

.reg-stats-headcount strong {
  color: #191A23;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.reg-stats-body {
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}

/* ── The approval-status presets ──────────────────────
   PILLS, NOT TILES, AND BUTTONS, NOT TEXT. They sit directly under the
   participation chips and have to read as the same family of objects while
   saying that these ones can be pressed - so the hover lift, the focus ring and
   the pressed outline all live here and nowhere else on the panel.

   The number leads and the label follows it on one line, a stat pill rather
   than the stacked KPI card this replaced: five stacked cards took the whole
   width of the panel, and this row now shares that width with a second one.

   Four of them most of the time, five when the date has cancelled seats, so
   they WRAP rather than grid - a fifth landing alone on a second row at
   one-quarter width with three empty cells beside it was what the old grid's
   `auto-fit` was working around. */
.reg-presets {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.reg-presets-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
}

.reg-preset {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  padding: 0.3rem 0.8rem;
  border: 1px solid #e5e7eb;
  border-radius: 999px;
  background: #f8fafc;
  font-family: inherit;
  cursor: pointer;
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.reg-preset:hover {
  box-shadow: 0 1px 5px rgba(0, 60, 130, 0.14);
  transform: translateY(-1px);
}

.reg-preset:focus-visible {
  outline: 2px solid #0071E3;
  outline-offset: 2px;
}

.reg-preset-value {
  font-size: 1.05rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  color: #191A23;
  font-variant-numeric: tabular-nums;
}

.reg-preset-label {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-secondary);
}

/* One tint per status, lifted from the `.reg-chip--*` swatches so a preset and
   the chip on a row wear the same colour for the same word. */
.reg-preset--total    { background: #eef6ff; border-color: #cfe3fb; }
.reg-preset--total .reg-preset-value,
.reg-preset--total .reg-preset-label { color: #0b4da2; }
.reg-preset--approved { background: #ecfdf5; border-color: #a7f3d0; }
.reg-preset--approved .reg-preset-value,
.reg-preset--approved .reg-preset-label { color: #047857; }
.reg-preset--pending  { background: #f3f4f6; border-color: #e5e7eb; }
.reg-preset--pending .reg-preset-value,
.reg-preset--pending .reg-preset-label { color: #4b5563; }
.reg-preset--no_answer { background: #f5f3ff; border-color: #ddd6fe; }
.reg-preset--no_answer .reg-preset-value,
.reg-preset--no_answer .reg-preset-label { color: #6d28d9; }
.reg-preset--rejected { background: #fef2f2; border-color: #fecaca; }
.reg-preset--rejected .reg-preset-value,
.reg-preset--rejected .reg-preset-label { color: #b91c1c; }
/* The fifth preset, on screen only while the date has cancelled seats. Same
   ink as `.reg-chip--deleted`, which is what the person's seat card marks a
   cancelled seat with, but tinted rather than filled: it is a filter, and a
   near-black pill in a row of pale ones reads as the selected one. */
.reg-preset--deleted  { background: #f1f3f6; border-color: #d3d8e0; }
.reg-preset--deleted .reg-preset-value,
.reg-preset--deleted .reg-preset-label { color: #374151; }

/* PRESSED, and it has to be unmistakable: this is the only control on the panel
   whose state decides what the table below holds, and a reader who cannot see
   which preset is on has no way to explain why the headline above counts fewer
   rows than the strip does. A ring rather than a fill, so the status tint
   survives and the row still reads as a breakdown. */
.reg-preset--active {
  box-shadow: 0 0 0 2px #191A23;
  border-color: #191A23;
}

.reg-preset--active .reg-preset-label {
  color: #191A23;
}

/* The day-level line - `צפויים באולם`, which follows no filter at all. Pushed
   to the far end of the panel so it cannot be read as a fourth member of the
   participation row or a sixth preset. */
.reg-stats-daylevel {
  display: flex;
  justify-content: flex-end;
}

/* Per-date breakdown matrix */
.reg-stats-matrix-wrap {
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

.reg-stats-matrix {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.84rem;
}

.reg-stats-matrix th {
  padding: 0.5rem 0.7rem;
  text-align: center;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #8e8f9e;
  border-bottom: 1px solid #eef2f8;
  white-space: nowrap;
}

.reg-stats-matrix td {
  padding: 0.5rem 0.7rem;
  text-align: center;
  border-bottom: 1px solid #f3f5f9;
  color: #374151;
}

.reg-stats-matrix tbody tr:hover td {
  background: #f8f9ff;
}

.reg-stats-matrix th.reg-stats-matrix-date,
.reg-stats-matrix td.reg-stats-matrix-date {
  text-align: left;
  white-space: nowrap;
}

.reg-stats-date-main {
  display: block;
  font-weight: 600;
  color: #191A23;
}

.reg-stats-num {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: #374151;
}

.reg-stats-num--zero {
  color: #cbd5e1;
  font-weight: 400;
}

.reg-stats-num--total {
  color: #191A23;
  font-weight: 700;
}

/* Neutral on purpose: this is the defined capacity, a reference number, so it
   carries no green "within limits" / amber "exceeded" signal any more. */
.reg-stats-cap {
  display: inline-block;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--text-color);
}

.reg-stats-matrix tfoot td {
  border-top: 2px solid #eef2f8;
  border-bottom: none;
  padding-top: 0.6rem;
  font-weight: 700;
  color: #191A23;
}

/* NOTE: the course-interest breakdown that used to open this block was removed
   on 2026-08-20 (owner) along with the roster's course filter. `.reg-course-chip`
   itself is NOT dead and must stay where it is defined above - four person-page
   components still render it (the contact-messages card, the contracts card,
   the open-day seat card and the lead tab). Only the strip's own layout rules
   went. */
.reg-stats-subtitle {
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #8e8f9e;
}

/* Participation-mode breakdown: total / in person / Zoom */
.reg-mode-breakdown {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.reg-mode-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
}

.reg-mode-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.25rem 0.7rem;
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
}

.reg-mode-chip b {
  margin-left: 0.3rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.reg-mode-chip i {
  font-size: 0.72rem;
  opacity: 0.75;
}

/* The total leads the row and is the only solid chip, so the two halves beside
   it read as a split OF it rather than as three peers. */
.reg-mode-chip--total     { background: #191A23; color: #fff;    border-color: #191A23; }
.reg-mode-chip--in-person { background: #e8f5e9; color: #1b5e20; border-color: #c8e6c9; }
.reg-mode-chip--zoom      { background: #ede7f6; color: #4527a0; border-color: #d1c4e9; }

/* PRESSABLE (owner, 2026-08-20). Only the two value pills wear this - the total
   beside them stays a plain reading - so the base rule keeps describing both and
   the affordance rides on a modifier.

   `font: inherit` is load-bearing: a <button> brings its own font family and
   size, and without it the two halves stop matching the total they sit beside.
   The size and weight are restated after it because the shorthand resets them.
   Padding, margin and box-sizing are already handled by the global `*` reset. */
.reg-mode-chip--preset {
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  transition: box-shadow 0.12s ease, border-color 0.12s ease;
}

.reg-mode-chip--preset:hover {
  border-color: #8e8f9e;
}

.reg-mode-chip--preset:focus-visible {
  outline: 2px solid #0071E3;
  outline-offset: 2px;
}

/* AFTER the two colour modifiers on purpose. All three are one class deep, so
   whichever is declared last wins the `border-color` tie - and with --active
   above them a pressed pill kept its pale green or purple border while a merely
   HOVERED pill wore the dark one, which reads as the wrong pill being selected.
   Hover is deliberately the lighter grey for the same reason: the dark ring
   belongs to the pressed state alone. Same treatment as `.reg-preset--active`,
   so one glance answers "what is filtering this list" across both strips. */
.reg-mode-chip--active,
.reg-mode-chip--preset.reg-mode-chip--active:hover {
  box-shadow: 0 0 0 2px #191A23;
  border-color: #191A23;
}

/* "Expected in the room" - approved AND in person, on this DATE, ignoring every
   filter on the screen. It used to close the participation row, which is a
   partition of the listed rows; it is neither a partition nor about the listed
   rows, so it sits alone on `.reg-stats-daylevel` at the foot of the panel (it
   shared the capacity strip until that strip was removed, 2026-08-20). It keeps
   the outlined-not-filled treatment for the same reason it had it in the row:
   the eye must not read a subset as another addend. */
.reg-chip--expected {
  gap: 0.3rem;
  background: #fff;
  color: #0b4da2;
  border-color: #0071E3;
  border-style: dashed;
}

/* A list request that FAILED, as opposed to a filter that matched nobody. It
   sits in the same slot as the empty state and must not read like one: on the
   morning of an open day the sentence under it is acted on. */
.reg-load-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.7rem;
}

/* ── Bulk-action bar ─────────────────────────────── */

.reg-bulk-bar {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.55rem 0.85rem;
  margin-bottom: 0.75rem;
  background: #0f172a;
  color: #fff;
  border-radius: 10px;
  box-shadow: 0 6px 20px rgba(0, 60, 130, 0.14);
  flex-wrap: wrap;
}

.reg-bulk-bar .btn {
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
  border-color: rgba(255, 255, 255, 0.14);
}

.reg-bulk-bar .btn:hover {
  background: rgba(255, 255, 255, 0.14);
  border-color: rgba(255, 255, 255, 0.22);
}

.reg-bulk-count {
  font-size: 0.85rem;
  font-weight: 600;
  color: #fff;
  padding: 0 0.75rem 0 0.25rem;
  letter-spacing: 0.01em;
}

/* Attendance picker in the bulk bar. The bar is a dark slab, so the select has
   to opt out of the light .form-select palette or it renders as a white hole. */
.reg-bulk-select {
  width: auto;
  padding: 0.32rem 1.6rem 0.32rem 0.6rem;
  font-size: 0.8rem;
  background-color: rgba(255, 255, 255, 0.08);
  color: #fff;
  border-color: rgba(255, 255, 255, 0.14);
}

.reg-bulk-select:hover {
  background-color: rgba(255, 255, 255, 0.14);
  border-color: rgba(255, 255, 255, 0.22);
}

/* The popup list is drawn by the OS against its own surface, so the options
   need the normal dark-on-light treatment rather than the slab's inverse. */
.reg-bulk-select option {
  color: #191a23;
  background-color: #fff;
}

.reg-bulk-spacer {
  margin-left: auto;
}

/* Bulk delete. It used to be #fecaca on the dark bulk bar, which reads as
   off-white next to seven identical siblings - the riskiest button on the
   screen was the hardest one to pick out. Now it carries a red wash and a
   solid red edge, and sits apart from the routine buttons. */
.reg-bulk-danger {
  color: #fca5a5;
  background: rgba(220, 38, 38, 0.16);
  border-color: #ef4444;
  margin-inline-start: 0.5rem;
}

.reg-bulk-danger:hover {
  background: #dc2626;
  color: #fff;
  border-color: #dc2626;
}

/* ── Table tweaks ────────────────────────────────── */

.reg-table {
  box-shadow: 0 1px 3px rgba(0, 60, 130, 0.05);
  overflow: hidden;
}

.reg-table th,
.reg-table td {
  text-align: left;
}

.reg-table input[type="checkbox"] {
  width: 16px;
  height: 16px;
  cursor: pointer;
  accent-color: #0071E3;
}

/* Sortable column headers */
.reg-th-sort {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  transition: color 0.12s, background 0.12s;
}

.reg-th-sort:hover {
  background: #f1f5fb;
  color: #191A23;
}

.reg-th-sort--active {
  color: #0071E3;
}

/* The sortable header is a <th> carrying a tabindex, not a <button>, so it has
   no focus ring of its own - and without one a keyboard user cannot see which
   column they are about to sort. Inset, because the header sits flush against
   the table edge and an outer ring would be clipped by the scroll container. */
.reg-th-sort:focus-visible {
  outline: 2px solid #0071E3;
  outline-offset: -2px;
  background: #f1f5fb;
  color: #191A23;
}

.reg-sort-icon {
  margin-left: 0.3rem;
  font-size: 0.72rem;
  color: #c2c8d2;
}

.reg-th-sort:hover .reg-sort-icon {
  color: #8e8f9e;
}

.reg-th-sort--active .reg-sort-icon {
  color: #0071E3;
}

/* The approval column says in full what it holds - "attendance-confirmation
   status for this open day" - so it can never be read as the LEAD's status.
   Sortable headers are nowrap, which would stretch this one past 250px on a
   table that already scrolls sideways; it wraps to two lines inside a fixed
   width instead, and the width is wider than the widest status pill below it. */
.reg-th-sort.reg-th-status {
  width: 172px;
  white-space: normal;
  line-height: 1.35;
}

.reg-row--selected td {
  background: #eff6ff !important;
}

/* THE ROW OPENS THE PERSON (owner item 18, 2026-08-09), and the affordance is
   the whole of the visual change: without a pointer cursor a clickable row is a
   surprise rather than a control.

   Only on rows that actually GO somewhere. A seat with no linked lead - or a
   viewer without `leads:read` - keeps the default cursor and no hover, because a
   row that looks live and answers nothing is worse than one that never claimed
   to be live. The class is applied per row, never on the table.

   No hover TINT here, deliberately. `.admin-table tr:hover td` already paints
   one for every row on this screen, and a second one scoped to the clickable
   rows would fight the `--selected` and `--returned` marks over the same
   surface - two of which use `!important` and an animation respectively. The
   cursor carries the message on its own. */
.reg-row--clickable {
  cursor: pointer;
}

/* THE LANDING MARK FOR A `?reg=` ARRIVAL.
   The reader pressed Back from a sale, or came in from the person page, and this
   is the row they left. It answers one question - "where was I" - and that
   question stops being worth answering a few seconds after it is answered, so
   the mark fades itself out. The component clears the class at 4s; the fade runs
   from 2s to 4s, so the element is still mounted when it lands.

   Three things here are load-bearing:

   1. PAINTED ON THE CELLS, not on the `<tr>`. In a `border-collapse` table a row
      is not a paint surface, so an `outline` or a `box-shadow` there renders
      nowhere at all - which is how this mark can look "not applied" while the
      class is demonstrably on the element.
   2. SPECIFICITY, not `!important`, and this is the difference between a rule
      that works and one that silently does nothing. `.admin-table tr:hover td`
      (0-1-2) outranks a bare `.reg-row--returned td` (0-1-1), which is why the
      neighbouring `--selected` rule reaches for `!important` - but an
      `!important` declaration BEATS AN ANIMATION, so borrowing that trick here
      would pin the tint on and never fade it. `.reg-table tr.…` is 0-2-2 and
      wins the static half honestly, leaving the animation free to do its job.
   3. `background-color`, never the `background` shorthand. The shorthand resets
      background-image with it, and one of these cells may yet grow one. */
.reg-table tr.reg-row--returned td {
  background-color: rgba(0, 113, 227, 0.1);
  animation: reg-row-returned-fade 2s ease-out 2s forwards;
}

/* To `transparent` and not to a colour: the cell's resting state is to show the
   table through, and naming white here would paint over a future striped row. */
@keyframes reg-row-returned-fade {
  to {
    background-color: transparent;
  }
}

/* The card layout below 1024px has no cells left to tint - the row IS the card -
   so it fades to the card's own white instead. */
@keyframes reg-row-returned-fade-card {
  to {
    background-color: #fff;
  }
}

/* A mark whose entire job is to catch the eye is exactly what this setting is
   about. The tint still appears and the component still clears it at 4s; only
   the fade goes away. */
@media (prefers-reduced-motion: reduce) {
  .reg-table tr.reg-row--returned td,
  .reg-table tbody tr.reg-row--returned {
    animation: none;
  }
}

.reg-cell-email {
  color: #374151;
  font-variant-numeric: tabular-nums;
  /* Cap so the labeled actions column keeps its width; long addresses wrap. */
  max-width: 180px;
  overflow-wrap: anywhere;
}

.reg-cell-sub {
  color: #8e8f9e;
  font-size: 0.82rem;
}

/* The as-typed name under the person's name (owner decision D1, 2026-08-20).
   The roster headlines the CRM's name for whoever holds the seat; the name the
   registration row was actually filled in with follows underneath, and only
   where the two say different things. Same 0.15rem step and the same tertiary
   ink as `.reg-cell-plate`, the sub-line under the phone number, so the table
   has one treatment for "a smaller true fact about this row" rather than two.

   A DESCENDANT rule on two classes that both already exist, rather than a class
   of its own. `.reg-cell-name` has no desktop rule at all - its only two live in
   the 767px block, where the table becomes a stack of cards laid out by CSS
   `order` keyed on the cell CLASS - so this is deliberately the one property the
   sub-line needs at every width, and nothing that could disturb that ladder. */
.reg-cell-name .reg-cell-sub {
  margin-top: 0.15rem;
}

/* No email address on the row: an approval or a reminder for this person is
   sent nowhere, so the empty cell is labelled instead of left blank. Amber,
   not red - the registration itself is perfectly valid, staff just have to
   phone rather than write. */
.reg-no-email {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  color: #92400e;
  background: #fef3c7;
  border-radius: 4px;
  padding: 0.1rem 0.4rem;
  font-size: 0.78rem;
  white-space: nowrap;
}

/* ── Referral attribution cell ─────────────────────
   Last-touch code as a mono chip + its name/group; earlier touches from
   the attribution path stack beneath it (oldest -> newest), quieter so the
   current code stays the focal point. */
.reg-th-referral {
  width: 150px;
}

.reg-cell-referral {
  line-height: 1.35;
}

.reg-ref-line {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  flex-wrap: wrap;
}

.reg-ref-code {
  display: inline-block;
  font-family: var(--font-mono, monospace);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: #0d47a1;
  background: #e8f1fd;
  border: 1px solid #c9def8;
  border-radius: 5px;
  padding: 0.04rem 0.34rem;
}

.reg-ref-code--prev {
  color: #6b7280;
  background: #f2f3f5;
  border-color: #e2e4e8;
  font-weight: 600;
}

.reg-ref-meta {
  font-size: 0.78rem;
  color: #5b6470;
}

.reg-ref-group {
  color: #98a0ac;
}

.reg-ref-prev {
  list-style: none;
  margin: 0.3rem 0 0;
  padding: 0.3rem 0 0;
  border-top: 1px dashed #e6e8ec;
  display: flex;
  flex-direction: column;
  gap: 0.22rem;
}

.reg-ref-prev li {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  flex-wrap: wrap;
  font-size: 0.76rem;
}

/* ── Inline status select per row ────────────────── */

.reg-status-select {
  padding: 0.28rem 1.6rem 0.28rem 0.65rem;
  border-radius: 999px;
  border: 1px solid transparent;
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  appearance: none;
  background-repeat: no-repeat;
  background-position: right 0.5rem center;
  background-size: 0.65rem;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6' fill='none' stroke='%23475569' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><path d='M1 1l4 4 4-4'/></svg>");
  transition: filter 0.15s;
  letter-spacing: 0.01em;
}

.reg-status-select:hover {
  filter: brightness(0.97);
}

.reg-status-select--pending   { background-color: #f3f4f6; color: #4b5563; border-color: #e5e7eb; }
.reg-status-select--approved  { background-color: #ecfdf5; color: #047857; border-color: #a7f3d0; }
.reg-status-select--rejected  { background-color: #fef2f2; color: #b91c1c; border-color: #fecaca; }
.reg-status-select--no_answer { background-color: #f5f3ff; color: #6d28d9; border-color: #ddd6fe; }
.reg-status-select--deleted   { background-color: #1f2937; color: #f3f4f6; border-color: #374151; }

/* ── Attendance checkbox per row ─────────────────────
   Tri-state: indeterminate = not set, checked = attended,
   unchecked = did not attend. Click cycles the states. */

.reg-th-attend {
  width: 96px;
}

.reg-cell-attend {
  white-space: nowrap;
}

.reg-attend {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  cursor: pointer;
  user-select: none;
}

.reg-attend-label {
  font-size: 0.78rem;
  font-weight: 600;
  color: #98a0ac;
}

.reg-attend--yes input[type="checkbox"] {
  accent-color: #047857;
}

.reg-attend--yes .reg-attend-label {
  color: #047857;
}

.reg-attend--no .reg-attend-label {
  color: #b91c1c;
}

/* Read-only variant (e.g. lead detail without open-day-registrations:full) */
.reg-attend--readonly,
.reg-attend--readonly input[type="checkbox"] {
  cursor: default;
}

/* ── "Attending in Zoom" checkbox per row ────────────
   Binary, unlike the tri-state attendance control above,
   and it always asks ONE question: is this person joining
   over Zoom? Ticked = Zoom, clear = at the venue (the
   default). The label beside the box therefore never
   changes with the state - only its weight and colour do,
   so a ticked row still reads at a glance down the column. */

.reg-th-mode {
  width: 124px;
}

.reg-cell-mode {
  white-space: nowrap;
}

.reg-mode {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  cursor: pointer;
  user-select: none;
}

.reg-mode-label {
  font-size: 0.78rem;
  font-weight: 600;
}

.reg-mode-label i {
  font-size: 0.88em;
  margin-inline-end: 0.18rem;
}

/* Off = not on Zoom. Muted, so the ticked rows are the ones that pop. */
.reg-mode--in-person .reg-mode-label {
  color: #98a0ac;
}

.reg-mode--zoom input[type="checkbox"] {
  accent-color: #6935d8;
}

.reg-mode--zoom .reg-mode-label {
  color: #5b21b6;
  font-weight: 700;
}

.reg-mode--readonly,
.reg-mode--readonly input[type="checkbox"] {
  cursor: default;
}

/* ── Calendar-invite state per row ───────────────────
   Read-only, unlike the two controls above it: the guest list lives in Google,
   so this column reports and the actions cell acts.

   The palette is BORROWED, not invented - accepted reuses the approved green,
   declined the rejected red, "maybe" the no_answer violet - because a reader
   scanning two chips across one row should not have to learn a second colour
   language to compare them. Only the two states with no counterpart in the
   approval axis are new: amber for a queued invite (something is going to
   happen), and the table's own "nothing to say" grey for a seat nobody has
   invited, which is most of them and must stay the quietest thing in the row. */

.reg-th-calendar {
  width: 116px;
}

.reg-cell-calendar {
  white-space: nowrap;
}

.reg-cal-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  border: 1px solid transparent;
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.5;
}

.reg-cal-chip i {
  font-size: 0.72rem;
  opacity: 0.75;
}

.reg-cal-chip--none      { background: #f6f7f9; color: #98a0ac; border-color: #eceef2; }
.reg-cal-chip--pending   { background: #fffbeb; color: #b45309; border-color: #fde68a; }
.reg-cal-chip--invited   { background: #eff6ff; color: #1d4ed8; border-color: #bfdbfe; }
.reg-cal-chip--accepted  { background: #ecfdf5; color: #047857; border-color: #a7f3d0; }
.reg-cal-chip--declined  { background: #fef2f2; color: #b91c1c; border-color: #fecaca; }
.reg-cal-chip--tentative { background: #f5f3ff; color: #6d28d9; border-color: #ddd6fe; }

/* ── Last action on the person ───────────────────────
   The newest timeline row on the registrant's LEAD, of any type - the same
   column the leads table prints, so the two screens are styled the same way:
   a type glyph beside WHAT happened, in Hebrew, over the spelled-out distance
   ("לפני 4 שעות"). Mirrors .lead-when / .lead-activity in admin-leads.css, and
   the one-line clip below mirrors .lead-comment-text there; keep the pairs in
   step. */

/* Paired with the max-width on the cell below, exactly the way
   .lead-th-comment / .lead-cell--comment are paired in admin-leads.css: the
   header's `width` is what the column ASKS for, the cell's `max-width` is what
   it is allowed to take. It was 118px, which was the whole cell back when the
   cell was two short timestamps; the cell carries a sentence now. */
.reg-th-activity {
  width: 200px;
}

/* This cell prints PROSE now - a staff note runs to whatever length its author
   felt like - and .reg-table is `table-layout: auto`, which sizes every column
   to the widest thing anywhere in it. One long note would therefore set this
   column's width for the whole table, and the table is already ~1500px inside a
   ~1184px page (see .reg-table-scroll). The cap is the answer; the ellipsis on
   .reg-activity-text below is what the reader sees where the cap bites.
   Dropping the `nowrap` instead would only trade width for height - the
   sentence would wrap to four lines and set the height of every row in the
   book. The untruncated body stays on the cell's own tooltip. */
.reg-cell-activity {
  white-space: nowrap;
  max-width: 200px;
}

.reg-activity {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.8rem;
  color: #475569;
  /* Shrink-to-fit sizes an inline-flex box to its content, so without this it
     would take the width of the sentence and hang out of the capped cell,
     carrying the ellipsis out with it. */
  max-width: 100%;
}

.reg-activity-icon {
  color: #94a3b8;
  font-size: 0.78rem;
}

.reg-when {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  line-height: 1.3;
  /* A flex item's automatic minimum size is its own content, so this column
     would otherwise refuse to give way and the clip would never engage. */
  min-width: 0;
}

/* Each line stays whole - "לפני 49 דקות" must not break mid-phrase. */
.reg-when > span {
  white-space: nowrap;
}

/* Where the cap bites. The `nowrap` half of the ellipsis contract is inherited
   from the rule directly above, which is the easy half to lose: take it away
   there and this silently becomes a wrapping block again. */
.reg-activity-text {
  overflow: hidden;
  text-overflow: ellipsis;
}

.reg-when-abs {
  color: #8e8f9e;
  font-size: 0.68rem;
  font-weight: 400;
}

/* Read-only mode tag inside the expanded row detail. Nothing to toggle there,
   so BOTH modes get a word and an icon rather than a box the reader has to
   invert. */
.reg-mode-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.14rem 0.55rem;
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: 0.76rem;
  font-weight: 600;
  line-height: 1.45;
  /* The value column of .reg-meta-row is whatever is left after a fixed 8.5rem
     key, so on a 320px phone this phrase is wider than its cell. It wraps inside
     the pill rather than spilling out of it. */
  max-width: 100%;
  white-space: normal;
}

.reg-mode-tag--zoom {
  background: #ede7f6;
  border-color: #d1c4e9;
  color: #4527a0;
}

.reg-mode-tag--in-person {
  background: #e8f5e9;
  border-color: #c8e6c9;
  color: #1b5e20;
}

/* Modal form variant: center the control on the row height of the
   sibling .form-select boxes so the grid row stays aligned. */
.reg-form-attend {
  min-height: 37px;
}

/* ── Labeled per-row actions ─────────────────────────
   Icon + text chips in a 2-col grid; labels make each
   action self-explanatory (no icon guessing). */

.reg-actions {
  display: grid;
  /* The 94px floor keeps the auto table layout from crushing the column
     and truncating the labels. */
  grid-template-columns: repeat(2, minmax(128px, 1fr));
  gap: 0.3rem;
}

.reg-action {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.28rem 0.42rem;
  border: 1px solid #e6ebf2;
  border-radius: 7px;
  background: #fff;
  color: #47505c;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
  width: fit-content;
}

.reg-action i {
  width: 0.9rem;
  font-size: 0.78rem;
  text-align: center;
  color: #8e96a3;
}

.reg-action:hover:not(:disabled) {
  background: #f4f7fb;
  border-color: #cfd9e6;
  color: #191A23;
}

.reg-action:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* The WhatsApp destination picker, kept on one line of the 2-column action
   grid. `1 / -1` spans both tracks; the segmented group then fills that width,
   so "reply in the panel", "WhatsApp Web" and "the app" always read as one
   choice of client rather than unrelated actions on different rows. */
.reg-actions-wa {
  grid-column: 1 / -1;
  display: flex;
}

.reg-actions-wa .wa-actions {
  flex: 1 1 auto;
  min-width: 0;
}

.reg-action--danger {
  color: #b91c1c;
}

.reg-action--danger i {
  color: #dc2626;
}

.reg-action--danger:hover:not(:disabled) {
  background: #fef2f2;
  border-color: #fecaca;
  color: #991b1b;
}

/* Car plate sub-line under the phone number */
.reg-cell-plate {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  margin-top: 0.15rem;
  font-family: var(--font-mono, monospace);
  font-size: 0.76rem;
  color: #5b6470;
  white-space: nowrap;
}

.reg-cell-plate i {
  font-size: 0.7rem;
  color: #98a0ac;
}

/* ── Row detail (expanded) ───────────────────────── */

.reg-row-detail > td {
  padding: 0;
  background: #f8fafc;
}

.reg-detail {
  display: grid;
  grid-template-columns: 1.4fr 1fr;
  gap: 1.25rem;
  padding: 1rem 1.25rem;
  border-top: 1px solid #e2e8f0;
}

.reg-detail-col {
  min-width: 0;
}

.reg-detail-label {
  font-size: 0.72rem;
  color: #6b7280;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 0.45rem;
}

.reg-detail-label--spaced {
  margin-top: 0.9rem;
}

.reg-detail-empty {
  color: #9ca3af;
  font-size: 0.82rem;
  font-style: italic;
}

.reg-detail-notes {
  padding: 0.6rem 0.75rem;
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  font-size: 0.85rem;
  color: #374151;
  white-space: pre-wrap;
  line-height: 1.5;
}

/* ── Lead card in the expanded row ─────────────────
   The person's CRM status plus the shared timeline (LeadActivityLog, styled by
   admin-activity-log.css). It sits OUTSIDE .reg-detail-cards - that grid is a
   two-column multicol, and a composer with a timeline under it is both tall and
   unbreakable, so a column item would either overflow or leave half the grid
   empty. Full width below the grid, above the questionnaire answers.

   Nothing here may be position: fixed - this pane lives inside
   .reg-table-scroll, a container-query container (see its comment above). */

.reg-card--lead {
  /* The card grid ends with a card carrying .reg-card's own 1rem bottom
     margin; matching it keeps the vertical rhythm across the boundary. */
  margin-bottom: 1rem;
}

/* The lead's CRM status. A plain select rather than the .reg-status-select pill:
   that pill takes its colour from a fixed set of registration values, and the
   lead statuses are DB rows with editable labels and colours. */
.reg-lead-status-select {
  padding: 0.3rem 0.6rem;
  border: 1px solid #d8dee8;
  border-radius: 8px;
  background: #fff;
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  color: #2d3340;
  cursor: pointer;
  max-width: 100%;
}

.reg-lead-status-select:disabled {
  cursor: default;
  opacity: 0.7;
}

/* Same value for a reader who may not write it. */
.reg-lead-status-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.16rem 0.55rem;
  border-radius: 999px;
  background: #eef2f7;
  color: #3a4252;
  font-size: 0.78rem;
  font-weight: 600;
}

.reg-detail-timeline {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  font-size: 0.82rem;
  color: #475569;
}

.reg-detail-timeline li {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.reg-detail-timeline li i {
  color: #0071E3;
  font-size: 0.7rem;
}

/* Answer sub-table */

.reg-answer-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  overflow: hidden;
}

.reg-answer-table td {
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid #f3f4f6;
  vertical-align: top;
}

.reg-answer-table tr:last-child td {
  border-bottom: none;
}

.reg-answer-q {
  color: #6b7280;
  width: 55%;
  font-size: 0.82rem;
}

.reg-answer-a {
  color: #111827;
  font-weight: 500;
}

/* ── Expanded row: card grid (registrations) ───────
   Masonry-style columns so variable-height cards pack tightly. Each card is
   a self-contained white panel whose header is a collapse toggle; the
   questionnaire-answers card sits full-width below the masonry columns.
   Distinct from the shared `.reg-detail` 2-col grid used by Contact
   Messages. */
/* The registrations table is ~1500px wide at desktop (13 columns) inside a
   ~1184px page, so it needs a horizontal scroller. It lives HERE rather than on
   .reg-page for two reasons: the new-registration and enrol modals are
   position:fixed and sit inside .reg-page (a container-type there would trap
   them in the page box instead of the viewport), and this element doubles as
   the container-query container that .reg-detail-wrap measures against. */
.reg-table-scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  container-type: inline-size;
}

/* An expanded row's detail card is inside a `colspan` cell, so it inherits the
   TABLE's width and used to start ~270px off the left edge of the visible area
   - the owner had to scroll the whole table sideways to read the channel
   evidence. 100cqi is the SCROLLER's visible width, not the table's, so the
   card always opens fully in view. */
.reg-detail-wrap {
  width: 100cqi;
  box-sizing: border-box;
  padding: 1.15rem 1.35rem 1.35rem;
  border-top: 1px solid #e8edf3;
  background:
    radial-gradient(120% 80% at 100% 0%, rgba(0, 113, 227, 0.04) 0%, transparent 55%),
    #f7f9fc;
}

.reg-detail-cards {
  columns: 2;
  column-gap: 1rem;
}

.reg-card {
  break-inside: avoid;
  margin-bottom: 1rem;
  background: #fff;
  border: 1px solid #e6ebf2;
  border-radius: 12px;
  box-shadow: 0 1px 2px rgba(0, 60, 130, 0.04);
  overflow: hidden;
}

.reg-card--answers {
  margin-bottom: 0;
}

.reg-card-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.7rem 1rem;
  background: none;
  border: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.reg-card-head:hover {
  background: #f7f9fc;
}

.reg-card--open > .reg-card-head {
  border-bottom: 1px solid #f0f3f7;
}

.reg-card-caret {
  flex: 0 0 auto;
  width: 0.8rem;
  font-size: 0.6rem;
  color: #98a0ac;
  text-align: center;
}

.reg-card-body {
  padding: 0.85rem 1rem 1rem;
}

.reg-card-icon {
  font-size: 0.78rem;
  color: #0071E3;
  width: 1.05rem;
  text-align: center;
}

.reg-card-title {
  font-size: 0.74rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #3a4252;
}

.reg-card-count {
  margin-left: auto;
  min-width: 1.3rem;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  background: #eef2f7;
  color: #5b6470;
  font-size: 0.72rem;
  font-weight: 700;
  text-align: center;
}

/* Badge rail in the marketing card header. The channel verdict and the partner
   code are two independent facts and get one badge each, so a real partner code
   can never be hidden behind a channel guess. The rail owns the auto-margin;
   the badges inside it must not (see the RTL mirror at the end of this file). */
.reg-source-badges {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 0.35rem;
  min-width: 0;
}

/* Lead-source badge in the marketing card header */
.reg-source-badge {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.32rem;
  padding: 0.12rem 0.5rem;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  border: 1px solid transparent;
}

.reg-source-badges .reg-source-badge {
  margin-left: 0;
}

.reg-source-badge i {
  font-size: 0.72rem;
}

/* Warning marker inside the channel badge: the verdict carries caveats. It sits
   in the badge because the badge is the one part of the marketing card that is
   readable while the card is collapsed. Colour is fixed rather than inherited so
   it stays a warning on every tier swatch. */
.reg-channel-caveat-flag {
  color: #b45309;
  opacity: 0.9;
}

/* Confidence tiers, keyed off ChannelVerdict.tier - NOT off the platform.
   "Which network" and "did we pay for it" are different questions, and the
   colour answers the second one. Swatches are lifted from the .reg-chip--*
   palette above so the page keeps one tint vocabulary. */
.reg-source-badge--paid {
  background: #f5f3ff;
  color: #6d28d9;
  border-color: #ddd6fe;
}

.reg-source-badge--likely-paid {
  background: #fffbeb;
  color: #92400e;
  border-color: #fde68a;
}

.reg-source-badge--referral {
  background: #ecfdf5;
  color: #047857;
  border-color: #a7f3d0;
}

.reg-source-badge--unpaid {
  background: #f3f4f6;
  color: #4b5563;
  border-color: #e5e7eb;
}

/* Unknown is not a claim, so it must not look like one: hollow fill and a
   dashed edge read as "undetermined" next to the solid tiers. */
.reg-source-badge--unknown {
  background: #fff;
  color: #6b7280;
  border-color: #cfd6e0;
  border-style: dashed;
  font-weight: 600;
}

.reg-source-badge--affiliate {
  background: #eafaf1;
  color: #197a4a;
  border-color: #bdeccf;
}

/* Key/value rows inside the marketing card */
.reg-meta-grid {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.reg-meta-row {
  display: grid;
  grid-template-columns: 8.5rem 1fr;
  gap: 0.5rem 0.75rem;
  align-items: start;
}

.reg-meta-key {
  margin: 0;
  font-size: 0.74rem;
  font-weight: 600;
  color: #8a93a3;
  padding-top: 0.12rem;
}

/* Sentence-length key ("attendance-confirmation status for this open day"):
   a wider key column keeps it to two lines and the chip beside it. */
.reg-meta-row.reg-meta-row--status {
  grid-template-columns: 11.5rem 1fr;
}

.reg-meta-val {
  margin: 0;
  font-size: 0.82rem;
  color: #2d3340;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.35rem;
  min-width: 0;
}

.reg-meta-val--link {
  align-items: center;
}

.reg-meta-val--ids {
  gap: 0.4rem;
}

/* ── Traffic-channel verdict rows ──────────────────
   The verdict text itself is English (it comes verbatim from the classifier);
   only the dt labels around it are Hebrew, so every value here pins dir="ltr"
   in the template. */
.reg-meta-val--channel {
  align-items: center;
  gap: 0.45rem;
}

.reg-channel-verdict {
  font-weight: 600;
  color: #2d3340;
}

.reg-channel-confidence {
  padding: 0.02rem 0.42rem;
  border-radius: 999px;
  background: #f3f5f8;
  border: 1px solid #e6eaf0;
  color: #6b7280;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.reg-meta-val--channel-details {
  gap: 0.75rem;
}

.reg-channel-detail {
  display: inline-flex;
  align-items: baseline;
  gap: 0.3rem;
  min-width: 0;
}

.reg-channel-detail-key {
  font-size: 0.72rem;
  color: #9aa2af;
}

.reg-channel-detail-val {
  font-size: 0.78rem;
  color: #2d3340;
  word-break: break-word;
}

/* Evidence / caveat lists. English sentences inside a Hebrew island, so the
   template pins dir="ltr" and the list aligns with it. */
.reg-channel-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  width: 100%;
  text-align: left;
}

.reg-channel-list li {
  /* global.css gives EVERY `li` an 8px bottom margin. Inherited here it stacks
     on top of this list's flex `gap` and spaces the bullets unevenly (and hangs
     8px of dead air off the last one). Zeroed on these two lists only - the
     global rule stays untouched for every other admin list that relies on it. */
  margin: 0;
  position: relative;
  padding-left: 0.85rem;
  font-size: 0.78rem;
  line-height: 1.45;
  color: #4b5563;
}

.reg-channel-list li::before {
  content: '';
  position: absolute;
  left: 0.2rem;
  top: 0.55em;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: #b6bdc8;
}

.reg-channel-list--caveat li {
  color: #92400e;
}

.reg-channel-list--caveat li::before {
  background: #e0a94a;
}

/* The caveat row is a warning, not another field. It sits directly under the
   verdict and gets a tinted panel so "we could not tie this to THIS
   registration" cannot be skimmed past as one more grey key/value line.
   Deliberately no directional border or physical inset: a left/right accent
   would need its own RTL mirror at the end of this file, while a symmetric
   tint reads identically in both directions. */
.reg-meta-row--caveat {
  background: #fffaf0;
  border: 1px solid #f6e2b8;
  border-radius: 8px;
  padding: 0.4rem 0.55rem;
}

/* Flex + gap rather than a margin on the icon: gap follows the writing
   direction, a physical margin would not. */
.reg-meta-key--caveat {
  display: flex;
  align-items: baseline;
  gap: 0.3rem;
  color: #92400e;
}

/* Standing note under the fbp chip: the pixel cookie is not a channel signal. */
.reg-id-note {
  flex: 1 1 100%;
  font-size: 0.72rem;
  line-height: 1.4;
  color: #9ca3af;
}

/* Email / phone links in the contact card */
.reg-contact-link {
  color: #0071E3;
  text-decoration: none;
  word-break: break-all;
}

.reg-contact-link:hover {
  text-decoration: underline;
}

/* The WhatsApp destination picker beside a phone number in an expanded
   registration row. It takes the line under the number rather than sitting on
   it: `.reg-meta-val` is a baseline-aligned wrapping flex row, and a 30px
   bordered control on a text baseline rides high against the digits. Two lone
   glyphs used to sit there, which is what a labelled group replaces. */
.reg-phone-wa-actions {
  flex: 1 1 100%;
  align-self: flex-start;
  margin-top: 0.1rem;
}

/* ── Inline field editing in the expanded pane ──────────
   First used by the car plate in the contact card, and written to be reused:
   a muted glyph beside the value opens the editor, the editor is the input
   plus a save/cancel pair. The glyph is deliberately ALWAYS visible rather
   than revealed on hover - hover affordances do not exist on the phone, and
   an editable field nobody can find is not editable.

   Logical properties throughout (margin-inline-*), so the Hebrew RTL island
   needs no mirror rule at the foot of this file. */
.reg-inline-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.18rem 0.3rem;
  border: 0;
  border-radius: 5px;
  background: none;
  color: #9aa2af;
  font-size: 0.74rem;
  line-height: 1;
  cursor: pointer;
  transition: color 0.15s, background-color 0.15s;
}

.reg-inline-btn:hover:not(:disabled) {
  color: #0071E3;
  background: rgba(0, 113, 227, 0.08);
}

.reg-inline-btn:disabled {
  opacity: 0.45;
  cursor: default;
}

.reg-inline-btn--edit {
  margin-inline-start: 0.15rem;
}

.reg-inline-btn--save {
  color: #197a4a;
}

.reg-inline-btn--save:hover:not(:disabled) {
  color: #0f5c37;
  background: rgba(25, 122, 74, 0.1);
}

.reg-inline-edit {
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  min-width: 0;
}

/* Overrides .form-input's full width and RTL default - a plate is a short LTR
   value and the field should not span the pane. */
.reg-inline-edit-input {
  width: 9.5rem;
  max-width: 100%;
  padding: 0.28rem 0.45rem;
  font-size: 0.82rem;
  direction: ltr;
  text-align: left;
}

/* No value yet. Labelled rather than blank, and rather than a bare dash: this
   row is now actionable, so it has to read as "nothing here yet, add one". */
.reg-meta-empty {
  color: #9aa2af;
  font-size: 0.8rem;
}

.reg-share-url {
  font-family: var(--font-mono, monospace);
  font-size: 0.74rem;
  color: #0071E3;
  text-decoration: none;
  word-break: break-all;
  line-height: 1.4;
}

.reg-share-url:hover {
  text-decoration: underline;
}

/* Chronological touch path (oldest -> newest) */
.reg-touch-path {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  width: 100%;
}

.reg-touch-path li {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  flex-wrap: wrap;
}

.reg-touch-meta {
  font-size: 0.76rem;
  color: #5b6470;
}

.reg-touch-ts {
  color: #9aa2af;
}

/* Opaque identifiers (fbc / fbp / visitor HMAC) */
.reg-id {
  font-family: var(--font-mono, monospace);
  font-size: 0.72rem;
  color: #5b6470;
  background: #f3f5f8;
  border: 1px solid #e6eaf0;
  border-radius: 5px;
  padding: 0.1rem 0.4rem;
  white-space: nowrap;
}

.reg-id-tag {
  color: #aeb5c0;
  font-weight: 700;
  margin-right: 0.15rem;
}

/* ── Wide modal (registration editor) ────────────── */

.modal--wide {
  width: 760px;
  max-width: 92vw;
}

/* Modal form inputs - LTR in the registrations editor since names/emails
   entered here are mostly English. Override the global RTL default. */
.reg-form-input {
  direction: ltr;
  text-align: left;
}

.form-label-hint {
  font-weight: 400;
  color: #9ca3af;
  font-size: 0.75rem;
  margin-left: 0.35rem;
}

/* ── Responsive ──────────────────────────────────── */

@media (max-width: 1024px) {
  .reg-filter-bar {
    flex-wrap: wrap;
  }
  .reg-filter-field,
  .reg-filter-field--grow {
    flex: 1 1 100%;
    min-width: 0;
  }
  .reg-detail {
    grid-template-columns: 1fr;
  }
  .reg-detail-cards {
    columns: 1;
  }
  .reg-meta-row {
    grid-template-columns: 7rem 1fr;
  }
  /* No 7rem column can hold the full approval-status sentence: stack it. */
  .reg-meta-row.reg-meta-row--status {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 767px) {
  /* A phone has no room for two columns, so the actions read as a footer
     under the state rather than as a second thing competing with it. They
     also stop shrinking - a 44px-tall row is the tap target, not a
     0.72rem chip. */
  .reg-cal {
    flex-direction: column;
    align-items: stretch;
  }
  .reg-cal-actions .btn {
    flex: 1 1 auto;
    justify-content: center;
    min-height: 40px;
  }
  .reg-bulk-bar {
    padding: 0.5rem 0.65rem;
  }
  .reg-bulk-spacer {
    margin-left: 0;
  }
  /* The presets wrap on their own; what a phone needs is a bigger tap
     target than a 0.3rem-tall pill and a number that survives the shrink. */
  .reg-preset {
    min-height: 38px;
    padding-inline: 0.75rem;
  }
}

/* ══════════════════════════════════════════════════════════
   Registrations table → stacked cards (tablet + phone).
   The 11-column desktop table can't fit below 1024px; the
   generic .admin-table fixed-layout rule crushes every column
   into an unreadable overlap. Here each data row becomes a
   self-contained card: a checkbox · name · expand header line,
   labelled meta rows (status & course first, referral last),
   and a tap-friendly action bar across the foot.
   ══════════════════════════════════════════════════════════ */
@media (max-width: 1024px) {
  /* Let the cards sit on the page background, not inside a
     clipped/overflowing table box. */
  .reg-table {
    display: block;
    overflow: visible;
    background: transparent;
    border: none;
    box-shadow: none;
  }
  .reg-table thead {
    display: none;
  }
  .reg-table tbody {
    display: block;
    width: 100%;
  }

  /* Each registration row is a card. */
  .reg-table tbody tr:not(.reg-row-detail) {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 0.6rem;
    width: 100%;
    margin-bottom: 0.85rem;
    padding: 0.85rem 1rem 0.65rem;
    background: #fff;
    border: 1px solid #e6ebf2;
    border-radius: 14px;
    box-shadow: 0 1px 3px rgba(0, 60, 130, 0.05);
  }

  /* Expanded card hands off its rounded foot to the detail panel. */
  .reg-table tbody tr.reg-row--expanded {
    margin-bottom: 0;
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
  }

  .reg-table tbody tr.reg-row--selected {
    border-color: #93c5fd;
    background: #f5f9ff;
    box-shadow: 0 0 0 1px #93c5fd;
  }
  .reg-table tbody tr.reg-row--selected td {
    background: transparent !important;
  }

  /* Once the table is a stack of cards there are no cells left to tint - the
     card IS the row - so the landing mark moves onto the `<tr>` and the cell
     rule is cancelled, exactly as the selected mark does one rule above. It
     fades to the card's own white rather than to `transparent`, which here would
     drop the card out of the page and show the panel background through it. */
  .reg-table tbody tr.reg-row--returned {
    border-color: #93c5fd;
    background-color: rgba(0, 113, 227, 0.1);
    animation: reg-row-returned-fade-card 2s ease-out 2s forwards;
  }
  .reg-table tbody tr.reg-row--returned td {
    background-color: transparent;
    animation: none;
  }

  /* Strip the inherited table-cell chrome from every cell. */
  .reg-table td {
    border: none;
    padding: 0;
    font-size: 0.86rem;
  }

  /* ── Header line: checkbox · name · expand ── */
  .reg-table td.reg-cell-check {
    order: 0;
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    padding-bottom: 0.6rem;
  }
  .reg-table td.reg-cell-name {
    order: 1;
    flex: 1 1 auto;
    min-width: 0;
    padding-bottom: 0.6rem;
    font-size: 1rem;
  }
  .reg-table td.reg-cell-name strong {
    font-weight: 700;
    color: #191a23;
  }
  .reg-table td.reg-cell-expand {
    order: 2;
    flex: 0 0 auto;
    padding-bottom: 0.6rem;
  }
  .reg-table td.reg-cell-expand .btn-icon {
    color: #9aa3b2;
  }

  /* ── Labelled meta rows ── */
  /* Default order keeps every meta row below the identity line (orders 0-2)
     even when a table doesn't spell out its own. The per-screen ladders - the
     one below, and `.leads-table td.lead-cell--*` in admin-leads.css - are
     keyed on CELL CLASSES. Nothing here keys on a data-label VALUE any more,
     and nothing new should: see the note on the ladder below for what that
     cost. */
  .reg-table td[data-label] {
    order: 5;
    flex: 1 1 100%;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.15rem 0.6rem;
    padding: 0.34rem 0;
    border-top: 1px solid #f1f4f8;
  }
  .reg-table td[data-label]::before {
    content: attr(data-label);
    flex: 0 0 5.25rem;
    color: #8a93a3;
    font-weight: 600;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    line-height: 1.7;
  }

  /* Meaningful meta order on the open-day registration card: the actionable
     fields first.
     ─────────────────────────────────────────────────────────────────────────
     Keyed on the CELL CLASS, not on the data-label value, and that is the fix
     rather than a style choice. Seven English-keyed rules stood here -
     "Status", "Course", "Email", "Phone", "Date", "Registered", "Referral" -
     against a table whose captions are Hebrew and have been since the screen
     was written. SIX of them matched nothing at all. The seventh, "Email",
     matched and set order 5, which is exactly what the `td[data-label]` default
     above already gives every meta cell - so it was a no-op too. Net effect:
     every meta row on this card rendered in raw DOM order for as long as the
     mobile card has existed, and nothing looked broken enough to report.
     "Course" was the tell: there is no Course cell in any `.reg-table` on the
     site, so that rule could never have matched anything on any day.

     The repo had already diagnosed this exact failure once, two rules further
     down (`.reg-table td.reg-cell-status .reg-status-select`, re-keyed off
     data-label because the Hebrew caption had silently stopped matching) and
     left the fix un-generalised. This generalises it. A Hebrew string literal
     would have been correct today and would have re-armed the identical trap on
     the next copy edit - and a copy edit silently killing a selector IS the bug
     being fixed here.

     SCOPED TO `.reg-page`, and that scope is not decoration either. `.reg-table`
     is worn by five tables (leads, contracts, contact messages, conversions and
     this one), and three of those carry `.reg-cell-email` cells of their own -
     two with no data-label at all, so they sit at the flex default of 0 today. A
     bare `.reg-table td.reg-cell-email { order: 6 }` would have quietly
     reordered the contracts and contact-message cards while fixing this one.
     `.reg-page` is the open-day registrations screen and nothing else. */
  .reg-page .reg-table td.reg-cell-status     { order: 3; }
  /* The PERSON's CRM status, directly under the seat's own approval status
     (issue 4, 2026-08-16). Two statuses about two different rows, and the
     answer to "why are these next to each other" is that reading them together
     is the whole reason the column came back - so on a phone card they stay
     adjacent rather than being pushed below the tap targets. Its arrival is the
     renumber the note below warned about: everything from the participation
     mode down moved one rung. */
  .reg-page .reg-table td.reg-cell-lead-status { order: 4; }
  /* The participation mode takes the slot the phantom "Course" rule held: it is
     the next actionable field on this table after the two statuses. */
  .reg-page .reg-table td.reg-cell-mode       { order: 5; }
  .reg-page .reg-table td.reg-cell-attend     { order: 6; }
  .reg-page .reg-table td.reg-cell-email      { order: 7; }
  .reg-page .reg-table td.reg-cell-phone      { order: 8; }
  .reg-page .reg-table td.reg-cell-date       { order: 9; }
  .reg-page .reg-table td.reg-cell-referral   { order: 10; }
  /* The calendar-invite state: a fact about the seat rather than a timestamp,
     so it belongs above the two time columns that close the card, and putting
     it here rather than beside נוכחות keeps the tap targets - the statuses,
     Zoom, attendance - together at the top where a thumb reaches them. The
     ladder is contiguous again; anything inserted next needs its own renumber,
     and 20 (the action bar, further down this file) is the ceiling. */
  .reg-page .reg-table td.reg-cell-calendar   { order: 11; }
  .reg-page .reg-table td.reg-cell-registered { order: 12; }
  .reg-page .reg-table td.reg-cell-activity   { order: 13; }

  .reg-table td.reg-cell-email {
    word-break: break-all;
  }

  /* The desktop cap is a TABLE fix and must not follow the sentence into the
     card, where this cell is a full-width labelled row with the room to say
     what actually happened. Three lines, the same allowance the leads card
     gives its comment (.lead-comment-text in admin-leads.css) and for the same
     reason: an ellipsis is a promise of a tooltip, and a touchscreen has no
     hover to redeem it with. `white-space` goes back to normal on the cell
     itself too, so the two-word Hebrew ::before label wraps inside its gutter
     instead of running out under the value. */
  .reg-table td.reg-cell-activity {
    max-width: none;
    white-space: normal;
  }

  /* The cell is a flex container here, so .reg-activity is a flex ITEM and
     needs the same permission to shrink that .reg-when carries above. Its
     glyph comes off the vertical centre with it: centred is right beside a
     one-line value and strands the icon against the middle line of a
     three-line one. */
  .reg-table td.reg-cell-activity .reg-activity {
    align-items: flex-start;
    min-width: 0;
  }

  /* Two classes on purpose. `.reg-when > span` carries the `nowrap` the desktop
     ellipsis depends on and outweighs a bare `.reg-activity-text`, so written
     the short way this rule would clamp nothing at all. */
  .reg-when > .reg-activity-text {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    overflow: hidden;
    text-overflow: unset;
    white-space: normal;
  }

  /* Every other card label is one word and shares its line with the value.
     The approval-status label is a sentence, so it takes the whole line and
     the status pill sits beneath it, still a full-size tap target. */
  .reg-table td.reg-cell-status::before {
    flex: 1 1 100%;
    letter-spacing: 0.04em;
    line-height: 1.5;
  }

  /* Status select: comfortable tap target under its label. Keyed on the cell
     class, not on data-label - the labels here are Hebrew, so the old
     [data-label="Status"] selector had silently stopped matching and the
     dropdown was left at its desktop size on phones. */
  .reg-table td.reg-cell-status .reg-status-select {
    flex: 0 0 auto;
    min-height: 44px;
    padding: 0.42rem 1.85rem 0.42rem 0.8rem;
    /* 16px iOS-zoom floor (see the consolidated floor at the end of the
       file, which repeats this selector so the floor survives a re-lower). */
    font-size: 16px;
  }

  /* ── Action bar across the card foot ── */
  /* order 20, not the 10 it carried before. The meta ladder above now genuinely
     runs to 12 instead of collapsing onto the default 5, so 10 stopped being
     last; 20 leaves room for anything inserted into that ladder later without
     the action bar drifting into the middle of the card. `.reg-cell-actions` is
     worn by this screen alone, so the number is safe to move in place rather
     than behind a `.reg-page` scope. */
  .reg-table td.reg-cell-actions {
    order: 20;
    flex: 1 1 100%;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 0.55rem;
    padding-top: 0.6rem;
    border-top: 1px solid #eef1f5;
  }
  .reg-table td.reg-cell-actions .reg-actions {
    flex: 1 1 100%;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.4rem;
  }
  .reg-table td.reg-cell-actions .reg-action {
    justify-content: center;
    height: 2.3rem;
    font-size: 0.8rem;
  }

  /* ── Expanded detail panel attaches beneath its card ── */
  .reg-table tbody tr.reg-row-detail {
    display: block;
    width: 100%;
    margin-bottom: 0.85rem;
  }
  .reg-table tbody tr.reg-row-detail > td {
    display: block;
    width: 100%;
    padding: 0;
    border: 1px solid #e6ebf2;
    border-top: none;
    border-radius: 0 0 14px 14px;
    overflow: hidden;
  }
}

@media (max-width: 767px) {
  .reg-table td[data-label]::before {
    flex-basis: 4.75rem;
  }
  /* Opaque id chips (fbc/fbp/visitor) must wrap inside narrow cards. */
  .reg-id {
    white-space: normal;
    word-break: break-all;
  }
  .reg-table td.reg-cell-actions .reg-actions {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .reg-table td.reg-cell-actions .reg-action {
    height: 2.5rem;
  }
}

/* ── Open Day Statistics ──────────────────────────── */

.stats-summary {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 0.75rem;
  margin-bottom: 1.25rem;
}

.stats-card {
  background: #fff;
  border: 1px solid #e8e8e8;
  border-radius: 8px;
  padding: 0.85rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.stats-card-value {
  font-size: 1.65rem;
  font-weight: 600;
  line-height: 1.1;
  color: #1a1a2e;
  font-variant-numeric: tabular-nums;
}

.stats-card-value--approved { color: #047857; }
.stats-card-value--pending  { color: #4b5563; }
.stats-card-value--no_answer { color: #6d28d9; }
.stats-card-value--rejected { color: #b91c1c; }

.stats-card-label {
  font-size: 0.78rem;
  color: #6b7280;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.stats-bydate-card {
  background: #fff;
  border: 1px solid #e8e8e8;
  border-radius: 8px;
  padding: 1rem 1.1rem;
  margin-bottom: 1.25rem;
}

.stats-bydate-title {
  font-size: 0.95rem;
  font-weight: 500;
  color: #1a1a2e;
  line-height: 1.4;
  padding-bottom: 0.65rem;
  margin-bottom: 0.5rem;
  border-bottom: 1px solid #f0f0f0;
}

.stats-bydate-subtle {
  font-weight: 400;
  font-size: 0.78rem;
  color: #6b7280;
  margin-left: 0.5rem;
}

.stats-bydate-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
}

.stats-bydate-table th,
.stats-bydate-table td {
  text-align: left;
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid #f0f0f0;
}

.stats-bydate-table th {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #6b7280;
  font-weight: 500;
}

.stats-bydate-table td {
  color: #1a1a2e;
}

.stats-bydate-table .num {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.stats-bydate-table tbody tr:last-child td {
  border-bottom: none;
}

.stats-bydate-table tfoot td {
  font-weight: 600;
  border-top: 2px solid #e8e8e8;
  border-bottom: none;
}

.stats-questions {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.stats-chart-card {
  background: #fff;
  border: 1px solid #e8e8e8;
  border-radius: 8px;
  padding: 1rem 1.1rem;
}

.stats-chart-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  padding-bottom: 0.65rem;
  margin-bottom: 0.75rem;
  border-bottom: 1px solid #f0f0f0;
}

.stats-chart-title {
  font-size: 0.95rem;
  font-weight: 500;
  color: #1a1a2e;
  line-height: 1.4;
  direction: rtl;
  text-align: right;
  flex: 1;
}

.stats-chart-meta {
  font-size: 0.78rem;
  color: #6b7280;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.25rem;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.stats-chart-badge {
  background: #f3f4f6;
  color: #4b5563;
  padding: 0.1rem 0.5rem;
  border-radius: 10px;
  font-size: 0.7rem;
  letter-spacing: 0.02em;
}

.stats-chart-empty {
  color: #aaa;
  font-size: 0.85rem;
  padding: 0.4rem 0;
}

.stats-bars {
  display: flex;
  align-items: flex-end;
  gap: 0.85rem;
  padding: 0.25rem 0.1rem 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

.stats-bar-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  min-width: 84px;
  flex: 1 1 84px;
  max-width: 150px;
}

.stats-bar-count {
  font-size: 0.85rem;
  font-weight: 500;
  color: #1a1a2e;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.stats-bar-track {
  width: 100%;
  max-width: 68px;
  height: 160px;
  background: #f3f4f6;
  border-radius: 4px;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
}

.stats-bar-fill {
  width: 100%;
  background: var(--primary-color);
  border-radius: 4px 4px 0 0;
  transition: height 0.25s ease;
  min-height: 2px;
}

.stats-bar-label {
  font-size: 0.75rem;
  color: #4b5563;
  direction: rtl;
  text-align: center;
  line-height: 1.3;
  overflow: hidden;
  word-break: break-word;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}

@media (max-width: 1024px) {
  .stats-summary {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .stats-bar-col {
    min-width: 72px;
    flex-basis: 72px;
  }
  .stats-bar-track {
    height: 140px;
    max-width: 56px;
  }
}

@media (max-width: 767px) {
  .stats-summary {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .stats-card-value {
    font-size: 1.35rem;
  }
  .stats-chart-header {
    flex-direction: column;
    gap: 0.4rem;
  }
  .stats-chart-meta {
    flex-direction: row;
    align-items: center;
    flex-wrap: wrap;
    align-self: flex-start;
  }
  .stats-bars {
    gap: 0.5rem;
  }
  .stats-bar-col {
    min-width: 64px;
    flex-basis: 64px;
  }
  .stats-bar-track {
    height: 120px;
    max-width: 48px;
  }
  .stats-bar-label {
    font-size: 0.7rem;
  }
}


/* ── Affiliate codes / groups / stats ──────────────── */

.affiliate-tabs {
  margin: 0 0 1.25rem;
}

.affiliate-share {
  font-family: var(--font-mono);
}

.affiliate-codes-toolbar {
  margin-bottom: 1rem;
  display: flex;
  align-items: center;
  gap: 1rem;
}

.toggle-inline {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  color: #555;
  cursor: pointer;
}

.affiliate-code-cell {
  font-weight: 600;
  letter-spacing: 0.04em;
}

.affiliate-share-url {
  font-size: 0.78rem;
  color: #6b7280;
  margin-inline-end: 0.4rem;
  word-break: break-all;
}

.affiliate-stats-table {
  margin-top: 0.25rem;
  font-variant-numeric: tabular-nums;
}

.affiliate-stats-table th,
.affiliate-stats-table td {
  font-size: 0.82rem;
}

.affiliate-stats-row {
  cursor: pointer;
}

.affiliate-stats-row:hover td {
  background: #f6f8fb;
}

.affiliate-journey {
  padding: 0.4rem 0.2rem;
}

.affiliate-journey-path {
  margin: 0.4rem 0 0.6rem;
  padding-inline-start: 1.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.affiliate-journey-path li {
  font-size: 0.85rem;
}

.affiliate-journey-step {
  font-family: var(--font-mono);
  font-size: 0.82rem;
}

.affiliate-journey-last {
  font-size: 0.85rem;
  color: #374151;
  padding-top: 0.4rem;
  border-top: 1px solid #f0f0f0;
}

/* Column widths (desktop) - replaces removed inline th style attrs so the
   table stays Chrome-Workspaces compatible (no inline styles in templates). */
.affiliate-col-code { width: 110px; }
.affiliate-col-toggle { width: 70px; }
.affiliate-col-count { width: 90px; }
.affiliate-col-date { width: 100px; }
.affiliate-col-actions { width: 110px; }

/* ── Codes tab: multi-select group filter dropdown ──────────────────── */
.affiliate-group-filter {
  position: relative;
}

.affiliate-group-filter-trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}

.affiliate-group-filter-trigger.is-filtered {
  border-color: var(--primary-color);
  color: var(--primary-color);
}

.affiliate-group-filter-caret {
  font-size: 0.7rem;
  opacity: 0.6;
}

.affiliate-group-filter-menu {
  position: absolute;
  z-index: 30;
  top: calc(100% + 0.4rem);
  left: 0;
  min-width: 230px;
  max-height: 320px;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0.35rem;
  background: #fff;
  border: 1px solid #e2e2e8;
  border-radius: 10px;
  box-shadow: 0 8px 26px rgba(20, 22, 40, 0.14);
}

.affiliate-group-filter-clear {
  display: block;
  width: 100%;
  padding: 0.45rem 0.6rem;
  margin-bottom: 0.25rem;
  border: none;
  border-bottom: 1px solid #f0f0f3;
  border-radius: 6px;
  background: none;
  font: inherit;
  font-size: 0.85rem;
  color: var(--primary-color);
  text-align: left;
  cursor: pointer;
}

.affiliate-group-filter-clear:hover {
  background: #f6f8fb;
}

.affiliate-group-filter-option {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.6rem;
  border-radius: 6px;
  font-size: 0.85rem;
  cursor: pointer;
}

.affiliate-group-filter-option:hover {
  background: #f6f8fb;
}

.affiliate-group-filter-option input {
  cursor: pointer;
}

.affiliate-group-filter-name {
  flex: 1;
}

.affiliate-group-filter-reset {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  cursor: pointer;
}

/* ── Codes tab: visual grouping by code group ───────────────────────── */
.affiliate-group-section-row th {
  padding: 0.6rem 0.85rem;
  background: #f4f6fa;
  border-top: 2px solid #e2e6ee;
  text-align: left;
}

.affiliate-group-block:first-of-type .affiliate-group-section-row th {
  border-top: none;
}

.affiliate-group-section-name {
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #4b4d63;
  margin-inline-end: 0.5rem;
}

.affiliate-group-section-count {
  margin-inline-start: 0.15rem;
}

/* ── Groups tab: clickable rows jump to filtered codes ──────────────── */
.affiliate-group-row {
  cursor: pointer;
}

.affiliate-group-row:hover td {
  background: #f6f8fb;
}

.empty-state-hint {
  display: block;
  margin-top: 0.35rem;
  font-size: 0.82rem;
  color: #8e8f9e;
}

/* ═══════════════════════════════════════════════════════════════════════
   Affiliate codes: combined groups + codes accordion (AdminAffiliateCodes.vue)
   Groups render as expandable panels; each carries inline group stats in its
   header and a per-code stats table in its body.
   ═══════════════════════════════════════════════════════════════════════ */

.aff-summary {
  margin-bottom: 1rem;
}

/* ── Recent links ─────────────────────────────────────────────────────
   Sits above the toolbar and the accordion so a link can be re-copied
   without touching a filter. Deliberately quieter than the group panels
   below it (tinted card, no shadow): it is a shortcut, not a report. */
.aff-recent {
  margin-bottom: 1.25rem;
  padding: 0.85rem 1rem 0.5rem;
  border: 1px solid #e3e6ec;
  border-radius: 10px;
  background: #f8fafc;
}

.aff-recent-head {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
  color: #494950;
}

.aff-recent-head .fa-clock-rotate-left {
  font-size: 0.82rem;
  color: #8e8f9e;
}

.aff-recent-title {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 600;
  color: #191a23;
}

.aff-recent-hint {
  font-size: 0.78rem;
  color: #8e8f9e;
}

/* The table is transparent so the tinted card shows through; the shared
   .admin-table paints its own white background. */
.aff-recent-table,
.aff-recent-table tbody tr {
  background: transparent;
}

.aff-recent-table th {
  white-space: nowrap;
}

.aff-recent-when-col {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: #494950;
}

.aff-recent-never {
  color: #8e8f9e;
  font-style: italic;
}

/* Two columns the phone card layout does not need - the group is visible
   from the accordion the reader came from, and the timestamp is the sort
   order rather than something anyone reads on a 360px screen. */
@media (max-width: 767px) {
  .aff-recent {
    padding: 0.75rem 0.75rem 0.4rem;
  }

  .aff-recent-group-col,
  .aff-recent-when-col {
    display: none;
  }
}

.aff-toolbar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.aff-toolbar-spacer {
  flex: 1 1 auto;
}

/* ── Search field ─────────────────────────────────────────────────────── */
.aff-search {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.aff-search .fa-magnifying-glass {
  position: absolute;
  left: 0.7rem;
  font-size: 0.8rem;
  color: #9aa1ad;
  pointer-events: none;
}

.aff-search-input {
  padding: 0.45rem 0.7rem 0.45rem 2rem;
  border: 1px solid #d9dce3;
  border-radius: 8px;
  background: #fff;
  font: inherit;
  font-size: 0.85rem;
  min-width: 240px;
}

.aff-search-input:focus {
  outline: none;
  border-color: var(--primary-color);
  box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.1);
}

/* ── Panels ───────────────────────────────────────────────────────────── */
.aff-panels {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.aff-panel {
  background: #fff;
  border: 1px solid #e6e8ee;
  border-radius: 10px;
  overflow: hidden;
}

.aff-panel.is-open {
  box-shadow: 0 1px 3px rgba(20, 30, 80, 0.06);
}

.aff-panel-head {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding-inline-end: 0.75rem;
}

.aff-panel-toggle {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 0.9rem;
  border: none;
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.aff-panel-toggle:hover {
  background: #f8fafc;
}

.aff-panel-chevron {
  flex: 0 0 auto;
  width: 0.85rem;
  font-size: 0.8rem;
  color: #9aa1ad;
}

.aff-panel-name {
  font-size: 0.92rem;
  font-weight: 600;
  color: #1a1a2e;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.aff-panel-count {
  flex: 0 0 auto;
}

/* Inline group stats, pushed to the trailing edge of the header. */
.aff-panel-headstats {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  margin-inline-start: auto;
  padding-inline-start: 1rem;
  font-variant-numeric: tabular-nums;
}

.aff-hstat {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  line-height: 1.15;
}

.aff-hstat-num {
  font-size: 0.9rem;
  font-weight: 600;
  color: #1a1a2e;
}

.aff-hstat-num--rev {
  color: #047857;
}

.aff-hstat-lbl {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #9aa1ad;
}

.aff-panel-actions {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.1rem;
}

/* ── Panel body (per-code table) ──────────────────────────────────────── */
.aff-panel-body {
  border-top: 1px solid #eef0f4;
  padding: 0.35rem 0.6rem 0.55rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}

.aff-panel-empty {
  padding: 0.85rem 0.4rem;
  color: #9aa1ad;
  font-size: 0.85rem;
}

.aff-codes-table th.num,
.aff-codes-table td.num {
  text-align: right;
  white-space: nowrap;
}

/* The verbose two-word column titles ("WhatsApp messages", "Quick form
   leads") may wrap onto a second line instead of forcing every numeric
   column wide enough to hold them on one; the values beneath stay nowrap.
   The min-width keeps the wrap at two lines - without it a squeezed column
   breaks a three-word title onto three. */
.aff-codes-table th.num {
  white-space: normal;
  vertical-align: bottom;
  line-height: 1.25;
  min-width: 6.2rem;
}

.aff-rev-cell {
  color: #047857;
  font-weight: 500;
}

/* Code column: chip + "copy code" button on one line, mirroring the share-URL
   cell so both copy affordances read the same. */
.aff-codes-table .affiliate-code-col {
  white-space: nowrap;
}

.aff-codes-table .affiliate-code-col .affiliate-code-cell {
  margin-inline-end: 0.3rem;
}

/* Share URL column: keep the link + copy button on one line in the scrolling
   per-code table (the card-mode wrapping rules only apply to the old layout). */
.aff-codes-table .affiliate-share-url-cell {
  white-space: nowrap;
}

.aff-codes-table .affiliate-share-url {
  word-break: normal;
}

/* Per-group "New Code" button sits beside the rename/delete icons. */
.aff-panel-newcode {
  white-space: nowrap;
  margin-inline-end: 0.15rem;
}

/* Tablet/phone: let the code table scroll instead of crushing its columns. */
@media (max-width: 1024px) {
  .aff-codes-table {
    min-width: 920px;
  }

  .aff-panel-headstats {
    gap: 0.9rem;
  }
}

/* Phone: the inline group header crushed the name to a single ellipsised
   letter and reduced the stats to bare, unlabelled numbers. Restack it into
   a taller, self-explanatory card - name on its own line, a full labelled
   stat strip beneath it, and the actions on a foot row - so every key metric
   reads clearly WITHOUT expanding the panel. */
@media (max-width: 767px) {
  .aff-search,
  .aff-search-input {
    width: 100%;
  }

  .aff-panel-head {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding-inline-end: 0;
  }

  .aff-panel-toggle {
    flex-wrap: wrap;
    align-items: center;
    row-gap: 0.75rem;
    padding: 0.85rem 0.95rem;
  }

  .aff-panel-name {
    flex: 1 1 auto;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    font-size: 1rem;
  }

  /* Stat strip: full width under the name, every metric labelled and shown
     (the secondary visits/conv counters are restored on the card). */
  .aff-panel-headstats {
    flex: 1 1 100%;
    margin-inline-start: 0;
    margin-top: 0.2rem;
    padding-inline-start: 0;
    padding-top: 0.75rem;
    border-top: 1px solid #eef0f4;
    display: flex;
    flex-wrap: wrap;
    gap: 0.7rem 1.3rem;
  }

  .aff-hstat--sec {
    display: flex;
  }

  .aff-hstat {
    align-items: flex-start;
    min-width: 3.1rem;
  }

  .aff-hstat-lbl {
    display: block;
  }

  .aff-hstat-num {
    font-size: 1rem;
  }

  /* Actions become a foot row of the header card. */
  .aff-panel-actions {
    justify-content: flex-start;
    gap: 0.4rem;
    padding: 0.5rem 0.7rem 0.6rem;
    border-top: 1px solid #eef0f4;
  }

  .aff-panel-newcode {
    flex: 1 1 auto;
    justify-content: center;
  }
}

/* ── Affiliate stats: real horizontal scroll on tablet/phone ──────────
   Overrides the generic .admin-table cramming rule (table-layout:fixed)
   so the 12-column stats grid scrolls instead of overlapping. */
@media (max-width: 1024px) {
  .stats-chart-card {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
  }

  .affiliate-stats-table {
    display: table;
    overflow: visible;
    min-width: 680px;
  }

  .affiliate-stats-table thead { display: table-header-group; }
  .affiliate-stats-table tbody { display: table-row-group; }

  .affiliate-stats-table tr {
    display: table-row;
    width: auto;
    table-layout: auto;
  }

  .affiliate-stats-table th,
  .affiliate-stats-table td {
    white-space: nowrap;
  }
}

/* ── Per-code stats → stacked cards on phone ──────────────────────────
   The 11-column per-code table is unreadable crammed into ~390px and
   forced an awkward horizontal scroll inside the panel. Collapse each
   code into a card: identity + active toggle on top, a labelled 3-up stat
   grid, then the share link and row actions. Every metric stays visible
   and readable without sideways scrolling. */
@media (max-width: 767px) {
  .aff-panel-body {
    overflow-x: visible;
    padding: 0.55rem 0.6rem 0.65rem;
  }

  .aff-codes-table,
  .aff-codes-table tbody {
    display: block;
    min-width: 0;
  }

  .aff-codes-table thead {
    display: none;
  }

  .aff-codes-table tr {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.1rem 0.8rem;
    background: #fff;
    border: 1px solid #e8eaf0;
    border-radius: 10px;
    box-shadow: 0 1px 2px rgba(0, 60, 130, 0.05);
    padding: 0.8rem 0.85rem 0.6rem;
    margin-bottom: 0.6rem;
  }

  .aff-codes-table tr.is-archived-row {
    opacity: 0.72;
    background: #fafbfc;
  }

  .aff-codes-table td {
    display: block;
    padding: 0;
    border: none;
    white-space: normal;
    text-align: left;
    font-size: 0.9rem;
  }

  /* Identity: code chip prominent, active toggle to the trailing edge. */
  .aff-codes-table td[data-label="Code"] {
    order: 1;
    flex: 0 1 auto;
    display: flex;
    align-items: center;
    gap: 0.15rem;
  }
  .aff-codes-table td[data-label="Code"]::before { display: none; }
  .aff-codes-table td[data-label="Code"] .affiliate-code-cell {
    font-size: 1rem;
  }

  .aff-codes-table td[data-label="Active"] {
    order: 2;
    flex: 0 0 auto;
    margin-inline-start: auto;
  }
  .aff-codes-table td[data-label="Active"]::before { display: none; }

  .aff-codes-table td[data-label="Label"] {
    order: 3;
    flex: 1 1 100%;
    color: #5a5b6a;
    padding-bottom: 0.55rem;
    border-bottom: 1px solid #f1f3f7;
  }
  .aff-codes-table td[data-label="Label"]::before { display: none; }

  /* Labelled 3-up stat grid (value over caption). */
  .aff-codes-table td.num {
    order: 4;
    flex: 1 1 26%;
    display: flex;
    flex-direction: column-reverse;
    align-items: flex-start;
    gap: 0.05rem;
    padding: 0.5rem 0 0.2rem;
    text-align: left;
    white-space: nowrap;
    font-size: 1.02rem;
    font-weight: 600;
    color: #1a1a2e;
  }
  .aff-codes-table td.num::before {
    content: attr(data-label);
    font-size: 0.6rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #8e8f9e;
  }
  .aff-codes-table td.aff-rev-cell {
    color: #047857;
  }

  /* Share URL: full-width row beneath the stats. */
  .aff-codes-table td.affiliate-share-url-cell {
    order: 5;
    flex: 1 1 100%;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 0.55rem;
    padding-top: 0.55rem;
    border-top: 1px solid #f1f3f7;
    white-space: normal;
  }
  .aff-codes-table td.affiliate-share-url-cell::before {
    content: "Share";
    flex: 0 0 auto;
    font-size: 0.6rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #8e8f9e;
  }
  .aff-codes-table td.affiliate-share-url-cell .affiliate-share-url {
    flex: 1 1 auto;
    min-width: 0;
    word-break: break-all;
    font-size: 0.78rem;
  }

  /* Row actions foot. */
  .aff-codes-table td[data-label="Actions"] {
    order: 6;
    flex: 1 1 100%;
    display: flex;
    gap: 0.4rem;
    margin-top: 0.5rem;
    padding-top: 0.55rem;
    border-top: 1px solid #f1f3f7;
  }
  .aff-codes-table td[data-label="Actions"]::before { display: none; }
  .aff-codes-table td[data-label="Actions"] .btn-icon {
    flex: 1 1 0;
    height: 2.3rem;
    border: 1px solid #e6ebf2;
    border-radius: 9px;
  }
}


/* ── Stats: top-level tabs + referrals breakdown ─────── */

.stats-tabs {
  margin: 0 0 1.25rem;
}

/* Breakdown-dimension switch: a labelled segmented control sitting on its
   own row above the table card. */
.stats-dim-bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 0 0 1rem;
  flex-wrap: wrap;
}

.stats-dim-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #6b7280;
  font-weight: 500;
}

/* Sortable headers: subtle affordance, never noisy. */
.stats-breakdown-table th.stats-sortable {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  transition: color 0.15s;
}

.stats-breakdown-table th.stats-sortable:hover {
  color: #1a1a2e;
}

.stats-breakdown-table th[aria-sort='ascending'],
.stats-breakdown-table th[aria-sort='descending'] {
  color: #1a1a2e;
}

.stats-sort-caret {
  font-size: 0.7rem;
  margin-inline-start: 0.25rem;
  color: var(--primary-color);
}

/* Right-align every numeric column for a clean money-table read. */
.stats-breakdown-table th.num,
.stats-breakdown-table td.num {
  text-align: right;
  white-space: nowrap;
}

.stats-breakdown-table tfoot td {
  font-weight: 600;
  color: #1a1a2e;
  border-top: 2px solid #e8e8e8;
  background: #fafafa;
}

/* Revenue cell: figure with a thin share-of-total bar beneath it. */
.stats-revenue {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.3rem;
}

.stats-revenue-val {
  font-variant-numeric: tabular-nums;
}

.stats-share-track {
  width: 64px;
  height: 4px;
  background: #eef0f3;
  border-radius: 999px;
  overflow: hidden;
}

.stats-share-fill {
  height: 100%;
  background: var(--primary-color);
  border-radius: 999px;
  min-width: 1px;
  transition: width 0.25s ease;
}

.affiliate-col-chevron {
  width: 28px;
}

/* The breakdown grid carries up to 13 columns - give it real horizontal
   scroll on tablet/phone instead of crushing cells. Declared after the
   generic .affiliate-stats-table rule so this wider min-width wins. */
@media (max-width: 1024px) {
  .stats-breakdown-table {
    min-width: 920px;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   Course Cohorts (AdminCourseCohorts.vue)
   ═══════════════════════════════════════════════════════════════════════ */

.cohort-filter-field {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 180px;
}

.cohort-filter-field .form-select {
  direction: ltr;
  text-align: left;
  padding: 0.45rem 0.7rem;
  font-size: 0.88rem;
}

/* Column widths for the cohort tables. */
.cohort-col-num { width: 90px; text-align: center; }
.cohort-col-status { width: 110px; }
.cohort-col-actions { width: 168px; }
.cohort-col-attendance { width: 220px; }

td.cohort-col-num { text-align: center; }

/* Master list rows are clickable. */
.cohort-row { cursor: pointer; }

/* Sits beside the cohort name, so "we stopped selling this" is visible while
   scanning the list rather than only inside the edit modal. */
.cohort-closed-badge { margin-inline-start: 0.5rem; }

.cohort-row--selected td {
  background: var(--primary-light, rgba(0, 113, 227, 0.06));
}

.cohort-row--dropped td {
  opacity: 0.55;
}

.cohort-actions-cell {
  white-space: nowrap;
}

/* ── Detail panel ─────────────────────────────────────── */

.cohort-detail {
  margin-top: 1.25rem;
  background: #fff;
  border: 1px solid #e8e8e8;
  border-radius: 12px;
  padding: 1.25rem 1.4rem 1.5rem;
}

.cohort-detail-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1rem;
}

.cohort-detail-title {
  margin: 0 0 0.4rem;
  font-size: 1.25rem;
  font-weight: 600;
  color: #191a23;
}

.cohort-detail-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1rem;
  font-size: 0.85rem;
  color: #6b6c7b;
}

.cohort-detail-meta i {
  margin-right: 0.25rem;
  color: #9a9bab;
}

.cohort-detail-head-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
}

/* ── Tabs ─────────────────────────────────────────────── */

.cohort-tabs {
  display: flex;
  gap: 0.25rem;
  border-bottom: 1px solid #e8e8e8;
  margin-bottom: 1rem;
}

.cohort-tab {
  appearance: none;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 0.55rem 0.9rem;
  font-size: 0.9rem;
  font-weight: 500;
  color: #6b6c7b;
  cursor: pointer;
  margin-bottom: -1px;
}

.cohort-tab:hover {
  color: #191a23;
}

.cohort-tab--active {
  color: var(--primary-color, #0071e3);
  border-bottom-color: var(--primary-color, #0071e3);
}

.cohort-tab-body {
  min-height: 60px;
}

.cohort-tab-toolbar {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.85rem;
}

.cohort-tab-hint {
  font-size: 0.82rem;
  color: #8e8f9e;
}

.cohort-tab-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* RouterLinks dressed as buttons (e.g. "All enrollments"). */
.cohort-tab-actions a.btn {
  text-decoration: none;
}

/* Cancelled enrollments stay in the table for the record but read as history. */
.cohort-row--cancelled td {
  opacity: 0.55;
}

/* ── Effective (inherited/override) cells ─────────────── */

.cohort-effective-cell {
  max-width: 320px;
}

.cohort-link {
  color: var(--primary-color, #0071e3);
  text-decoration: none;
  word-break: break-all;
}

.cohort-link:hover {
  text-decoration: underline;
}

.cohort-source-hint {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0.05rem 0.4rem;
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border-radius: 999px;
  background: #f3f4f6;
  color: #6b7280;
  vertical-align: middle;
}

.cohort-source-hint--override {
  background: #ecfdf5;
  color: #047857;
}

/* Marks a roster row the student reaches through a bundle enrollment on a
   sibling cohort, rather than a direct enrollment on this one. */
.cohort-badge {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0.05rem 0.4rem;
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border-radius: 999px;
  vertical-align: middle;
}

.cohort-badge--bundle {
  background: #eef2ff;
  color: #4338ca;
}

/* ── Attendance toggle ────────────────────────────────── */

.cohort-attendance-toggle {
  display: inline-flex;
  border: 1px solid #e2e3ea;
  border-radius: 8px;
  overflow: hidden;
}

.cohort-att-btn {
  appearance: none;
  background: #fff;
  border: none;
  padding: 0.4rem 0.9rem;
  font-size: 0.82rem;
  font-weight: 500;
  color: #6b6c7b;
  cursor: pointer;
}

.cohort-att-btn + .cohort-att-btn {
  border-left: 1px solid #e2e3ea;
}

.cohort-att-btn:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

.cohort-att-btn--present.cohort-att-btn--on {
  background: #ecfdf5;
  color: #047857;
}

.cohort-att-btn--absent.cohort-att-btn--on {
  background: #fef2f2;
  color: #b91c1c;
}

/* ── Enroll modal: segment toggle + student picker ────── */

.cohort-segment {
  display: inline-flex;
  border: 1px solid #e2e3ea;
  border-radius: 8px;
  overflow: hidden;
  margin-bottom: 1rem;
}

.cohort-segment-btn {
  appearance: none;
  background: #fff;
  border: none;
  padding: 0.45rem 1.1rem;
  font-size: 0.85rem;
  font-weight: 500;
  color: #6b6c7b;
  cursor: pointer;
}

.cohort-segment-btn + .cohort-segment-btn {
  border-left: 1px solid #e2e3ea;
}

.cohort-segment-btn--active {
  background: var(--primary-color, #0071e3);
  color: #fff;
}

.cohort-student-list {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  max-height: 240px;
  overflow-y: auto;
  overscroll-behavior: contain;
  margin-bottom: 1rem;
  border: 1px solid #eef0f3;
  border-radius: 8px;
  padding: 0.4rem;
}

.cohort-student-item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1rem;
  text-align: left;
  appearance: none;
  background: #fff;
  border: 1px solid transparent;
  border-radius: 6px;
  padding: 0.5rem 0.7rem;
  cursor: pointer;
}

.cohort-student-item:hover {
  background: #f7f8fa;
}

.cohort-student-item--active {
  border-color: var(--primary-color, #0071e3);
  background: var(--primary-light, rgba(0, 113, 227, 0.06));
}

.cohort-student-name {
  font-weight: 600;
  color: #191a23;
  font-size: 0.9rem;
}

.cohort-student-sub {
  font-size: 0.78rem;
  color: #8e8f9e;
}

.cohort-enroll-who {
  margin-bottom: 1rem;
  padding: 0.6rem 0.8rem;
  background: #f7f8fa;
  border-radius: 8px;
  font-size: 0.88rem;
  color: #4b4c5a;
}

/* Registrations: enroll-in-cohort */
.reg-enroll-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.reg-enroll-item {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 0.85rem;
}
.reg-enroll-cohort {
  font-weight: 600;
}
.reg-enroll-status {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid transparent;
}
.reg-enroll-status--active {
  color: #15803d;
  background: rgba(22, 163, 74, 0.1);
  border-color: rgba(22, 163, 74, 0.3);
}
.reg-enroll-status--cancelled {
  color: #6b7280;
  background: rgba(107, 114, 128, 0.1);
  border-color: rgba(107, 114, 128, 0.3);
}
.reg-enroll-paid {
  color: var(--text-secondary, #5a5b6a);
}
/* The whole line opens that sale's own page, so the anchor - not the <li> -
   carries the row layout: the target is the line, not the three words in it.
   The border is transparent at rest so nothing moves when it appears on hover. */
.reg-enroll-link {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 4px 8px;
  border: 1px solid transparent;
  border-radius: 8px;
  color: inherit;
  text-decoration: none;
}
.reg-enroll-link:hover {
  border-color: rgba(0, 113, 227, 0.3);
  background: rgba(0, 113, 227, 0.06);
}
.reg-enroll-link:focus-visible {
  outline: 2px solid var(--primary-color, #0071e3);
  outline-offset: 1px;
}
.reg-enroll-go {
  margin-inline-start: auto;
  font-size: 0.7rem;
  color: var(--text-tertiary, #8e8f9e);
}
.reg-enroll-link:hover .reg-enroll-go {
  color: var(--primary-color, #0071e3);
}
.reg-enroll-subject {
  margin: 0 0 16px;
  font-size: 0.95rem;
  font-weight: 600;
}

/* ── Lead's full open-day history (in the registration detail card) ──── */
.reg-lead-regs {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px dashed var(--admin-border, #e0e0e6);
}
.reg-lead-regs-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.reg-lead-regs-title {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-secondary, #5a5b6a);
}
.reg-lead-regs-title i {
  margin-inline-end: 4px;
  opacity: 0.7;
}
.reg-lead-regs-link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--primary-color, #0071e3);
  text-decoration: none;
  padding: 3px 9px;
  border: 1px solid rgba(0, 113, 227, 0.3);
  border-radius: 999px;
  background: rgba(0, 113, 227, 0.06);
  white-space: nowrap;
}
.reg-lead-regs-link:hover {
  background: rgba(0, 113, 227, 0.12);
  border-color: rgba(0, 113, 227, 0.5);
}
.reg-lead-regs-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.reg-lead-reg-item {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-size: 0.85rem;
  padding: 6px 10px;
  border: 1px solid var(--admin-border, #e0e0e6);
  border-radius: 8px;
  background: #fafafb;
}
.reg-lead-reg-item--current {
  background: rgba(0, 113, 227, 0.06);
  border-color: rgba(0, 113, 227, 0.35);
}
.reg-lead-reg-date {
  font-weight: 600;
  flex: 1 1 auto;
}
.reg-lead-reg-attend {
  color: var(--text-secondary, #5a5b6a);
}
.reg-lead-reg-current {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 1px 8px;
  border-radius: 999px;
  color: var(--primary-color, #0071e3);
  background: rgba(0, 113, 227, 0.12);
}

/* ── Hebrew RTL islands: open-day + leads management pages ─────────────
   Same scoped-island pattern as .ct-page (admin-contracts.css): these pages'
   UI copy is Hebrew, so each page root re-establishes RTL inside the
   LTR-pinned .admin-main. LTR-sensitive values (phones, emails, dates,
   links, IDs) keep dir="ltr" attributes in the templates. */
.odd-page,
.reg-page,
.ods-page,
.leads-page,
.ld-page,
.wai-page {
  direction: rtl;
  text-align: right;
}

/* The RTL islands still scrolling at the PAGE ROOT. RTL content overflows
   LEFTWARD, an unscrollable direction for the LTR .admin-main scroller (wide
   tables would be clipped with no scrollbar); an RTL scroll container extends
   its scroll region leftward instead.

   This group is SHRINKING, not growing (mobile revamp): a page root that
   scrolls sideways drags its heading and filters along with the table, so
   wide tables move into their own `.adm-table-scroll` wrapper (top of this
   file) and the page root stops scrolling. .odd-page, .reg-page, .ods-page
   and .ct-page have made that move; .leads-page and .ld-page are the two
   still to migrate. Do not add a page here - wrap its table instead.

   .wai-page is DELIBERATELY ABSENT from this group and must stay absent. The
   WhatsApp inbox is not a wide table inside a scrolling page: it is
   `overflow: hidden` at its root with each of its three panes scrolling on its
   own. Adding it here gives it a phantom horizontal scrollbar and breaks the
   grid height chain that makes the panes full-height. It belongs to the
   direction group above ONLY. */
.leads-page,
.ld-page {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}

/* Mirror the search box adornments (admin.css positions them physically). */
.reg-page .admin-search .fa-search,
.leads-page .admin-search .fa-search {
  left: auto;
  right: 10px;
}
.reg-page .search-clear,
.leads-page .search-clear {
  right: auto;
  left: 6px;
}
/* .enr-page mirrors the two glyphs in admin-enrollments.css but not this
   padding, and the filter field's shorthand `padding` (see .reg-filter-field
   .form-select/.form-input) wipes the base offsets - so without this the
   enrollments search text runs under the flipped icon. */
.reg-page .admin-search input,
.leads-page .admin-search input,
.enr-page .admin-search input {
  padding-left: 28px;
  padding-right: 30px;
}

/* Cell/table alignment follows the RTL flow. */
.reg-page .reg-table th,
.reg-page .reg-table td,
.leads-page .reg-table th,
.leads-page .reg-table td {
  text-align: right;
}
.reg-page .reg-card-head {
  text-align: right;
}
.reg-page .reg-stats-matrix th.reg-stats-matrix-date,
.reg-page .reg-stats-matrix td.reg-stats-matrix-date {
  text-align: right;
}
.ods-page .stats-bydate-table th,
.ods-page .stats-bydate-table td {
  text-align: right;
}

/* Auto-margins and physical offsets flip sides. */
.reg-page .reg-bulk-spacer,
.leads-page .reg-bulk-spacer {
  margin-left: 0;
  margin-right: auto;
}
.reg-page .reg-card-count {
  margin-left: 0;
  margin-right: auto;
}
/* The badge RAIL carries the auto-margin now, not the badges inside it. Mirror
   the rail only - mirroring each badge would push the second one back across
   the header and land the pair on opposite sides of the row. */
.reg-page .reg-card--marketing .reg-source-badges {
  margin-left: 0;
  margin-right: auto;
}
/* .enr-page is the third island using the shared sortable-header markup - in
   RTL the caret sits LEFT of the label, so the base margin-left pushed it
   toward the column edge instead of the text it annotates. */
.reg-page .reg-sort-icon,
.leads-page .reg-sort-icon,
.enr-page .reg-sort-icon {
  margin-left: 0;
  margin-right: 0.3rem;
}
.reg-page .reg-mode-chip b {
  margin-left: 0;
  margin-right: 0.3rem;
}
.reg-page .reg-id-tag {
  margin-right: 0;
  margin-left: 0.15rem;
}
.reg-page .meta-item-remove {
  left: auto;
  right: 0.5rem;
}
/* The Zoom-link field has to escape the .odd-page RTL island. `dir="ltr"` on
   the input cannot do it on its own: the attribute is a presentational hint and
   loses to `.form-input { direction: rtl }` above, so the caret would sit at the
   right-hand end of an https:// URL. */
.odd-page .odd-ltr-input {
  direction: ltr;
  text-align: left;
}

/* The calendar chip on a date row is an <a>, and there is no anchor reset in
   this file - without these it renders default browser blue and underlined in
   the middle of the table. */
.odd-page .odd-cal-link {
  color: #666;
  text-decoration: none;
}
.odd-page .odd-cal-link:hover {
  color: #0071e3;
  text-decoration: underline;
}

.odd-page .form-label-hint,
.reg-page .form-label-hint,
.ods-page .form-label-hint,
.leads-page .form-label-hint,
.ld-page .form-label-hint {
  margin-left: 0;
  margin-right: 0.35rem;
}

/* Hebrew option labels: filter selects drop the legacy LTR pin. Date/time
   pickers and LTR-content selects re-pin via dir="ltr" in the template. */
.reg-page .reg-filter-field .form-select,
.ods-page .reg-filter-field .form-select,
.leads-page .reg-filter-field .form-select,
.enr-page .reg-filter-field .form-select {
  direction: rtl;
  text-align: right;
}
.reg-page .admin-search .form-input,
.leads-page .admin-search .form-input,
.enr-page .admin-search .form-input {
  direction: rtl;
  text-align: right;
}

/* Modal inputs: Hebrew-first; emails/phones re-pin LTR via dir="ltr". */
.reg-page .reg-form-input,
.leads-page .reg-form-input {
  direction: rtl;
  text-align: right;
}


button.reg-action.reg-action--danger {
    min-width: 100px;
}
/* ═══════════════════════════════════════════════════════════════════════
   Admin bottom sheet (.adm-sheet) - src/components/admin/AdminSheet.vue

   The phone-side surface for filter panels, sort pickers and option lists.
   Fixed-position but rendered IN PLACE (no Teleport), so it inherits the
   page island's direction and the .admin-layout token scope. On desktop
   widths the same panel presents as a centered card; callers that want a
   desktop popover should gate the sheet to phones instead.

   Fill is deliberately MORE opaque than the desktop glass tokens: a sheet
   floats over a scrolling list on mid-range phones, where heavy
   backdrop-filter over moving content costs compositing time and the text
   behind it costs contrast.

   Z-ladder: mobile bar 1010 < tab/bulk bars 1015 < peeked sidebar 1016 < modal 1018 <
   nav scrim 1020 < drawer 1030 < sheet scrim 1040 < sheet 1050 < toast 2000.
   ═══════════════════════════════════════════════════════════════════════ */

.adm-sheet-layer {
  position: fixed;
  inset: 0;
  z-index: 1040;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

.adm-sheet-scrim {
  position: absolute;
  inset: 0;
  background: rgba(25, 26, 35, 0.32);
}

.adm-sheet {
  position: relative;
  z-index: 1050;
  width: 100%;
  max-height: 84dvh;
  display: flex;
  flex-direction: column;
  /* NO backdrop-filter here, deliberately: a filter makes this box the
     containing block for position:fixed DESCENDANTS, and callers nest
     fixed-layer widgets inside sheets (the leads filter sheet holds the
     date-range picker) - the nested layer would be trapped inside the
     sheet's box instead of covering the viewport. Near-opaque fill does
     the same visual job without creating the trap. */
  background: var(--adm-surface-solid, rgba(255, 255, 255, 0.97));
  border: 1px solid var(--adm-glass-border);
  border-block-end: none;
  border-start-start-radius: 20px;
  border-start-end-radius: 20px;
  box-shadow: 0 -12px 40px rgba(0, 60, 130, 0.16);
  outline: none;
}

.adm-sheet__head {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 1.1rem 1.1rem 0.7rem;
}

/* iOS-style grab hint. Decorative only - the close button and the scrim are
   the real dismissal paths (NN/g: grab handles get missed). */
.adm-sheet__grab {
  position: absolute;
  inset-block-start: 7px;
  inset-inline: 0;
  margin-inline: auto;
  width: 40px;
  height: 4px;
  border-radius: 999px;
  background: rgba(25, 26, 35, 0.14);
}

.adm-sheet__title {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  color: var(--adm-ink);
  flex: 1;
}

.adm-sheet__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin: -10px;
  border: none;
  background: none;
  border-radius: 12px;
  color: var(--adm-ink-3);
  font-size: 1.05rem;
  cursor: pointer;
}

.adm-sheet__close:hover {
  color: var(--adm-ink);
  background: rgba(25, 26, 35, 0.05);
}

.adm-sheet__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0 1.1rem 1rem;
}

.adm-sheet__footer {
  display: flex;
  gap: 0.6rem;
  padding: 0.8rem 1.1rem calc(0.8rem + env(safe-area-inset-bottom));
  border-block-start: 1px solid var(--adm-line);
  background: rgba(255, 255, 255, 0.9);
}

.adm-sheet__footer .btn {
  flex: 1;
  justify-content: center;
  min-height: 46px;
}

/* Without a footer, the body itself must clear the home indicator. */
.adm-sheet__body:last-child {
  padding-block-end: calc(1rem + env(safe-area-inset-bottom));
}

/* Enter/leave: slide up + fade. */
.adm-sheet-enter-active,
.adm-sheet-leave-active {
  transition: opacity 0.18s ease;
}

.adm-sheet-enter-active .adm-sheet,
.adm-sheet-leave-active .adm-sheet {
  transition: transform 0.22s cubic-bezier(0.32, 0.72, 0.22, 1);
}

.adm-sheet-enter-from,
.adm-sheet-leave-to {
  opacity: 0;
}

.adm-sheet-enter-from .adm-sheet,
.adm-sheet-leave-to .adm-sheet {
  transform: translateY(24px);
}

/* Desktop fallback: centered card. */
@media (min-width: 768px) {
  .adm-sheet-layer {
    align-items: center;
    padding: 2rem;
  }

  .adm-sheet {
    width: min(460px, 100%);
    max-height: min(640px, 86vh);
    border-radius: 16px;
    border-block-end: 1px solid var(--adm-glass-border);
    box-shadow: 0 24px 64px rgba(0, 60, 130, 0.2);
  }

  .adm-sheet__grab {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .adm-sheet-enter-active,
  .adm-sheet-leave-active,
  .adm-sheet-enter-active .adm-sheet,
  .adm-sheet-leave-active .adm-sheet {
    transition: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   Date-range picker (.adm-drp) - AdminDateRangePicker.vue

   One trigger button + one panel. Desktop: anchored popover (presets column
   beside a calendar). Phone (<=767px): the SAME panel docks as a bottom
   sheet and the columns stack. Weeks start Sunday; the grid is generated
   Sunday-first by the component, the CSS only paints.
   ═══════════════════════════════════════════════════════════════════════ */

.adm-drp {
  position: relative;
}

.adm-drp__trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  min-height: 2.45rem;
  padding: 0.35rem 0.75rem;
  background: #fff;
  border: 1px solid var(--adm-line);
  border-radius: 10px;
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--adm-ink);
  cursor: pointer;
  white-space: nowrap;
  max-width: 100%;
}

.adm-drp__trigger > .far {
  color: var(--adm-ink-3);
  font-size: 0.85rem;
}

.adm-drp__trigger:hover {
  border-color: rgba(0, 113, 227, 0.35);
}

.adm-drp__trigger--open {
  border-color: var(--adm-accent);
  box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.12);
}

.adm-drp__trigger--active {
  background: var(--primary-light, rgba(0, 113, 227, 0.06));
  border-color: rgba(0, 113, 227, 0.4);
  color: var(--adm-accent-ink);
}

.adm-drp__trigger--active > .far {
  color: var(--adm-accent);
}

.adm-drp__trigger-range {
  font-size: 0.76rem;
  color: var(--adm-ink-2);
  overflow: hidden;
  text-overflow: ellipsis;
}

.adm-drp__caret {
  font-size: 0.6rem;
  color: var(--adm-ink-3);
}

.adm-drp__scrim {
  display: none;
}

.adm-drp__panel {
  position: absolute;
  inset-block-start: calc(100% + 6px);
  inset-inline-start: 0;
  z-index: 60;
  width: min(560px, 92vw);
  background: rgba(255, 255, 255, 0.97);
  -webkit-backdrop-filter: blur(24px) saturate(1.5);
  backdrop-filter: blur(24px) saturate(1.5);
  border: 1px solid var(--adm-glass-border);
  border-radius: 16px;
  box-shadow: 0 20px 56px rgba(0, 60, 130, 0.18);
}

.adm-drp__head {
  display: none;
}

.adm-drp__cols {
  display: grid;
  grid-template-columns: 176px minmax(0, 1fr);
  gap: 0;
}

.adm-drp__presets {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 0.6rem;
  border-inline-end: 1px solid var(--adm-line);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.adm-drp__preset {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.44rem 0.6rem;
  border: none;
  background: none;
  border-radius: 9px;
  font-size: 0.84rem;
  font-weight: 500;
  color: var(--adm-ink);
  cursor: pointer;
  text-align: start;
}

.adm-drp__preset:hover {
  background: rgba(0, 113, 227, 0.06);
}

.adm-drp__preset--active {
  background: rgba(0, 113, 227, 0.1);
  color: var(--adm-accent-ink);
  font-weight: 600;
}

.adm-drp__preset > span {
  flex: 1;
}

.adm-drp__preset > .fa-check {
  font-size: 0.7rem;
  color: var(--adm-accent);
}

.adm-drp__preset-range {
  font-size: 0.68rem;
  color: var(--adm-ink-3);
}

.adm-drp__custom {
  padding: 0.8rem 0.9rem 0.7rem;
  min-width: 0;
}

.adm-drp__cal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-block-end: 0.5rem;
}

.adm-drp__cal-title {
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--adm-ink);
}

.adm-drp__cal-nav {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: none;
  background: none;
  border-radius: 9px;
  color: var(--adm-ink-2);
  font-size: 0.75rem;
  cursor: pointer;
}

.adm-drp__cal-nav:hover {
  background: rgba(0, 113, 227, 0.07);
  color: var(--adm-accent-ink);
}

.adm-drp__cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 2px;
}

.adm-drp__cal-wd {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 26px;
  font-family: var(--font-mono);
  font-size: 0.62rem;
  color: var(--adm-ink-3);
}

.adm-drp__cal-day {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  min-height: 34px;
  border: none;
  background: none;
  border-radius: 9px;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  color: var(--adm-ink);
  cursor: pointer;
}

.adm-drp__cal-day--pad {
  visibility: hidden;
  pointer-events: none;
}

.adm-drp__cal-day:not(.adm-drp__cal-day--pad):hover {
  background: rgba(0, 113, 227, 0.08);
}

.adm-drp__cal-day--today {
  box-shadow: inset 0 0 0 1px rgba(0, 113, 227, 0.45);
}

.adm-drp__cal-day--in {
  background: rgba(0, 113, 227, 0.1);
  border-radius: 0;
}

.adm-drp__cal-day--edge {
  background: var(--adm-accent);
  color: #fff;
  font-weight: 600;
}

.adm-drp__cal-day--edge:not(.adm-drp__cal-day--pad):hover {
  background: var(--adm-accent-ink);
}

.adm-drp__hint {
  margin: 0.55rem 0 0;
  font-size: 0.72rem;
  color: var(--adm-accent-ink);
}

.adm-drp__hint--idle {
  color: var(--adm-ink-3);
}

.adm-drp-enter-active,
.adm-drp-leave-active {
  transition: opacity 0.14s ease;
}

.adm-drp-enter-from,
.adm-drp-leave-to {
  opacity: 0;
}

/* Phone: the panel docks to the bottom as a sheet. */
@media (max-width: 767px) {
  .adm-drp {
    position: static;
  }

  .adm-drp__layer {
    position: fixed;
    inset: 0;
    z-index: 1040;
  }

  .adm-drp__scrim {
    display: block;
    position: absolute;
    inset: 0;
    background: rgba(25, 26, 35, 0.32);
  }

  .adm-drp__panel {
    position: absolute;
    inset-block-start: auto;
    inset-block-end: 0;
    inset-inline: 0;
    width: 100%;
    max-height: 88dvh;
    display: flex;
    flex-direction: column;
    border-radius: 20px 20px 0 0;
    border-block-end: none;
    box-shadow: 0 -12px 40px rgba(0, 60, 130, 0.16);
  }

  .adm-drp__head {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 1.1rem 1.1rem 0.4rem;
  }

  .adm-drp__head-title {
    margin: 0;
    font-size: 1rem;
    font-weight: 700;
    color: var(--adm-ink);
    flex: 1;
  }

  .adm-drp__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin: -10px;
    border: none;
    background: none;
    border-radius: 12px;
    color: var(--adm-ink-3);
    font-size: 1.05rem;
    cursor: pointer;
  }

  .adm-drp__cols {
    grid-template-columns: minmax(0, 1fr);
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-block-end: calc(0.9rem + env(safe-area-inset-bottom));
  }

  .adm-drp__presets {
    border-inline-end: none;
    border-block-end: 1px solid var(--adm-line);
    padding: 0.5rem 0.8rem 0.7rem;
  }

  .adm-drp__preset {
    min-height: 46px;
    font-size: 0.92rem;
  }

  .adm-drp__preset-range {
    font-size: 0.74rem;
  }

  .adm-drp__custom {
    padding: 0.8rem 1rem 0.4rem;
  }

  .adm-drp__cal-day {
    min-height: 44px;
    font-size: 0.9rem;
  }

  .adm-drp__cal-wd {
    height: 30px;
    font-size: 0.68rem;
  }
}

/* Desktop head is hidden; on phones the panel head shows. The trigger's
   full-width growth inside stacked filter rows is the caller's concern. */

/* ═══════════════════════════════════════════════════════════════════════
   Bottom tab bar (.adm-tabbar) - src/components/admin/AdminTabBar.vue

   Phone-only navigation between the two staff work surfaces (inbox, leads)
   plus the drawer. Hidden entirely above 767px, and the component itself
   does not render inside an open conversation thread. Glass surface at
   high opacity (it floats over scrolling lists), top hairline, safe-area
   padding for the home indicator.

   Z-ladder note: 1015 - above content and the fixed bulk bar company, below
   the nav scrim (1020) so the drawer overlays the tabs when open.
   ═══════════════════════════════════════════════════════════════════════ */

.adm-tabbar {
  display: none;
}

@media (max-width: 767px) {
  .adm-tabbar {
    position: fixed;
    inset-inline: 0;
    inset-block-end: 0;
    z-index: 1015;
    display: flex;
    align-items: stretch;
    /* Solid, not glass, at phone width (F5): the bar floats over scrolling
       lists, which is exactly where a backdrop blur repaints per frame. */
    background: var(--adm-surface-solid, rgba(255, 255, 255, 0.97));
    border-block-start: 1px solid var(--adm-line);
    padding-block-end: max(0px, env(safe-area-inset-bottom));
    box-shadow: 0 -6px 24px rgba(0, 60, 130, 0.06);
  }

  .adm-tabbar__tab {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: var(--adm-tabbar-h);
    padding: 6px 4px;
    border: none;
    background: none;
    color: var(--adm-ink-3);
    font-size: 0.66rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }

  .adm-tabbar__tab--active {
    color: var(--adm-accent-ink);
  }

  .adm-tabbar__icon-wrap {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 26px;
    font-size: 1.15rem;
  }

  .adm-tabbar__label {
    line-height: 1;
  }

  .adm-tabbar__badge {
    position: absolute;
    inset-block-start: -4px;
    inset-inline-end: -8px;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: #e11d48;
    color: #fff;
    font-size: 0.6rem;
    font-weight: 700;
    line-height: 1;
    border-radius: 999px;
    border: 2px solid rgba(255, 255, 255, 0.95);
  }

  /* Content must clear the bar. Keyed on the bar actually rendering, so a
     thread view (no bar) keeps its full height and the inbox list pane stops
     above the tabs. `.admin-main` is the page scroller; `.wai-page` is the
     viewport-locked inbox, which pads its own grid instead.

     `:not(:has(.wai-page))` is what makes that last sentence true. Without it
     this rule ALSO fired on the inbox - it scores (0,3,0) and outranks the
     `padding: 0` reset the inbox installs on `.admin-main` at (0,2,0)
     (admin-whatsapp.css) - so the clearance was paid twice: 76px here plus the
     56px below, 132px held back for a 56px bar. The inbox is viewport-locked,
     so the surplus came straight off the conversation list (472px of scroller
     where 548px was available at 390x760, about one row), and because
     `.admin-main` is transparent the extra band showed the light shell canvas
     as a bright seam under a dark WhatsApp pane. Every other admin page is a
     scrolling page and still needs this. */
  .admin-layout:has(.adm-tabbar) .admin-main:not(:has(.wai-page)) {
    padding-block-end: calc(1.25rem + var(--adm-tabbar-h) + env(safe-area-inset-bottom));
  }

  .admin-layout:has(.adm-tabbar) .wai-page {
    padding-block-end: calc(var(--adm-tabbar-h) + env(safe-area-inset-bottom));
  }
}

/* Selection mode owns the bottom edge. When the leads bulk bar is up (fixed,
   dark slab) the nav tabs yield - a contextual action bar REPLACES navigation
   for the duration of the selection (the Material selection-mode convention),
   so two fixed bars never stack on a 390px screen. */
@media (max-width: 767px) {
  .admin-layout:has(.leads-page--bulk) .adm-tabbar {
    display: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   Multi-select filter (.adm-ms) - src/components/admin/AdminMultiSelect.vue

   The checkbox-list replacement for a single-value `.form-select` in an
   admin filter bar, so the trigger deliberately borrows that control's
   geometry (6px radius, 1px #ddd, 0.45rem/0.7rem padding, 0.88rem text) and
   nothing else on the row moves when a filter is widened.

   Structure mirrors the date-range picker: trigger + layer + scrim + panel.
   Desktop is an anchored popover with the scrim inert; at 767px and below
   the SAME panel docks to the bottom as a sheet, with the grab handle and
   the close button that only make sense there. Z-ladder: 60 for the
   desktop popover (in flow, above the filter bar), 1040 for the phone layer,
   which is `AdminSheet`'s rung - a filter sheet is exactly where this
   control renders at that width.
   ═══════════════════════════════════════════════════════════════════════ */

.adm-ms {
  position: relative;
}

.adm-ms__trigger {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  width: 100%;
  /* 2.45rem is the measured height of the `.form-select` and the date-range
     trigger it shares a filter row with: a button's line box is shorter than a
     select's, so without a floor this control sits 5px low on that baseline. */
  min-height: 2.45rem;
  padding: 0.45rem 0.7rem;
  background: #fff;
  border: 1px solid #ddd;
  border-radius: 6px;
  font-family: inherit;
  font-size: 0.88rem;
  color: var(--adm-ink);
  text-align: start;
  cursor: pointer;
  transition: border-color 0.2s;
}

.adm-ms__trigger:hover:not(:disabled) {
  border-color: rgba(0, 113, 227, 0.35);
}

.adm-ms__trigger:focus-visible,
.adm-ms__trigger--open {
  outline: none;
  border-color: var(--adm-accent);
  box-shadow: 0 0 0 3px rgba(0, 113, 227, 0.12);
}

.adm-ms__trigger--active {
  background: var(--primary-light, rgba(0, 113, 227, 0.06));
  border-color: rgba(0, 113, 227, 0.4);
  color: var(--adm-accent-ink);
}

.adm-ms__trigger:disabled {
  background: #f6f7f9;
  color: var(--adm-ink-3);
  cursor: not-allowed;
}

.adm-ms__summary {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.adm-ms__badge {
  flex: 0 0 auto;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  background: rgba(0, 113, 227, 0.14);
  color: var(--adm-accent-ink);
  font-size: 0.7rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.adm-ms__caret {
  flex: 0 0 auto;
  font-size: 0.6rem;
  color: var(--adm-ink-3);
}

.adm-ms__scrim {
  display: none;
}

.adm-ms__panel {
  position: absolute;
  inset-block-start: calc(100% + 6px);
  inset-inline-start: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  /* At least as wide as its trigger, but never wider than the cap: a plain
     `min-width: 100%` beats `max-width` in the cascade, and a filter field that
     spans a whole row (the roster's do) would then open a 900px panel of 200px
     rows. `min()` is what keeps both halves true. */
  min-width: min(100%, 340px);
  width: max-content;
  max-width: min(340px, 92vw);
  background: rgba(255, 255, 255, 0.97);
  -webkit-backdrop-filter: blur(24px) saturate(1.5);
  backdrop-filter: blur(24px) saturate(1.5);
  border: 1px solid var(--adm-glass-border);
  border-radius: 12px;
  box-shadow: 0 20px 56px rgba(0, 60, 130, 0.18);
}

.adm-ms__head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.7rem 0.3rem;
}

.adm-ms__head-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.72rem;
  font-weight: 600;
  color: #8e8f9e;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.adm-ms__clear {
  flex: 0 0 auto;
  padding: 0.12rem 0.35rem;
  border: none;
  background: none;
  border-radius: 6px;
  font-family: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--adm-accent-ink);
  cursor: pointer;
}

.adm-ms__clear:hover {
  background: rgba(0, 113, 227, 0.08);
}

.adm-ms__close {
  display: none;
}

.adm-ms__search {
  padding: 0 0.55rem 0.4rem;
}

.adm-ms__search-input {
  width: 100%;
  padding: 0.35rem 0.55rem;
  border: 1px solid var(--adm-line);
  border-radius: 8px;
  background: #fff;
  font-family: inherit;
  font-size: 0.84rem;
  color: var(--adm-ink);
  outline: none;
}

.adm-ms__search-input:focus {
  border-color: var(--adm-accent);
}

.adm-ms__list {
  display: flex;
  flex-direction: column;
  gap: 1px;
  max-height: 19rem;
  padding: 0 0.4rem 0.5rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.adm-ms__group {
  padding: 0.45rem 0.5rem 0.2rem;
  font-size: 0.68rem;
  font-weight: 700;
  color: var(--adm-ink-3);
  letter-spacing: 0.04em;
}

.adm-ms__opt {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.4rem 0.5rem;
  border: none;
  background: none;
  border-radius: 8px;
  font-family: inherit;
  font-size: 0.84rem;
  font-weight: 500;
  color: var(--adm-ink);
  text-align: start;
  cursor: pointer;
}

.adm-ms__opt:hover {
  background: rgba(0, 113, 227, 0.06);
}

.adm-ms__opt:focus-visible {
  outline: none;
  background: rgba(0, 113, 227, 0.1);
  box-shadow: inset 0 0 0 1px rgba(0, 113, 227, 0.45);
}

.adm-ms__opt--on {
  color: var(--adm-accent-ink);
  font-weight: 600;
}

.adm-ms__opt--muted .adm-ms__opt-label {
  color: var(--adm-ink-3);
}

.adm-ms__box {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border: 1px solid #c9cedb;
  border-radius: 4px;
  background: #fff;
  font-size: 0.6rem;
  color: #fff;
}

.adm-ms__opt--on .adm-ms__box {
  background: var(--adm-accent);
  border-color: var(--adm-accent);
}

.adm-ms__opt-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.adm-ms__count {
  flex: 0 0 auto;
  padding: 0.05rem 0.4rem;
  border-radius: 999px;
  background: #eef2f7;
  color: #5b6470;
  font-size: 0.7rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.adm-ms__opt--on .adm-ms__count {
  background: rgba(0, 113, 227, 0.14);
  color: var(--adm-accent-ink);
}

.adm-ms__empty {
  margin: 0;
  padding: 0.6rem 0.5rem;
  font-size: 0.8rem;
  color: var(--adm-ink-3);
}

.adm-ms-enter-active,
.adm-ms-leave-active {
  transition: opacity 0.14s ease;
}

.adm-ms-enter-from,
.adm-ms-leave-to {
  opacity: 0;
}

/* Phone: the panel docks to the bottom as a sheet, and the rows grow to a
   thumb-sized target. */
@media (max-width: 767px) {
  .adm-ms {
    position: static;
  }

  .adm-ms__layer {
    position: fixed;
    inset: 0;
    z-index: 1040;
  }

  .adm-ms__scrim {
    display: block;
    position: absolute;
    inset: 0;
    background: rgba(25, 26, 35, 0.32);
  }

  .adm-ms__panel {
    position: absolute;
    inset-block-start: auto;
    inset-block-end: 0;
    inset-inline: 0;
    width: 100%;
    max-width: none;
    max-height: 88dvh;
    border-radius: 20px 20px 0 0;
    border-block-end: none;
    box-shadow: 0 -12px 40px rgba(0, 60, 130, 0.16);
  }

  .adm-ms__head {
    position: relative;
    padding: 1.1rem 1.1rem 0.5rem;
  }

  .adm-ms__head-title {
    font-size: 1rem;
    font-weight: 700;
    color: var(--adm-ink);
    text-transform: none;
    letter-spacing: 0;
  }

  .adm-ms__clear {
    font-size: 0.86rem;
    padding: 0.3rem 0.5rem;
  }

  .adm-ms__close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin-inline-end: -10px;
    border: none;
    background: none;
    border-radius: 12px;
    color: var(--adm-ink-3);
    font-size: 1.05rem;
    cursor: pointer;
  }

  .adm-ms__search {
    padding: 0 1rem 0.5rem;
  }

  .adm-ms__search-input {
    padding: 0.55rem 0.7rem;
    /* 16px, not 0.95rem: under the iOS-zoom floor the old size sat exactly
       in the range that made focusing the search zoom the whole sheet. */
    font-size: 16px;
  }

  .adm-ms__list {
    max-height: none;
    padding: 0 0.7rem calc(0.9rem + env(safe-area-inset-bottom));
  }

  .adm-ms__opt {
    min-height: 46px;
    font-size: 0.95rem;
  }

  .adm-ms__box {
    width: 20px;
    height: 20px;
    font-size: 0.7rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .adm-ms-enter-active,
  .adm-ms-leave-active {
    transition: none;
  }
}

/* F5 for BOTH picker panels below the desktop breakpoint, stated once, after
   both base rules so the tie goes this way. At <=767px each panel docks as a
   sheet inside a fixed layer - phone chrome outright; in the tablet band they
   are anchored popovers, but they still float over scrolling content on
   touch hardware and their 0.97 fill already hides the blur, so below 1025px
   the filter is pure compositing cost. Desktop keeps the glass. */
@media (max-width: 1024px) {
  .adm-drp__panel,
  .adm-ms__panel {
    background: var(--adm-surface-solid, rgba(255, 255, 255, 0.97));
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   Open-day roster: the Zoom-link strip, the CRM-status column, the
   send-details checkbox and the calendar connection modal
   (owner decisions D4/D6/D8 + issues 1/4/20, 2026-08-16)

   Appended as one delimited block rather than threaded into the `.reg-*`
   section above, so a concurrent session editing that section and this
   one cannot land on the same lines. Everything here is scoped to
   `.reg-page` or carries a `reg-` prefix that only that screen uses.

   Two classes this work deliberately does NOT redefine:
   `.reg-lead-status-select` and `.reg-lead-status-chip`, which already
   live further up this file. They were written for the expanded detail
   row that was deleted in 2026-08, and they are exactly right for the
   column that replaced it - a plain control rather than the
   `.reg-status-select` pill, because that pill takes its colour from a
   fixed set of REGISTRATION values while the lead statuses are DB rows
   with editable labels. Reusing them is what keeps one status wearing
   one look on both screens.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── The CRM-status column ────────────────────────────
   180px is the measured width of the longest shipped label
   ("נרשם ליום פתוח") plus the select's own chrome; below that the control
   truncates a status name, which is the one thing this column exists to
   show. */

.reg-th-lead-status {
  min-width: 11rem;
}

.reg-cell-lead-status {
  white-space: nowrap;
}

/* A seat with nobody to have a status. Deliberately the same muted dash
   the activity column uses for "nothing has happened yet": both are the
   absence of a fact rather than a fact, and ranking them differently
   would invite a reader to look for a difference that is not there. */
.reg-lead-status-none {
  display: inline-block;
}

/* ── The create modal's send-details checkbox ─────────
   Its own row under the form grid, full width: it is the only control in
   that dialog that reaches outside our database, and a checkbox sharing a
   column with a text field reads as one more field. */

.reg-form-sendmail {
  margin-top: 0.25rem;
}

.reg-sendmail {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  padding: 0.6rem 0.75rem;
  background: #f7f9fc;
  border: 1px solid #e3e9f2;
  border-radius: 8px;
  cursor: pointer;
}

.reg-sendmail input {
  /* Nudged onto the label's first-line baseline; the hint below it makes
     the text block two lines tall and a centred box floats mid-sentence. */
  margin-top: 0.15rem;
  flex: 0 0 auto;
}

.reg-sendmail-text {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.reg-sendmail-label {
  font-size: 0.88rem;
  font-weight: 600;
  color: #2d3340;
}

/* ── The calendar connection modal ────────────────────
   A definition list in flex rows rather than a grid: the values are a
   calendar name, an event title and a relative time, none of which share
   a length, and a fixed key column would leave a gutter on every line. */

.reg-calmodal-row {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.45rem 0;
  border-bottom: 1px solid #f0f3f8;
}

.reg-calmodal-row:last-of-type {
  border-bottom: 0;
}

.reg-calmodal-key {
  flex: 0 0 auto;
  min-width: 8.5rem;
  font-size: 0.82rem;
  font-weight: 600;
  color: #6b7280;
}

.reg-calmodal-value {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5rem;
  min-width: 0;
  font-size: 0.9rem;
  color: #2d3340;
  /* A calendar id is an email address and can outrun the dialog. */
  overflow-wrap: anywhere;
}

.reg-calmodal-open {
  font-size: 0.8rem;
  white-space: nowrap;
  color: var(--primary-color, #0071E3);
}

.reg-calmodal-rsvp {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.75rem;
}

.reg-calmodal-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.2rem 0.6rem;
  border: 1px solid #e6ebf2;
  border-radius: 999px;
  background: #f8fafc;
  font-size: 0.8rem;
  color: #3a4252;
}

/* The three answered states borrow the invite chip's palette from further
   up this file rather than inventing a second one: the same RSVP wearing
   two colours on one screen is how a reader stops trusting either. */
.reg-calmodal-chip--accepted  { background: #ecfdf5; color: #047857; border-color: #a7f3d0; }
.reg-calmodal-chip--declined  { background: #fef2f2; color: #b91c1c; border-color: #fecaca; }
.reg-calmodal-chip--tentative { background: #f5f3ff; color: #6d28d9; border-color: #ddd6fe; }

.reg-calmodal-note {
  margin: 0.5rem 0 0;
  font-size: 0.8rem;
  line-height: 1.5;
}

.reg-calmodal-note--muted {
  color: #6b7280;
}

.reg-calmodal-note--error {
  color: #b91c1c;
}

/* ── Dates screen: why a save is refusing ─────────────
   The Zoom link is mandatory on a new date (issue 1), and the save button
   goes disabled for it. This is the sentence that says so, under the
   field rather than beside the button, because that is where the reader
   is looking when they cause it. */

.odd-page .odd-field-error {
  margin: 0.35rem 0 0;
  font-size: 0.8rem;
  color: #b91c1c;
}

/* The blank-create promise under the Zoom field: the meeting is minted
   server-side on save. Muted, because it describes the happy path. */
.odd-page .odd-field-hint {
  margin: 0.35rem 0 0;
  font-size: 0.8rem;
  color: #8e8f9e;
}

/* The floor, as a muted suffix inside the location cell - it qualifies the
   venue rather than standing as a fact of its own. */
.odd-page .odd-floor {
  margin-inline-start: 0.35rem;
  font-size: 0.78rem;
  color: #5a5b6a;
  white-space: nowrap;
}

/* ── Dates screen: the Zoom column ────────────────────
   A link when the row has one, the create button when it does not. The link is
   the URL minus its protocol, hard-ellipsised so a long personal-meeting URL
   cannot widen the table; the full link lives in the title and in the href. */
.odd-page .odd-zoom-cell {
  white-space: nowrap;
}
.odd-page .odd-zoom-link {
  /* inline-block, not inline-flex: `text-overflow` only ellipsises a block
     container's own text, and a flex container turns the text into an
     anonymous item it will not clip. */
  display: inline-block;
  vertical-align: middle;
  max-inline-size: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.8rem;
  color: #0071e3;
  text-decoration: none;
}
.odd-page .odd-zoom-link:hover {
  text-decoration: underline;
}

/* ── Dates screen: the per-date WhatsApp template preview ─────────
   Prints the CATALOGUE body verbatim - {{n}} placeholders, newlines and
   all - so the operator reads exactly what Meta approved. `pre-wrap`
   keeps the template's own line breaks; the body mixes Hebrew and Latin
   lines, so each line resolves its own direction from its first strong
   character (`unicode-bidi: plaintext`, the same rule admin-person.css
   applies to mixed labels). */
.odd-page .odd-wa-preview {
  margin-top: 0.5rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--border-light);
  border-radius: var(--radius-sm);
  background: var(--bg-secondary);
}
.odd-page .odd-wa-preview-name {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  color: var(--text-secondary, #5a5b6a);
  margin-bottom: 0.4rem;
}
.odd-page .odd-wa-preview-body {
  white-space: pre-wrap;
  unicode-bidi: plaintext;
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--text-color, #191a23);
  max-block-size: 260px;
  overflow-y: auto;
}
/* The stored override name, printed as plain text while the catalogue is
   unreachable and the locked select cannot show it. */
.odd-page .odd-wa-stored {
  margin: 0.35rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--text-secondary, #5a5b6a);
}

@media (max-width: 767px) {
  /* The card row is a labelled line, not a table cell: the select may use
     the width it needs and the nowrap that keeps the desktop column tidy
     would push it under its own ::before label. */
  .reg-page .reg-table td.reg-cell-lead-status {
    white-space: normal;
  }

  .reg-calmodal-row {
    flex-direction: column;
    gap: 0.15rem;
  }
}

/* 1. Curriculum on touch (track E2; scope hook .cur-page is in the template) */
@media (max-width: 1024px) {
  .cur-page .tree-header { flex-wrap: wrap; }
  .cur-page .tree-actions { opacity: 1; flex-wrap: wrap; }
  .cur-page .btn, .cur-page .btn-icon { min-height: 44px; min-width: 44px; }
  .cur-page .form-input, .cur-page .form-select, .cur-page .form-textarea, .cur-page .inline-edit-input { font-size: 16px; }
  .cur-page .form-input, .cur-page .form-select, .cur-page .form-textarea { min-height: 44px; }
  /* The other three .course-filter-tab hosts (.ods-page, .stg-page, .pay-page)
     already float this floor from their own sheets - Curriculum was the one
     left at its 29px desktop height. */
  .cur-page .course-filter-tab { min-height: 44px; }
}

/* 2. Affiliates on touch (scope hook .aff-page is in the template) */
@media (max-width: 1024px) {
  .aff-page .btn, .aff-page .btn-icon { min-height: 44px; min-width: 44px; }
  .aff-page .form-input, .aff-page .form-select, .aff-page .aff-search-input { font-size: 16px; min-height: 44px; }
  .aff-page .aff-panel-toggle { min-height: 44px; }
  .aff-page .affiliate-group-filter-trigger, .aff-page .affiliate-group-filter-clear, .aff-page .affiliate-group-filter-option { min-height: 44px; }
  .aff-page .toggle::after { content: ''; position: absolute; inset: -12px -8px; }
}

/* 3. Login glyphs */
@media (max-width: 1024px) {
  .admin-login .login-input-action { width: 44px; height: 44px; }
  .admin-login .login-eye-btn { left: 0; }
  .admin-login .login-clear-btn { left: 44px; }
}

/* 4. Sidebar identity card - logout on touch. 32px measured against the 44px
   floor; the visible box stays the size the identity card's own row height
   was measured against (see .admin-layout--rail .admin-user above), so the
   touch target grows around it instead, the same invisible-halo pattern
   .toggle::after uses at E2 and .admin-hamburger::after uses just above. */
@media (max-width: 1024px) {
  .admin-logout::after {
    content: '';
    position: absolute;
    inset: -6px;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   The touch floors: 16px text entry, 44px tap targets (mobile revamp)

   THIS SECTION STAYS LAST IN THE FILE BY CONTRACT. Every selector below
   also carries a smaller size somewhere above (the desktop look); these
   blocks win those ties by source order and match the compound
   competitors at their own specificity. All of it applies to the whole
   <=1024px range: iPads run the same zoom heuristic and are touched by
   the same thumbs.

   The 16px half (orchestrator decision on note e): iOS Safari zooms the
   page onto any focused control whose COMPUTED font-size is under 16px,
   and on this viewport-locked shell the zoom also drags the layout
   sideways. So below the desktop breakpoint, every text-entry control
   this file sizes computes at least 16px. Stated in px on purpose - a
   rem here would re-tie the floor to the root font size, which the
   public stylesheets move at some widths. A new text-entry control
   added to this file joins the list, and a per-view stylesheet may
   restate the same value. Non-entry controls (buttons, checkboxes,
   options) stay out of the FONT floor - iOS does not zoom onto them and
   their compact type is deliberate - but their tap BOXES are floored by
   the 44px half below (orchestrator decision X6), which grows only the
   minimum box, never the padding or the typography.
   ═══════════════════════════════════════════════════════════════════════ */

@media (max-width: 1024px) {
  .form-input,
  .form-select,
  .form-textarea,
  .admin-search input,
  .inline-edit-input,
  .inline-edit-desc-input,
  .inline-edit-hours-input,
  .rich-editor-content,
  .rich-editor-source,
  .rich-editor-anchor-select,
  .reg-filter-field .form-select,
  .reg-filter-field .form-input,
  .reg-table td.reg-cell-status .reg-status-select,
  .reg-bulk-select,
  .reg-status-select,
  .reg-lead-status-select,
  .reg-inline-edit-input,
  .reg-zoom-input,
  .aff-search-input,
  .cohort-filter-field .form-select,
  .adm-ms__search-input {
    font-size: 16px;
  }
}

/* The 44px tap-target floor (X6). The per-view tracks floored the surfaces
   they own; this is the panel-wide net under everything that fell through
   (AdminSaleResolver's buttons, stray dialog footers, one-off toolbars).
   Minimum box only - paddings and type stay what each control declares. */
@media (max-width: 1024px) {
  .btn,
  .btn-icon,
  .btn-sm {
    min-block-size: 44px;
  }

  .btn-icon {
    min-inline-size: 44px;
  }
}

/* The same 44px floor inside the two shared pickers, which no view owns:
   the date-range popover/sheet and the multi-select. Day cells, preset and
   option rows, the triggers they hang off, and the clear/close controls -
   everything a thumb is expected to hit. The glyph-only controls get the
   inline floor too, so they are squares rather than slivers. The phone
   blocks above already size some of these at 44-46px via min-height; that
   property coexists with min-block-size and the larger of the two wins. */
@media (max-width: 1024px) {
  .adm-drp__trigger,
  .adm-drp__preset,
  .adm-drp__cal-day,
  .adm-drp__cal-nav,
  .adm-drp__close,
  .adm-ms__trigger,
  .adm-ms__opt,
  .adm-ms__clear,
  .adm-ms__close {
    min-block-size: 44px;
  }

  .adm-drp__cal-nav,
  .adm-drp__close,
  .adm-ms__close {
    min-inline-size: 44px;
  }
}
