/* ==========================================================================
   Loyal Sathi — shop admin panel
   A calm, dense back-office. Every colour, space, radius, shadow and easing
   comes from tokens.css, which is loaded first. Nothing is fetched from a CDN.

   Layout: a fixed vertical sidebar on the left from 900px up; an off-canvas
   drawer below that. The page itself scrolls; the sidebar never does.
   ========================================================================== */

/* ---------------------------------------------------------- typography ---
   Montserrat now comes from tokens.css, for every surface at once — the two
   panels, the public site and the customer's own card.

   This file used to override the tokens here, because the card kept Playfair
   and Inter. That split meant a shop owner saw one typeface in their panel and
   their customers saw another, which read as two products rather than one. The
   override is gone; only the spacing tweak below is still needed.
   ------------------------------------------------------------------------ */

/* Montserrat is wider than Playfair at the same size, so headings are pulled
   in slightly to stop panel titles wrapping where they used to fit. */
h1, h2, h3, h4{ letter-spacing: var(--track-tight); }

:root{
  --side-w:      248px;   /* expanded sidebar          */
  --side-mini:    76px;   /* icons-only sidebar        */
  --side-drawer: 284px;   /* off-canvas drawer         */
  --side-cur:      0px;   /* the width in force now    */
  --a-top-h:      56px;   /* slim mobile bar           */
  --stick-top: var(--a-top-h);

  /* Sidebar mode switches. The defaults below are the "labels visible" set;
     the mini set is applied at 900–1099px and whenever the owner has
     collapsed the sidebar at 1100px and up. */
  --sd-lbl-pos:  static;
  --sd-lbl-w:    auto;
  --sd-lbl-h:    auto;
  --sd-lbl-ovf:  visible;
  --sd-lbl-clip: none;
  --sd-lbl-m:    0;
  --sd-just:     flex-start;
  --sd-pad:      0 var(--s-3);
  --sd-tip:      none;
  --sd-nav-ovf:  hidden auto;
  --sd-chev:     rotate(0deg);
  --sd-badge-r:  var(--s-3);
}

@media (min-width: 900px){
  :root{
    --side-cur:    var(--side-mini);
    --stick-top:   0px;
    --sd-lbl-pos:  absolute;
    --sd-lbl-w:    1px;
    --sd-lbl-h:    1px;
    --sd-lbl-ovf:  hidden;
    --sd-lbl-clip: inset(50%);
    --sd-lbl-m:    -1px;
    --sd-just:     center;
    --sd-pad:      0;
    --sd-tip:      block;
    --sd-nav-ovf:  visible;
    --sd-chev:     rotate(180deg);
    --sd-badge-r:  10px;
  }
}

@media (min-width: 1100px){
  :root{
    --side-cur:    var(--side-w);
    --sd-lbl-pos:  static;
    --sd-lbl-w:    auto;
    --sd-lbl-h:    auto;
    --sd-lbl-ovf:  visible;
    --sd-lbl-clip: none;
    --sd-lbl-m:    0;
    --sd-just:     flex-start;
    --sd-pad:      0 var(--s-3);
    --sd-tip:      none;
    --sd-nav-ovf:  hidden auto;
    --sd-chev:     rotate(0deg);
    --sd-badge-r:  var(--s-3);
  }
  html.ck-side-collapsed{
    --side-cur:    var(--side-mini);
    --sd-lbl-pos:  absolute;
    --sd-lbl-w:    1px;
    --sd-lbl-h:    1px;
    --sd-lbl-ovf:  hidden;
    --sd-lbl-clip: inset(50%);
    --sd-lbl-m:    -1px;
    --sd-just:     center;
    --sd-pad:      0;
    --sd-tip:      block;
    --sd-nav-ovf:  visible;
    --sd-chev:     rotate(180deg);
    --sd-badge-r:  10px;
  }
}

/* The icons-only rail shows tooltips, which need to escape the nav, so the nav
   cannot be a scroll container there. When the icons do not all fit, that spills
   them over the Collapse / signed-in block and draws two icons on top of each
   other.

   This used to be guarded by `@media (max-height: 620px)` — a guess at the
   height below which the list would not fit. The guess was wrong the moment the
   panels grew: at 900px tall the shop rail needs 848px of icons in a 651px box,
   which is well above the threshold and still overlaps.

   A stylesheet cannot ask "does this content fit", so sidebar.js measures it and
   sets .is-cramped on the rail. The variable is set on the sidebar rather than
   the nav so it simply inherits down, and tooltips are only given up on the
   screens that actually cannot afford them. */
.a-side.is-cramped{ --sd-nav-ovf: hidden auto; }

/* ---------------------------------------------------------------- base --- */
/* The sidebar is position:fixed, so letting the page rubber-band past its ends
   drags the content out from under it. Also kills pull-to-refresh, which staff
   trigger by accident when swiping a long customer list on a phone. */
/*
 * Both the horizontal clip and the rubber-band suppression belong on <html>,
 * which is the element that actually scrolls the viewport.
 *
 * Putting overflow-x on the BODY is the trap: it forces the body's overflow-y
 * to compute to `auto`, turning the body into its own scroll container. That
 * container is exactly as tall as its content so it can never scroll, and with
 * overscroll-behavior:none on it too, a mouse wheel anywhere over the page is
 * absorbed by it rather than chaining up to the root. Dragging the scrollbar
 * still works, which makes it look like the wheel is broken rather than the CSS.
 */
html{
  overflow-x: hidden;
  overscroll-behavior-y: none;
}
/* Fill the screen on a short page — but measured against the viewport you can
   actually SEE.

   On a phone, 100vh is the height the page would have with the browser's
   toolbar retracted, which is taller than the visible area while the toolbar
   is showing. A panel page with two cards on it was therefore always a little
   taller than the screen and always scrolled, with nothing to scroll to.

   100dvh tracks the visible area instead. The 100vh line above it stays as the
   fallback for browsers that do not know dvh, which is the same behaviour as
   before rather than a broken one. */
body{
  min-height: 100vh;
  min-height: 100dvh;
}

a{ color: var(--navy); }
a:hover{ color: var(--navy-deep); }

h1{ font-size: var(--t-xl); }
h2{ font-size: var(--t-md); }
h3{ font-size: var(--t-base); }
h4{ font-size: var(--t-sm); }
h1, h2, h3, h4{ margin: 0 0 var(--s-2); }
p{ margin: 0 0 var(--s-2); }
hr{ border: none; border-top: 1px solid var(--line); margin: var(--s-4) 0; }

code, kbd{
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  color: var(--text);
  background: var(--surface-3);
  padding: 1px 5px;
  border-radius: var(--r-sm);
}

.skip-link{
  position: absolute;
  left: var(--s-3);
  top: -100px;
  z-index: 200;
  background: var(--surface);
  color: var(--navy);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  padding: var(--s-2) var(--s-4);
  font-size: var(--t-sm);
  font-weight: 600;
  text-decoration: none;
  transition: top var(--dur-fast) var(--ease);
}
.skip-link:focus{ top: calc(var(--s-3) + var(--safe-top)); }

/* ============================================================= SIDEBAR === */
.a-side{
  position: fixed;
  inset: 0 auto 0 0;
  z-index: 90;
  width: var(--side-drawer);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  padding: calc(var(--s-4) + var(--safe-top)) var(--s-3) calc(var(--s-3) + var(--safe-bottom));
  color: var(--text-on-dark);
  /* visible, not hidden: the collapsed-state tooltips have to escape the rail */
  overflow: visible;

  /* Frosted navy: the same recipe as the customer app's bottom bar, blended
     toward navy instead of toward the light surface. Browsers without
     color-mix keep the solid brand gradient below. */
  background: var(--grad-navy);
  background: linear-gradient(155deg,
      color-mix(in srgb, var(--navy-soft) 88%, transparent) 0%,
      color-mix(in srgb, var(--navy)      90%, transparent) 45%,
      color-mix(in srgb, var(--navy-deep) 94%, transparent) 100%);
  -webkit-backdrop-filter: blur(16px) saturate(1.5);
  backdrop-filter: blur(16px) saturate(1.5);

  border-right: 1px solid color-mix(in srgb, var(--line) 26%, transparent);
  border-radius: 0 var(--r-xl) var(--r-xl) 0;
  box-shadow: var(--shadow-lg);
  box-shadow: 8px 0 28px color-mix(in srgb, var(--text) 14%, transparent);

  /* Off-canvas until opened; visibility keeps it out of the tab order.
     Visibility is switched instantly on open (so the first item can take
     focus straight away) and only after the slide finishes on close. */
  transform: translateX(-102%);
  visibility: hidden;
  transition: transform var(--dur-slow) var(--ease),
              visibility 0s linear var(--dur-slow);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){
  .a-side{ background: var(--grad-navy); }
}
.a-side.is-open{
  transform: none;
  visibility: visible;
  transition: transform var(--dur-slow) var(--ease), visibility 0s;
}

@media (min-width: 900px){
  .a-side{
    width: var(--side-cur);
    transform: none;
    visibility: visible;
    transition: width var(--dur) var(--ease);
  }
}

/* ------------------------------------------------------------ the brand -- */
.a-brand{
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-width: 0;
  min-height: 48px;
  padding: var(--s-1) var(--s-2);
  margin-bottom: var(--s-2);
  border-radius: var(--r-md);
  color: inherit;
  text-decoration: none;
  justify-content: var(--sd-just);
  transition: background var(--dur) var(--ease);
}
.a-brand:hover{ background: color-mix(in srgb, var(--text-on-dark) 8%, transparent); color: inherit; }

.a-logo{
  width: 38px;
  height: 38px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--navy-deep);
  border: 2px solid var(--gold);
  color: var(--gold);
  overflow: hidden;
}
.a-logo img{ width: 100%; height: 100%; object-fit: cover; display: block; }
.a-logo .icon{ width: 1.15em; height: 1.15em; }

