/* ==========================================================================
   shell.css — the two application shells.

   PHONE  : a real app frame. The document never scrolls; a stack of screens
            slides in and out over a fixed tab bar, each screen owning its own
            header and scroll region. Swipe-from-edge pops the stack.
   DESKTOP: the same screens re-hosted in a sidebar + content layout, with the
            stack flattened to a single column and slide motion replaced by a
            short fade.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Root frame
   -------------------------------------------------------------------------- */
body.mode-app {
  height: 100dvh;
  overflow: hidden;
  position: fixed;
  inset: 0;
  width: 100%;
}

.appframe {
  position: relative;
  height: 100dvh;
  width: 100%;
  overflow: hidden;
  background: var(--canvas);
  display: flex;
  flex-direction: column;
}

/* --------------------------------------------------------------------------
   Screen stack
   -------------------------------------------------------------------------- */
.screens {
  position: relative;
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

.screen {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  background: var(--canvas);
  will-change: transform;
}

/* Depth shading: the screen underneath a pushed one dims slightly. */
.screen::after {
  content: '';
  position: absolute;
  inset: 0;
  background: #000;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur) var(--ease-out);
}
.screen.is-under::after { opacity: .085; }

.screen.anim { transition: transform var(--dur) var(--ease-out); }
.screen.enter-right { transform: translate3d(100%, 0, 0); }
.screen.exit-left { transform: translate3d(-24%, 0, 0); }
.screen.at-rest { transform: translate3d(0, 0, 0); }

/* Tab switches cross-fade instead of sliding — they are siblings, not depth. */
.screen.fade-in { animation: screenFade var(--dur) var(--ease-out); }
@keyframes screenFade {
  from { opacity: 0; transform: scale(.985); }
  to { opacity: 1; transform: scale(1); }
}

/* Dragging during an edge swipe: no transition, follow the finger. */
.screen.dragging { transition: none !important; }

/* --------------------------------------------------------------------------
   App bar (per screen)
   -------------------------------------------------------------------------- */
.appbar {
  position: relative;
  z-index: 4;
  flex-shrink: 0;
  padding-top: var(--safe-top);
  background: var(--grad-brand);
  color: #fff;
}
.appbar.plain {
  background: var(--surface);
  color: var(--text);
  border-bottom: 1px solid var(--line);
}
.appbar-inner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 13px 18px 16px;
  min-height: var(--appbar-h);
}
.appbar-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 18px;
  letter-spacing: -.02em;
  flex: 1;
  min-width: 0;
}
.appbar-sub { font-size: 10.5px; opacity: .85; font-weight: 600; margin-top: 1px; }
/* A pill sitting on the gradient — term pickers, Save, counts. */
.appbar .hpill {
  font-size: 10.5px; font-weight: 700; background: rgba(255,255,255,.18);
  padding: 5px 11px; border-radius: var(--r-pill); color: #fff; white-space: nowrap;
}
.appbar .backbtn {
  width: 34px; height: 34px; margin-left: -6px; border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast);
}
.appbar .backbtn:active { transform: scale(.88); background: rgba(255,255,255,.16); }
.appbar.plain .backbtn:active { background: var(--surface-2); }
.appbar-extra { padding: 0 18px 14px; margin-top: -4px; }

/* Simulated status bar — present only in the phone-frame demo preview. */
.statusbar {
  display: flex; justify-content: space-between; align-items: center;
  font-size: 11.5px; font-weight: 700; padding: 6px 18px 0; letter-spacing: .01em;
}

/* Hero header: the gradient greeting block that the stat strip overlaps. */
.hero {
  background: var(--grad-brand);
  color: #fff;
  padding: 13px 18px 22px;
  padding-top: calc(var(--safe-top) + 13px);
}
.hero .greet { font-size: 12px; opacity: .85; font-weight: 500; }
.hero .who { font-family: var(--font-display); font-weight: 700; font-size: 19px; letter-spacing: -.02em; margin-top: 1px; }
.hero .sub { font-size: 11px; opacity: .85; font-weight: 600; margin-top: 2px; }
/* The stat strip rides 12px up over the bottom edge of the gradient. The
   extra 14px cancels the scroller's own top padding. */
.hero-overlap { margin-top: -26px; position: relative; z-index: 2; }