.a-brand-txt{
  min-width: 0;
  display: flex;
  flex-direction: column;
  position: var(--sd-lbl-pos);
  width: var(--sd-lbl-w);
  height: var(--sd-lbl-h);
  overflow: var(--sd-lbl-ovf);
  clip-path: var(--sd-lbl-clip);
  margin: var(--sd-lbl-m);
}
.a-shop{
  font-family: var(--font-display);
  font-size: var(--t-base);
  font-weight: 700;
  letter-spacing: var(--track-tight);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.a-sub{
  font-size: var(--t-xs);
  color: var(--gold-soft);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  line-height: 1.4;
}

/* -------------------------------------------------------------- the nav -- */
.a-nav{
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 auto;
  min-height: 0;

  /* Scrolls whenever the labels are showing, and stays unclipped in the
     icons-only rail so the hover tooltips can escape it. Driven by the sidebar
     state variables above rather than a height guess, so adding a nav item can
     never quietly push the last one underneath the sign-out block again. */
  overflow: var(--sd-nav-ovf);

  /* Reserve the scrollbar's width even when it is not showing, so the thumb
     never sits on top of the nav pills. */
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  scrollbar-color: color-mix(in srgb, var(--text-on-dark) 22%, transparent) transparent;
}
.a-nav::-webkit-scrollbar{ width: 6px; }
.a-nav::-webkit-scrollbar-track{ background: transparent; }
.a-nav::-webkit-scrollbar-thumb{
  background: color-mix(in srgb, var(--text-on-dark) 18%, transparent);
  border-radius: var(--r-full);
}
.a-nav:hover::-webkit-scrollbar-thumb{
  background: color-mix(in srgb, var(--text-on-dark) 32%, transparent);
}

.a-nav-item{
  position: relative;
  display: flex;
  /* Same reason as .a-nav-group: min-height is not a floor once flexbox
     decides to shrink a column item, so say so outright. */
  flex: 0 0 auto;
  align-items: center;
  gap: var(--s-3);
  justify-content: var(--sd-just);
  min-height: 48px;
  padding: var(--sd-pad);
  border-radius: var(--r-md);
  font-size: var(--t-sm);
  font-weight: 500;
  color: color-mix(in srgb, var(--text-on-dark) 72%, transparent);
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--dur) var(--ease),
              background var(--dur) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.a-nav-item:hover{
  color: var(--text-on-dark);
  background: color-mix(in srgb, var(--text-on-dark) 8%, transparent);
}
.a-nav-item:active{ transform: scale(0.97); }
.a-nav-item.on{
  color: var(--text-on-dark);
  font-weight: 600;
  background: color-mix(in srgb, var(--text-on-dark) 12%, transparent);
}

/* The gold indicator: a pill on the leading edge that grows into place with
   the same timing as the customer app's active dot. */
.a-nav-dot{
  position: absolute;
  left: 0;
  top: 50%;
  margin-top: -11px;
  width: 4px;
  height: 22px;
  border-radius: var(--r-full);
  background: var(--gold);
  opacity: 0;
  transform: scaleY(0.4);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.a-nav-item.on .a-nav-dot{ opacity: 1; transform: none; }

.a-nav-ico{
  display: inline-flex;
  flex: none;
  line-height: 1;
}
.a-nav-ico .icon{ width: 1.5em; height: 1.5em; }

.a-nav-label{
  position: var(--sd-lbl-pos);
  width: var(--sd-lbl-w);
  height: var(--sd-lbl-h);
  overflow: var(--sd-lbl-ovf);
  clip-path: var(--sd-lbl-clip);
  margin: var(--sd-lbl-m);
  white-space: nowrap;
}

/* Count pill — the same gold-on-navy chip the customer app uses. */
.a-nav-badge{
  position: absolute;
  top: 7px;
  right: var(--sd-badge-r);
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: var(--r-full);
  background: var(--grad-gold);
  color: var(--navy-deep);
  font-size: var(--t-xs);
  font-weight: 700;
  line-height: 18px;
  text-align: center;
  box-shadow: 0 0 0 2px var(--navy-deep);
}

/* The hero action. Scan is what staff do all day, so it carries the same
   filled treatment as the customer app's centre button. */
.a-nav-item.hero{
  margin: var(--s-1) 0;
  background: color-mix(in srgb, var(--text-on-dark) 6%, transparent);
  border: 1px solid color-mix(in srgb, var(--gold) 30%, transparent);
}
.a-nav-item.hero .a-nav-ico{
  width: 40px;
  height: 40px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--grad-navy);
  color: var(--gold);
  box-shadow: var(--shadow-gold), 0 0 0 4px var(--navy-deep);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.a-nav-item.hero .a-nav-ico .icon{ width: 1.75em; height: 1.75em; }
.a-nav-item.hero.on{
  background: color-mix(in srgb, var(--gold) 14%, transparent);
  border-color: color-mix(in srgb, var(--gold) 52%, transparent);
}
.a-nav-item.hero.on .a-nav-ico{
  background: var(--grad-gold);
  color: var(--navy-deep);
}

/* On a laptop-height window the full menu does not fit at 48px a row, and a
   nav you have to find a scrollbar in is a nav whose last item never gets
   clicked. Tighten the rows instead so everything stays on screen. The 48px
   tap target only matters on touch, which is the drawer below 900px, so the
   desktop rail and sidebar can safely go smaller. */
/* Fitting ten items and three group headings without a scrollbar.
 *
 * Retuned when the panel moved to Montserrat, which sits taller than Inter at
 * the same size — the old thresholds were measured against the old face and
 * the last item started falling below the fold. Each step compresses the rows
 * and the headings together, because a heading costs as much vertical space
 * as half a row.
 */
/* Ten items and three headings, measured against the real content:
 *   header ~90 + nav ~590 + collapse/user/sign-out ~150  =  ~830px
 * so full-size rows fit comfortably down to about 860px and only tighten
 * below that. Compressing earlier than necessary left the rows squeezed AND a
 * gap at the bottom, which looks like a mistake rather than a dense menu.
 */
/* Fitting the menu on a shorter window.
 *
 * The space comes out of the SPACING, not the rows. Squeezing the rows makes
 * the labels hard to read and the whole menu look cramped, while the padding
 * between groups is dead space nobody is looking at — there is about 80px of
 * it, which is most of what is needed.
 *
 * Measured: at full size the menu is 651px and the brand and sign-out blocks
 * take 249px around it, so 900px is where the default stops fitting.
 */
@media (min-width: 900px) and (max-height: 899px){
  .a-nav{ gap: 0; }
  .a-nav-group{ margin: 10px 0 1px; }
}
@media (min-width: 900px) and (max-height: 840px){
  .a-nav-group{ margin: 6px 0 0; }
  .a-side-foot{ padding-top: var(--s-2); padding-bottom: var(--s-2); }
}
@media (min-width: 900px) and (max-height: 795px){
  /* Only now do the rows give a little, and only to 44px — still a
     comfortable touch target and the label stays at full size. */
  .a-nav-item{ min-height: 44px; padding-block: 0; }
  /* The headings carry their own line-height, which is enough separation on
     its own once the margins are gone. */
  .a-nav-group{ margin: 0; }

  /* The rest comes out of the block underneath the menu. Collapse, the signed
     in line and Log out are pressed once a session at most, so they can be
     tighter than a menu that gets used all day. */
  .a-side-foot{ gap: 0; }
  .a-side-foot .a-collapse,
  .a-side-foot .a-who,
  .a-side-foot .a-logout{ min-height: 34px; }
  .a-side-foot .a-who{ padding-block: 0; }
  /* "Signed in" is a caption above the name; the name alone still says it. */
  .a-side-foot .a-who .a-who-lbl{ display: none; }
}
/* Below roughly 700px nothing fits ten items and three headings at a size
   worth reading, so it scrolls rather than shrink any further. */

/* Tooltips — only when the labels are clipped. */
.a-nav-item::after,
.a-side-foot [data-label]::after{
  content: attr(data-label);
  display: var(--sd-tip);
  position: absolute;
  left: calc(100% + 10px);
  top: 50%;
  transform: translateY(-50%);
  background: var(--navy-deep);
  color: var(--text-on-dark);
  border: 1px solid color-mix(in srgb, var(--gold) 34%, transparent);
  border-radius: var(--r-sm);
  padding: 6px var(--s-3);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
  box-shadow: var(--shadow-md);
  opacity: 0;
  pointer-events: none;
  z-index: 5;
  transition: opacity var(--dur-fast) var(--ease);
}
.a-nav-item:hover::after,
.a-nav-item:focus-visible::after,
.a-side-foot [data-label]:hover::after,
.a-side-foot [data-label]:focus-visible::after{ opacity: 1; }

/* --------------------------------------------------------- sidebar foot -- */
.a-side-foot{
  flex: none;
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  padding-top: var(--s-2);
  border-top: 1px solid color-mix(in srgb, var(--text-on-dark) 14%, transparent);
}

.a-collapse{
  display: none;
  align-items: center;
  gap: var(--s-3);
  justify-content: var(--sd-just);
  min-height: 44px;
  padding: var(--sd-pad);
  border: none;
  background: none;
  border-radius: var(--r-md);
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 500;
  color: color-mix(in srgb, var(--text-on-dark) 62%, transparent);
  cursor: pointer;
  position: relative;
  transition: color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.a-collapse .icon{ transform: var(--sd-chev); transition: transform var(--dur) var(--ease); }
.a-collapse:hover{
  color: var(--text-on-dark);
  background: color-mix(in srgb, var(--text-on-dark) 8%, transparent);
}
@media (min-width: 1100px){ .a-collapse{ display: flex; } }

.a-who{
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  justify-content: var(--sd-just);
  min-height: 44px;
  padding: var(--sd-pad);
  min-width: 0;
}
.a-who-ico{
  width: 30px;
  height: 30px;
  flex: none;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: color-mix(in srgb, var(--text-on-dark) 12%, transparent);
  color: var(--gold-soft);
}
.a-who-txt{
  min-width: 0;
  display: flex;
  flex-direction: column;
  position: var(--sd-lbl-pos);
  width: var(--sd-lbl-w);
  height: var(--sd-lbl-h);
  overflow: var(--sd-lbl-ovf);
  clip-path: var(--sd-lbl-clip);
  margin: var(--sd-lbl-m);
}
.a-who-lbl{
  font-size: var(--t-xs);
  color: var(--gold-soft);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  line-height: 1.3;
}
.a-who-name{
  font-size: var(--t-sm);
  font-weight: 600;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.a-logout{
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--s-3);
  justify-content: var(--sd-just);
  min-height: 44px;
  padding: var(--sd-pad);
  border-radius: var(--r-md);
  font-size: var(--t-sm);
  font-weight: 500;
  color: color-mix(in srgb, var(--text-on-dark) 72%, transparent);
  text-decoration: none;
  transition: color var(--dur) var(--ease), background var(--dur) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.a-logout:hover{
  color: var(--text-on-dark);
  background: color-mix(in srgb, var(--text-on-dark) 10%, transparent);
}
.a-logout:active{ transform: scale(0.97); }

/* ----------------------------------------------------------- the scrim --- */
.a-scrim{
  position: fixed;
  inset: 0;
  z-index: 85;
  background: color-mix(in srgb, var(--navy-deep) 58%, transparent);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.a-scrim.is-on{ opacity: 1; }
.a-scrim[hidden]{ display: none; }
@media (min-width: 900px){ .a-scrim{ display: none !important; } }

/* ========================================================== SLIM TOP BAR = */
.a-top{
  position: sticky;
  top: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: var(--s-2);
  height: calc(var(--a-top-h) + var(--safe-top));
  padding: var(--safe-top) var(--s-2) 0;
  background: color-mix(in srgb, var(--navy) 92%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  backdrop-filter: blur(14px) saturate(1.4);
  color: var(--text-on-dark);
  box-shadow: var(--shadow-sm);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){
  .a-top{ background: var(--navy); }
}
@media (min-width: 900px){ .a-top{ display: none; } }

.a-burger,
.a-top-logout{
  width: 44px;
  height: 44px;
  flex: none;
  display: grid;
  place-items: center;
  border: none;
  background: none;
  color: inherit;
  border-radius: var(--r-md);
  cursor: pointer;
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.a-burger:hover, .a-top-logout:hover{
  background: color-mix(in srgb, var(--text-on-dark) 12%, transparent);
  color: inherit;
}
.a-burger:active, .a-top-logout:active{ transform: scale(0.97); }
.a-burger .icon, .a-top-logout .icon{ width: 1.5em; height: 1.5em; }

.a-top-title{
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-display);
  font-size: var(--t-md);
  font-weight: 700;
  letter-spacing: var(--track-tight);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ============================================================== SHELL ==== */
.a-shell{
  margin-left: var(--side-cur);
  min-height: 100vh;
  min-height: 100dvh;   /* see the note on body — same reason */
  transition: margin-left var(--dur) var(--ease);
}
.a-main{
  max-width: var(--admin-max);
  margin: 0 auto;
  padding: var(--s-4) var(--s-4) var(--s-12);
  outline: none;
}
@media (min-width: 900px){
  .a-main{ padding: var(--s-5) var(--s-6) var(--s-10); }
}

/* -------------------------------------------------------- the page head -- */
.a-page-head{
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
  margin-bottom: var(--s-4);
}
.a-page-head h1{
  margin: 0;
  font-size: clamp(1.35rem, 1.1rem + 1vw, var(--t-xl));
}
.a-page-head .sub{
  font-size: var(--t-sm);
  color: var(--text-muted);
  margin: 3px 0 0;
  line-height: var(--lh-snug);
}
.a-page-head .acts{
  display: flex;
  gap: var(--s-2);
  flex-wrap: wrap;
  align-items: center;
}

/* --------------------------------------------------------- sticky tools -- */
.a-toolbar{
  position: sticky;
  top: var(--stick-top);
  z-index: 20;
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-2) var(--s-3);
  margin-bottom: var(--s-3);
  box-shadow: var(--shadow-xs);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){
  .a-toolbar{ background: var(--bg); }
}
.a-toolbar .filters{ gap: var(--s-2); }
.a-toolbar .field{ margin-bottom: 0; }
/* A two-row toolbar is too tall to pin; it scrolls away and the sticky table
   header carries the context instead. */
.a-toolbar.no-stick{
  position: static;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  background: var(--surface);
}

/* A sticky <th> resolves its offset against the nearest scroll container. A
   `.tbl-wrap` (overflow-x) or a `.card.flush` (overflow:hidden) is one, and
   since neither ever scrolls vertically an offset there would simply shove
   the header down over the first row. So the header pins to 0 by default,
   and only the list tables that opt out of both wrappers pin to the page. */
@media (min-width: 900px){
  .card.flush.open{ overflow: visible; }
  .card.flush.open .tbl-wrap.fits{ overflow: visible; }
  .stick-under .tbl-wrap.fits table.tbl thead th{ top: 62px; }
  .card.flush.open table.tbl thead tr th:first-child{ border-top-left-radius: var(--r-lg); }
  .card.flush.open table.tbl thead tr th:last-child{ border-top-right-radius: var(--r-lg); }
}

/* -------------------------------------------------------- sticky footer -- */
.a-stickbar{
  position: sticky;
  bottom: 0;
  z-index: 15;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
  margin-top: var(--s-3);
  padding: var(--s-2) var(--s-3) calc(var(--s-2) + var(--safe-bottom));
  background: color-mix(in srgb, var(--bg) 93%, transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){
  .a-stickbar{ background: var(--bg); }
}

/* ============================================================ MESSAGES === */
.msg{
  font-size: var(--t-sm);
  line-height: var(--lh-snug);
  padding: var(--s-3);
  border-radius: var(--r-md);
  margin-bottom: var(--s-3);
  border: 1px solid transparent;
}
.msg.ok{   background: var(--ok-bg);     color: var(--ok);     border-color: color-mix(in srgb, var(--ok) 26%, transparent); }
.msg.err{  background: var(--danger-bg); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 26%, transparent); }
.msg.warn{ background: var(--warn-bg);   color: var(--warn);   border-color: color-mix(in srgb, var(--warn) 26%, transparent); }
.msg a{ color: currentColor; font-weight: 600; }

.banner-warn{
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  background: var(--warn-bg);
  border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent);
  color: var(--warn);
  border-radius: var(--r-md);
  padding: var(--s-3);
  margin-bottom: var(--s-3);
  font-size: var(--t-sm);
  line-height: var(--lh-snug);
}
.banner-warn a{ color: currentColor; font-weight: 700; }
.banner-warn .b-ico{ display: inline-flex; flex: none; margin-top: 1px; }

/* Signed in as somebody else, from the platform panel.
   Deliberately the loudest thing on the page — a subtle version of this
   warning is the same as not having one. */
.banner-impersonate{
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s-3);
  background: #3B1E55;
  color: #F3E9FB;
  border-radius: var(--r-md);
  padding: var(--s-3) var(--s-4);
  margin-bottom: var(--s-3);
  font-size: var(--t-sm);
  line-height: var(--lh-snug);
}
.banner-impersonate span{ flex: 1 1 240px; }
.banner-impersonate strong{ color: #fff; }
.banner-impersonate .b-ico{ display: inline-flex; flex: none; }
.banner-impersonate .btn{
  flex: none;
  background: rgb(255 255 255 / 14%);
  color: #fff;
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 30%);
}
.banner-impersonate .btn:hover{ background: rgb(255 255 255 / 24%); }

/* ============================================================== PANELS === */
.card{
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-4);
  box-shadow: var(--shadow-xs);
  margin-bottom: var(--s-3);
}
.card.tight{ padding: var(--s-3); }
.card.flush{ padding: 0; overflow: hidden; }
.card > h2:first-child, .card > h3:first-child{ margin-top: 0; }

.card-head{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  flex-wrap: wrap;
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--line);
  background: var(--surface-2);
}
.card-head h2, .card-head h3{ margin: 0; }
.card-body{ padding: var(--s-4); }

.section-label{
  font-size: var(--t-xs);
  letter-spacing: var(--track-wide);
  color: var(--text-muted);
  text-transform: uppercase;
  margin: 0 0 var(--s-3);
  font-weight: 700;
}
.hint{ font-size: var(--t-sm); color: var(--text-muted); margin: var(--s-1) 0 0; line-height: var(--lh-snug); }
.muted{ color: var(--text-muted); }
.small{ font-size: var(--t-sm); }
.mono{ font-family: var(--font-mono); }
.empty{
  font-size: var(--t-sm);
  color: var(--text-muted);
  text-align: center;
  padding: var(--s-6) var(--s-3);
  line-height: var(--lh-snug);
}

.grid2{ display: grid; grid-template-columns: 1fr; gap: var(--s-3); }
.grid3{ display: grid; grid-template-columns: 1fr; gap: var(--s-3); }
@media (min-width: 760px){
  .grid2{ grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
  .grid3{ grid-template-columns: repeat(3, minmax(0, 1fr)); align-items: start; }
}

/* A responsive column flow that keeps long stacks short: sections fill one
   column then the next, so a six-section page is half as tall. */
.a-columns{ columns: 1; column-gap: var(--s-4); }
@media (min-width: 1000px){ .a-columns{ columns: 2; } }
.a-columns > *{ break-inside: avoid; -webkit-column-break-inside: avoid; }

/* Customer detail: everything about the card on the left, the activity feed
   on the right, so the page is as tall as its tallest half, not their sum. */
.cv-grid{ display: grid; grid-template-columns: 1fr; gap: 0 var(--s-4); align-items: start; }
@media (min-width: 1000px){ .cv-grid{ grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr); } }
.cv-grid > *{ min-width: 0; }

/* ============================================================= BUTTONS === */
.btn{
  font-family: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  padding: var(--s-2) var(--s-4);
  border-radius: var(--r-full);
  border: 1px solid var(--navy);
  background: var(--navy);
  color: var(--text-on-dark);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 44px;
  text-decoration: none;
  text-align: center;
  line-height: 1.2;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
              color var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              transform var(--dur-fast) var(--ease);
}
.btn:hover{ background: var(--navy-deep); border-color: var(--navy-deep); color: var(--text-on-dark); }
.btn:active{ transform: scale(0.98); }
.btn:disabled{ opacity: 0.5; cursor: not-allowed; transform: none; }
.btn .icon{ width: 1.15em; height: 1.15em; }

.btn.gold{ background: var(--grad-gold); border-color: var(--gold); color: var(--navy-deep); }
.btn.gold:hover{ background: var(--gold); border-color: var(--gold); color: var(--navy-deep); box-shadow: var(--shadow-gold); }
.btn.ghost{ background: var(--surface); color: var(--navy); border-color: var(--line-strong); }
.btn.ghost:hover{ background: var(--surface-2); border-color: var(--navy); color: var(--navy); }
.btn.danger{ background: var(--danger); border-color: var(--danger); color: var(--surface); }
.btn.danger:hover{ background: color-mix(in srgb, var(--danger) 85%, var(--text)); border-color: transparent; }
.btn.danger-ghost{ background: var(--surface); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 30%, transparent); }
.btn.danger-ghost:hover{ background: var(--danger-bg); color: var(--danger); border-color: var(--danger); }
.btn.gold-outline{ background: var(--surface); color: var(--gold-ink); border: 2px solid var(--gold); }
.btn.gold-outline:hover{ background: var(--gold-wash); color: var(--gold-ink); border-color: var(--gold); }

.btn.small{ font-size: var(--t-sm); padding: var(--s-1) var(--s-3); min-height: 36px; }
.btn.block{ width: 100%; }
.btn.big{ font-size: var(--t-md); padding: var(--s-4) var(--s-6); min-height: 56px; }
.btn-row{ display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; }

/* A 36px control with a 44px hit area, so dense rows stay tappable. */
.icon-btn{
  position: relative;
  width: 36px;
  height: 36px;
  min-height: 36px;
  padding: 0;
  flex: none;
  border-radius: var(--r-md);
}
.icon-btn::before{ content: ""; position: absolute; inset: -4px; }
.icon-btn .icon{ width: 1.2em; height: 1.2em; }

.link-btn{
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 500;
  color: var(--navy);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

/* ============================================================== FIELDS === */
.field{ margin-bottom: var(--s-3); }
.field label, .lbl{
  display: block;
  font-size: var(--t-sm);
  color: var(--text-muted);
  margin-bottom: var(--s-1);
  font-weight: 600;
}
/* Text-like fields.
 *
 * `input:not([type])` is in this list for a reason worth keeping. A bare
 * <input> with no type attribute is a perfectly valid text box — the browser
 * treats it as type="text" — but an attribute selector does not match an
 * attribute that is not there. Three fields on the Branches page were written
 * that way, so they alone in the whole panel got no padding, no border radius,
 * no 44px tap target and the browser's default black text, and that page
 * looked broken next to every other one.
 *
 * The three have been given their types. This line means the next one nobody
 * remembers to type is styled anyway, rather than shipping a page that looks
 * unfinished for a reason no one can see.
 *
 * `time` and `datetime-local` were the other half of the same bug, and the
 * backstop above could not catch them because they ARE typed — just typed with
 * something nobody added here. That left the seven time boxes on the Opening
 * hours card as raw browser controls: no border, no radius, no 44px target,
 * the wrong font, sitting inside a panel where every other field is styled.
 * A typed field missing from this list fails exactly like a typeless one, so
 * adding a new input type to the app means adding it here.
 */
input:not([type]),
input[type=text], input[type=password], input[type=number], input[type=search],
input[type=tel], input[type=date], input[type=time], input[type=datetime-local],
input[type=url], input[type=email], select, textarea{
  width: 100%;
  font-size: var(--t-base);
  font-family: inherit;
  padding: var(--s-2) var(--s-3);
  min-height: 44px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--text);
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
textarea{ min-height: 88px; resize: vertical; line-height: var(--lh-body); }
input:hover, select:hover, textarea:hover{ border-color: var(--text-soft); }

/* A placeholder is a hint, not a value.
 *
 * Left to the browser it renders at roughly #757575 on white, which is close
 * enough to real text that "Thamel" in an empty Branch name box reads as a
 * branch already called Thamel. Tied to the panel's own soft ink instead, so
 * it is legible but unmistakably not something anybody typed. */
input::placeholder, textarea::placeholder{
  color: var(--text-soft);
  opacity: 1;   /* Firefox dims placeholders by default; the colour above already does that. */
}
input:focus, select:focus, textarea:focus{
  outline: none;
  border-color: var(--gold);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--gold) 30%, transparent);
}
input:focus-visible, select:focus-visible, textarea:focus-visible{
  outline: 2px solid var(--gold);
  outline-offset: 1px;
}
/* The upload control.
 *
 * A file input is two things in one box: a drop target, and a button the
 * browser draws itself. The dashed box below was styled and the button was
 * not, so every upload on both panels — a shop logo, a cover photo, menu
 * pictures, spin prizes, and the platform's own logo in the super admin
 * settings — showed a grey operating-system button sitting inside a carefully
 * styled frame, looking like a page that had not finished loading.
 *
 * ::file-selector-button is the standard way to reach it. The -webkit- form is
 * the same rule for Safari and older Chrome, and has to be written as a
 * separate block: a selector list containing one pseudo-element a browser does
 * not know is thrown away whole, taking the working half with it.
 */
input[type=file]{
  font-size: var(--t-sm);
  padding: var(--s-2);
  background: var(--surface);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-md);
  width: 100%;
  cursor: pointer;
  /* The filename sits beside the button and is the part that can be long. */
  overflow: hidden;
  text-overflow: ellipsis;
}
input[type=file]:hover{ border-color: var(--navy); background: var(--surface-2); }

/* The box has to react when the BUTTON inside it takes focus, because the
   button is what the keyboard lands on — without this, tabbing to an upload
   shows nothing at all on the frame around it. */
input[type=file]:focus-within{
  border-color: var(--gold);
  border-style: solid;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--gold) 30%, transparent);
}

input[type=file]::file-selector-button{
  font-family: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  padding: var(--s-2) var(--s-4);
  margin: 0 var(--s-3) 0 0;
  border-radius: var(--r-full);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--navy);
  cursor: pointer;
  min-height: 34px;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
input[type=file]::-webkit-file-upload-button{
  font-family: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  padding: var(--s-2) var(--s-4);
  margin: 0 var(--s-3) 0 0;
  border-radius: var(--r-full);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--navy);
  cursor: pointer;
  min-height: 34px;
}
input[type=file]:hover::file-selector-button{ background: var(--surface-2); border-color: var(--navy); }
input[type=file]:hover::-webkit-file-upload-button{ background: var(--surface-2); border-color: var(--navy); }
/* Fields side by side.
 *
 * Bottom-aligning these looks tidy only while every field carries the same
 * amount of helper text. As soon as one note wraps to a second line its input
 * is pushed up relative to its neighbour and the row reads as crooked. Instead
 * the fields stretch to a common height, the labels and inputs line up at the
 * top, and the notes sink to the bottom however long they run. */
.inline-fields{ display: flex; gap: var(--s-3); flex-wrap: wrap; align-items: stretch; }
.inline-fields .field{
  margin-bottom: 0;
  flex: 1 1 160px;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.inline-fields .field > .field-note{ margin-top: auto; padding-top: var(--s-1); }
.field-note{ font-size: var(--t-xs); color: var(--text-muted); margin-top: var(--s-1); line-height: var(--lh-snug); }
fieldset{ border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s-3); margin: 0 0 var(--s-3); }
legend{ font-size: var(--t-sm); color: var(--text-muted); padding: 0 var(--s-2); }

/* toggle switch */
.toggle-row{
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-2) 0;
  font-size: var(--t-sm);
  border-bottom: 1px solid var(--line);
}
.toggle-row:last-child{ border-bottom: none; }
.toggle-row .t-txt{ min-width: 0; }
.toggle-row .t-txt b{ display: block; font-size: var(--t-sm); }
.toggle-row .t-txt span{ display: block; font-size: var(--t-xs); color: var(--text-muted); margin-top: 2px; line-height: var(--lh-snug); }
.switch{ position: relative; width: 46px; height: 28px; flex: none; display: inline-block; }
.switch input{ opacity: 0; width: 0; height: 0; position: absolute; }
.slider-toggle{
  position: absolute;
  cursor: pointer;
  inset: 0;
  background: var(--line-strong);
  border-radius: var(--r-full);
  transition: background var(--dur) var(--ease);
}
.slider-toggle::before{
  content: "";
  position: absolute;
  height: 22px;
  width: 22px;
  left: 3px;
  top: 3px;
  background: var(--surface);
  border-radius: 50%;
  box-shadow: var(--shadow-xs);
  transition: transform var(--dur) var(--ease);
}
.switch input:checked + .slider-toggle{ background: var(--navy); }
.switch input:checked + .slider-toggle::before{ transform: translateX(18px); }
.switch input:focus-visible + .slider-toggle{ outline: 2px solid var(--gold); outline-offset: 2px; }

/* ================================================================ KPIs === */
.kpi-grid{
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-2);
  margin-bottom: var(--s-4);
}
@media (min-width: 700px){ .kpi-grid{ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s-3); } }

.kpi{
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-3);
  box-shadow: var(--shadow-xs);
  min-width: 0;
  color: inherit;
  text-decoration: none;
  display: block;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              transform var(--dur-fast) var(--ease);
}
a.kpi:hover{ border-color: var(--line-strong); box-shadow: var(--shadow-sm); color: inherit; }
a.kpi:active{ transform: scale(0.99); }

.kpi .k-ico{
  position: absolute;
  top: var(--s-3);
  right: var(--s-3);
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: var(--r-sm);
  background: var(--surface-3);
  color: var(--text-muted);
}
.kpi .k-ico.navy{ background: var(--info-bg); color: var(--info); }
.kpi .k-ico.gold{ background: var(--gold-wash); color: var(--gold-ink); }
.kpi .k-ico.ok{   background: var(--ok-bg);    color: var(--ok); }
.kpi .k-ico.warn{ background: var(--warn-bg);  color: var(--warn); }

.kpi .lbl{
  font-size: var(--t-xs);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: var(--track-wide);
  font-weight: 600;
  line-height: 1.3;
  padding-right: 34px;
}
.kpi .num{
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 1.2rem + 1vw, 2rem);
  font-weight: 700;
  color: var(--navy);
  line-height: 1.05;
  letter-spacing: var(--track-tight);
  font-variant-numeric: tabular-nums;
  margin-top: var(--s-1);
}
.kpi .delta{ font-size: var(--t-xs); color: var(--text-muted); margin-top: 2px; line-height: var(--lh-snug); }
.kpi .delta.flat{ color: var(--text-muted); }
.kpi .delta a{ color: var(--navy); }

.kpi.hero{ background: var(--grad-navy); border-color: var(--navy-deep); box-shadow: var(--shadow-sm); }
.kpi.hero .num{ color: var(--gold); }
.kpi.hero .lbl{ color: var(--gold-soft); }
.kpi.hero .delta{ color: var(--gold-soft); }
.kpi.hero .k-ico{ background: color-mix(in srgb, var(--text-on-dark) 14%, transparent); color: var(--gold); }

/* "waiting to collect" always reads gold */
.kpi.is-gold{ border-color: var(--gold); background: var(--gold-wash); }
.kpi.is-gold .num{ color: var(--gold-ink); }
.kpi.is-gold .lbl{ color: var(--gold-ink); }
.kpi.is-gold .delta{ color: var(--gold-ink); }
.kpi.is-gold .k-ico{ background: var(--surface); color: var(--gold-ink); }

/* =============================================================== TABLE === */
.tbl-wrap{ overflow-x: auto; -webkit-overflow-scrolling: touch; }
table.tbl{
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--t-sm);
}
table.tbl th, table.tbl td{
  padding: var(--s-2) var(--s-3);
  text-align: left;
  vertical-align: middle;
}
table.tbl thead th{
  position: sticky;
  top: 0;
  z-index: 2;
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-wide);
  color: var(--text-muted);
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
  font-weight: 700;
}
table.tbl thead th a{
  color: var(--text-muted);
  text-decoration: none;
  display: inline-flex;
  gap: var(--s-1);
  align-items: center;
}
table.tbl thead th a:hover{ color: var(--navy); }
table.tbl thead th a.sorted{ color: var(--navy); font-weight: 700; }
table.tbl thead th a .icon{ width: 0.95em; height: 0.95em; }

table.tbl tbody tr{ border-bottom: 1px solid var(--line); }
table.tbl tbody td{ border-bottom: 1px solid var(--line); }
table.tbl tbody tr:last-child td{ border-bottom: none; }
table.tbl tbody tr:hover td{ background: var(--surface-2); }
table.tbl td .row-acts{ display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; }
table.tbl td.num, table.tbl th.num{ text-align: right; font-variant-numeric: tabular-nums; }
table.tbl .nm{ font-weight: 600; color: var(--navy); text-decoration: none; }
table.tbl .nm:hover{ text-decoration: underline; }
table.tbl .sub-line{ display: block; font-size: var(--t-xs); color: var(--text-muted); }

@media (max-width: 700px){
  table.tbl, table.tbl tbody, table.tbl td{ display: block; width: 100%; }
  table.tbl thead{ display: none; }
  /* Two label/value pairs per line: the same stacked card, half as tall. */
  table.tbl tbody tr{
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    margin-bottom: var(--s-2);
    padding: var(--s-1) 0;
    box-shadow: var(--shadow-xs);
  }
  table.tbl tbody td{
    display: block;
    align-self: end;
    padding: var(--s-1) var(--s-3);
    border: none;
    text-align: left;
    min-width: 0;
  }
  /* The name and the actions always get the full width. */
  table.tbl tbody td:first-child,
  table.tbl tbody td:last-child,
  table.tbl tbody td[colspan]{ grid-column: 1 / -1; }
  table.tbl tbody td:first-child{ font-size: var(--t-base); padding-top: var(--s-2); }
  table.tbl tbody td:last-child{ padding-bottom: var(--s-2); }
  table.tbl tbody tr:hover td{ background: none; }
  table.tbl td::before{
    content: attr(data-label);
    display: block;
    font-size: var(--t-xs);
    text-transform: uppercase;
    letter-spacing: var(--track-wide);
    color: var(--text-muted);
    text-align: left;
    font-weight: 700;
    line-height: 1.4;
  }
  table.tbl td.num{ text-align: left; }
  table.tbl td[data-label=""]::before{ content: none; }
  table.tbl td .row-acts{ justify-content: flex-start; width: 100%; }
  table.tbl tbody tr.row-detail:not([hidden]){ display: block; }
  table.tbl tbody tr.row-detail > td{ grid-column: 1 / -1; padding: 0; }
}

/* --------------------------------------------------- in-place row detail - */
tr.row-detail > td{ padding: 0 !important; background: var(--surface-2); }
tr.row-detail[hidden]{ display: none; }
.row-panel{
  padding: var(--s-3);
  border-top: 1px solid var(--line);
  display: flex;
  gap: var(--s-3);
  flex-wrap: wrap;
  align-items: flex-end;
}
.row-panel .field{ margin-bottom: 0; flex: 1 1 150px; min-width: 0; }
.row-panel .rp-msg{ flex: 1 1 100%; margin: 0; }
@media (max-width: 700px){
  tr.row-detail > td{ padding: 0 !important; }
  tr.row-detail > td::before{ content: none; }
}

/* =============================================================== CHIPS === */
.chip{
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  font-size: var(--t-xs);
  padding: 3px var(--s-2);
  border-radius: var(--r-full);
  background: var(--gold-wash);
  color: var(--gold-ink);
  font-weight: 700;
  white-space: nowrap;
  line-height: 1.5;
}
.chip .icon{ width: 1em; height: 1em; }
.chip.ok{    background: var(--ok-bg);     color: var(--ok); }
.chip.bad{   background: var(--danger-bg); color: var(--danger); }
.chip.navy{  background: var(--navy);      color: var(--gold); }
.chip.plain{ background: var(--surface-3); color: var(--text); }
.chip.big{   font-size: var(--t-sm); padding: var(--s-1) var(--s-3); }
.chip.wait{  background: var(--gold-wash); color: var(--gold-ink); }
.chip.done{  background: var(--ok-bg);     color: var(--ok); }
.chip.gone{  background: var(--surface-3); color: var(--text); }
.chip.exp{   background: var(--danger-bg); color: var(--danger); }

/* ============================================================ PROGRESS === */
.progress-track{ height: 8px; background: var(--surface-3); border-radius: var(--r-full); overflow: hidden; }
.progress-fill{ height: 100%; background: var(--grad-gold); border-radius: var(--r-full); transition: width var(--dur-slow) var(--ease); }
.progress-track.dark{ background: color-mix(in srgb, var(--text-on-dark) 22%, transparent); }

/* ---------------------------------------------------------- stamp cells -- */
.stamp-row{ display: flex; gap: var(--s-2); flex-wrap: wrap; }
.stamp-circle{
  width: 36px;
  height: 36px;
  border-radius: 50%;
  flex: none;
  display: grid;
  place-items: center;
  font-size: var(--t-sm);
  font-weight: 600;
  border: 2px dashed var(--line-strong);
  color: var(--text-muted);
}
.stamp-circle .icon{ width: 1.05em; height: 1.05em; }
.stamp-circle.filled{ background: var(--navy); border: 2px solid var(--navy); color: var(--gold); }

/* ============================================================ ACTIVITY === */
.feed{ list-style: none; margin: 0; padding: 0; }
.feed li{
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) 0;
  border-bottom: 1px solid var(--line);
}
.feed li:last-child{ border-bottom: none; }
.feed .f-ico{
  width: 30px;
  height: 30px;
  border-radius: 50%;
  flex: none;
  background: var(--gold-wash);
  color: var(--gold-ink);
  display: grid;
  place-items: center;
}
.feed .f-ico .icon{ width: 1em; height: 1em; }
.feed .f-ico.plus{ background: var(--navy); color: var(--gold); }
.feed .f-ico.minus{ background: var(--danger-bg); color: var(--danger); }
.feed .f-main{ flex: 1; min-width: 0; }
.feed .f-main b{ display: block; font-size: var(--t-sm); font-weight: 600; }
.feed .f-main span{ display: block; font-size: var(--t-xs); color: var(--text-muted); }
.feed .f-when{ font-size: var(--t-xs); color: var(--text-muted); flex: none; text-align: right; white-space: nowrap; }

/* ------------------------------------------------- unified activity log -- */
.day-head{
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin: var(--s-3) 0 var(--s-2);
  font-size: var(--t-xs);
  text-transform: uppercase;
  letter-spacing: var(--track-wide);
  color: var(--text-muted);
  font-weight: 700;
}
.day-head:first-child{ margin-top: 0; }
.day-head::after{ content: ""; flex: 1; height: 1px; background: var(--line); }
.day-head .day-sum{ text-transform: none; letter-spacing: 0; font-weight: 400; font-size: var(--t-xs); }

.log{ list-style: none; margin: 0; padding: 0; }
.log li{
  display: flex;
  gap: var(--s-3);
  align-items: flex-start;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  padding: var(--s-2) var(--s-3);
  margin-bottom: var(--s-1);
}
.log li.is-redeem{ background: var(--gold-wash); border-color: color-mix(in srgb, var(--gold) 42%, transparent); }
.log li.is-remove{ background: var(--danger-bg); border-color: color-mix(in srgb, var(--danger) 22%, transparent); }
.log .l-ico{
  width: 28px;
  height: 28px;
  border-radius: 50%;
  flex: none;
  display: grid;
  place-items: center;
  background: var(--surface-3);
  color: var(--text-muted);
  margin-top: 1px;
}
.log .l-ico .icon{ width: 0.95em; height: 0.95em; }
.log li.is-stamp .l-ico{ background: var(--navy); color: var(--gold); }
.log li.is-redeem .l-ico{ background: var(--gold); color: var(--navy-deep); }
.log li.is-remove .l-ico{ background: var(--danger-bg); color: var(--danger); }
.log .l-main{ flex: 1; min-width: 0; }
.log .l-what{ font-size: var(--t-sm); font-weight: 600; overflow-wrap: anywhere; }
.log .l-meta{
  font-size: var(--t-xs);
  color: var(--text-muted);
  margin-top: 2px;
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--s-3);
}
.log .l-meta b{ color: var(--text); font-weight: 600; }
/* The tinted rows sit on gold-wash / danger-bg, where muted grey drops
   just under AA — the meta text steps up to full ink there. */
.log li.is-redeem .l-meta, .log li.is-remove .l-meta,
.log li.is-redeem .l-after, .log li.is-remove .l-after,
.log li.is-redeem .l-delta.flat, .log li.is-remove .l-delta.flat{ color: var(--text); }
.log .l-right{ flex: none; text-align: right; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
.log .l-delta{ font-size: var(--t-md); font-weight: 700; font-variant-numeric: tabular-nums; }
.log .l-delta.up{ color: var(--ok); }
.log .l-delta.down{ color: var(--danger); }
.log .l-delta.flat{ color: var(--text-muted); font-size: var(--t-sm); }
.log .l-after{ font-size: var(--t-xs); color: var(--text-muted); white-space: nowrap; }
.log .l-code{
  display: inline-block;
  font-family: var(--font-mono);
  font-weight: 700;
  letter-spacing: 1.5px;
  background: var(--surface);
  border: 1px dashed var(--gold);
  border-radius: var(--r-sm);
  padding: 1px var(--s-2);
  margin-top: var(--s-1);
  font-size: var(--t-xs);
  color: var(--navy);
}
@media (max-width: 520px){
  .log li{ flex-wrap: wrap; }
  .log .l-right{ flex-direction: row; align-items: baseline; gap: var(--s-3); width: 100%; justify-content: flex-start; padding-left: 40px; }
}

/* A long feed should not set the height of the page. */
.log-scroll{ overscroll-behavior: contain; }
@media (min-width: 1000px){
  .log-scroll{ max-height: min(58vh, 620px); overflow-y: auto; padding-right: var(--s-2); }
  .log-scroll::-webkit-scrollbar{ width: 8px; }
  .log-scroll::-webkit-scrollbar-thumb{ background: var(--line-strong); border-radius: var(--r-full); }
}

.log-filter{ display: flex; gap: var(--s-1); flex-wrap: wrap; }
.log-filter a{
  font-size: var(--t-sm);
  padding: var(--s-1) var(--s-3);
  border-radius: var(--r-full);
  min-height: 36px;
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--navy);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease), transform var(--dur-fast) var(--ease);
}
.log-filter a:hover{ background: var(--surface-2); color: var(--navy); }
.log-filter a:active{ transform: scale(0.98); }
.log-filter a.on{ background: var(--navy); color: var(--text-on-dark); border-color: var(--navy); font-weight: 600; }