/* A translucent figure strip inside a gradient header (attendance tally). */
.headstats {
  display: flex; gap: 6px; align-items: center;
  background: rgba(0,0,0,.14); border-radius: 10px; padding: 8px 10px;
}
.headstats .hs { flex: 1; text-align: center; min-width: 0; }
.headstats .hs .v { font-family: var(--font-display); font-weight: 800; font-size: 15px; }
.headstats .hs .k { font-size: 8.5px; opacity: .85; font-weight: 600; }
.headstats .sep { width: 1px; height: 22px; background: rgba(255,255,255,.25); flex-shrink: 0; }

/* Equal-width segmented control on a gradient header. */
.headseg { display: flex; gap: 6px; }
.headseg > button {
  flex: 1; text-align: center; font-size: 10px; font-weight: 700;
  padding: 7px 4px; border-radius: 9px;
  background: rgba(255,255,255,.18); color: #fff;
  -webkit-tap-highlight-color: transparent;
}
.headseg > button.on { background: #fff; color: var(--brand-dark); }

/* --------------------------------------------------------------------------
   Scroll body
   -------------------------------------------------------------------------- */
.scroller {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  scrollbar-width: none;
  padding: 14px 14px 20px;
}
.scroller::-webkit-scrollbar { display: none; }
.scroller.flush { padding-left: 0; padding-right: 0; }
.scroller > .pad { padding: 0 14px; }

/* Room for the tab bar and any sticky action bar. */
.screen.has-tabbar .scroller { padding-bottom: calc(var(--tabbar-h) + var(--safe-bottom) + 18px); }
.screen.has-actionbar .scroller { padding-bottom: calc(76px + var(--safe-bottom) + 12px); }
.screen.has-tabbar.has-actionbar .scroller { padding-bottom: calc(var(--tabbar-h) + 76px + var(--safe-bottom) + 12px); }

/* --------------------------------------------------------------------------
   Pull to refresh
   -------------------------------------------------------------------------- */
.ptr {
  position: absolute; top: 0; left: 0; right: 0;
  display: flex; align-items: center; justify-content: center;
  height: 54px; pointer-events: none; z-index: 3;
  opacity: 0; transform: translateY(-12px);
  transition: opacity var(--dur-fast), transform var(--dur-fast);
}
.ptr.on { opacity: 1; transform: translateY(0); }
.ptr .ring {
  width: 26px; height: 26px; border-radius: 50%;
  border: 2.5px solid rgba(255,255,255,.35); border-top-color: #fff;
}
.ptr.spinning .ring { animation: spin .7s linear infinite; }

/* --------------------------------------------------------------------------
   Bottom tab bar
   -------------------------------------------------------------------------- */
.tabbar {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 30;
  display: flex;
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  -webkit-backdrop-filter: saturate(180%) blur(18px);
  backdrop-filter: saturate(180%) blur(18px);
  border-top: 1px solid var(--line);
  padding-bottom: var(--safe-bottom);
}
.tabbar > button {
  flex: 1;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 9px 6px 14px;
  color: var(--text-faint);
  -webkit-tap-highlight-color: transparent;
  position: relative;
  transition: color var(--dur-fast);
}
.tabbar > button .tl { font-size: 9px; font-weight: 700; letter-spacing: .005em; }
.tabbar > button.on { color: var(--brand); }
.tabbar > button.on .tico { transform: translateY(-1px); }
.tabbar > button .tico { transition: transform var(--dur) var(--ease-spring); }
.tabbar > button:active .tico { transform: scale(.86); }
.tabbar > button .badge-count { position: absolute; top: 4px; right: 50%; margin-right: -20px; transform: scale(.8); }
/* active pip */
.tabbar > button.on::before {
  content: ''; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 22px; height: 3px; border-radius: 0 0 4px 4px; background: var(--brand);
}

/* --------------------------------------------------------------------------
   Sticky action bar (Pay now / Submit attendance / Save)
   -------------------------------------------------------------------------- */
.actionbar {
  position: absolute; left: 0; right: 0; z-index: 25;
  bottom: 0;
  padding: 11px 14px calc(18px + var(--safe-bottom));
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px);
  border-top: 1px solid var(--line);
  display: flex; gap: 10px; align-items: center;
}
.screen.has-tabbar .actionbar { bottom: calc(var(--tabbar-h) + var(--safe-bottom)); padding-bottom: 11px; }