.tz-note{ font-size: var(--t-xs); color: var(--text-muted); margin: 0 0 var(--s-3); }
.a-foot{
  text-align: center;
  font-size: var(--t-xs);
  color: var(--text-muted);
  padding: var(--s-4) var(--s-3) var(--s-2);
  border-top: 1px solid var(--line);
  margin-top: var(--s-5);
}

/* =============================================================== CHART === */
.chart-box{ width: 100%; overflow-x: auto; }
.chart-box svg{ display: block; max-width: 100%; height: auto; }
.chart-box svg .bar{ fill: var(--navy); transition: fill var(--dur-fast) var(--ease); }
.chart-box svg .bar:hover{ fill: var(--gold); }
.chart-box svg .bar.hi{ fill: var(--gold); }
.chart-box svg .axis{ stroke: var(--line-strong); stroke-width: 1; }
.chart-box svg .grid{ stroke: var(--line); stroke-width: 1; stroke-dasharray: 2 4; }
.chart-box svg .lbl{ font-size: 10px; fill: var(--text-muted); font-family: var(--font-body); }
.chart-box svg .val{ font-size: 10px; fill: var(--navy); font-family: var(--font-body); font-weight: 600; }
.chart-box svg .line{ fill: none; stroke: var(--navy); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chart-box svg .area{ fill: color-mix(in srgb, var(--gold) 20%, transparent); }
.chart-box svg .dot{ fill: var(--gold); stroke: var(--navy); stroke-width: 1.5; }
.chart-legend{ font-size: var(--t-xs); color: var(--text-muted); margin: var(--s-2) 0 0; }

/* =========================================================== PAGINATOR === */
.pager{ display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); flex-wrap: wrap; }
.pager .pages{ display: flex; gap: var(--s-1); flex-wrap: wrap; }
.pager a, .pager span.cur, .pager span.gap{
  min-width: 38px;
  min-height: 38px;
  padding: 0 var(--s-2);
  border-radius: var(--r-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-1);
  font-size: var(--t-sm);
  font-weight: 500;
  text-decoration: none;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--navy);
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.pager a:hover{ background: var(--surface-2); border-color: var(--navy); }
.pager a .icon{ width: 1em; height: 1em; }
.pager span.cur{ background: var(--navy); color: var(--text-on-dark); border-color: var(--navy); font-weight: 700; }
.pager span.gap{ border: none; background: none; color: var(--text-soft); min-width: 20px; }
.pager .count{ font-size: var(--t-sm); color: var(--text-muted); }

/* ========================================================== FILTER BAR === */
.filters{ display: flex; gap: var(--s-3); flex-wrap: wrap; align-items: flex-end; }
.filters .field{ margin-bottom: 0; flex: 1 1 170px; min-width: 0; }
.filters .btn{ flex: none; }

/* ============================================================ SCAN PAGE == */
.scan-grid{ display: grid; grid-template-columns: 1fr; gap: var(--s-4); }
@media (min-width: 980px){ .scan-grid{ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; } }

#scannerBox{
  width: 100%;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--navy-deep);
  min-height: 300px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--gold-soft);
  font-size: var(--t-sm);
  text-align: center;
  padding: var(--s-4);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--gold) 24%, transparent);
}
#scannerBox video{ width: 100% !important; height: auto !important; display: block; border-radius: var(--r-lg); }
#scannerBox canvas, #scannerBox img{ max-width: 100%; }

.scan-status{ font-size: var(--t-sm); color: var(--text-muted); margin-top: var(--s-2); min-height: 18px; }
.scan-status.is-err{ color: var(--danger); }
.scan-status.is-ok{ color: var(--ok); }

.scan-tabs{ display: flex; gap: var(--s-1); margin-bottom: var(--s-3); flex-wrap: wrap; }
.scan-tabs button{
  font-family: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  padding: var(--s-2) var(--s-4);
  border-radius: var(--r-full);
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--navy);
  cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease), transform var(--dur-fast) var(--ease);
}
.scan-tabs button:active{ transform: scale(0.98); }
.scan-tabs button.on{ background: var(--navy); color: var(--text-on-dark); border-color: var(--navy); }

.result-card{
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.result-head{ background: var(--grad-navy); color: var(--text-on-dark); padding: var(--s-4); }
.result-head .r-name{ font-family: var(--font-display); font-size: var(--t-lg); font-weight: 700; letter-spacing: var(--track-tight); }
.result-head .r-phone{ font-size: var(--t-sm); color: var(--gold-soft); margin-top: 2px; letter-spacing: 0.4px; }
.result-head .r-count{
  font-family: var(--font-display);
  font-size: var(--t-2xl);
  font-weight: 700;
  color: var(--gold);
  margin: var(--s-3) 0 var(--s-2);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.result-head .r-count span{ font-family: var(--font-body); font-size: var(--t-base); font-weight: 400; }
.result-head .r-meta{ display: flex; gap: var(--s-4); flex-wrap: wrap; font-size: var(--t-xs); color: var(--gold-soft); margin-top: var(--s-3); }
.result-body{ padding: var(--s-4); }

.reward-line{
  display: flex;
  align-items: center;
  gap: var(--s-3);
  background: var(--gold-wash);
  border: 1px solid color-mix(in srgb, var(--gold) 45%, transparent);
  border-radius: var(--r-md);
  padding: var(--s-2) var(--s-3);
  margin-bottom: var(--s-2);
  font-size: var(--t-sm);
  color: var(--gold-ink);
}
.reward-line .r-ico{ display: inline-flex; flex: none; }
.reward-line .r-ico .icon{ width: 1.35em; height: 1.35em; }

/* PIN pad — used at arm's length under counter lighting. */
.pin-boxes{ display: flex; gap: var(--s-3); justify-content: flex-start; margin: var(--s-1) 0 var(--s-2); }
.pin-boxes input{
  width: 62px;
  height: 70px;
  padding: 0;
  text-align: center;
  font-family: var(--font-display);
  font-size: var(--t-xl);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  border: 2px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--navy);
  min-height: 70px;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.pin-boxes input:focus{
  border-color: var(--gold);
  outline: none;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--gold) 28%, transparent);
}
.pin-boxes input.filled{ border-color: var(--navy); }
@media (max-width: 400px){
  .pin-boxes{ gap: var(--s-2); }
  .pin-boxes input{ width: 100%; height: 64px; min-height: 64px; }
}

.success-state{ text-align: center; padding: var(--s-6) var(--s-4); }
.success-state .tick{
  width: 76px;
  height: 76px;
  border-radius: 50%;
  background: var(--ok-bg);
  color: var(--ok);
  display: grid;
  place-items: center;
  margin: 0 auto var(--s-3);
}
.success-state .tick .icon{ width: 2.4em; height: 2.4em; }
.success-state .tick.vault{ background: var(--gold-wash); color: var(--gold-ink); }
.success-state .s-added{
  font-family: var(--font-display);
  font-size: var(--t-2xl);
  font-weight: 700;
  color: var(--navy);
  line-height: 1.05;
  letter-spacing: var(--track-tight);
}
.success-state .s-added.sm{ font-size: var(--t-xl); }
.success-state .s-name{ font-size: var(--t-md); font-weight: 600; margin-top: var(--s-1); }
.success-state .s-total{ font-size: var(--t-sm); color: var(--text-muted); margin-top: var(--s-1); }
.success-state .s-reward{
  background: var(--grad-gold);
  color: var(--navy-deep);
  border-radius: var(--r-md);
  padding: var(--s-3);
  font-weight: 700;
  margin: var(--s-4) 0 var(--s-1);
  font-size: var(--t-base);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
}

.sticky-actions{
  position: sticky;
  bottom: 0;
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  padding: var(--s-2) 0 calc(var(--s-1) + var(--safe-bottom));
  border-top: 1px solid var(--line);
  margin-top: var(--s-3);
  display: flex;
  gap: var(--s-2);
  z-index: 5;
}
.sticky-actions .btn{ flex: 1; min-height: 52px; }
@media (min-width: 900px){
  .sticky-actions{ position: static; background: none; border-top: none; -webkit-backdrop-filter: none; backdrop-filter: none; }
}

/* ============================================================= SHOP QR === */
.qr-holder{
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--s-4);
  min-height: 250px;
}
.qr-holder img, .qr-holder canvas{ display: block; max-width: 100%; height: auto; }
.qr-url{
  font-family: var(--font-mono);
  font-size: var(--t-sm);
  word-break: break-all;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: var(--s-2) var(--s-3);
  color: var(--navy);
}

/* the printable poster — hidden on screen, the only thing printed */
.poster{ display: none; }
@media print{
  body{ background: var(--surface); }
  .a-side, .a-scrim, .a-top, .skip-link, .a-main .no-print, .banner-warn, .msg, .a-foot{ display: none !important; }
  .a-shell{ margin-left: 0 !important; min-height: 0; }
  .a-main{ padding: 0; max-width: none; }
  .poster{ display: block !important; text-align: center; padding: 40px 30px; color: var(--navy); }
  .poster .p-shop{ font-family: var(--font-display); font-size: 34px; font-weight: 700; letter-spacing: 1px; }
  .poster .p-tag{ font-size: 15px; color: var(--text-muted); margin-top: 6px; letter-spacing: 2px; text-transform: uppercase; }
  .poster .p-qr{ margin: 34px auto; display: flex; justify-content: center; }
  .poster .p-qr img, .poster .p-qr canvas{ width: 220px !important; height: 220px !important; }
  .poster .p-call{ font-size: 19px; font-weight: 700; margin-top: 10px; }
  .poster .p-note{ font-size: 13px; color: var(--text-muted); margin-top: 12px; }
  .poster .p-rule{ margin: 26px auto 0; max-width: 420px; font-size: 13px; color: var(--text-muted); line-height: 1.7; }
  .print-only{ display: block !important; }
}
.print-only{ display: none; }

/* =============================================================== LOGIN === */
.login-wrap{
  min-height: 100vh;
  min-height: 100dvh;   /* see the note on body — same reason */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s-6) var(--s-4);
  background: var(--grad-navy);
}
.login-card{
  width: 100%;
  max-width: 400px;
  background: var(--surface);
  border-radius: var(--r-xl);
  padding: var(--s-8) var(--s-6);
  box-shadow: var(--shadow-lg);
}
.login-card .l-brand{ text-align: center; margin-bottom: var(--s-5); }
.login-card .l-brand .l-logo{
  width: 64px; height: 64px; border-radius: 50%; object-fit: cover;
  border: 3px solid var(--gold); margin: 0 auto var(--s-3); display: block; background: var(--surface);
}
.login-card .l-brand .l-mark{
  width: 64px; height: 64px; border-radius: 50%; border: 3px solid var(--gold);
  background: var(--navy); color: var(--gold);
  display: grid; place-items: center; margin: 0 auto var(--s-3);
}
.login-card .l-brand .l-mark .icon{ width: 1.9em; height: 1.9em; }
.login-card .l-brand h1{ font-size: var(--t-lg); margin: 0; }
.login-card .l-brand p{
  font-size: var(--t-xs); color: var(--text-muted); letter-spacing: var(--track-wide);
  text-transform: uppercase; margin: var(--s-1) 0 0;
}
.login-card .l-hint{
  font-size: var(--t-sm); color: var(--text-muted); background: var(--surface-2);
  border: 1px dashed var(--line-strong); border-radius: var(--r-md);
  padding: var(--s-3); margin-top: var(--s-4); text-align: center;
}
.login-card .l-hint b{ color: var(--navy); }
.login-back{ text-align: center; margin-top: var(--s-4); font-size: var(--t-sm); }
.login-back a{ color: var(--gold-soft); }
.login-back a:hover{ color: var(--gold); }

/* ======================================================= NOTIFICATIONS === */
.phone-preview{
  background: var(--grad-navy);
  border-radius: var(--r-2xl);
  padding: var(--s-4) var(--s-3);
  border: 5px solid var(--navy-deep);
  max-width: 300px;
  margin: 0 auto;
  box-shadow: var(--shadow-md);
}
.phone-preview .p-time{ color: var(--gold-soft); font-size: var(--t-xs); text-align: center; margin-bottom: var(--s-2); letter-spacing: 0.5px; }
.phone-preview .p-note{
  background: var(--surface);
  border-radius: var(--r-lg);
  padding: var(--s-3);
  display: flex;
  gap: var(--s-3);
  align-items: flex-start;
}
.phone-preview .p-app{
  width: 32px; height: 32px; border-radius: var(--r-sm);
  background: var(--navy); color: var(--gold); flex: none;
  display: grid; place-items: center;
}
.phone-preview .p-txt{ min-width: 0; flex: 1; }
.phone-preview .p-shop{ font-size: var(--t-xs); color: var(--text-muted); text-transform: uppercase; letter-spacing: var(--track-wide); }
.phone-preview .p-title{ font-size: var(--t-sm); font-weight: 700; margin-top: 1px; overflow-wrap: anywhere; }
.phone-preview .p-body{ font-size: var(--t-sm); color: var(--text-muted); margin-top: 2px; overflow-wrap: anywhere; }

.recip-count{
  display: flex; align-items: baseline; gap: var(--s-2);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-md); padding: var(--s-2) var(--s-3); margin-bottom: var(--s-3);
}
.recip-count b{ font-family: var(--font-display); font-size: var(--t-xl); color: var(--navy); font-variant-numeric: tabular-nums; }
.recip-count span{ font-size: var(--t-sm); color: var(--text-muted); }

/* ============================================================= REWARDS === */
.icon-pick{ display: flex; gap: var(--s-1); flex-wrap: wrap; margin-top: var(--s-2); }
.icon-pick button{
  width: 40px; height: 40px; padding: 0;
  display: grid; place-items: center;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--text-muted);
  border-radius: var(--r-md);
  cursor: pointer;
  position: relative;
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease),
              background var(--dur) var(--ease), transform var(--dur-fast) var(--ease);
}
.icon-pick button::before{ content: ""; position: absolute; inset: -2px; }
.icon-pick button:hover{ background: var(--surface-2); border-color: var(--gold); color: var(--gold-ink); }
.icon-pick button:active{ transform: scale(0.97); }
.icon-pick button[aria-pressed="true"]{
  background: var(--gold-wash); border-color: var(--gold); color: var(--gold-ink);
  box-shadow: 0 0 0 1px var(--gold);
}
.icon-pick .icon{ width: 1.25em; height: 1.25em; }

.reward-icon-lg{
  width: 42px; height: 42px; border-radius: var(--r-md);
  background: var(--navy); color: var(--gold);
  display: grid; place-items: center; flex: none;
}
.reward-icon-lg .icon{ width: 1.35em; height: 1.35em; }

.reward-row{
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-3); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--s-2) var(--s-3);
}
.reward-row.is-unlocked{ background: var(--gold-wash); border-color: color-mix(in srgb, var(--gold) 45%, transparent); }
.reward-row .rr-main{ display: flex; align-items: center; gap: var(--s-3); min-width: 0; }

/* ==================================================== CUSTOMER DETAIL === */
.profile-head{
  background: var(--grad-navy);
  color: var(--text-on-dark);
  border-radius: var(--r-lg);
  padding: var(--s-4);
  margin-bottom: var(--s-3);
  box-shadow: var(--shadow-sm);
}
.profile-head .p-name{
  font-family: var(--font-display);
  font-size: var(--t-lg);
  font-weight: 700;
  letter-spacing: var(--track-tight);
}
.profile-head .p-meta{ font-size: var(--t-sm); color: var(--gold-soft); margin-top: 2px; }
.profile-head .p-stats{
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-2) var(--s-4);
  margin-top: var(--s-3);
}
@media (min-width: 620px){ .profile-head .p-stats{ grid-template-columns: repeat(6, minmax(0, 1fr)); } }
.profile-head .p-stats div span{
  display: block; font-size: var(--t-xs); color: var(--gold-soft);
  text-transform: uppercase; letter-spacing: var(--track-wide); line-height: 1.3;
}
.profile-head .p-stats div b{
  font-family: var(--font-display); font-size: var(--t-md); color: var(--gold);
  font-variant-numeric: tabular-nums; line-height: 1.2;
}
.profile-head .p-stats div b.sm{ font-family: var(--font-body); font-size: var(--t-sm); font-weight: 600; }

.claim-card{ border-color: var(--gold); border-width: 2px; }
.claim-card h2{ display: flex; align-items: center; gap: var(--s-2); }
.profile-head .p-progress{ font-size: var(--t-sm); color: var(--gold-soft); margin-bottom: var(--s-1); }
.pin-inline{ max-width: 150px; letter-spacing: 6px; font-family: var(--font-mono); }

.claim-code{
  font-family: var(--font-mono);
  font-size: var(--t-lg);
  font-weight: 700;
  letter-spacing: 4px;
  color: var(--navy);
  background: var(--surface);
  border: 2px dashed var(--gold);
  border-radius: var(--r-md);
  padding: var(--s-3);
  text-align: center;
  margin: var(--s-2) 0;
}
.def-list{
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-1) var(--s-4);
  font-size: var(--t-sm);
  align-items: baseline;
  margin: 0;
}
.def-list dt{ color: var(--text-muted); font-size: var(--t-sm); }
.def-list dd{ margin: 0; }

/* --------------------------------------------------------- reward vault -- */
.vault-panel{
  border: 1px solid var(--gold);
  background: var(--gold-wash);
  border-radius: var(--r-lg);
  padding: var(--s-3);
  margin-bottom: var(--s-3);
}
.vault-panel .v-head{
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-2); flex-wrap: wrap; margin-bottom: var(--s-1);
}
.vault-panel .v-head h2, .vault-panel .v-head h3{
  margin: 0; font-size: var(--t-base); color: var(--gold-ink);
  display: flex; align-items: center; gap: var(--s-2);
}
.vault-panel .v-lead{ font-size: var(--t-sm); color: var(--gold-ink); margin: 0 0 var(--s-2); line-height: var(--lh-snug); }
.vault-panel .v-lead:last-child{ margin-bottom: 0; }
.vault-panel .v-lead a{ color: var(--gold-ink); font-weight: 600; }

.vault-list{ display: flex; flex-direction: column; gap: var(--s-2); list-style: none; margin: 0; padding: 0; }
.vault-item{
  background: var(--surface);
  border: 1px solid color-mix(in srgb, var(--gold) 45%, transparent);
  border-left: 4px solid var(--gold);
  border-radius: var(--r-md);
  padding: var(--s-3);
  display: flex;
  gap: var(--s-3);
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
}
.vault-item .v-main{ flex: 1 1 210px; min-width: 0; }
.vault-item .v-title{
  font-size: var(--t-sm); font-weight: 700; color: var(--navy);
  overflow-wrap: anywhere; display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap;
}
.vault-item .v-title .icon{ color: var(--gold-ink); }
.vault-item .v-meta{
  font-size: var(--t-xs); color: var(--text-muted); margin-top: var(--s-1);
  display: flex; gap: 0 var(--s-3); flex-wrap: wrap;
}
.vault-item .v-meta b{ color: var(--text); }
.vault-item .v-acts{ display: flex; gap: var(--s-2); flex-wrap: wrap; flex: none; align-items: center; }
.vault-item .v-acts form{ margin: 0; }
.vault-item.is-expired{ border-left-color: color-mix(in srgb, var(--danger) 50%, transparent); background: var(--danger-bg); }
.vault-item.is-done{ border-left-color: color-mix(in srgb, var(--ok) 45%, transparent); background: var(--ok-bg); }

.v-code{
  display: inline-block; margin-top: var(--s-1); padding: 2px var(--s-2);
  font-family: var(--font-mono); font-size: var(--t-sm); font-weight: 700; letter-spacing: 3px;
  color: var(--navy); background: var(--surface-2);
  border: 1px dashed var(--gold); border-radius: var(--r-sm);
}
.mono-code{ font-family: var(--font-mono); font-weight: 700; letter-spacing: 2px; }

@media (min-width: 701px){
  table.tbl.vouchers td[data-label="Reward"]{ min-width: 158px; }
  table.tbl.vouchers td[data-label="Customer"]{ min-width: 130px; }
  table.tbl.vouchers td[data-label="Earned"],
  table.tbl.vouchers td[data-label="Collected"],
  table.tbl.vouchers td[data-label="Claim code"],
  table.tbl.vouchers td[data-label="Expires"]{ white-space: nowrap; }
}
table.tbl.vouchers tr.is-waiting td{ background: var(--gold-wash); }
table.tbl.vouchers tr.is-waiting:hover td{ background: color-mix(in srgb, var(--gold) 22%, var(--surface)); }
/* the badge has to stay legible against the gold row it sits on */
table.tbl.vouchers tr.is-waiting .chip.wait{ background: var(--surface); box-shadow: inset 0 0 0 1px var(--gold); }
table.tbl.vouchers tr.is-waiting .muted{ color: var(--gold-ink); }

/* the counter-side choice: give it now, or save it for later */
.reward-choice{
  border: 2px solid var(--gold);
  background: var(--gold-wash);
  border-radius: var(--r-lg);
  padding: var(--s-4);
  margin-top: var(--s-3);
}
.reward-choice h3{
  margin: 0 0 var(--s-1); font-size: var(--t-base); color: var(--gold-ink);
  display: flex; align-items: center; gap: var(--s-2);
}
.reward-choice .rc-lead{ font-size: var(--t-sm); color: var(--gold-ink); margin: 0 0 var(--s-3); line-height: var(--lh-snug); }
.reward-choice .rc-btns{ display: grid; grid-template-columns: 1fr; gap: var(--s-2); }
@media (min-width: 430px){ .reward-choice .rc-btns{ grid-template-columns: 1fr 1fr; } }
.reward-choice .rc-btns .btn{ width: 100%; min-height: 62px; flex-direction: column; gap: 1px; padding: var(--s-2) var(--s-3); }
.reward-choice .rc-btns .btn span{ display: inline-flex; align-items: center; gap: var(--s-2); }
.reward-choice .rc-btns .btn small{ display: block; font-size: var(--t-xs); font-weight: 400; line-height: 1.3; opacity: 0.9; }
.reward-choice .rc-note{ font-size: var(--t-xs); color: var(--gold-ink); margin: var(--s-2) 0 0; text-align: center; line-height: var(--lh-snug); }
.reward-choice .rc-note a{ color: var(--gold-ink); font-weight: 600; }
.reward-choice .field label{ color: var(--gold-ink); }

.success-state .s-vault{
  margin-top: var(--s-3); padding: var(--s-3); border-radius: var(--r-md);
  background: var(--gold-wash); border: 1px solid color-mix(in srgb, var(--gold) 45%, transparent);
  color: var(--gold-ink); font-size: var(--t-sm); font-weight: 600; line-height: var(--lh-snug);
}

@media (max-width: 520px){
  .vault-item{ flex-direction: column; flex-wrap: nowrap; }
  .vault-item .v-main{ flex: 0 0 auto; width: 100%; }
  .vault-item .v-acts{ width: 100%; }
  .vault-item .v-acts .btn{ flex: 1 1 130px; }
  .vault-item .v-acts form{ flex: 1 1 130px; display: flex; }
  .vault-item .v-acts form .btn{ width: 100%; }
}

/* ============================================================ SETTINGS === */
.set-nav{ display: flex; gap: var(--s-1); flex-wrap: wrap; }
.set-card{ scroll-margin-top: calc(var(--stick-top) + var(--s-4)); }
.set-card.is-flash{ outline: 2px solid var(--gold); outline-offset: 3px; }
.set-card h2{
  display: flex; align-items: baseline; gap: var(--s-2);
  font-size: var(--t-md);
}
.is-danger{ color: var(--danger); }
.steps{ padding-left: var(--s-5); margin: 0; font-size: var(--t-sm); line-height: 1.75; color: var(--text-muted); }
.steps li{ margin-bottom: 2px; }
.logo-preview{
  width: 64px; height: 64px; border-radius: 50%; object-fit: cover;
  border: 2px solid var(--gold); flex: none;
}
.set-card h2 .set-n{
  font-family: var(--font-body); font-size: var(--t-xs); font-weight: 700;
  color: var(--gold-ink); background: var(--gold-wash);
  border-radius: var(--r-full); padding: 1px var(--s-2); letter-spacing: var(--track-wide);
}

/* =========================================================== UTILITIES === */
.stack{ display: flex; flex-direction: column; gap: var(--s-2); }
.row-between{ display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); flex-wrap: wrap; }
.nowrap{ white-space: nowrap; }
.mt0{ margin-top: 0; } .mb0{ margin-bottom: 0; }
.mt8{ margin-top: var(--s-2); } .mt14{ margin-top: var(--s-3); } .mt20{ margin-top: var(--s-5); }
.hidden{ display: none !important; }

/* The browser's own [hidden] rule is only `display:none` at UA weight, so any
   class that sets a display — .inline-fields, .tbl rows — quietly beats it and
   the element stays on screen. Restate it so `hidden` means hidden. */
[hidden]{ display: none !important; }
.with-ico{ display: inline-flex; align-items: center; gap: var(--s-2); }

.spinner{
  width: 16px; height: 16px;
  border: 2px solid color-mix(in srgb, currentColor 35%, transparent);
  border-top-color: currentColor;
  border-radius: 50%;
  display: inline-block;
  animation: ck-spin 0.7s linear infinite;
}
@keyframes ck-spin{ to{ transform: rotate(360deg); } }

/* ------------------------------------------------------- touch comfort --
   Below 900px every control is finger-sized, even the ones that are compact
   on a desktop where there is a mouse. Density is bought back with padding
   and type size, never with a smaller hit area. */
@media (max-width: 899.98px){
  .btn.small{ min-height: 44px; padding: var(--s-2) var(--s-3); }
  .log-filter a{ min-height: 44px; min-width: 44px; justify-content: center; }
  .pager a, .pager span.cur{ min-width: 44px; min-height: 44px; }
  .icon-btn{ width: 44px; height: 44px; min-height: 44px; }
  input[type=file]{ min-height: 44px; display: flex; align-items: center; }
  .skip-link{ min-height: 44px; display: inline-flex; align-items: center; }
  /* A name in a list is the main way into a record on a phone, so it gets a
     full 44px row. Names inside a running feed keep a 24px target — WCAG's
     minimum for a link that sits inside a line of text. */
  table.tbl tbody td .nm{
    display: inline-block;
    min-height: 30px;
    padding: 7px 0;
    line-height: 30px;
  }
  .feed .f-main .nm{ display: inline-block; padding: 4px 0; }
}

@media (prefers-reduced-motion: reduce){
  .a-nav-item:active,
  .a-logout:active,
  .btn:active,
  .kpi:active,
  .scan-tabs button:active,
  .log-filter a:active,
  .icon-pick button:active{ transform: none; }
}

/* ===========================================================================
 *  Staff page
 *  Rows of people, each with an edit panel that folds out underneath.
 * ======================================================================== */

/* Several small forms sharing one table cell. */
.row-form{ display: inline-block; }
.row-form + .row-form{ margin-left: 4px; }

/* A switched-off account is still listed, just visibly out of service. */
.tbl tr.row-off > td{ opacity: .55; }

/* The fold-out edit panel. */
.tbl tr.edit-row > td{
  background: var(--surface-2);
  border-top: 0;
  padding: var(--s-4) var(--s-4) var(--s-5);
}
.tbl tr.edit-row .stack{
  display: grid;
  gap: var(--s-3);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  align-items: end;
}
.tbl tr.edit-row .btn-row{ grid-column: 1 / -1; }

/* ===========================================================================
 *  Settings — the Look section
 *  A theme picker and a live preview of the customer's card.
 * ======================================================================== */

.theme-pick{
  display: grid;
  gap: var(--s-2);
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  margin-top: var(--s-2);
}

.theme-opt{
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: var(--s-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}
.theme-opt:hover{ border-color: var(--line-strong); }
.theme-opt.is-on{
  border-color: var(--gold);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--gold) 34%, transparent);
}

/* The radio itself is the accessible control; the tile is what you see. */
.theme-opt input{
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  min-height: 0;
}
.theme-opt input:focus-visible + .theme-swatch{
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}

/* Two colours, split on the diagonal — the whole theme at a glance. */
.theme-swatch{
  width: 100%;
  aspect-ratio: 5 / 3;
  border-radius: var(--r-sm);
  background: linear-gradient(135deg, var(--a) 0 52%, var(--b) 52% 100%);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--text) 12%, transparent);
}
.theme-name{
  font-size: var(--t-xs);
  font-weight: 600;
  color: var(--text-muted);
  text-align: center;
}
.theme-opt.is-on .theme-name{ color: var(--text); }

.cover-preview{
  width: 220px;
  max-width: 45%;
  aspect-ratio: 8 / 3;
  object-fit: cover;
  border-radius: var(--r-md);
  border: 1px solid var(--line);
}

/* ------------------------------------------------------------- preview --- */
/*
 * A miniature of the customer's card. It carries .ls-themed and its own
 * --main/--accent, so assets/css/theme.css shades it exactly the way it shades
 * the real card — there is no second copy of the palette to keep in step.
 */
.card-preview{
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--line);
  max-width: 360px;
  margin-top: var(--s-2);
  background: var(--bg);
}
.card-preview .cp-top{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  background: var(--grad-navy);
  color: var(--text-on-dark);
}
.card-preview .cp-brand{
  font-family: var(--font-display);
  font-size: var(--t-md);
  line-height: 1.2;
}
.card-preview .cp-tag{
  font-size: var(--t-xs);
  opacity: .75;
}
.card-preview .cp-chip{
  font-size: 9px;
  letter-spacing: .12em;
  font-weight: 700;
  padding: 4px 8px;
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--gold) 88%, transparent);
  color: var(--navy-deep);
  white-space: nowrap;
}
.card-preview .cp-body{
  padding: var(--s-4);
  background: var(--surface);
}
.card-preview .cp-stamps{
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 6px;
  margin-bottom: var(--s-3);
}
.card-preview .cp-dot{
  aspect-ratio: 1;
  border-radius: 50%;
  border: 2px dashed var(--line-strong);
}
.card-preview .cp-dot.on{
  background: var(--gold);
  border: 2px solid var(--gold);
}
.card-preview .cp-line{
  margin: 0 0 var(--s-3);
  font-size: var(--t-xs);
  color: var(--gold-ink);
  font-weight: 600;
}
.card-preview .cp-btn{
  display: block;
  text-align: center;
  padding: 9px;
  border-radius: var(--r-full);
  background: var(--grad-gold);
  color: var(--navy-deep);
  font-size: var(--t-sm);
  font-weight: 700;
}

/* ----------------------------------------------------- nav group headings -
 * A flat list of ten items reads as one wall. These separate the work staff do
 * every minute from the things an owner sets up once. In the icons-only rail
 * there is no room for words, so the heading becomes a plain divider — the
 * grouping still reads, just without the labels.
 */
.a-nav-group{
  display: block;

  /* Never shrink.
     .a-nav is a flex column and the nav items all carry a min-height, so when
     the list is taller than the sidebar these headings are the only things
     flexbox is able to compress — and it does, slicing "COUNTER" in half.
     flex: 0 0 auto takes them out of that calculation entirely. */
  flex: 0 0 auto;
  line-height: 1.5;

  margin: var(--s-4) 0 var(--s-1);
  padding: 0 var(--s-3);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--text-on-dark) 42%, transparent);
  white-space: nowrap;
  overflow: hidden;
}
.a-nav-group:first-child{ margin-top: var(--s-1); }

@media (min-width: 900px) and (max-width: 1099px){
  .a-nav-group{ font-size: 0; margin: var(--s-3) var(--s-2) var(--s-2); padding: 0; }
  .a-nav-group::before{
    content: "";
    display: block;
    height: 1px;
    background: color-mix(in srgb, var(--text-on-dark) 16%, transparent);
  }
}
html.ck-side-collapsed .a-nav-group{
  font-size: 0;
  margin: var(--s-3) var(--s-2) var(--s-2);
  padding: 0;
}
html.ck-side-collapsed .a-nav-group::before{
  content: "";
  display: block;
  height: 1px;
  background: color-mix(in srgb, var(--text-on-dark) 16%, transparent);
}

/* ============================================================================
 *  Confirmation dialog  (assets/js/confirm.js)
 *
 *  Replaces window.confirm, which looked like an operating system error rather
 *  than part of the panel and could not say which shop or person was about to
 *  be affected. <dialog> gives us the modal behaviour, focus trap, Escape key
 *  and backdrop without any of it being written here.
 * ==========================================================================*/
dialog.ls-confirm{
  border: 0; padding: 0; max-width: 440px; width: calc(100% - 32px);
  border-radius: var(--r-xl);
  background: var(--surface); color: var(--text);
  box-shadow: 0 24px 64px rgba(16,24,40,.28);
}
dialog.ls-confirm::backdrop{
  background: rgba(16, 10, 24, .45);
  backdrop-filter: blur(2px);
}
dialog.ls-confirm form{ padding: var(--s-6); margin: 0; }

.ls-confirm .lc-title{
  margin: 0 0 var(--s-2); font-size: var(--t-lg); font-weight: 700;
  color: var(--navy); line-height: var(--lh-snug);
}
.ls-confirm .lc-text{
  margin: 0; color: var(--text-muted); font-size: var(--t-sm);
  line-height: var(--lh-body);
}
.ls-confirm .lc-acts{
  display: flex; gap: var(--s-2); justify-content: flex-end;
  margin-top: var(--s-6); flex-wrap: wrap;
}
.ls-confirm .lc-btn{
  border: 0; cursor: pointer; border-radius: var(--r-md);
  padding: 10px 18px; font-family: var(--font-body);
  font-size: var(--t-sm); font-weight: 650;
}
.ls-confirm .lc-cancel{
  background: var(--surface-2); color: var(--text);
  box-shadow: inset 0 0 0 1px var(--line);
}
.ls-confirm .lc-cancel:hover{ background: var(--surface-3); }
.ls-confirm .lc-ok{ background: var(--navy); color: var(--text-on-dark); }
.ls-confirm .lc-ok:hover{ filter: brightness(1.14); }
.ls-confirm .lc-ok.is-danger{ background: var(--danger); }

/* Small screens: full-width buttons, confirm on top where the thumb is. */
@media (max-width: 420px){
  .ls-confirm .lc-acts{ flex-direction: column-reverse; }
  .ls-confirm .lc-btn{ width: 100%; }
}

/* The prize picture, on the shop's own spin page. */
.prize-pic{
  display: flex; align-items: center; gap: var(--s-3);
  margin-top: var(--s-2);
}
.prize-pic img{
  width: 52px; height: 52px; border-radius: var(--r-md);
  object-fit: cover; background: var(--surface-2);
  border: 1px solid var(--line);
}
.prize-pic .none{
  width: 52px; height: 52px; border-radius: var(--r-md);
  display: grid; place-items: center;
  background: var(--surface-2); border: 1px dashed var(--line-strong);
  color: var(--text-soft);
}