/* Floating action button */
.fab {
  position: absolute; right: 16px; z-index: 26;
  bottom: calc(var(--tabbar-h) + var(--safe-bottom) + 16px);
  width: 54px; height: 54px; border-radius: 18px;
  background: var(--accent); color: #fff;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 16px 30px -12px var(--accent);
  transition: transform var(--dur) var(--ease-spring);
  -webkit-tap-highlight-color: transparent;
}
.fab:active { transform: scale(.9); }

/* --------------------------------------------------------------------------
   Bottom sheet
   -------------------------------------------------------------------------- */
.scrim {
  position: fixed; inset: 0; z-index: 60;
  background: var(--overlay);
  opacity: 0; transition: opacity var(--dur) var(--ease-out);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
.scrim.on { opacity: 1; }

.sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 61;
  background: var(--surface);
  border-radius: 24px 24px 0 0;
  box-shadow: var(--sh-sheet);
  max-height: 92dvh;
  display: flex; flex-direction: column;
  transform: translate3d(0, 100%, 0);
  transition: transform var(--dur-slow) var(--ease-out);
  padding-bottom: var(--safe-bottom);
}
.sheet.on { transform: translate3d(0, 0, 0); }
.sheet.dragging { transition: none; }
.sheet-grip {
  width: 38px; height: 4.5px; border-radius: 3px; background: var(--line-strong);
  margin: 9px auto 4px; flex-shrink: 0;
}
.sheet-head {
  display: flex; align-items: center; gap: 10px;
  padding: 6px 16px 12px; flex-shrink: 0;
}
.sheet-head .t { font-family: var(--font-display); font-weight: 700; font-size: 16px; flex: 1; letter-spacing: -.02em; }
.sheet-body { overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 0 16px 16px; scrollbar-width: none; }
.sheet-body::-webkit-scrollbar { display: none; }
.sheet-foot {
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--line); display: flex; gap: 9px; flex-shrink: 0;
}

/* Action sheet — a list of choices, iOS style. */
.actionsheet { padding: 0 10px 10px; }
.actionsheet .as-group {
  background: var(--surface-2); border-radius: 15px; overflow: hidden; margin-bottom: 9px;
}
.actionsheet .as-item {
  width: 100%; padding: 14px; font-size: 15px; font-weight: 600; text-align: center;
  border-bottom: 1px solid var(--line);
}
.actionsheet .as-item:last-child { border-bottom: none; }
.actionsheet .as-item.danger { color: var(--bad); }
.actionsheet .as-cancel {
  width: 100%; padding: 14px; font-size: 15px; font-weight: 800;
  background: var(--surface); border-radius: 15px; border: 1px solid var(--line);
}

/* --------------------------------------------------------------------------
   Centre modal (used on desktop; phones prefer sheets)
   -------------------------------------------------------------------------- */
.modal {
  position: fixed; inset: 0; z-index: 61;
  display: flex; align-items: center; justify-content: center; padding: 20px;
  pointer-events: none;
}
.modal-card {
  background: var(--surface); border-radius: var(--r-xl);
  width: 100%; max-width: 520px; max-height: 86dvh;
  display: flex; flex-direction: column; box-shadow: var(--sh-xl);
  border: 1px solid var(--line);
  pointer-events: auto;
  opacity: 0; transform: scale(.96) translateY(8px);
  transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}
.modal.on .modal-card { opacity: 1; transform: none; }
.modal-head { display: flex; align-items: center; gap: 10px; padding: 16px 18px 12px; }
.modal-head .t { font-family: var(--font-display); font-weight: 700; font-size: 17px; flex: 1; }
.modal-body { overflow-y: auto; padding: 0 18px 18px; }
.modal-foot { padding: 13px 18px; border-top: 1px solid var(--line); display: flex; gap: 9px; justify-content: flex-end; }

/* --------------------------------------------------------------------------
   Toast
   -------------------------------------------------------------------------- */