/* The five "find us online" boxes, two or three to a row on a desk. */
.social-fields{
  display: grid; gap: var(--s-3);
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.social-fields .field{ margin-bottom: 0; }
.social-fields label .icon{ vertical-align: -3px; margin-right: 4px; }

/* A reward scanned off a customer's screen, ready to hand over. */
.voucher-scan .vs-code{
  margin-top: var(--s-2);
  font-family: var(--font-mono); font-size: var(--t-lg);
  letter-spacing: .18em; color: var(--gold-soft);
}
.voucher-scan .vs-note{
  margin: 0 0 var(--s-4); color: var(--text-muted); font-size: var(--t-sm);
}

/* ==================================================== REWARD TIER LIMITS ===
   A tier can be capped, dated, or both, and the owner's first question is
   never "what did I type" but "is this still going out?". So the card leads
   with the running facts — the photograph, the count, the window — and the
   fields that set them sit underneath. */

/* The photograph, where a tier has one, in place of the glyph badge. */
.reward-thumb{
  width: 42px; height: 42px; border-radius: var(--r-md);
  object-fit: cover; flex: none;
  background: var(--surface-2); border: 1px solid var(--line);
}

/* The upload row inside the edit form, matching the spin page's .prize-pic. */
.reward-pic{
  display: flex; align-items: center; gap: var(--s-3);
  margin-top: var(--s-2); flex-wrap: wrap;
}
.reward-pic img{
  width: 64px; height: 64px; border-radius: var(--r-md);
  object-fit: cover; background: var(--surface-2);
  border: 1px solid var(--line);
}
.reward-pic .none{
  width: 64px; height: 64px; border-radius: var(--r-md);
  display: grid; place-items: center;
  background: var(--surface-2); border: 1px dashed var(--line-strong);
  color: var(--text-soft);
}

/* "31 of 50 given away · 19 left · 1 Oct to 31 Oct" */
.reward-stock{ margin: 0 0 var(--s-2); }
.reward-stock b{ color: var(--text); }
.reward-stock + .progress-track{ margin-bottom: var(--s-1); }

.reward-limits .field{ margin-bottom: 0; }
.reward-limits .inline-fields{ margin-bottom: 0; }
.reward-why{
  margin-top: var(--s-3); color: var(--warn);
  display: flex; align-items: flex-start; gap: var(--s-1);
}
.reward-why .icon{ width: 1em; height: 1em; flex: none; margin-top: 2px; }

/* A tier the shop has switched on that is nonetheless reaching nobody. The
   left edge is the whole signal — a card in a column of cards has no room for
   a banner, and colouring the background would fight the theme. */
.card.reward-stopped-card{ border-left: 3px solid var(--warn); }

.reward-stopped{ margin: var(--s-2) 0 0; padding-left: var(--s-4); }
.reward-stopped li{ margin-top: 2px; }

/* On a phone the three limit fields stack; they must not shrink to slivers. */
@media (max-width: 560px){
  .reward-limits .inline-fields > .field{ flex: 1 1 100% !important; }
  .reward-pic img, .reward-pic .none{ width: 52px; height: 52px; }
}

/* ==========================================================================
   REPORTS — a second series, and the key that explains it
   ========================================================================== */

/* "New versus returning" draws two bars per week. The second carries .alt so
   the comparison is a colour rather than a second chart the eye has to hold. */
.chart-box svg .bar.alt{ fill: var(--gold); }
.chart-box svg .bar.alt:hover{ fill: var(--navy); }

.chart-legend .legend-key{
  display: inline-flex; align-items: center; gap: 6px;
  margin-right: var(--s-3); white-space: nowrap;
}
.chart-legend .k-swatch{
  width: 10px; height: 10px; border-radius: 2px;
  background: var(--navy); flex: 0 0 auto;
}
.chart-legend .k-swatch.alt{ background: var(--gold); }

/* ==========================================================================
   SETTINGS — opening hours
   ========================================================================== */

.hours-grid{ display: flex; flex-direction: column; gap: var(--s-2); margin-bottom: var(--s-3); }

/* One line per day on a desk; on a phone the day name sits above its fields
   and the two times share the row, which keeps all seven visible at once. */
.hours-row{
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-areas:
    "day     day"
    "open    close"
    "closed  reads";
  gap: var(--s-2);
  align-items: end;
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-2);
}
/* The settings cards sit in CSS columns, so this card is only ever about half
   the page wide however big the screen is. One line per day does not fit in
   that; two do, and the day still reads as one block. */
@media (min-width: 560px){
  .hours-row{
    /* Wide enough for "Closed all day" on one line, which shares the column. */
    grid-template-columns: 110px 1fr 1fr;
    grid-template-areas:
      "day    open  close"
      "closed reads reads";
    align-items: center;
  }
}

.hours-row .h-day{ grid-area: day; font-weight: 600; color: var(--navy); }
.hours-row .h-time{ margin-bottom: 0; }
.hours-row .h-time:nth-of-type(1){ grid-area: open; }
.hours-row .h-time:nth-of-type(2){ grid-area: close; }
.hours-row .h-time label{ font-size: var(--t-xs); }
.hours-row .h-time input{ width: 100%; }

.hours-row .h-closed{
  grid-area: closed;
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--t-sm); color: var(--text-muted); cursor: pointer;
  white-space: nowrap;
  /* A tap target of its own, so a thumb does not have to find the box. */
  min-height: 32px;
}
.hours-row .h-reads{
  grid-area: reads;
  font-size: var(--t-sm); color: var(--text-muted); text-align: right;
}

/* A closed day is dimmed rather than hidden — the owner still has to see the
   line to change their mind about it. */
.hours-row.is-closed{ background: var(--surface-3); }
.hours-row.is-closed .h-time{ opacity: 0.55; }
.hours-row.is-closed .h-reads{ color: var(--danger); font-weight: 600; }

/* ==========================================================================
   SETTINGS — the change history
   ========================================================================== */

.set-log summary{
  display: flex; align-items: center; gap: var(--s-2);
  cursor: pointer; list-style: none;
  padding: var(--s-1) 0;
  /* Folded shut by default, so it never pushes the settings off the screen. */
  min-height: 40px;
}
.set-log summary::-webkit-details-marker{ display: none; }
.set-log summary::before{
  content: "▸"; color: var(--text-muted); font-size: var(--t-sm);
  transition: transform var(--dur-fast) var(--ease);
}
.set-log details[open] > summary::before{ transform: rotate(90deg); }
.set-log .sl-title{
  font-family: var(--font-display); font-weight: 700; color: var(--navy);
  font-size: var(--t-md);
}
.set-log summary:focus-visible{ outline: 2px solid var(--gold); outline-offset: 2px; border-radius: var(--r-sm); }

/* The old value is context, not the answer — it reads quieter than the new. */
.set-log .sl-old{ color: var(--text-muted); text-decoration: line-through; }

/* ==========================================================================
   CUSTOMER TAGS, BULK ACTIONS AND MERGE
   (appended — everything below is scoped to the customers screens)
   ========================================================================== */

/* ------------------------------------------------------------------ tags -
   Six fixed colours rather than shop-themed ones. A tag colour is a choice
   the shop made to tell its own labels apart at a glance, so it has to mean
   the same thing on every screen — if "red" shifted with the panel's accent,
   two tags could come out identical in one shop and not in another. */
.ck-tag{
  display: inline-flex;
  align-items: center;
  max-width: 100%;
  font-size: var(--t-xs);
  font-weight: 700;
  line-height: 1.5;
  padding: 2px var(--s-2);
  border-radius: var(--r-full);
  border: 1px solid transparent;
  overflow-wrap: anywhere;
  background: #EEF0F2; color: #3E4A56; border-color: #D6DBE0;
}
.ck-tag.is-slate { background: #EEF0F2; color: #3E4A56; border-color: #D6DBE0; }
.ck-tag.is-gold  { background: #FBF0D8; color: #7D5A1B; border-color: #EDDCAE; }
.ck-tag.is-green { background: #E6F0E9; color: #2F6B45; border-color: #C9E0D2; }
.ck-tag.is-red   { background: #FBE9E6; color: #A6342B; border-color: #F0CDC7; }
.ck-tag.is-blue  { background: #E5EEF8; color: #235184; border-color: #C6DAEE; }
.ck-tag.is-purple{ background: #EFE9F7; color: #57377F; border-color: #DACEEB; }

.ck-tags{ display: inline-flex; flex-wrap: wrap; gap: 4px; max-width: 100%; }
.profile-head .p-tags{ margin-top: 6px; }

/* Choosing a customer's tags: a checkbox behind each chip, big enough to hit
   with a thumb on a counter tablet. */
.ck-tag-picks{ display: flex; flex-wrap: wrap; gap: var(--s-2); }
.ck-tag-pick{
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: 6px var(--s-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  cursor: pointer;
  min-height: 40px;
}
.ck-tag-pick:hover{ background: var(--surface-2); }
.ck-tag-pick input{ width: 18px; height: 18px; margin: 0; flex: none; }
.ck-tag-pick:has(input:checked){ border-color: var(--gold); background: var(--gold-wash); }

/* The owner's tag list: name, colour and order edited on one line. */
.ck-tag-edit{ display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center; }
.ck-tag-edit input[type=text]{ flex: 1 1 130px; min-width: 110px; }
.ck-tag-edit select{ flex: 0 1 110px; }
.ck-tag-edit input[type=number]{ flex: 0 0 78px; }
.ck-tag-edit input, .ck-tag-edit select{ margin-bottom: 0; }

/* --------------------------------------------------------- row selection -
   The box sits inside the name cell rather than in a column of its own: on a
   phone the table becomes stacked cards, and a column of its own would take a
   full-width row to hold one checkbox. */
.ck-pick{
  display: inline-flex; align-items: center; justify-content: center;
  vertical-align: middle;
  min-width: 34px; min-height: 34px;
  margin: -6px 2px -6px -6px;
}
/* The browser's own blue tick is the one thing on this panel that is not in
   the shop's colours, and a whole column of them is hard to miss. */
.ck-pick input,
.ck-tag-pick input{ accent-color: var(--navy); }
.ck-pick input{ width: 18px; height: 18px; margin: 0; cursor: pointer; }
@media (max-width: 700px){
  /* A 44px target once there is no mouse involved. */
  .ck-pick{ min-width: 44px; min-height: 44px; margin: -8px 2px -8px -10px; }
  .ck-pick input{ width: 22px; height: 22px; }
}

/* ------------------------------------------------------------ bulk actions */
.ck-bulk{ border-color: var(--line-strong); }
.ck-bulk-head h2{ display: flex; align-items: center; gap: var(--s-2); margin-bottom: 4px; }
.ck-bulk-head .hint{ margin-bottom: var(--s-2); }
.ck-bulk-pickers{ margin-bottom: var(--s-3); flex-wrap: wrap; }
.ck-bulk .inline-fields{ align-items: flex-end; }
.ck-bulk .field{ margin-bottom: 0; }
.ck-bulk .field[hidden]{ display: none; }

/*
 * Once something is ticked the bar follows you down the page.
 *
 * Without this it sits below twenty-five rows — about eight thousand pixels
 * on a phone — so ticking a box at the top and then finding the button is a
 * scroll nobody would make twice. It only sticks while a selection exists, so
 * an ordinary visit to the list is not permanently short of a bar's height.
 *
 * z-index 16 puts it over the paging strip (15) rather than beside it: while
 * a selection is live the actions matter and the page numbers do not.
 */
.ck-bulk.is-armed{
  position: sticky;
  bottom: 0;
  z-index: 16;
  border-color: var(--gold);
  box-shadow: 0 -8px 24px color-mix(in srgb, var(--navy) 16%, transparent),
              var(--shadow-sm, 0 2px 10px rgba(0,0,0,.06));
  max-height: 64vh;
  overflow-y: auto;
}
@supports not (color: color-mix(in oklab, red 50%, blue)){
  .ck-bulk.is-armed{ box-shadow: 0 -8px 24px rgba(22, 50, 63, .16); }
}
@media (max-width: 700px){
  /* Stuck to the bottom of a 844px screen, the heading and the long note are
     the first things to go — the count and the controls are the bar. */
  .ck-bulk.is-armed .ck-bulk-head h2,
  .ck-bulk.is-armed > form > .field-note{ display: none; }
  .ck-bulk.is-armed .ck-bulk-head .hint{ margin-bottom: var(--s-2); font-weight: 700; }
}
@media (max-width: 520px){
  /* One control per line: at 390px a four-across row of selects is unreadable
     and the submit button ends up off the edge. */
  .ck-bulk .inline-fields{ display: block; }
  .ck-bulk .inline-fields > .field{ margin-bottom: var(--s-3); }
  .ck-bulk .inline-fields > button{ width: 100%; }
  .ck-bulk-pickers .btn{ flex: 1 1 auto; }
}

/* On a phone the tags line is worth keeping only when there is something on
   it — twenty-five rows each carrying an empty "TAGS —" is a lot of scrolling
   for nothing. Tags and the action buttons are moved to the end of the card so
   the numbers stay in tidy pairs. */
@media (max-width: 700px){
  #customersTable tbody td.ck-no-tags{ display: none; }
  #customersTable tbody td[data-label="Tags"]{ grid-column: 1 / -1; order: 1; }
  #customersTable tbody td[data-label="Actions"]{ order: 2; }
}

/* ------------------------------------------------------- merge duplicates */
.ck-dupes{ list-style: none; margin: 0 0 var(--s-3); padding: 0; }
.ck-dupe{
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s-3);
  margin-bottom: var(--s-3);
  background: var(--surface-2);
}
.ck-dupe-why{
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: var(--track-wide);
  color: var(--text-muted); font-weight: 700; margin: 0 0 var(--s-2);
}
.ck-dupe-pair{ display: grid; gap: var(--s-2); grid-template-columns: 1fr; }
@media (min-width: 640px){ .ck-dupe-pair{ grid-template-columns: 1fr 1fr; } }
.ck-dupe-side{
  display: flex; flex-direction: column; gap: 4px; align-items: flex-start;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: var(--s-2) var(--s-3);
  min-width: 0;
}
.ck-dupe-side .nm{ font-weight: 700; color: var(--navy); text-decoration: none; overflow-wrap: anywhere; }
.ck-dupe-side .nm:hover{ text-decoration: underline; }
.ck-dupe-side form{ margin-top: 4px; width: 100%; }
.ck-dupe-side form .btn{ width: 100%; }

/* ---------------------------------------------------------- counter PINs */
.ck-pin-head{ display: flex; align-items: center; gap: var(--s-2); margin-bottom: var(--s-2); }
.ck-pin-head .icon{ color: var(--gold-ink); }

/* A money figure is three or four times as long as a stamp count — "Rs.
   39,365.00" against "71" — and at the full KPI size it runs into the edge of
   its tile. Shrunk just enough to fit, and allowed to wrap rather than clip. */
.kpi .num.money{
  font-size: clamp(1.1rem, 0.8rem + 0.9vw, 1.6rem);
  overflow-wrap: anywhere;
}

/* ==================================================== SETTING UP (A3/B3) ===
   The onboarding checklist on the dashboard. Deliberately not a .card: it is
   temporary furniture that leaves the moment the shop is set up, so it reads
   as a note pinned to the top of the page rather than as another panel of
   numbers. Every colour comes from the shop's own theme.
   ========================================================================= */
.onb-card{
  background: linear-gradient(180deg, var(--gold-wash) 0%, var(--surface) 62%);
  border: 1px solid color-mix(in srgb, var(--gold) 42%, transparent);
  border-radius: var(--r-lg);
  padding: var(--s-4);
  margin-bottom: var(--s-3);
  box-shadow: var(--shadow-xs);
}
.onb-head{
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
}
.onb-head h2{ margin: 0; font-size: var(--t-md); }
.onb-sub{ margin: 2px 0 0; font-size: var(--t-sm); color: var(--text-muted); line-height: var(--lh-snug); }
.onb-head-right{ display: flex; align-items: center; gap: var(--s-2); flex: none; }
.onb-count{
  font-size: var(--t-sm);
  font-weight: 700;
  color: var(--gold-ink);
  background: var(--surface);
  border: 1px solid color-mix(in srgb, var(--gold) 40%, transparent);
  border-radius: var(--r-full);
  padding: 4px var(--s-3);
  white-space: nowrap;
}
.onb-dismiss{ margin: 0; }

.onb-track{
  height: 6px;
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--gold) 22%, var(--surface));
  overflow: hidden;
  margin: var(--s-3) 0;
}
.onb-fill{
  height: 100%;
  border-radius: inherit;
  background: var(--grad-gold);
  transition: width var(--dur) var(--ease);
}

.onb-list{ list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); }
.onb-step{
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--s-2) var(--s-3);
}
.onb-tick{
  display: grid;
  place-items: center;
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--surface-3);
  color: var(--text-muted);
}
.onb-tick .icon{ width: 16px; height: 16px; }
.onb-step.is-done{ background: var(--surface-2); border-color: color-mix(in srgb, var(--ok) 22%, transparent); }
.onb-step.is-done .onb-tick{ background: var(--ok-bg); color: var(--ok); }
.onb-step.is-done .onb-title{ color: var(--text-muted); text-decoration: line-through; }
.onb-step.is-done .onb-why{ color: var(--ok); font-weight: 600; }

.onb-body{ display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 220px; }
.onb-title{ font-weight: 600; color: var(--navy); text-decoration: none; }
a.onb-title:hover{ text-decoration: underline; }
.onb-why{ font-size: var(--t-xs); color: var(--text-muted); line-height: var(--lh-snug); }
.onb-go{ flex: none; }

@media (max-width: 560px){
  .onb-go{ width: 100%; }
}

/* ================================================= RETENTION COHORTS (B12) ==
   A heat table: one row per joining month, one column per month afterwards.
   Not `table.tbl`, which collapses into stacked cards below 700px — a grid
   whose whole meaning is the shape of the numbers must stay a grid and scroll
   sideways instead.
   ========================================================================= */
table.cohort{
  width: 100%;
  min-width: 560px;
  border-collapse: separate;
  border-spacing: 3px;
  font-size: var(--t-sm);
}
table.cohort th{
  font-weight: 700;
  color: var(--text-muted);
  font-size: var(--t-xs);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  text-align: left;
  padding: var(--s-2);
  white-space: nowrap;
}
table.cohort thead th.num{ text-align: right; }
table.cohort tbody th, table.cohort tfoot th{
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--t-sm);
  color: var(--navy);
}
table.cohort td{
  padding: var(--s-2);
  text-align: right;
  font-variant-numeric: tabular-nums;
  border-radius: var(--r-sm);
  white-space: nowrap;
}
table.cohort tfoot th, table.cohort tfoot td{
  border-top: 2px solid var(--line);
  font-weight: 700;
}

/* The shading. --heat is 0..1, written on the cell by reports.php, so the
   depth of the colour IS the percentage rather than a decoration beside it. */
.co-cell{
  --heat: 0;
  background: color-mix(in srgb, var(--gold) calc(var(--heat) * 100%), var(--surface-2));
  color: var(--text);
  font-weight: 600;
  text-align: center;
}
.co-cell.is-none{
  background: repeating-linear-gradient(135deg,
    var(--surface-2) 0 6px, var(--surface-3) 6px 12px);
  color: var(--text-soft);
  font-weight: 400;
}
.co-cell.is-partial{ box-shadow: inset 0 0 0 1px var(--line-strong); }
.co-star{ color: var(--gold-ink); font-weight: 700; }

/* ===========================================================================
   NOTIFICATIONS — the audience builder, the queue and the template list
   (B14 / B13 / B9 / B8). Appended, so nothing above is disturbed.
   =========================================================================== */

/* The builder is a fieldset rather than a bare div so screen readers announce
   "Build the audience" once and then read the controls as a group — which is
   the difference between eight labelled boxes and eight loose boxes. */
.ls-builder{
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: var(--s-3);
  margin: 0 0 var(--s-3);
  background: var(--surface-2, var(--surface));
}
.ls-builder legend{
  display: inline-flex; align-items: center; gap: 6px;
  padding: 0 var(--s-2);
  font-size: var(--t-xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: var(--track-wide); color: var(--text-muted);
}
.ls-builder legend .icon{ width: 1em; height: 1em; }
.ls-builder .inline-fields{ margin-bottom: var(--s-2); }
.ls-builder .inline-fields:last-of-type{ margin-bottom: 0; }
.ls-builder .btn-row{ margin-top: var(--s-3); }

/* Tag choices wrap into as many rows as they need. Each is a whole tappable
   chip, not a 13px checkbox with a word beside it — this screen is used on a
   phone behind a counter. */
.ls-tagpick{ display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-2); }
.ls-tag{
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px var(--s-3);
  min-height: 40px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  font-size: var(--t-sm);
  cursor: pointer;
  user-select: none;
}
.ls-tag input{ width: 16px; height: 16px; margin: 0; accent-color: var(--navy); }
.ls-tag:has(input:checked){ border-color: var(--navy); background: var(--gold-wash); font-weight: 700; }
.ls-tag:focus-within{ outline: 2px solid var(--gold); outline-offset: 2px; }

/* Saving a template sits under the send buttons, separated by a rule, because
   it is a different intention from sending and must not be mistaken for one. */
.ls-tplsave{
  margin-top: var(--s-4);
  padding-top: var(--s-3);
  border-top: 1px solid var(--line);
}
.ls-tplsave .inline-fields{ align-items: flex-end; }

/* A cell in an inline row that holds nothing but its button.
   Without this it takes an equal share of the row and a two-word Save button
   stretches to half the card, which reads as the most important thing on the
   screen when it is the least. */
.ls-act{ flex: 0 0 auto !important; justify-content: flex-end; }
.ls-act .btn{ width: auto; }

/* The queue list: one card-within-a-card per job. */
.ls-jobs{ list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-2); }
.ls-job{
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: var(--s-2) var(--s-3);
  background: var(--surface);
  display: flex; flex-direction: column; gap: 4px;
}
.ls-job-top{ display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.ls-job-top b{ overflow-wrap: anywhere; }
.ls-job form{ margin-top: 4px; }

/* The recipient count is the one number on this screen somebody might send
   three thousand messages on the strength of, so it is allowed to be large
   even when it is long. */
.recip-count #lsCount{ overflow-wrap: anywhere; }

@media (max-width: 640px){
  .ls-builder{ padding: var(--s-2); }
  /* On a phone the row stacks, so the button goes back to full width — a
     40px-tall control on the left of an empty line is a smaller target for
     no reason. */
  .ls-tplsave .inline-fields .field{ flex: 1 1 100%; }
  .ls-act{ flex: 1 1 100% !important; }
  .ls-act .btn{ width: 100%; }
}

/* ==========================================================================
   SHOP TOOLS — the counter PIN at the till, the CSV import, drag to reorder
   ========================================================================== */

/* ------------------------------------------------ the counter PIN (B24) -- */

/* The banner at the top of the scan screen. An icon beside a sentence, so it
   reads as a state the till is in rather than as an error. */
.ck-pin-note{ display: flex; align-items: flex-start; gap: var(--s-2); }
.ck-pin-note .icon{ flex: 0 0 auto; margin-top: 2px; }

.ck-pin-mode{ margin: var(--s-3) 0 var(--s-2); }

/* The staff member's own PIN box, above the customer's. Marked out with the
   gold edge because typing your own PIN into the customer's boxes is the one
   mistake this field exists to prevent. */
.ck-counter-field{
  border-left: 3px solid var(--gold);
  padding-left: var(--s-3);
  margin-bottom: var(--s-3);
}
.ck-counter-field input{ letter-spacing: 4px; font-family: var(--font-mono); }

/* Who the stamp was recorded against, on the success screen. */
.success-state .s-by{
  font-size: var(--t-sm);
  color: var(--text-muted);
  margin-top: var(--s-1);
}

/* ------------------------------------------------------ CSV import (B21) - */

/* What the file will do, before it does it. Four lines, each a count first —
   the numbers are the point and they should line up down the left. */
.ck-import-tally{
  list-style: none;
  margin: var(--s-3) 0;
  padding: 0;
  display: flex; flex-direction: column; gap: 4px;
  font-size: var(--t-sm);
  color: var(--text-muted);
}
.ck-import-tally li{
  display: flex; align-items: baseline; gap: var(--s-2);
}
.ck-import-tally b{
  min-width: 3.5ch; text-align: right;
  font-family: var(--font-mono);
  color: var(--text);
}

.ck-import-sample{
  margin: var(--s-2) 0;
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  line-height: 1.7;
  overflow-x: auto;
}

/* ------------------------------------------------ drag to reorder (B29) -- */
/*
   An addition to the number boxes, never a replacement — see the note in
   assets/js/admin.js. With JavaScript off this is a plain ordered list of what
   is in what order, which is still worth having on the page.
*/
.ck-ro-list{
  list-style: none;
  margin: var(--s-3) 0 var(--s-2);
  padding: 0;
  display: flex; flex-direction: column; gap: 6px;
}
.ck-ro-item{
  display: flex; align-items: center; gap: var(--s-2);
  padding: 6px var(--s-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  /* The row is lifted while it is being dragged, so it needs its own layer to
     be lifted above. */
  position: relative;
  transition: box-shadow var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.ck-ro-item .ck-ro-name{
  flex: 1 1 auto; min-width: 0;
  font-weight: 600;
  overflow-wrap: anywhere;
}
.ck-ro-item .ck-ro-pos{
  flex: 0 0 auto;
  min-width: 3.5ch; text-align: right;
  font-family: var(--font-mono);
  font-size: var(--t-xs);
  color: var(--text-muted);
}

/* The handle. A 44px target, because the whole point is a thumb — and
   touch-action:none so dragging it does not scroll the page underneath. */
.ck-ro-grip{
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  margin: -6px 0 -6px calc(var(--s-2) * -1);
  border: 0; border-radius: var(--r-sm);
  background: transparent;
  color: var(--text-muted);
  cursor: grab;
  touch-action: none;
}
.ck-ro-grip:hover{ color: var(--navy); background: var(--surface-2); }
.ck-ro-grip:focus-visible{ outline: 2px solid var(--gold); outline-offset: -2px; color: var(--navy); }
.ck-ro-grip .icon{ width: 18px; height: 18px; pointer-events: none; }

.ck-ro-item.is-dragging{
  z-index: 2;
  cursor: grabbing;
  border-color: var(--gold);
  box-shadow: 0 8px 20px rgb(0 0 0 / .16);
  background: var(--gold-wash);
}
/* While a drag is in progress nothing else on the list should look clickable,
   and a stray text selection under a moving finger looks like a fault. */
.ck-ro-list.is-dragging{ user-select: none; }
.ck-ro-list.is-dragging .ck-ro-grip{ cursor: grabbing; }

.ck-ro-msg{
  margin: 0;
  min-height: 1.2em;
  font-size: var(--t-xs);
  color: var(--text-muted);
}
.ck-ro-msg.is-ok{ color: var(--ok); font-weight: 600; }
.ck-ro-msg.is-err{ color: var(--danger); font-weight: 600; }

@media (prefers-reduced-motion: reduce){
  .ck-ro-item{ transition: none; }
}

/* On a narrow screen the grid scrolls sideways inside .tbl-wrap, which is fine
   until the month column scrolls away with it and the percentages lose their
   labels. The first column stays put instead, so a thumb-drag across six
   months always has "Jun 2026" beside it. */
@media (max-width: 700px){
  table.cohort{ min-width: 520px; font-size: var(--t-xs); }
  table.cohort th, table.cohort td{ padding: var(--s-1) var(--s-2); }
  table.cohort thead th:first-child,
  table.cohort tbody th,
  table.cohort tfoot th{
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--surface);
    box-shadow: 3px 0 0 var(--surface), 4px 0 0 var(--line);
  }
}

/* ======================================================================
   THE OFFLINE STAMP QUEUE  (scan page)
   ----------------------------------------------------------------------
   Deliberately not styled like a success. The card is warm-edged rather
   than green, and the rows read as an outstanding list, because every
   entry in here is a stamp the shop's records do not have yet.
====================================================================== */
.queue-card{ border-left: 3px solid var(--warn); }
.queue-card .card-head{ align-items: center; }

.q-count{
  display: inline-block;
  margin-left: var(--s-2);
  padding: 2px 9px;
  border-radius: 999px;
  font-size: var(--t-sm);
  font-weight: 700;
  background: var(--warn-bg);
  color: var(--warn);
  white-space: nowrap;
}
.q-count.is-bad{ background: var(--danger-bg); color: var(--danger); }

.queue-list{ list-style: none; margin: 0; padding: 0; }
.q-row{
  display: flex;
  align-items: center;
  gap: var(--s-2);
  flex-wrap: wrap;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}
.q-row:last-child{ border-bottom: 0; }
.q-main{ display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.q-main b{ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.q-when{ font-size: var(--t-sm); color: var(--text-muted); }
.q-state{
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--t-sm);
  color: var(--warn);
  flex: 0 1 auto;
}
.q-row.is-bad .q-state{ color: var(--danger); }
.q-state .icon{ flex: 0 0 auto; }

/* The held-not-sent result panel. The glyph is a struck-through wifi mark,
   never the tick — that difference is the whole point of the feature. */
.success-state .tick.held{ background: var(--warn-bg); color: var(--warn); }
.success-state .s-note{
  margin: var(--s-3) auto 0;
  max-width: 34em;
  font-size: var(--t-sm);
  color: var(--text-muted);
  line-height: 1.55;
}

/* On a phone the state and the Remove button drop under the name rather
   than squeezing it to two characters. */
@media (max-width: 560px){
  .q-row{ align-items: flex-start; }
  .q-main{ flex: 1 1 100%; }
  .q-state{ flex: 1 1 auto; }
}

/* ==================================================== B26  FEEDBACK ======= */
/* The owner's private view of what customers said after a reward was handed
   over. Everything here is built from the existing tokens and the existing
   .tbl / .chip / .kpi furniture — the only new shapes are the star row, the
   distribution bars and the month strip. */

/* ------------------------------------------------------------- the stars --- */
/* Five sprite stars, filled up to the score. The empty ones are drawn rather
   than left out so the row is always the same width, which keeps the numbers
   in the column beside them lined up. */
.fb-stars{ display: inline-flex; gap: 1px; vertical-align: -0.15em; }
.fb-star .icon{
  width: 14px; height: 14px;
  color: var(--line-strong);
  vertical-align: baseline;
}
.fb-star.on .icon{ color: var(--gold); fill: currentColor; stroke: currentColor; }

/* ------------------------------------------------------ the distribution --- */
.fb-dist{ list-style: none; margin: var(--s-3) 0 0; padding: 0; display: grid; gap: 2px; }
.fb-dist-row > a{
  display: grid;
  grid-template-columns: minmax(120px, 170px) 1fr auto;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-1) var(--s-2);
  border-radius: var(--r-sm);
  text-decoration: none;
  color: var(--text);
  min-height: 36px;
}
.fb-dist-row > a:hover{ background: var(--surface-2); }
.fb-dist-row > a:focus-visible{ outline: 2px solid var(--gold); outline-offset: -2px; }
.fb-dist-row.is-on > a{ background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--line-strong); }

.fb-dist-lbl{
  display: inline-flex; align-items: center; gap: var(--s-1);
  font-size: var(--t-sm); white-space: nowrap; min-width: 0;
}
.fb-dist-lbl b{ font-variant-numeric: tabular-nums; }

.fb-dist-track{
  height: 10px; min-width: 40px;
  background: var(--surface-3);
  border-radius: var(--r-full);
  overflow: hidden;
}
.fb-dist-fill{ display: block; height: 100%; background: var(--grad-gold); border-radius: var(--r-full); }
/* A score under the shop's threshold is the thing to act on, so it is red on
   the bar as well as in the table. */
.fb-dist-row.is-low .fb-dist-fill{ background: var(--danger); }

.fb-dist-num{
  min-width: 58px; text-align: right;
  font-weight: 700; font-variant-numeric: tabular-nums;
  line-height: var(--lh-snug);
}
.fb-dist-pct{ display: block; font-size: var(--t-xs); font-weight: 500; color: var(--text-muted); }

/* Narrow: the label takes its own line so the bar keeps a usable length
   instead of collapsing to a stub beside five stars. */
@media (max-width: 560px){
  .fb-dist-row > a{ grid-template-columns: 1fr auto; gap: var(--s-2) var(--s-3); padding: var(--s-2); }
  .fb-dist-lbl{ grid-column: 1 / -1; }
}

/* ------------------------------------------------------------ month strip --- */
.fb-trend{
  list-style: none; margin: var(--s-3) 0 0; padding: 0;
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(44px, 1fr);
  gap: var(--s-2); align-items: end;
}
.fb-trend li{ display: grid; justify-items: center; gap: 2px; min-width: 0; }
.fb-t-bar{
  display: flex; align-items: flex-end;
  width: 24px; height: 68px;
  background: var(--surface-3);
  border-radius: var(--r-sm);
  overflow: hidden;
}
.fb-t-fill{ display: block; width: 100%; background: var(--grad-gold); border-radius: var(--r-sm); }
.fb-trend li.is-low .fb-t-fill{ background: var(--danger); }
.fb-t-avg{ font-size: var(--t-sm); font-weight: 700; font-variant-numeric: tabular-nums; }
.fb-t-mon,
.fb-t-n{ font-size: var(--t-xs); color: var(--text-muted); text-align: center; }

/* ---------------------------------------------------------- the comments --- */
/* A comment is prose in a table, so it gets room to wrap and a real quotation
   mark rather than a straight one. */
.fb-cmt{ max-width: 34ch; white-space: normal; }
.fb-cmt q{ quotes: "\201C" "\201D"; font-style: italic; color: var(--text); }

/* A low rating is marked on the row itself, not only in the chip: the page is
   read by scanning down the left edge for red. */
table.fb-tbl tbody tr.is-low > td{ background: color-mix(in srgb, var(--danger-bg) 60%, transparent); }
table.fb-tbl tbody tr.is-low:hover > td{ background: var(--danger-bg); }
table.fb-tbl tbody tr.is-low > td:first-child{ box-shadow: inset 3px 0 0 var(--danger); }

@media (max-width: 700px){
  /* Stacked, the row IS the card, so the red edge moves to its border. */
  table.fb-tbl tbody tr.is-low{ border-color: var(--danger); box-shadow: inset 3px 0 0 var(--danger); }
  table.fb-tbl tbody tr.is-low > td:first-child{ box-shadow: none; }
  /* The comment and the reward are the two cells worth reading in full. */
  table.fb-tbl tbody td.fb-cmt,
  table.fb-tbl tbody td[data-label="Reward they rated"]{ grid-column: 1 / -1; }
  .fb-cmt{ max-width: none; }
}

/* ===========================================================================
   Stamp rules: the double-stamp schedule, and the birthday treat
   (B15 / B16 / C12 — admin/settings.php)
   =========================================================================== */

/* A sub-section inside a settings card. The Rewards card carries two forms —
   what a full card is worth, and what a birthday is worth — and without a rule
   between them the second reads as more of the first. */
.set-sub{ scroll-margin-top: calc(var(--stick-top) + var(--s-4)); }
.set-sub > h3:first-child{ margin-top: 0; }

/* Seven day tiles rather than a seven-row grid like the opening hours: these
   carry one bit each, so a row per day would be six lines of nothing. */
.day-pick{
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-1);
  margin-top: var(--s-1);
}