.toasts {
  position: fixed; left: 0; right: 0; z-index: 90;
  bottom: calc(var(--tabbar-h) + var(--safe-bottom) + 14px);
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  pointer-events: none; padding: 0 16px;
}
body.no-tabbar .toasts { bottom: calc(var(--safe-bottom) + 18px); }
.toast {
  display: flex; align-items: center; gap: 9px;
  background: #1b1b26; color: #fff;
  padding: 11px 15px; border-radius: 14px;
  font-size: 13px; font-weight: 600; max-width: 440px;
  box-shadow: 0 18px 34px -16px rgba(0,0,0,.6);
  animation: toastIn .26s var(--ease-spring);
}
.toast.out { animation: toastOut .2s var(--ease-in-out) forwards; }
.toast .ti { flex-shrink: 0; }
.toast-good .ti { color: #4ade80; }
.toast-bad .ti { color: #fca5a5; }
.toast-warn .ti { color: #fcd34d; }
@keyframes toastIn { from { opacity: 0; transform: translateY(14px) scale(.94); } to { opacity: 1; transform: none; } }
@keyframes toastOut { to { opacity: 0; transform: translateY(8px) scale(.97); } }

/* --------------------------------------------------------------------------
   Drawer (desktop sidebar on phones, and the public-site menu)
   -------------------------------------------------------------------------- */
.drawer {
  position: fixed; top: 0; bottom: 0; left: 0; z-index: 61;
  width: 288px; max-width: 86vw;
  background: var(--surface); border-right: 1px solid var(--line);
  transform: translate3d(-100%, 0, 0);
  transition: transform var(--dur-slow) var(--ease-out);
  display: flex; flex-direction: column;
  padding-top: var(--safe-top);
}
.drawer.on { transform: translate3d(0, 0, 0); }
.drawer-body { flex: 1; overflow-y: auto; padding: 12px; scrollbar-width: none; }
.drawer-body::-webkit-scrollbar { display: none; }

/* --------------------------------------------------------------------------
   DESKTOP  (>= 900px)
   -------------------------------------------------------------------------- */
@media (min-width: 900px) {
  body.mode-app { position: static; overflow: auto; height: auto; }

  .appframe {
    height: 100dvh;
    display: grid;
    grid-template-columns: var(--sidebar-w) 1fr;
    grid-template-rows: 100dvh;
  }

  /* Sidebar becomes permanent navigation. */
  .sidebar {
    display: flex; flex-direction: column;
    background: var(--grad-deep, var(--brand-deep)); color: rgba(255,255,255,.82);
    overflow-y: auto; scrollbar-width: none;
    padding: 22px 16px;
  }
  .sidebar::-webkit-scrollbar { display: none; }
  .sidebar .sb-brand {
    display: flex; align-items: center; gap: 11px;
    padding: 0 6px 20px; border-bottom: 1px solid rgba(255,255,255,.15);
  }
  .sidebar .sb-brand .n { font-family: var(--font-display); font-weight: 700; font-size: 13.5px; color: #fff; line-height: 1.2; }
  .sidebar .sb-brand .s { font-size: 10.5px; opacity: .6; font-weight: 600; }
  .sidebar .sb-group { padding: 16px 0 2px; }
  .sidebar .sb-group > .h {
    font-size: 9.5px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase;
    color: rgba(255,255,255,.4); padding: 0 8px 7px;
  }
  .sidebar .sb-item {
    display: flex; align-items: center; gap: 11px; width: 100%;
    padding: 10px 12px; border-radius: 11px; margin-bottom: 3px;
    font-size: 13px; font-weight: 600; color: rgba(255,255,255,.78); text-align: left;
    transition: background var(--dur-fast), color var(--dur-fast);
  }
  .sidebar .sb-item:hover { background: rgba(255,255,255,.1); color: #fff; }
  .sidebar .sb-item.on { background: rgba(255,255,255,.16); color: #fff; font-weight: 700; }
  .sidebar .sb-item .badge-count { margin-left: auto; }
  .sidebar .sb-foot { margin-top: auto; padding-top: 14px; }
  .sidebar .sb-year {
    background: rgba(255,255,255,.12); border-radius: var(--r-lg); padding: 13px; margin-bottom: 10px;
  }
  .sidebar .sb-year .y1 { font-size: 11px; opacity: .8; font-weight: 600; }
  .sidebar .sb-year .y2 { font-family: var(--font-display); font-weight: 700; font-size: 15px; margin-top: 2px; color: #fff; }

  .screens { overflow: auto; background: var(--canvas); }

  /* Flatten the stack: only the top screen is shown, statically positioned. */
  .screen { position: relative; inset: auto; min-height: 100dvh; transform: none !important; }
  .screen:not(.is-top) { display: none; }
  .screen::after { display: none; }
  .screen.anim { transition: none; }
  .screen { animation: deskFade .18s var(--ease-out); }
  @keyframes deskFade { from { opacity: 0; } to { opacity: 1; } }

  /* Desktop app bar: light, sticky, with a breadcrumb-ish title. */
  .appbar {
    background: color-mix(in srgb, var(--surface) 86%, transparent);
    -webkit-backdrop-filter: saturate(180%) blur(16px); backdrop-filter: saturate(180%) blur(16px);
    color: var(--text);
    border-bottom: 1px solid var(--line);
    position: sticky; top: 0; padding-top: 0;
  }
  .appbar-inner { padding: 0 26px; min-height: 66px; }
  .appbar-title { font-size: 20px; }
  .appbar .backbtn { display: none; }
  .appbar .iconbtn.glass { background: var(--surface-2); color: var(--text-soft); }
  .appbar .searchbox.on-header { background: var(--surface-2); border-color: var(--line); }
  .appbar .searchbox.on-header input { color: var(--text); }
  .appbar .searchbox.on-header input::placeholder { color: var(--text-faint); }
  .appbar .headtabs > button { background: var(--surface-2); color: var(--text-soft); }
  .appbar .headtabs > button.on { background: var(--brand); color: #fff; }
  .appbar-extra { padding: 0 26px 12px; }

  /* Hero keeps its gradient but breathes. */
  .hero { padding: 22px 26px 32px; border-radius: 0; }
  .hero .who { font-size: 24px; }

  .scroller {
    overflow: visible; padding: 0 26px 48px;
    max-width: 1280px; width: 100%; margin: 0 auto;
  }
  .screen.has-tabbar .scroller,
  .screen.has-actionbar .scroller,
  .screen.has-tabbar.has-actionbar .scroller { padding-bottom: 48px; }
  .scroller > .pad { padding: 0; }

  .tabbar { display: none; }
  .fab { display: none; }
  .statusbar { display: none; }

  .actionbar {
    position: sticky; bottom: 0; margin: 0 -26px -48px;
    padding: 14px 26px calc(14px + env(safe-area-inset-bottom, 0px));
    justify-content: flex-end;
  }
  .screen.has-tabbar .actionbar { bottom: 0; }

  .toasts { bottom: 22px; }

  /* Sheets become centre modals on a desktop viewport. */
  .sheet {
    left: 50%; right: auto; bottom: auto; top: 50%;
    transform: translate3d(-50%, -46%, 0) scale(.97);
    width: 560px; max-width: calc(100vw - 48px);
    max-height: 84dvh; border-radius: var(--r-xl);
    opacity: 0; box-shadow: var(--sh-xl);
    transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
  }
  .sheet.on { transform: translate3d(-50%, -50%, 0) scale(1); opacity: 1; }
  .sheet-grip { display: none; }
  .sheet-head { padding: 18px 20px 12px; }
  .sheet-body { padding: 0 20px 20px; }
  .sheet-foot { padding: 14px 20px; justify-content: flex-end; }
  .actionsheet { padding: 0 20px 20px; }
  .actionsheet .as-item { text-align: left; }

  .drawer { display: none; }

  /* Denser typography where there is room for it. */
  .sec-head .t { font-size: 16px; }
  .lrow { padding: 13px 15px; }
  .lrow .lr-t { font-size: 13.5px; }
  .lrow .lr-s { font-size: 11.5px; }
  .tiles { grid-template-columns: repeat(8, 1fr); gap: 10px; }
  .tile { padding: 14px 6px; }
  .tile .l { font-size: 11px; }
  .statstrip { padding: 16px; gap: 10px; }
  .stat-cell .v { font-size: 22px; }
  .stat-cell .k { font-size: 11px; }
  .grid-d2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-d3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .grid-d4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .hide-desk { display: none !important; }
}

@media (max-width: 899px) {
  .sidebar { display: none; }
  .hide-mob { display: none !important; }
  /* Screens that are pushed (not tabs) hide the tab bar for focus. */
  body.no-tabbar .tabbar { display: none; }
}

/* --------------------------------------------------------------------------
   Mobile table → stacked cards
   Any `table.data.cardify` becomes a list of labelled cards below 900px, so a
   14-column admin table stays readable on a phone instead of scrolling.
   -------------------------------------------------------------------------- */
@media (max-width: 899px) {
  table.data.cardify, table.data.cardify tbody, table.data.cardify tr, table.data.cardify td {
    display: block; width: 100%;
  }
  table.data.cardify thead { display: none; }
  table.data.cardify tr {
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--r-md); margin-bottom: 8px; padding: 4px 2px;
    box-shadow: var(--sh-xs);
  }
  table.data.cardify td {
    display: flex; justify-content: space-between; align-items: center; gap: 12px;
    border: none; padding: 7px 13px; text-align: right;
  }
  table.data.cardify td::before {
    content: attr(data-l);
    font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
    color: var(--text-faint); text-align: left; flex-shrink: 0;
  }
  table.data.cardify td[data-l=""]::before { content: none; }
  table.data.cardify td.rowhead {
    border-bottom: 1px solid var(--divider); padding-bottom: 9px; margin-bottom: 3px;
    font-weight: 700; font-size: 13.5px; text-align: left;
  }
  table.data.cardify td.rowhead::before { content: none; }
  .tablewrap:has(table.cardify) { background: none; border: none; box-shadow: none; border-radius: 0; }
}


/* --------------------------------------------------------------------------
   Guided tour — a coach-mark ring plus a docked explainer card. Deliberately
   fixed-position rather than anchored beside the target: with ~20 heterogeneous
   screens to point at, a docked card that never has to dodge a viewport edge
   is far more robust than one that tries to float beside every shape.
   -------------------------------------------------------------------------- */
.tour-dim {
  position: fixed; inset: 0; z-index: 200;
  background: var(--overlay);
  -webkit-backdrop-filter: blur(1px); backdrop-filter: blur(1px);
  animation: tourFade .2s var(--ease-out);
}
.tour-ring {
  position: fixed; z-index: 201; display: none;
  border: 3px solid var(--brand); border-radius: 14px;
  box-shadow: 0 0 0 3px rgba(0, 0, 0, .22), 0 16px 34px -16px rgba(0, 0, 0, .55);
  pointer-events: none;
  transition: top var(--dur) var(--ease-out), left var(--dur) var(--ease-out),
              width var(--dur) var(--ease-out), height var(--dur) var(--ease-out);
  animation: tourPulse 1.6s ease-in-out infinite;
}
@keyframes tourFade { from { opacity: 0; } to { opacity: 1; } }
@keyframes tourPulse { 0%, 100% { opacity: 1; } 50% { opacity: .5; } }
.tour-card {
  position: fixed; z-index: 202;
  left: 12px; right: 12px; bottom: calc(var(--safe-bottom) + 14px);
  background: var(--surface); border-radius: var(--r-xl);
  box-shadow: var(--sh-xl); border: 1px solid var(--line);
  padding: 16px 16px 14px;
  animation: tourCardIn .28s var(--ease-spring);
}
/* The one step that spotlights the bottom tab bar: docking at the bottom
   as usual would bury the tab bar it is pointing at under the card. */
.tour-card.pos-top { bottom: auto; top: calc(var(--safe-top) + 14px); }
@keyframes tourCardIn { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
.tour-eyebrow {
  font-size: 10.5px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--brand); margin-bottom: 6px; padding-right: 26px;
}
.tour-close {
  position: absolute; top: 12px; right: 12px; color: var(--text-faint);
  width: 26px; height: 26px; display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
}
.tour-close:active { background: var(--surface-2); }
.tour-title {
  font-family: var(--font-display); font-weight: 700; font-size: 16.5px;
  margin-bottom: 4px; letter-spacing: -.01em;
}
.tour-body { font-size: 13px; line-height: 1.55; color: var(--text-soft); margin-bottom: 12px; }
.tour-dots { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.tour-dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--surface-3); flex-shrink: 0;
  transition: background var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}
.tour-dot.is-done { background: var(--brand); opacity: .45; }
.tour-dot.is-active { background: var(--brand); transform: scale(1.35); }
.tour-foot { display: flex; gap: 9px; }

@media (min-width: 900px) {
  .tour-card { left: auto; right: 24px; bottom: 24px; width: 380px; padding: 18px 18px 16px; }
}

/* --------------------------------------------------------------------------
   Installed to the home screen
   Standalone gives the app the whole screen, including the area behind the
   status bar and the home indicator. --safe-top / --safe-bottom already carry
   those insets; this only stops iOS rubber-banding the whole app frame.
   -------------------------------------------------------------------------- */
@media (display-mode: standalone) {
  html, body { overscroll-behavior: none; }
  body.mode-app { -webkit-user-select: none; user-select: none; }
  /* Text the user may legitimately want to copy stays selectable. */
  body.mode-app input,
  body.mode-app textarea,
  body.mode-app .t-body,
  body.mode-app .receipt { -webkit-user-select: text; user-select: text; }
}