.day-opt{
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 54px;
  min-height: 40px;
  padding: var(--s-1) var(--s-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  cursor: pointer;
  user-select: none;
  font-weight: 600;
  transition: border-color var(--dur-fast) var(--ease),
              background var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}
.day-opt:hover{ border-color: var(--line-strong); }

/* The checkbox is the accessible control; the tile is what you see. :has()
   keeps the tile honest as it is clicked, with no script to go out of step —
   the server-rendered .is-on only has to be right on the first paint. */
.day-opt input{
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  min-height: 0;
}
.day-opt.is-on,
.day-opt:has(input:checked){
  border-color: var(--gold);
  background: color-mix(in srgb, var(--gold) 16%, var(--surface));
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--gold) 30%, transparent);
}
.day-opt:has(input:focus-visible){
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}

@media (max-width: 700px){
  /* A thumb, not a mouse: the tiles grow to the 44px target and share the row
     evenly so all seven still fit across a phone. */
  .day-pick{ gap: 6px; }
  .day-opt{
    flex: 1 1 calc((100% - 6 * 6px) / 7);
    min-width: 0;
    min-height: 44px;
    padding-inline: 0;
  }
}

/* The customer asked for a particular reward before walking up (C16). Stated
   above the choice, not instead of it — staff still confirm and still take the
   PIN, so this reads as information rather than as an instruction. */
.rc-asked{
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0 0 var(--s-3);
  padding: 10px 12px;
  border-radius: var(--r-sm, 10px);
  background: var(--gold-wash);
  color: var(--gold-ink);
  font-size: var(--t-sm);
  line-height: 1.5;
}
.rc-asked .icon{ flex: 0 0 auto; margin-top: 2px; }

/* ===========================================================================
   TRY THE CARD — admin/preview.php
   ===========================================================================
   The owner's own stamp card, driven by them, saving nothing. The card itself
   is painted by assets/css/app.css inside a same-origin iframe, so nothing
   below touches it: this is only the band that says what the page is, the
   phone-shaped frame around it, and the counter beside it.
   ------------------------------------------------------------------------ */

/* The band. Permanent, at the top of the page, and deliberately not one of the
   panel colours — a sandbox warning that looks like the rest of the panel is
   a sandbox warning nobody reads. The card inside the frame carries its own
   copy of this, because a screenshot of the card alone must not be mistakable
   for a real customer's. */
.pv-notice{
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s-3);
  background: #4B1D8F;
  color: #F1E7FF;
  border-radius: var(--r-md);
  padding: var(--s-3) var(--s-4);
  margin-bottom: var(--s-3);
  font-size: var(--t-sm);
  line-height: var(--lh-snug);
}
.pv-notice-txt{ flex: 1 1 320px; }
.pv-notice b{ color: #fff; }
.pv-notice-ico{ display: inline-flex; flex: none; }
.pv-notice .btn{
  flex: none;
  background: rgb(255 255 255 / 14%);
  color: #fff;
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 34%);
}
.pv-notice .btn:hover{ background: rgb(255 255 255 / 26%); }

/* Phone first, counter under it. Two columns once there is room for both,
   with the phone sticky so the buttons never scroll away from the card they
   are driving. */
.pv-layout{ display: grid; grid-template-columns: 1fr; gap: var(--s-4); align-items: start; }
@media (min-width: 1080px){
  .pv-layout{ grid-template-columns: minmax(0, 420px) minmax(0, 1fr); }
  .pv-phone-col{ position: sticky; top: var(--s-4); }
}

/* The bezel. A frame, not a toy: it exists so the owner reads the card at the
   width a phone actually gives it, which is the whole point of looking. */
.pv-phone{
  /* The handset numbers are in tokens.css so /demo and the home page can draw
     the same phone. These four values ARE that shape - they were the ones
     copied out. */
  width: min(100%, var(--phone-w));
  margin-inline: auto;
  background: #14181B;
  border-radius: var(--phone-radius);
  padding: var(--phone-bezel);
  box-shadow: 0 18px 44px rgb(20 24 27 / 26%), inset 0 0 0 1px rgb(255 255 255 / 8%);
}
.pv-phone-top{
  width: 78px;
  height: 5px;
  margin: 2px auto 8px;
  border-radius: 99px;
  background: rgb(255 255 255 / 22%);
}
.pv-screen{
  display: block;
  width: 100%;
  height: var(--phone-h);
  border: 0;
  border-radius: 22px;
  background: var(--surface-2);
}
@media (max-width: 560px){
  .pv-screen{ height: 620px; }
}
.pv-phone-hint{ max-width: var(--phone-w); margin: var(--s-3) auto 0; text-align: center; }

/* ---------------------------------------------------------- the counter -- */
.pv-side{ min-width: 0; }

.pv-nums{
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-4);
  margin: var(--s-3) 0 var(--s-2);
  font-size: var(--t-sm);
  color: var(--text-muted);
}
.pv-nums b{
  display: inline-block;
  min-width: 1ch;
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--ink);
}

.pv-controls{ margin-top: var(--s-3); }
.pv-row{ display: flex; flex-wrap: wrap; gap: var(--s-2); margin-bottom: var(--s-2); }
.pv-row .btn{ flex: 0 1 auto; }

.pv-sub{
  margin: var(--s-4) 0 var(--s-2);
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.pv-amount{
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin-bottom: var(--s-2);
  font-size: var(--t-sm);
}
.pv-amount-in{
  width: 8rem;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
}

.pv-err{ margin-top: var(--s-3); }

/* The honest list. A definition list rather than bullets, because each item is
   a claim and its reason, and the reason is the half that matters. */
.pv-limits{ margin: var(--s-3) 0 0; }
.pv-limits dt{
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: var(--s-3);
  font-weight: 700;
  line-height: var(--lh-snug);
}
.pv-limits dt:first-child{ margin-top: 0; }
.pv-limits dt .icon{ flex: 0 0 auto; margin-top: 3px; color: var(--gold-ink); }
.pv-limits dd{
  margin: 4px 0 0 26px;
  font-size: var(--t-sm);
  line-height: 1.55;
  color: var(--text-muted);
}

/* ======================================================================
   IN-PAGE TABS  (panes.js)
   ----------------------------------------------------------------------
   Turns a long stack of cards into one card at a time. The strip looks
   the only tab strip in the panel. A page-level one was tried and taken
   out again: it sat directly above this and the two rows of navigation
   stacked on each other read as a mistake rather than as a hierarchy.
   The sidebar moves you between pages; this moves you within one.

   So: pills on a surface, not underlined text on a rule.
====================================================================== */
.pane-nav{
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 var(--s-5);
  padding: 6px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}

.pane-tab{
  flex: 0 1 auto;
  appearance: none;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: var(--t-sm);
  font-weight: 600;
  padding: 8px 14px;
  min-height: 38px;
  border-radius: var(--r-md);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.pane-tab:hover{ color: var(--text); background: var(--surface); }
.pane-tab:focus-visible{ outline: 2px solid var(--gold); outline-offset: 1px; }

.pane-tab.is-on{
  background: var(--surface);
  border-color: var(--line);
  color: var(--text);
  box-shadow: var(--shadow-xs);
}

/* The section itself keeps its own card styling; it only loses the margin
   that separated it from the sibling now hidden behind a tab. */
.is-pane.is-on{ margin-top: 0; }

/* On a phone nine labels wrap to four rows — 200px of tab strip before any
   content, on an 844px screen. One scrolling row costs 44px instead, and is
   what the page-level strip above already does, so the two behave alike. */
@media (max-width: 700px){
  /* `.a-page-head .acts` sets flex-wrap:wrap at two-class specificity, and an
     adopted strip IS that element — so this has to match it or the strip keeps
     wrapping however late in the file the rule sits. */
  .pane-nav,
  .a-page-head .pane-nav{
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    gap: 4px;
    padding: 4px;
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
    /* Bleed to the edges so a scrollable row reads as scrollable rather than
       looking clipped inside the page's padding. */
    margin-left: calc(var(--s-4) * -1);
    margin-right: calc(var(--s-4) * -1);
    padding-left: var(--s-4);
    padding-right: var(--s-4);
    border-radius: 0;
  }
  .pane-tab{ flex: 0 0 auto; padding: 8px 12px; min-height: 40px; }
}

/* Print the whole page. panes.js also unhides on beforeprint for browsers
   that honour it; this covers the ones that print from a saved copy. */
@media print{
  .pane-nav{ display: none; }
  .is-pane[hidden]{ display: block !important; }
}

/* One section at a time has no use for two columns: a multi-column block with a
   single visible child lays it out in the first column at half width, leaving
   the other half empty. Only applied while panes.js is actually running. */
.a-columns.has-panes{ columns: 1; }

/* A strip the page already had, adopted as the tab list. It keeps its own
   spacing and simply gains a selected state, so the page does not suddenly
   grow a second row of the same words in a different shape. */
.pane-nav.is-adopted{
  background: none;
  border: 0;
  padding: 0;
  border-radius: 0;
}
.pane-nav.is-adopted .pane-tab.is-on,
.set-nav a.pane-tab.is-on{
  background: var(--main, var(--navy));
  border-color: var(--main, var(--navy));
  color: var(--text-on-dark);
}

/* Queue mode, on the scan page.
 *
 * Sits under the camera/phone tabs because that is where the person on the
 * till is already looking, and a rush is over by the time anybody has gone
 * hunting through Settings for it. */
.scan-queue{
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  margin: var(--s-3) 0 0;
  padding: var(--s-3);
  background: var(--surface-2);
  border-radius: var(--r-md);
  font-size: var(--t-sm);
  color: var(--text-soft);
  cursor: pointer;
}
.scan-queue input{
  width: 18px;
  height: 18px;
  min-height: 0;
  margin: 1px 0 0;
  flex: 0 0 18px;
  accent-color: var(--gold);
}
.scan-queue:hover{ color: var(--text); }

/* The shop's own note about a customer, at the till.
 *
 * Quiet but not invisible: it must be noticed while somebody is standing
 * there, and must not compete with the reward panel below it, which is the
 * thing that changes what staff DO. */
.cust-note{
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  margin: 0 0 var(--s-3);
  padding: var(--s-3);
  background: var(--surface-2);
  border-left: 3px solid var(--line-strong);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  font-size: var(--t-sm);
  color: var(--text);
}
.cust-note > .i{ flex: 0 0 auto; margin-top: 2px; color: var(--text-soft); }

/* Giving a reward away.
 *
 * Folded shut because it is rare and it costs the shop money — it must not sit
 * open beside "redeem", where a tired hand at a till finds it by accident. */
.gift-box{
  margin-top: var(--s-4);
  padding-top: var(--s-4);
  border-top: 1px solid var(--line);
}
.gift-box > summary{
  cursor: pointer;
  font-size: var(--t-sm);
  font-weight: 600;
  color: var(--text-soft);
  padding: var(--s-2) 0;
}
.gift-box > summary:hover{ color: var(--text); }
.gift-box[open] > summary{ margin-bottom: var(--s-2); }

/* A change against the period before.
 *
 * Colour carries the direction, but the arrow and the sign carry it too — a
 * shop owner with any degree of colour blindness must be able to read this,
 * and "green means good" is not universal anyway. */
.kpi .delta.is-up{   color: var(--ok, #2f7d4f); }
.kpi .delta.is-down{ color: var(--danger); }
.kpi .delta.is-flat{ color: var(--text-muted); }

/* ---------------------------------------------------------------------------
   The week, hour by hour.

   A table rather than a picture: the numbers ARE the chart, they read on a
   phone, they can be copied into a spreadsheet, and a screen reader gets
   figures rather than the word "chart".
   ------------------------------------------------------------------------- */

.heat{
  border-collapse: separate;
  border-spacing: 2px;
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
  /* Wider than a phone by design; .tbl-wrap scrolls it. */
  min-width: 100%;
}
.heat th{
  font-weight: 600;
  color: var(--text-soft);
  padding: 2px 4px;
  white-space: nowrap;
  text-align: center;
}
.heat th[scope="row"]{ text-align: left; padding-right: var(--s-3); }

.heat td{
  width: 34px;
  height: 26px;
  text-align: center;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--text-soft);
}

/*
 * Five steps, not a gradient. Neighbouring shades have to be distinguishable
 * to carry meaning, and a 1% difference in opacity is not. The darkest two
 * flip the text to light so the figure stays readable on them.
 */
.heat td.h1{ background: color-mix(in srgb, var(--navy) 10%, var(--surface)); }
.heat td.h2{ background: color-mix(in srgb, var(--navy) 26%, var(--surface)); }
.heat td.h3{ background: color-mix(in srgb, var(--navy) 45%, var(--surface)); }
.heat td.h4{ background: color-mix(in srgb, var(--navy) 68%, var(--surface)); color: var(--text-on-dark); }
.heat td.h5{ background: var(--navy); color: var(--text-on-dark); }

@media (max-width: 640px){
  .heat td{ width: 28px; height: 24px; }
}

/* ==================================================== the marketing kit === */
/*
 * Four printable sheets and the offer-picture maker (admin/kit.php).
 *
 * The sheets are laid out for A4 and each takes its own page. On screen they
 * are shown at a readable size inside the panel; at print time the admin chrome
 * is dropped and only these come out.
 */
.kit-maker{
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-4);
  align-items: start;
}
@media (min-width: 860px){ .kit-maker{ grid-template-columns: 1fr 380px; } }

.kit-preview{ display: flex; justify-content: center; }
#offerCanvas{
  width: 100%;
  max-width: 340px;
  height: auto;
  border-radius: var(--r-md, 12px);
  border: 1px solid var(--line);
  background: var(--surface-2);
}

.kit-sheet{
  background: #fff;
  color: #12191C;
  border: 1px solid var(--line);
  border-radius: var(--r-lg, 14px);
  margin: 0 0 var(--s-5);
  padding: 0;
  overflow: hidden;
}

.kit-window, .kit-counter, .kit-leaflet, .kit-crib{ padding: 32px; }

.kit-window{ text-align: center; background: #fff; }
.kit-window h2{ margin: 6px 0 10px; font-size: 34px; color: var(--kit-main, #16323F); }
.kit-big{
  font-size: 26px;
  font-weight: 700;
  line-height: 1.25;
  margin: 0 0 18px;
  color: var(--kit-main, #16323F);
}
.kit-eyebrow{
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--kit-accent, #D9A93A);
}
.kit-small{ font-size: 12px; color: #5a6570; margin: 12px 0 0; }
.kit-logo{ max-width: 110px; max-height: 80px; margin: 0 auto 4px; display: block; }
.kit-qr{ display: flex; justify-content: center; margin: 14px 0; }
.kit-qr img, .kit-qr canvas{ display: block; }

.kit-counter{ display: flex; gap: 24px; align-items: center; justify-content: space-between; }
.kit-counter-side{ flex: 1 1 0; }
.kit-counter h3{ margin: 4px 0 8px; font-size: 26px; color: var(--kit-main, #16323F); }

.kit-leaflet h3{ margin: 0 0 14px; color: var(--kit-main, #16323F); }
.kit-leaflet ol, .kit-crib ol{ margin: 0; padding-left: 22px; }
.kit-leaflet li, .kit-crib li{ margin-bottom: 10px; line-height: 1.5; }
.kit-q{ font-weight: 700; margin: 16px 0 4px; color: var(--kit-main, #16323F); }
.kit-crib hr{ border: 0; border-top: 1px solid #e6e1d8; margin: 20px 0; }

@media print{
  /* One sheet per page, and nothing of the panel around them. */
  .kit-sheet{
    border: 0;
    border-radius: 0;
    margin: 0;
    break-after: page;
    page-break-after: always;
  }
  .kit-sheet:last-child{ break-after: auto; page-break-after: auto; }
  .kit-window, .kit-counter, .kit-leaflet, .kit-crib{ padding: 18mm; }
}

/* The gentle rung of the renewal ladder — same shape as .banner-warn, calmer. */
.banner-info{
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--text-soft);
  border-radius: var(--r-md);
  padding: var(--s-3);
  margin-bottom: var(--s-3);
  font-size: var(--t-sm);
}
.banner-info .b-ico{ flex: none; }

/* ================================================= cross-page smoothness == */
/*
 * Cross-document view transitions: a navigation inside the panel crossfades
 * instead of flashing white, and because the sidebar is pixel-identical on
 * both sides it reads as never having reloaded — which, with panel-nav.js
 * keeping its scroll, is the "content updates, page doesn't" feel without
 * swapping any DOM under the pages' own scripts.
 *
 * Pure enhancement: browsers without support ignore all of it, and reduced
 * motion switches it off entirely (the house rule: off, not down).
 */
@view-transition { navigation: auto; }

::view-transition-old(root),
::view-transition-new(root){
  animation-duration: 140ms;
  animation-timing-function: ease;
}

@media (prefers-reduced-motion: reduce){
  @view-transition { navigation: none; }
  ::view-transition-old(root),
  ::view-transition-new(root){ animation: none; }
}
