/* ============================================================
   Catalog Template — plain CSS, no build step, no external fonts.
   Mobile first: phone styles are the defaults, wider screens add
   to them further down.

   Layout of this file
     1. Design tokens (colour, type scale, spacing, radius, shadow)
     2. Base + reset
     3. App bar, staff menu, footer
     4. Browse bar (chips, search, sort/filter sheets)
     5. Product grid + cards
     6. Product detail
     7. Staff screens (panels, forms, tables, master cards)
     8. Buttons, action colours, tags
     9. Feedback (flash, notice, toast, empty)
    10. Tablet / desktop
   ============================================================ */

/* ============================================================
   1. Design tokens
   ============================================================ */
:root{
  /* --- Surfaces --- */
  --bg:#f4f7fb;              /* page */
  --bg-deep:#e8eef7;         /* wells, image placeholders */
  --card:#fff;
  --card-2:#fbfcfe;          /* nested surface inside a card */

  /* --- Ink. Darkened for contrast: 15.6:1 and 5.9:1 on white. --- */
  --ink:#0d1729;
  --ink-2:#2b3a55;           /* secondary heading / strong body */
  --muted:#54637c;           /* meta text — passes AA at 15px */
  --faint:#93a1b5;           /* placeholders only, never body copy */

  /* --- Lines --- */
  --line:#dfe6f0;
  --line-2:#c9d5e6;          /* stronger rule, form borders */

  /* --- Accent (blue) --- */
  --accent:#1d4ed8;
  --accent-600:#1e40af;
  --accent-700:#1b3a9c;
  --accent-soft:#eef4ff;
  --accent-line:#c7dbfe;
  --accent-grad:linear-gradient(140deg,#2563eb 0%,#1d4ed8 55%,#1b3fa8 100%);

  /* --- Money (green) --- */
  --price:#047857;
  --price-deep:#065f46;
  --price-soft:#e7f7f0;

  /* --- WhatsApp --- */
  --wa:#25d366;
  --wa-ink:#07361e;
  --wa-grad:linear-gradient(140deg,#37dd77 0%,#22c55e 55%,#16a34a 100%);

  /* --- Category badge (warm, sits on photos) --- */
  --badge-grad:linear-gradient(140deg,#f59e0b 0%,#d97706 60%,#b45309 100%);

  /* --- Type scale --- */
  --font:-apple-system,BlinkMacSystemFont,"Segoe UI Variable Text","Segoe UI",
         Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,
         "Apple Color Emoji","Segoe UI Emoji";
  --font-mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,
              "Liberation Mono",monospace;
  --t-2xs:11px;
  --t-xs:12px;
  --t-sm:13px;
  --t-base:15px;
  --t-md:16px;
  --t-lg:18px;
  --t-xl:21px;
  --t-2xl:25px;
  --t-3xl:31px;

  /* --- Spacing scale (4px rhythm) --- */
  --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:22px; --s6:30px; --s7:42px;

  /* --- Radius --- */
  --r-xs:8px; --r-sm:10px; --r:13px; --r-lg:16px; --r-xl:20px; --r-pill:999px;

  /* --- Elevation. Two-layer shadows read as real depth. --- */
  --sh-1:0 1px 2px rgba(13,23,41,.06), 0 1px 3px rgba(13,23,41,.05);
  --sh-2:0 2px 4px rgba(13,23,41,.05), 0 6px 16px rgba(13,23,41,.08);
  --sh-3:0 4px 8px rgba(13,23,41,.06), 0 16px 34px rgba(13,23,41,.12);
  --sh-4:0 8px 18px rgba(13,23,41,.10), 0 26px 56px rgba(13,23,41,.18);
  --sh-up:0 -2px 6px rgba(13,23,41,.05), 0 -10px 28px rgba(13,23,41,.10);
  --ring:0 0 0 3px rgba(37,99,235,.22);

  --appbar-h:56px;
  --ease:cubic-bezier(.4,.14,.3,1);
}

/* ============================================================
   2. Base
   ============================================================ */
*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--bg);color:var(--ink);
  font-family:var(--font);font-size:var(--t-base);line-height:1.55;
  font-weight:450;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  font-feature-settings:"kern" 1,"liga" 1;
  -webkit-tap-highlight-color:transparent;
}
a{color:inherit;text-decoration:none}
img{max-width:100%}
h1,h2,h3{line-height:1.2;letter-spacing:-.018em;font-weight:800;color:var(--ink)}
b,strong{font-weight:700}

/* One visible focus ring for the whole app — keyboard and screen-reader users
   never lose their place, mouse users never see it. */
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:6px}
input:focus-visible,select:focus-visible,textarea:focus-visible{outline:none}

.wrap{max-width:1180px;margin:0 auto;padding:0 var(--s3)}
.wrap.narrow{max-width:460px}

/* Page head */
main{padding-top:var(--s4)}
.page-head{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2) var(--s3);
  margin-bottom:var(--s4)}
.page-head h1{margin:0;font-size:var(--t-xl)}
.count{color:var(--muted);font-size:var(--t-sm);font-weight:600}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms !important;transition-duration:.001ms !important}
}

/* ============================================================
   3. App bar, staff menu, footer
   ============================================================ */
.appbar{
  position:sticky;top:0;z-index:40;
  background:rgba(255,255,255,.94);
  -webkit-backdrop-filter:saturate(180%) blur(12px);
  backdrop-filter:saturate(180%) blur(12px);
  border-bottom:1px solid rgba(13,23,41,.07);
  box-shadow:0 1px 3px rgba(13,23,41,.05), 0 6px 18px rgba(13,23,41,.05);
}
.appbar-in{display:flex;align-items:center;justify-content:space-between;
  height:var(--appbar-h);gap:var(--s2)}
.brand{display:flex;align-items:center;gap:var(--s2);min-width:0;
  padding:4px 6px 4px 0;border-radius:var(--r-sm)}
.brand-mark{width:32px;height:32px;flex:0 0 32px;border-radius:10px;
  background:var(--accent-grad);color:#fff;font-weight:800;font-size:var(--t-md);
  display:flex;align-items:center;justify-content:center;
  box-shadow:0 2px 6px rgba(29,78,216,.32), inset 0 1px 0 rgba(255,255,255,.28)}
.brand-text{font-weight:800;font-size:var(--t-lg);letter-spacing:-.028em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--ink)}
.appbar-actions{display:flex;align-items:center;gap:7px}

.icon-btn{width:42px;height:42px;border-radius:50%;border:0;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  background:#e9eef6;color:var(--ink-2);
  transition:transform .16s var(--ease),box-shadow .16s var(--ease),
             background-color .16s var(--ease)}
.icon-btn:active{transform:scale(.92)}
.icon-btn.wa{background:var(--wa-grad);color:#fff;
  box-shadow:0 2px 8px rgba(22,163,74,.34)}
.icon-btn.call{background:var(--accent-grad);color:#fff;
  box-shadow:0 2px 8px rgba(29,78,216,.30)}
.icon-btn.add{background:var(--accent-grad);color:#fff;
  box-shadow:0 2px 8px rgba(29,78,216,.30)}
@media (hover:hover){
  /* Only the plain buttons go grey on hover.
     `.icon-btn:hover` is (0,2,0) — the same specificity as `.icon-btn.wa` —
     and it sits later in the file, so without the :not() it won the cascade
     and painted grey straight over the green and blue gradients. The
     brightness rule underneath then simply brightened the grey. */
  .icon-btn:not(.wa):not(.call):not(.add):hover{background:#dde5f1;color:var(--ink)}
  .icon-btn.wa:hover,.icon-btn.call:hover,.icon-btn.add:hover{filter:brightness(1.08)}
}

/* Staff menu (never shown to customers) */
.staff-menu{background:var(--card);border-top:1px solid var(--line);
  box-shadow:var(--sh-3)}
.staff-menu .wrap{display:flex;flex-direction:column;
  padding-top:var(--s2);padding-bottom:var(--s3)}
.staff-who{font-size:var(--t-2xs);color:var(--accent);text-transform:uppercase;
  letter-spacing:.09em;font-weight:800;padding:var(--s2) 2px var(--s1)}
.staff-menu a{padding:14px 4px;font-weight:650;font-size:var(--t-md);
  color:var(--ink-2);border-bottom:1px solid var(--line);min-height:48px;
  display:flex;align-items:center;border-radius:var(--r-xs)}
.staff-menu a:last-child{border-bottom:0}
.staff-menu a:active{background:var(--accent-soft);color:var(--accent)}
.staff-menu a.danger{color:#b91c1c}

.foot{margin-top:var(--s7);padding:var(--s5) 0 var(--s6);
  border-top:1px solid var(--line);color:var(--muted);font-size:var(--t-sm)}

/* ============================================================
   4. Browse bar
   ============================================================ */
.browse{position:sticky;top:var(--appbar-h);z-index:30;
  background:rgba(244,247,251,.93);
  -webkit-backdrop-filter:saturate(180%) blur(10px);
  backdrop-filter:saturate(180%) blur(10px);
  margin:0 calc(var(--s3) * -1) var(--s3);
  padding:var(--s2) var(--s3) var(--s2);
  border-bottom:1px solid var(--line)}

.chips-row{display:flex;gap:7px;overflow-x:auto;padding-bottom:var(--s2);
  scrollbar-width:none;-webkit-overflow-scrolling:touch}
.chips-row::-webkit-scrollbar{display:none}
.chip-tab{flex:0 0 auto;padding:9px 15px;border-radius:var(--r-pill);
  background:var(--card);color:var(--ink-2);
  font-size:var(--t-sm);font-weight:700;letter-spacing:-.005em;
  white-space:nowrap;border:1px solid var(--line);min-height:38px;
  display:inline-flex;align-items:center;
  box-shadow:0 1px 2px rgba(13,23,41,.04);
  transition:transform .15s var(--ease),box-shadow .15s var(--ease),
             color .15s var(--ease),border-color .15s var(--ease)}
.chip-tab:active{transform:scale(.96)}
.chip-tab.on{background:var(--accent-grad);color:#fff;border-color:transparent;
  box-shadow:0 2px 8px rgba(29,78,216,.34)}
@media (hover:hover){
  .chip-tab:hover{border-color:var(--accent-line);color:var(--accent)}
  .chip-tab.on:hover{color:#fff}
}

.search-row{display:flex;gap:7px;align-items:stretch}
.search-box{flex:1;min-width:0;display:flex;align-items:center;gap:var(--s2);
  background:var(--card);border:1px solid var(--line-2);border-radius:var(--r);
  padding:0 12px;color:var(--muted);
  box-shadow:0 1px 2px rgba(13,23,41,.04);
  transition:border-color .15s var(--ease),box-shadow .15s var(--ease)}
.search-box:focus-within{border-color:var(--accent);box-shadow:var(--ring);
  color:var(--accent)}
.search-box input{flex:1;min-width:0;border:0;outline:0;background:none;
  font-family:inherit;font-size:var(--t-md);font-weight:500;
  padding:12px 0;color:var(--ink)}
.search-box input::placeholder{color:var(--faint);font-weight:450}

.tool{display:flex;align-items:center;gap:5px;padding:0 12px;min-height:46px;
  border:1px solid var(--line-2);border-radius:var(--r);background:var(--card);
  color:var(--ink-2);font-family:inherit;font-size:var(--t-2xs);font-weight:800;
  text-transform:uppercase;letter-spacing:.06em;cursor:pointer;white-space:nowrap;
  box-shadow:0 1px 2px rgba(13,23,41,.04);
  transition:transform .15s var(--ease),border-color .15s var(--ease),
             color .15s var(--ease),background-color .15s var(--ease)}
.tool:active{transform:scale(.96)}
.tool.on{border-color:var(--accent);color:var(--accent);background:var(--accent-soft);
  box-shadow:0 1px 2px rgba(29,78,216,.14)}
@media (hover:hover){ .tool:hover{border-color:var(--accent);color:var(--accent)} }

/* z-index matters: the product thumbs below are position:relative, so without
   it the open sheet paints underneath them on wide screens. */
.sheet{position:absolute;z-index:50;left:var(--s3);right:var(--s3);margin-top:var(--s2);
  background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  overflow:hidden;box-shadow:var(--sh-4);max-height:62vh;overflow-y:auto;
  animation:sheet-in .16s var(--ease)}
@keyframes sheet-in{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
.sheet-title{padding:var(--s3) var(--s4) var(--s1);font-size:var(--t-2xs);
  font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.08em}
.sheet-item{display:flex;align-items:center;min-height:48px;
  padding:12px var(--s4);border-bottom:1px solid var(--line);
  font-size:var(--t-md);font-weight:600;color:var(--ink-2)}
.sheet-item:last-child{border-bottom:0}
.sheet-item.on{color:var(--accent);background:var(--accent-soft);font-weight:750;
  box-shadow:inset 3px 0 0 var(--accent)}
.sheet-item.clear{color:#b91c1c;font-weight:700}
@media (hover:hover){
  /* Same trap as .icon-btn: a bare :hover here is (0,2,0), matching
     .sheet-item.on, and being later it wiped the accent tint off the option
     you had chosen the moment you pointed at it. The selected row deepens
     instead. */
  .sheet-item:not(.on):hover{background:var(--bg)}
  .sheet-item.on:hover{background:#e3ecfd}
}

.result-line{display:flex;align-items:center;justify-content:space-between;
  gap:var(--s3);color:var(--muted);font-size:var(--t-sm);font-weight:600;
  margin:2px 0 var(--s3)}
.result-line span{min-width:0}

/* Filter bar (staff pages) */
.filters{display:flex;flex-wrap:wrap;gap:var(--s2);margin-bottom:var(--s4)}
.filters input,.filters select{
  padding:12px 13px;border:1px solid var(--line-2);border-radius:var(--r);
  background:var(--card);color:var(--ink);
  font-family:inherit;font-size:var(--t-md);min-width:130px;min-height:46px;
  box-shadow:0 1px 2px rgba(13,23,41,.04)}
.filters input{flex:1 1 200px;min-width:150px}
/* A tick box in a filter row is not a text field. The rule above would give it
   a 150px minimum width and a 46px box — which is the giant empty square that
   turned up beside "Include products already in a group". */
.filters input[type=checkbox]{
  flex:0 0 auto;width:16px;height:16px;min-width:0;min-height:0;
  padding:0;border:0;border-radius:0;box-shadow:none;
  accent-color:var(--accent);cursor:pointer}
/* Height still matches the fields beside it so the row lines up; only the
   lettering and the tick come down to the size of a filter, not a button. */
.filters .chip{flex:0 0 auto;gap:7px;padding:9px 14px;font-size:var(--t-sm)}

/* ============================================================
   5. Product grid + cards
   ============================================================ */
.grid{display:grid;gap:var(--s3);grid-template-columns:repeat(2,minmax(0,1fr))}

.card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  overflow:hidden;display:flex;flex-direction:column;box-shadow:var(--sh-1)}
.thumb{position:relative;aspect-ratio:1/1;
  background:linear-gradient(150deg,#f2f6fc,#e6ecf6);
  display:flex;align-items:center;justify-content:center;overflow:hidden}
.thumb img{width:100%;height:100%;object-fit:cover;display:block}
.thumb .ph{color:var(--faint);font-size:var(--t-2xs);letter-spacing:.1em;
  text-transform:uppercase;font-weight:700}
.card-body{padding:11px 11px 12px;display:flex;flex-direction:column;gap:1px;flex:1}
.art{font-weight:750;font-size:var(--t-base);line-height:1.3;color:var(--ink);
  letter-spacing:-.012em}
.meta{color:var(--muted);font-size:var(--t-xs);font-weight:550}
.rate{margin-top:auto;padding-top:var(--s2);font-weight:800;
  font-size:var(--t-lg);color:var(--price);letter-spacing:-.02em;
  font-variant-numeric:tabular-nums}

/* Art no and price on one line, remark under it.
   The line carries the margin-top:auto that used to sit on .rate, so the pair
   still hugs the bottom of the card; the price inside it must give that up or
   the two would sit on different baselines. */
.card-line{margin-top:auto;padding-top:var(--s2);display:flex;
  align-items:baseline;justify-content:space-between;gap:var(--s2)}
.card-line .meta{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.card-line .rate{margin-top:0;padding-top:0;flex:0 0 auto}
/* The remark: small, at most two lines, and darker than the art no above it
   rather than a different hue. On a grid of a dozen near-identical cardigans
   it is often the only line that tells them apart, so it should not read as
   more metadata — but a coloured line on every card is louder than the point
   deserves. Weight and depth do the work instead. 11.4:1 on white. */
.rmk{margin-top:3px;color:var(--ink-2);font-size:var(--t-2xs);font-weight:650;
  line-height:1.35;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;
  -webkit-box-orient:vertical}
.tag{align-self:flex-start;font-size:var(--t-2xs);padding:3px 9px;
  border-radius:var(--r-pill);background:var(--accent-soft);color:var(--accent);
  font-weight:700;border:1px solid var(--accent-line);white-space:nowrap}

/* Category badge sitting on the photo. Deepened so white text clears AA.
   Transparent border in the base rule so the frosted version below is exactly
   the same size — only the colours change.

   Bottom left, not top left: a garment photo carries its subject in the upper
   half, so a label up there lands on the product. Down here it sits over the
   quiet end of the frame, and it is clear of the staff buttons top right. */
.cat-badge{position:absolute;bottom:8px;left:8px;z-index:1;
  background:var(--badge-grad);color:#fff;border:1px solid transparent;
  font-size:var(--t-2xs);font-weight:800;letter-spacing:.05em;
  text-transform:uppercase;padding:4px 9px;border-radius:var(--r-xs);
  max-width:76%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  box-shadow:0 2px 6px rgba(120,53,15,.32), inset 0 1px 0 rgba(255,255,255,.24)}

/* Frosted glass: the photo shows through the badge instead of being covered
   by a solid slab. The blur behind it and the text shadow are what keep white
   text readable once the colour is no longer opaque — a plain alpha drop on
   its own turns the label to mush over a pale photo. Browsers without
   backdrop-filter keep the solid badge above, which is the safe fallback. */
@supports ((-webkit-backdrop-filter:blur(1px)) or (backdrop-filter:blur(1px))){
  .cat-badge{
    background:linear-gradient(140deg,
      rgba(245,158,11,.80) 0%, rgba(217,119,6,.84) 60%, rgba(180,83,9,.88) 100%);
    border-color:rgba(255,255,255,.34);
    -webkit-backdrop-filter:blur(9px) saturate(150%);
    backdrop-filter:blur(9px) saturate(150%);
    text-shadow:0 1px 2px rgba(88,38,7,.5);
    box-shadow:0 2px 8px rgba(120,53,15,.24), inset 0 1px 0 rgba(255,255,255,.3)}
}

a.card{transition:box-shadow .18s var(--ease),transform .18s var(--ease),
                  border-color .18s var(--ease)}
a.card:active{transform:scale(.975)}
@media (hover:hover){
  a.card:hover{box-shadow:var(--sh-3);transform:translateY(-3px);
    border-color:var(--accent-line)}
}

/* Pagination */
.pager{display:flex;gap:6px;justify-content:center;margin-top:var(--s6);
  flex-wrap:wrap}
.pager a,.pager span{display:inline-flex;align-items:center;justify-content:center;
  min-width:44px;min-height:44px;padding:0 14px;border-radius:var(--r-sm);
  border:1px solid var(--line);background:var(--card);font-size:var(--t-sm);
  font-weight:700;color:var(--ink-2);box-shadow:0 1px 2px rgba(13,23,41,.04)}
.pager .on{background:var(--accent-grad);color:#fff;border-color:transparent;
  box-shadow:0 2px 8px rgba(29,78,216,.30)}
.pager .off{color:var(--faint);box-shadow:none;background:transparent}
@media (hover:hover){ .pager a:hover{border-color:var(--accent);color:var(--accent)} }

/* ============================================================
   6. Product detail
   ============================================================ */
.crumb{margin:0 0 var(--s3);font-size:var(--t-sm);font-weight:650;color:var(--muted)}
.crumb a{color:var(--accent)}
.crumb a:hover{text-decoration:underline}

.detail-page{padding-bottom:96px}      /* room for the fixed action bar */
.pcard{background:var(--card);border:1px solid var(--line);border-radius:var(--r-xl);
  overflow:hidden;box-shadow:var(--sh-2)}
.pshot{background:linear-gradient(160deg,#f2f6fc,#e6ecf6);display:block}
.pshot img{width:100%;height:auto;display:block;cursor:zoom-in}
.pshot-empty{aspect-ratio:1/1;display:flex;align-items:center;justify-content:center;
  color:var(--faint);font-size:var(--t-sm);letter-spacing:.1em;
  text-transform:uppercase;font-weight:700}
.pstrip{display:flex;gap:var(--s2);padding:var(--s2) var(--s3);overflow-x:auto;
  scrollbar-width:none;border-top:1px solid var(--line)}
.pstrip::-webkit-scrollbar{display:none}
.shot{flex:0 0 62px;width:62px;height:62px;padding:0;border:1px solid var(--line);
  border-radius:var(--r-sm);overflow:hidden;background:var(--card);cursor:pointer;
  transition:border-color .15s var(--ease),box-shadow .15s var(--ease),
             transform .15s var(--ease)}
.shot:active{transform:scale(.94)}
.shot.on{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-line)}
.shot img{width:100%;height:100%;object-fit:cover;display:block}

.pbody{padding:var(--s4) var(--s4) var(--s5)}
.pbody h1{margin:0;font-size:var(--t-2xl);font-weight:800;letter-spacing:-.026em}
.p-art{color:var(--muted);font-size:var(--t-sm);margin-top:var(--s1);font-weight:600}
.p-art span{font-family:var(--font-mono);color:var(--ink);font-weight:700;
  background:var(--bg-deep);border-radius:6px;padding:1px 7px;font-size:var(--t-xs);
  letter-spacing:0}
.p-name{color:var(--muted);font-size:var(--t-base);margin-top:2px}
.p-price{font-size:var(--t-3xl);font-weight:800;color:var(--price);
  letter-spacing:-.03em;margin:var(--s3) 0 var(--s1);
  font-variant-numeric:tabular-nums;line-height:1.1}

.spec-tiles{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--s2);margin-top:var(--s4)}
.spec{background:var(--card-2);border:1px solid var(--line);
  border-radius:var(--r);padding:10px 12px;font-size:var(--t-sm);min-width:0}
.spec span{display:block;color:var(--muted);font-size:var(--t-2xs);
  text-transform:uppercase;letter-spacing:.07em;font-weight:700;margin-bottom:2px}
.spec b{font-size:var(--t-base);color:var(--ink);font-weight:700;
  overflow-wrap:anywhere;letter-spacing:-.01em}
.p-remarks{margin-top:var(--s4);padding-top:var(--s3);
  border-top:1px solid var(--line);font-size:var(--t-base);line-height:1.6;
  color:var(--ink-2)}
.p-remarks span{color:var(--muted);font-weight:700;text-transform:uppercase;
  font-size:var(--t-2xs);letter-spacing:.07em}

/* More in category — horizontal rail with snap */
.more{margin-top:var(--s5)}
.more-head{display:flex;align-items:baseline;justify-content:space-between;
  gap:var(--s3);margin:0 2px var(--s3)}
.more-head h2{margin:0;font-size:var(--t-lg);font-weight:800}
.swipe-hint{font-size:var(--t-xs);color:var(--muted);font-weight:600;
  white-space:nowrap}
.more-rail{display:flex;gap:var(--s3);overflow-x:auto;padding:2px 0 var(--s2);
  scroll-snap-type:x mandatory;scrollbar-width:none}
.more-rail::-webkit-scrollbar{display:none}
.mini-card{flex:0 0 138px;width:138px;background:var(--card);
  border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;
  scroll-snap-align:start;box-shadow:var(--sh-1);
  transition:box-shadow .18s var(--ease),transform .18s var(--ease)}
.mini-card:active{transform:scale(.975)}
@media (hover:hover){ .mini-card:hover{box-shadow:var(--sh-2);transform:translateY(-2px)} }
.mini-card .thumb{aspect-ratio:1/1}
.mini-body{padding:8px 9px 10px}
.mini-body .art{font-size:var(--t-sm);font-weight:750;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.mini-body .meta{font-size:var(--t-2xs);white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.mini-body .rate{font-size:var(--t-base);padding-top:var(--s1)}

/* Fixed action bar (customer) */
.action-bar{position:fixed;left:0;right:0;bottom:0;z-index:45;display:flex;
  gap:var(--s2);padding:10px var(--s3) calc(10px + env(safe-area-inset-bottom));
  background:rgba(255,255,255,.92);border-top:1px solid var(--line);
  -webkit-backdrop-filter:saturate(180%) blur(14px);
  backdrop-filter:saturate(180%) blur(14px);
  box-shadow:var(--sh-up)}
.ab-btn{flex:1;display:flex;align-items:center;justify-content:center;gap:8px;
  min-height:50px;border-radius:var(--r);font-weight:800;font-size:var(--t-md);
  letter-spacing:-.01em;
  transition:transform .15s var(--ease),filter .15s var(--ease)}
.ab-btn:active{transform:scale(.97)}
.ab-call{flex:0 0 33%;background:#e9eef6;color:var(--ink);
  border:1px solid var(--line-2)}
.ab-wa{background:var(--wa-grad);color:var(--wa-ink);
  box-shadow:0 3px 12px rgba(22,163,74,.36), inset 0 1px 0 rgba(255,255,255,.30)}
@media (hover:hover){
  .ab-call:hover{background:#dde5f1}
  .ab-wa:hover{filter:brightness(1.05)}
}

/* Older detail markup kept working (gallery / info / specs / related) */
.detail{display:grid;gap:var(--s4);grid-template-columns:minmax(0,1fr)}
.gallery .thumb.main{border:1px solid var(--line);border-radius:var(--r-lg);
  background:var(--card)}
.strip{display:flex;gap:7px;margin-top:var(--s3);overflow-x:auto;padding-bottom:var(--s1)}
.info h1{margin:var(--s2) 0 2px;font-size:var(--t-2xl)}
.info .sub{margin:0;color:var(--muted)}
.info .price{margin:var(--s3) 0;font-size:var(--t-2xl);font-weight:800;color:var(--price)}
.btn-wa{display:inline-flex;align-items:center;gap:var(--s2);
  background:var(--wa-grad);color:var(--wa-ink);text-decoration:none;
  width:100%;justify-content:center;padding:14px;font-size:var(--t-md)}
.btn-wa:hover{filter:brightness(1.05)}
.wa-hint{margin:var(--s2) 0 0;font-size:var(--t-xs);color:var(--muted)}
.specs{display:grid;grid-template-columns:auto 1fr;gap:7px var(--s4);margin:0;
  padding-top:var(--s4);border-top:1px solid var(--line);font-size:var(--t-base)}
.specs dt{color:var(--muted)}
.specs dd{margin:0;font-weight:650}
.remarks{margin-top:var(--s5);padding-top:var(--s4);border-top:1px solid var(--line)}
.remarks h2{margin:0 0 var(--s2);font-size:var(--t-xs);color:var(--muted);
  text-transform:uppercase;letter-spacing:.07em}
.remarks p{margin:0;font-size:var(--t-base)}
.related{margin-top:var(--s6)}
.related h2{font-size:var(--t-lg);margin:0 0 var(--s3)}

/* ============================================================
   7. Staff screens
   ============================================================ */
/* Tabs — scroll sideways instead of wrapping into a wall */
.tabs{display:flex;gap:2px;margin-bottom:var(--s5);
  border-bottom:1px solid var(--line);overflow-x:auto;scrollbar-width:none;
  -webkit-overflow-scrolling:touch}
.tabs::-webkit-scrollbar{display:none}
.tabs a{padding:11px 15px;font-size:var(--t-sm);font-weight:700;color:var(--muted);
  border-bottom:2.5px solid transparent;margin-bottom:-1px;white-space:nowrap;
  border-radius:var(--r-xs) var(--r-xs) 0 0;
  transition:color .15s var(--ease),background-color .15s var(--ease)}
.tabs a:hover{color:var(--ink);background:rgba(29,78,216,.05)}
.tabs a.on{color:var(--accent);border-bottom-color:var(--accent);
  background:var(--accent-soft)}

/* Panels */
.panel{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:var(--s4);margin-bottom:var(--s4);box-shadow:var(--sh-1)}
.panel h2{margin:0 0 var(--s1);font-size:var(--t-lg);letter-spacing:-.022em}
.panel .hint{margin:0 0 var(--s4);color:var(--muted);font-size:var(--t-sm);
  line-height:1.5}
.panel .hint code{background:var(--bg-deep);border-radius:5px;padding:1px 6px;
  font-family:var(--font-mono);font-size:var(--t-xs);color:var(--ink-2)}
.danger-zone{border-color:#f3b8b8;background:linear-gradient(#fffafa,#fff)}

/* Toggle chips */
.chips{display:flex;flex-wrap:wrap;gap:var(--s2)}
.chip{display:inline-flex;align-items:center;gap:8px;padding:10px 15px;
  cursor:pointer;border:1px solid var(--line-2);border-radius:var(--r-pill);
  font-size:var(--t-base);font-weight:700;color:var(--ink-2);background:var(--card);
  user-select:none;min-height:46px;
  transition:border-color .15s var(--ease),background-color .15s var(--ease),
             color .15s var(--ease)}
.chip input{accent-color:var(--accent);width:18px;height:18px;margin:0}
.chip.on{border-color:var(--accent);background:var(--accent-soft);color:var(--accent);
  box-shadow:0 1px 3px rgba(29,78,216,.14)}
@media (hover:hover){ .chip:hover{border-color:var(--accent)} }

/* Form fields */
.fields{display:grid;gap:var(--s4);grid-template-columns:1fr}
.fields label,.cell{display:flex;flex-direction:column;gap:6px;
  font-size:var(--t-sm);color:var(--muted);font-weight:700;
  letter-spacing:.005em}
.fields input,.fields select,.fields textarea,.cell input,.stack input,
.add-master input,.rename-form input,.m-rename input[type=text],.m-order,
.mini-select,.mini-input{
  border:1px solid var(--line-2);border-radius:var(--r);background:var(--card);
  font-family:inherit;color:var(--ink);font-weight:500;
  box-shadow:0 1px 2px rgba(13,23,41,.04);
  transition:border-color .15s var(--ease),box-shadow .15s var(--ease)}
.fields input,.fields select,.fields textarea,.cell input,.stack input{
  padding:12px 13px;width:100%;font-size:var(--t-md);min-height:48px}
.fields textarea{resize:vertical;line-height:1.55;min-height:96px}
.fields input:focus,.fields select:focus,.fields textarea:focus,.cell input:focus,
.stack input:focus,.filters input:focus,.filters select:focus,
.add-master input:focus,.rename-form input:focus,.m-rename input:focus,
.m-order:focus,.mini-select:focus,.mini-input:focus{
  outline:none;border-color:var(--accent);box-shadow:var(--ring)}
.fields input::placeholder,.stack input::placeholder,
.add-master input::placeholder,.filters input::placeholder{color:var(--faint)}
.fields .hidden{display:none}
.fields .wide{grid-column:1 / -1}
.fields .self-end{justify-content:flex-end}
.field-hint{font-weight:500;font-size:var(--t-xs);color:var(--muted);line-height:1.45}

/* Select + "New" button pairs on the upload form */
.pick:not(input){display:flex;gap:6px;align-items:stretch}
.pick:not(input) select{flex:1;min-width:0}
.new-btn{flex:0 0 auto;padding:0 14px;min-height:48px;
  border:1.5px dashed var(--accent);border-radius:var(--r);
  background:var(--accent-soft);color:var(--accent);
  font-family:inherit;font-size:var(--t-sm);font-weight:800;cursor:pointer;
  white-space:nowrap;transition:background-color .15s var(--ease),color .15s var(--ease)}
.new-btn.on{background:var(--accent-grad);color:#fff;border-style:solid;
  border-color:transparent;box-shadow:0 2px 8px rgba(29,78,216,.28)}
.new-input{margin-top:6px}

/* One row per image: small image left, changing columns right */
.rows{display:flex;flex-direction:column;gap:var(--s3);margin-top:var(--s4)}
.row{display:flex;align-items:center;gap:var(--s3);padding:var(--s3);
  border:1px solid var(--line);border-radius:var(--r-lg);background:var(--card-2)}
.shot-sm{width:60px;height:60px;flex:0 0 60px;object-fit:cover;
  border-radius:var(--r-sm);background:var(--bg-deep);display:block;
  border:1px solid var(--line)}
.row-main{flex:1;min-width:0}
.cells{display:grid;gap:var(--s2);grid-template-columns:1fr}
.row-label{font-size:var(--t-2xs);text-transform:uppercase;letter-spacing:.07em;
  font-weight:800;color:var(--muted)}
.cell input{padding:11px 12px;font-size:var(--t-md);min-height:46px}
.row .file{margin-top:6px;font-size:var(--t-xs);color:var(--muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.row .x{flex:0 0 auto;width:40px;height:40px;border:1px solid var(--line-2);
  border-radius:var(--r-sm);background:var(--card);color:var(--muted);
  font-size:19px;line-height:1;cursor:pointer;
  transition:border-color .15s var(--ease),color .15s var(--ease),
             background-color .15s var(--ease)}
.row .x:hover{border-color:#fca5a5;color:#b91c1c;background:#fef2f2}
#picker{font-family:inherit;font-size:var(--t-base);width:100%;padding:12px 0}
.actions{display:flex;flex-wrap:wrap;gap:var(--s3);align-items:center;
  margin:var(--s5) 0 var(--s2)}
.actions .btn{flex:1 1 auto}

/* Manage table */
.table-wrap{overflow-x:auto;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r-lg);box-shadow:var(--sh-1)}
.tbl{width:100%;border-collapse:collapse;font-size:var(--t-sm);min-width:640px}
.tbl th{text-align:left;padding:12px 13px;font-size:var(--t-2xs);
  text-transform:uppercase;letter-spacing:.07em;font-weight:800;color:var(--muted);
  background:var(--card-2);border-bottom:1px solid var(--line-2);white-space:nowrap}
.tbl td{padding:12px 13px;border-bottom:1px solid var(--line);vertical-align:middle}
.tbl tbody tr:nth-child(even) td{background:rgba(244,247,251,.55)}
.tbl tbody tr:last-child td{border-bottom:0}
@media (hover:hover){ .tbl tbody tr:hover td{background:var(--accent-soft)} }
.tbl .num{text-align:right;font-variant-numeric:tabular-nums;font-weight:700}
.tbl .strong{font-weight:750;color:var(--ink);white-space:nowrap}
.tbl .strong:hover{color:var(--accent);text-decoration:underline}
.tbl tr.dim td:not(.row-acts){opacity:.5}
.ph-sm{display:flex;align-items:center;justify-content:center;color:var(--faint);
  background:linear-gradient(150deg,#f2f6fc,#e6ecf6);border-radius:var(--r-sm);
  border:1px solid var(--line)}
.row-acts{white-space:nowrap;text-align:right}
.row-acts form{display:inline-block;margin:0}
.row-acts>*+*{margin-left:6px}
.pick-col{width:38px}
.pick-col input{width:20px;height:20px}
.tbl input[type=checkbox]{accent-color:var(--accent)}

/* Compact "⋮" row menu */
.more-menu{position:relative;display:inline-block}
.dots{width:40px;height:40px;border:1px solid var(--line-2);border-radius:var(--r-sm);
  background:var(--card);color:var(--ink-2);font-size:20px;line-height:1;cursor:pointer}
.dots:hover{border-color:var(--accent);color:var(--accent)}
.menu-pop{position:absolute;right:0;top:44px;z-index:20;min-width:168px;
  background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  overflow:hidden;box-shadow:var(--sh-4)}
.menu-pop a,.menu-pop button{display:flex;align-items:center;gap:10px;width:100%;
  text-align:left;padding:13px 15px;border:0;border-bottom:1px solid var(--line);
  background:none;font-family:inherit;font-size:var(--t-base);font-weight:650;
  cursor:pointer;color:var(--ink-2);min-height:48px}
.menu-pop a:last-child,.menu-pop button:last-child{border-bottom:0}
.menu-pop a:hover,.menu-pop button:hover{background:var(--bg)}
.menu-pop .i-edit{color:var(--info)}
.menu-pop .i-view{color:var(--alt)}
.menu-pop .i-hide{color:var(--warn)}
.menu-pop .i-show{color:var(--ok)}
.menu-pop .i-del{color:var(--bad)}

.dupe-note{margin:calc(var(--s2) * -1) 0 var(--s4);font-size:var(--t-sm);
  color:var(--muted)}
.dupe-note a{color:var(--accent);font-weight:700;text-decoration:underline}
.warn{font-size:var(--t-xs);font-weight:700;color:var(--warn)}
.warn a{text-decoration:underline}

/* Masters: brands / categories / sizes as cards */
.add-master{display:flex;flex-direction:column;align-items:stretch;gap:var(--s2);
  margin-bottom:var(--s4)}
.add-master input{flex:0 0 auto;width:100%;padding:12px 13px;
  font-size:var(--t-md);min-height:48px}
.add-master .btn{width:100%}
.m-cards{display:grid;gap:var(--s3);grid-template-columns:1fr}
.m-card{border:1px solid var(--line);border-radius:var(--r-lg);
  padding:var(--s3) var(--s3) var(--s3);background:var(--card-2);
  box-shadow:0 1px 2px rgba(13,23,41,.04)}
.m-card.off{opacity:.62}
.m-top{display:flex;align-items:center;justify-content:space-between;gap:var(--s2)}
.m-name{font-weight:800;font-size:var(--t-md);min-width:0;overflow-wrap:anywhere;
  letter-spacing:-.02em;color:var(--ink)}
.m-uses{color:var(--muted);font-size:var(--t-xs);margin-top:1px;font-weight:600}
.m-acts{margin-top:var(--s3);display:flex;flex-direction:column;gap:var(--s2)}
.m-rename{display:flex;gap:6px}
.m-rename input[type=text]{flex:1;min-width:0;padding:11px 12px;
  font-size:var(--t-md);min-height:46px}
.m-order{width:70px;padding:11px 8px;font-size:var(--t-md);min-height:46px;
  text-align:center}
.m-buttons{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
.m-buttons form{display:flex;gap:5px;align-items:center;margin:0}
.m-merge .mini-select{max-width:118px}
.rename-form,.merge-form{display:flex;gap:6px;align-items:center}
.rename-form input{flex:1;min-width:120px;padding:10px 12px;font-size:var(--t-md)}
.mini-select{padding:10px 7px;font-size:var(--t-base);max-width:118px;
  min-height:44px;font-weight:600}
.mini-input{width:140px;padding:10px 11px;font-size:var(--t-base);min-height:44px}

/* Edit page images */
.img-grid{display:grid;gap:var(--s2);grid-template-columns:repeat(3,minmax(0,1fr));
  margin-bottom:var(--s4)}
.img-tile{position:relative;border:1px solid var(--line);border-radius:var(--r);
  overflow:hidden;background:var(--card);box-shadow:var(--sh-1)}
.img-tile img{width:100%;aspect-ratio:1/1;object-fit:cover;display:block}
.cover-badge{position:absolute;top:8px;left:8px;background:var(--accent-grad);
  color:#fff;font-size:var(--t-2xs);font-weight:800;padding:3px 9px;
  border-radius:var(--r-pill);box-shadow:0 2px 6px rgba(29,78,216,.35);
  letter-spacing:.03em}
.img-actions{display:flex;gap:5px;padding:7px;justify-content:center;flex-wrap:wrap}
.img-actions form{margin:0}
.img-tile[draggable]{cursor:grab}
.img-tile.dragging{opacity:.4;cursor:grabbing}
.img-tile.over{outline:2px dashed var(--accent);outline-offset:-2px}
.add-images{display:flex;flex-direction:column;align-items:stretch;gap:var(--s3);
  padding-top:var(--s4);border-top:1px solid var(--line)}
.add-images input{flex:0 0 auto;width:100%}
.add-images .btn{width:100%}

/* Auth */
.who{font-size:var(--t-sm);font-weight:700;color:var(--ink)}
.stack{display:flex;flex-direction:column;gap:var(--s3)}
.stack label{display:flex;flex-direction:column;gap:6px;font-size:var(--t-sm);
  color:var(--muted);font-weight:700}
.stack .actions{margin:var(--s2) 0 0}
.reset-form{display:inline-flex;gap:5px;align-items:center}

/* Settings page */
.set-preview{display:flex;align-items:center;gap:var(--s3)}
.set-preview .brand-mark{width:48px;height:48px;flex-basis:48px;font-size:24px;
  border-radius:14px}

/* Sticky staff bars */
.bulk-bar,.submit-bar{position:fixed;left:0;right:0;bottom:0;z-index:45;
  display:flex;align-items:center;gap:var(--s2);
  padding:10px var(--s3) calc(10px + env(safe-area-inset-bottom));
  background:rgba(255,255,255,.94);border-top:1px solid var(--line);
  -webkit-backdrop-filter:saturate(180%) blur(14px);
  backdrop-filter:saturate(180%) blur(14px);
  box-shadow:var(--sh-up)}
.bb-count,.sb-count{flex:1;min-width:0;font-size:var(--t-sm);font-weight:700;
  color:var(--muted)}
.submit-bar .btn{flex:0 0 auto}
#bulk-form,form#bulk-form,#manage-form,form#manage-form{padding-bottom:84px}

/* ============================================================
   8. Buttons, action colours, tags
   ============================================================ */
input,select,textarea,button{font-family:inherit}
/* iOS zooms the page when a focused input is under 16px. */
input[type=text],input[type=search],input[type=number],input[type=password],
input[type=email],input[type=tel],select,textarea{font-size:16px}

.btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;
  padding:13px 20px;border:0;border-radius:var(--r);background:var(--accent-grad);
  color:#fff;font-family:inherit;font-size:var(--t-base);font-weight:750;
  letter-spacing:-.008em;cursor:pointer;min-height:48px;
  box-shadow:0 2px 8px rgba(29,78,216,.28), inset 0 1px 0 rgba(255,255,255,.18);
  transition:transform .15s var(--ease),box-shadow .15s var(--ease),
             filter .15s var(--ease)}
.btn:active{transform:translateY(1px) scale(.985);
  box-shadow:0 1px 4px rgba(29,78,216,.30)}
@media (hover:hover){ .btn:hover{filter:brightness(1.07);
  box-shadow:0 4px 14px rgba(29,78,216,.34)} }
.btn:disabled{opacity:.45;cursor:not-allowed;box-shadow:none;filter:none}
.btn svg{display:block}
.btn-ghost{background:var(--card);color:var(--ink-2);border:1px solid var(--line-2);
  box-shadow:0 1px 2px rgba(13,23,41,.05)}
.btn-ghost:hover{filter:none;background:var(--bg);border-color:var(--accent);
  color:var(--accent);box-shadow:0 1px 3px rgba(13,23,41,.08)}
.btn-add{padding:10px 15px;min-height:44px;font-size:var(--t-sm);white-space:nowrap}
.page-head .btn-add{margin-left:auto}

/* Link-styled submit button (manage.php) */
.as-link{border:0;background:none;padding:0;font-family:inherit;
  font-size:inherit;font-weight:700;color:var(--accent);cursor:pointer;
  text-decoration:underline}

/* Each action keeps the same colour everywhere:
   green = show/save, amber = hide, red = delete, blue = edit/view,
   violet = merge. */
:root{
  --ok:#047857;      --ok-bg:#ecfdf5;      --ok-line:#9ae6c4;
  --warn:#b45309;    --warn-bg:#fffbeb;    --warn-line:#f8d488;
  --bad:#b91c1c;     --bad-bg:#fef2f2;     --bad-line:#f9bcbc;
  --info:#1d4ed8;    --info-bg:#eff6ff;    --info-line:#b6d2fd;
  --alt:#6d28d9;     --alt-bg:#f5f3ff;     --alt-line:#d3c8fd;
}
.mini{display:inline-flex;align-items:center;justify-content:center;gap:6px;
  padding:9px 13px;border-radius:var(--r-sm);border:1px solid var(--line-2);
  background:var(--card);color:var(--ink-2);font-family:inherit;
  font-size:var(--t-sm);font-weight:700;cursor:pointer;min-height:44px;
  transition:background-color .15s var(--ease),color .15s var(--ease),
             border-color .15s var(--ease),transform .15s var(--ease)}
.mini:active{transform:scale(.95)}
.mini:hover{color:var(--ink);border-color:var(--line-2)}
.mini svg{display:block}
.mini.danger:hover{border-color:var(--bad-line);color:var(--bad);background:var(--bad-bg)}
.mini[disabled]{opacity:.35;cursor:not-allowed}

.mini.act-ok   {color:var(--ok);   background:var(--ok-bg);   border-color:var(--ok-line)}
.mini.act-warn {color:var(--warn); background:var(--warn-bg); border-color:var(--warn-line)}
.mini.act-bad  {color:var(--bad);  background:var(--bad-bg);  border-color:var(--bad-line)}
.mini.act-info {color:var(--info); background:var(--info-bg); border-color:var(--info-line)}
.mini.act-alt  {color:var(--alt);  background:var(--alt-bg);  border-color:var(--alt-line)}
.mini.act-ok:hover  {background:var(--ok);   color:#fff;border-color:var(--ok)}
.mini.act-warn:hover{background:var(--warn); color:#fff;border-color:var(--warn)}
.mini.act-bad:hover {background:var(--bad);  color:#fff;border-color:var(--bad)}
.mini.act-info:hover{background:var(--info); color:#fff;border-color:var(--info)}
.mini.act-alt:hover {background:var(--alt);  color:#fff;border-color:var(--alt)}

/* Icon-only: square, no label text */
.mini.ico{width:44px;height:44px;padding:0}
.mini.ico svg{width:19px;height:19px}

/* Bulk bar buttons are solid, so the destructive one is unmistakable */
.bulk-bar .mini{border:0;color:#fff;font-weight:750;padding:11px 15px;
  font-size:var(--t-sm);box-shadow:var(--sh-1)}
.bulk-bar .act-ok{background:var(--ok)}
.bulk-bar .act-warn{background:var(--warn)}
.bulk-bar .act-bad{background:var(--bad)}
.bulk-bar .act-ok:hover{background:#036a4d;color:#fff}
.bulk-bar .act-warn:hover{background:#9a4708;color:#fff}
.bulk-bar .act-bad:hover{background:#a11616;color:#fff}

.btn-danger{background:var(--bad);
  box-shadow:0 2px 8px rgba(185,28,28,.28), inset 0 1px 0 rgba(255,255,255,.18)}
.btn-danger:hover{box-shadow:0 4px 14px rgba(185,28,28,.34)}

.tag-off{background:#eef1f6;color:var(--muted);border-color:var(--line-2)}
.tag-warn{background:var(--warn-bg);color:var(--warn);border-color:var(--warn-line)}

/* ============================================================
   9. Feedback: empty, flash, notice, toast
   ============================================================ */
.empty{background:var(--card);border:2px dashed var(--line-2);
  border-radius:var(--r-xl);padding:var(--s7) var(--s5);text-align:center;
  color:var(--muted);font-size:var(--t-md);line-height:1.6}
.empty p{margin:0 0 var(--s3)}
.empty p:last-child{margin-bottom:0}
.empty a:not(.btn){color:var(--accent);font-weight:700;text-decoration:underline}

.flash{border-radius:var(--r);padding:14px 16px;margin-bottom:var(--s4);
  font-size:var(--t-base);font-weight:600;line-height:1.5;
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--s3);
  box-shadow:var(--sh-1)}
.flash.ok{background:var(--ok-bg);border:1px solid var(--ok-line);
  border-left:4px solid var(--ok);color:var(--price-deep)}
.flash.bad{background:var(--bad-bg);border:1px solid var(--bad-line);
  border-left:4px solid var(--bad);color:#911616;display:block}
.flash ul{margin:var(--s2) 0 0;padding-left:20px}
.flash li{margin-bottom:3px}

.notice{background:linear-gradient(#fffdf5,#fffbeb);border:1px solid #f6dc9a;
  border-left:4px solid #d97706;border-radius:var(--r-lg);padding:var(--s5);
  margin:var(--s5) 0;box-shadow:var(--sh-1)}
.notice h2{margin:0 0 var(--s2);font-size:var(--t-lg);color:#7c3f06}
.notice p{margin:0 0 var(--s2)}
.notice p:last-child{margin-bottom:0}
.notice a{color:var(--accent);font-weight:700;text-decoration:underline}
.notice code{background:#fff;border:1px solid #f6dc9a;border-radius:6px;
  padding:1px 6px;font-family:var(--font-mono);font-size:var(--t-sm)}

/* Undo toast — replaces the "are you sure?" dialog for reversible deletes */
.toast{position:fixed;left:var(--s3);right:var(--s3);bottom:var(--s4);z-index:60;
  display:flex;align-items:center;gap:var(--s3);padding:14px 16px;
  border-radius:var(--r-lg);
  background:linear-gradient(150deg,#182545,#0d1729);
  color:#fff;font-size:var(--t-base);font-weight:650;
  box-shadow:0 12px 34px rgba(13,23,41,.40), inset 0 1px 0 rgba(255,255,255,.10);
  animation:toast-in .2s var(--ease)}
.toast span{flex:1;min-width:0}
.toast form{margin:0}
.toast-btn{border:0;background:rgba(125,211,252,.14);color:#8fd8fb;
  font-family:inherit;font-size:var(--t-sm);font-weight:800;text-transform:uppercase;
  letter-spacing:.06em;cursor:pointer;padding:9px 14px;border-radius:var(--r-xs);
  min-height:40px;transition:background-color .15s var(--ease)}
.toast-btn:hover{background:rgba(125,211,252,.26);color:#bae6fd}
.toast.gone{opacity:0;transform:translateY(10px);pointer-events:none;
  transition:opacity .22s,transform .22s}
@keyframes toast-in{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}

/* Hide the bars while scrolling down, bring them back on scroll up. The phone's
   own address bar hides on the same gesture, so the screen fills with products. */
.appbar{transition:transform .24s var(--ease)}
.browse{transition:top .24s var(--ease)}
body.bars-off .appbar{transform:translateY(-100%)}
body.bars-off .browse{top:0}

/* ============================================================
   9b. Product groups
   ------------------------------------------------------------
   None of this reaches a screen until an admin switches grouping
   on in Manage, so the catalog is untouched by default.
   ============================================================ */

/* Heading between two grids — "More products", "In this group". A rule under
   it and more room above than below is what makes it read as the start of a
   new section rather than a bigger paragraph. */
.sec-head{display:flex;align-items:baseline;gap:var(--s2);
  margin:var(--s6) 0 var(--s3);padding-bottom:var(--s2);
  border-bottom:1px solid var(--line);
  font-size:var(--t-lg);color:var(--ink);letter-spacing:-.022em}
.sec-head .count{font-weight:600}

/* --- The group card in the catalog grid --- */
/* An ordinary product card with a count where the category badge would be, so
   groups and loose products sit in one grid without a seam. */
.g-count-badge{position:absolute;top:8px;right:8px;z-index:1;
  background:rgba(13,23,41,.74);color:#fff;border:1px solid rgba(255,255,255,.22);
  font-size:var(--t-2xs);font-weight:800;letter-spacing:.04em;white-space:nowrap;
  padding:4px 9px;border-radius:var(--r-xs);
  box-shadow:0 2px 6px rgba(13,23,41,.28)}
@supports ((-webkit-backdrop-filter:blur(1px)) or (backdrop-filter:blur(1px))){
  .g-count-badge{background:rgba(13,23,41,.52);
    -webkit-backdrop-filter:blur(9px) saturate(150%);
    backdrop-filter:blur(9px) saturate(150%)}
}
/* A second edge peeking out below: a group is more than one thing, and that
   reads before any label does. */
.g-cover{position:relative;margin-bottom:5px}
.g-cover::after{content:"";position:absolute;left:10px;right:10px;bottom:-5px;height:5px;
  background:var(--card-2);border:1px solid var(--line);border-top:0;
  border-radius:0 0 var(--r-lg) var(--r-lg)}

/* Inside a group: the title strip above the listing. The way back out is a
   real link — the installed app has no address bar to press Back in. */
.in-group{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2) var(--s3);
  margin-bottom:var(--s3)}
.in-group h1{margin:0;font-size:var(--t-xl);letter-spacing:-.024em}
.back-mini{flex:0 0 auto}

/* --- Manage: the switch for the whole feature --- */
/* A capped top edge, the same move `.master-sec` uses for its own colour —
   here it just marks "this panel is a switch", not a list, before you read
   a word of it. */
.group-switch,
.g-settings{border-top:3px solid var(--accent)}
.group-switch summary,
.g-settings summary{display:flex;align-items:center;gap:var(--s2);cursor:pointer;
  list-style:none;font-weight:800;font-size:var(--t-md);color:var(--ink)}
.group-switch summary::-webkit-details-marker,
.g-settings summary::-webkit-details-marker{display:none}
.group-switch[open] summary,
.g-settings[open] summary{margin-bottom:var(--s3)}
.gs-title{display:inline-flex;align-items:center;gap:8px}
.gs-acts{display:flex;flex-wrap:wrap;gap:var(--s2);margin:0}
.gs-note{margin:var(--s3) 0 0}

/* Import is two steps and the second one is a panel further down the page.
   This line sits under the Preview button and says so. */
.step-note{margin:var(--s3) 0 0;padding-top:var(--s3);border-top:1px solid var(--line)}
.panel h2 .count{font-weight:600}

/* Fetching the photos an import named: how far through it is. */
.fetch-bar{height:8px;border-radius:var(--r-pill);background:var(--bg-deep);
  overflow:hidden;margin:var(--s3) 0}
.fetch-bar span{display:block;height:100%;background:var(--accent-grad);
  border-radius:var(--r-pill);transition:width .3s var(--ease)}
.fail-list{margin:0 0 var(--s3);padding-left:18px;font-size:var(--t-sm);
  color:var(--muted);line-height:1.7}
.fail-list code{font-family:var(--font-mono);font-size:var(--t-xs);
  overflow-wrap:anywhere;color:var(--ink-2)}

/* The group control in Manage's bulk bar. On a phone it takes a line of its
   own — a dropdown, a text box and three buttons will not share one. */
.bulk-bar{flex-wrap:wrap;row-gap:8px}
.bb-group{display:flex;align-items:center;gap:6px;flex:1 1 100%;flex-wrap:wrap}
.bb-group .mini-input{flex:1 1 120px;width:auto}
/* Room to scroll past the taller bar. Browsers without :has() keep the 84px
   above, which is one row short — no worse than before this control existed. */
#manage-form:has(.bb-group){padding-bottom:150px}

/* --- Groups page: one card per group --- */
/* Same shape as the group card the catalog shows a customer (`.g-cover` +
   `.g-count-badge`, above) rather than a plain product thumb — a staff
   member sees the same "this is a stack of things" cue as everyone else. */
/* A section and the groups inside it are drawn as one block, the section first
   and its subgroups behind it — the arrangement on screen is the arrangement a
   customer will walk through. */
.g-section{margin-bottom:var(--s4)}
.g-section.has-kids{border:1px solid var(--line);border-radius:var(--r-lg);
  padding:var(--s3);background:var(--card-2)}
.g-sub{border-style:dashed}
.g-sub .thumb{aspect-ratio:16/10}

/* Breadcrumb back to the section, on a subgroup's own page. */
.g-crumb{display:inline-flex;align-items:center;gap:6px;font-size:var(--t-sm);
  font-weight:700;color:var(--muted)}
.g-crumb a{color:var(--accent);text-decoration:underline}

.g-cards{display:grid;gap:var(--s3);grid-template-columns:repeat(2,minmax(0,1fr))}
.g-card{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  overflow:hidden;display:flex;flex-direction:column;box-shadow:var(--sh-1);
  transition:box-shadow .18s var(--ease),transform .18s var(--ease),
             border-color .18s var(--ease)}
.g-card:active{transform:scale(.98)}
@media (hover:hover){
  .g-card:hover{box-shadow:var(--sh-3);transform:translateY(-3px);
    border-color:var(--accent-line)}
}
.g-card.off{opacity:.62}
.g-card-link{display:flex;flex-direction:column;flex:1;min-width:0}
.g-card-body{padding:11px 12px 12px;display:flex;flex-direction:column;gap:4px;flex:1}
/* Name and its on/off state read as one line, the way a master card pairs
   them — `.m-top` already does exactly this layout, nothing group-specific
   about it. */
.g-card-body .m-top{width:100%}
.g-card-body .art{flex:1 1 auto;min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.g-card-acts{display:flex;flex-wrap:wrap;gap:6px;align-items:center;padding:0 12px 12px}

/* --- Members and the picker share one row --- */
/* A line each, not a card each: a hundred products fit on one screenful, which
   is what building a group out of a range actually needs. */
.p-list{border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;
  background:var(--card);box-shadow:var(--sh-1)}
/* Rhythm from an alternating tint rather than a rule under every row — a
   hundred rows read as stripes to scan, not a hundred thin lines to parse. */
.p-row{display:flex;align-items:center;gap:var(--s3);padding:9px 12px;
  min-height:58px;background:var(--card)}
.p-row:nth-child(even){background:var(--card-2)}
.p-row.off{opacity:.6}
/* The cover product gets the same left accent bar a selected picker row
   gets below — one visual language for "this row is the one that matters". */
.p-row.is-cover{background:var(--accent-soft);box-shadow:inset 3px 0 0 var(--accent)}

.p-thumb{position:relative;flex:0 0 42px;width:42px;height:42px;border-radius:var(--r-xs);
  overflow:hidden;background:var(--bg-deep);display:flex;align-items:center;
  justify-content:center}
.p-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.p-noimg{color:var(--faint);font-weight:700;font-size:var(--t-sm)}
.p-cover{position:absolute;right:0;bottom:0;background:var(--accent);color:#fff;
  line-height:0;padding:2px;border-radius:var(--r-xs) 0 0 0}

.p-main{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:1px}
.p-art{font-weight:750;font-size:var(--t-base);color:var(--ink);letter-spacing:-.012em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.p-meta{color:var(--muted);font-size:var(--t-xs);font-weight:550;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.p-tags{flex:0 0 auto;display:flex;gap:5px;align-items:center}
.p-rate{flex:0 0 auto;font-weight:800;color:var(--price);font-size:var(--t-base);
  font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.p-acts{flex:0 0 auto;display:flex;gap:4px;align-items:center}
.p-acts form{margin:0}

/* The whole picker row is the checkbox's label, so a thumb anywhere on the line
   ticks it — a hundred products go in without aiming at a hundred small boxes.
   Shift-click takes a run. */
.p-pick{cursor:pointer;-webkit-user-select:none;user-select:none}
.p-pick > input{flex:0 0 auto;width:20px;height:20px;margin:0;
  accent-color:var(--accent);cursor:pointer}
/* Same inset bar as a group's cover row — ticked is ticked, at a glance,
   without hunting for the checkbox itself. */
.p-pick.on{background:var(--accent-soft);box-shadow:inset 3px 0 0 var(--accent)}
@media (hover:hover){ .p-pick:hover{background:var(--bg)} .p-pick.on:hover{background:var(--accent-soft)} }

.pick-head{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s2) var(--s3);
  margin-bottom:var(--s3)}
.pick-head .count{flex:1 1 auto;min-width:0}
.pick-add{flex:0 0 auto}
/* Same disabled treatment as every other button in the app — the grayscale
   filter this used to add on top just muddied the accent gradient further. */
.pick-add[disabled]{opacity:.45;cursor:not-allowed;box-shadow:none;filter:none}

/* On a phone the row keeps the four things you pick by — photo, art no, what
   it is, and the price — on one line. Status tags and the row's own action
   buttons move to a second line rather than squeezing everything down until
   nothing is a comfortable tap target.

   The break between the two lines is a generated empty item (`::after`) with
   `flex-basis:100%`, placed by `order` right after the price. An item that
   claims the full row width always wraps whatever comes next, so tags and
   actions land together on line two regardless of how long the art no or
   price text runs — no fixed pixel width to get wrong on a narrow phone. */
@media (max-width:519px){
  .p-row{gap:var(--s2);padding:9px;flex-wrap:wrap}
  .p-thumb{order:1}
  .p-main{order:2}
  .p-rate{order:3}
  /* A member row carries three buttons as well, which will not share a line
     with the name and the price at this width without dropping under the 44px
     a thumb needs. Give them a line of their own.
     Picker rows have no buttons, so they must NOT wrap — a hundred of them is
     the whole point of the list, and a wasted second line each doubles the
     scrolling. :not() rather than :has() so old phones get this right too. */
  .p-row:not(.p-pick)::after{content:"";flex-basis:100%;height:0;order:4}
  .p-tags{order:5;padding-left:54px}
  .p-acts{order:6;margin-left:auto}
  /* On a picker row the tags stay on the line and give way to the art no,
     which is what you are reading. */
  .p-pick .p-tags{order:4;padding-left:0;flex:0 1 auto;min-width:0;overflow:hidden}
}

/* Rename row on a group's page — the same shape as a master's rename. */
.g-rename{display:flex;gap:6px;margin-bottom:var(--s3)}
.g-rename input{flex:1;min-width:0;padding:11px 12px;font-size:var(--t-md);
  min-height:46px}

/* Renaming a group, out in the open rather than behind a fold — it is one of
   the two things this page exists to change. */
.g-name-edit{display:flex;flex-wrap:wrap;gap:var(--s2);align-items:center}
.g-name-edit label{flex:0 0 auto;font-size:var(--t-sm);font-weight:700;color:var(--muted)}
.g-name-edit input{flex:1 1 200px;min-width:0;padding:11px 12px;font-size:var(--t-md);
  min-height:46px}
.g-name-edit .btn{flex:0 0 auto}

/* Choosing the cover: every photo in the group, one tap each. "Automatic"
   is a tile of its own so that letting the group choose is a choice you make,
   not the absence of one. */
.cover-hint{margin:var(--s4) 0 var(--s2)}
.cover-pick{display:flex;gap:var(--s2);overflow-x:auto;padding-bottom:var(--s2);
  scrollbar-width:thin;-webkit-overflow-scrolling:touch}
.cp-tile{margin:0;flex:0 0 auto}
.cp-tile button{display:flex;flex-direction:column;align-items:center;gap:5px;
  width:88px;padding:6px;cursor:pointer;position:relative;
  background:var(--card);border:2px solid var(--line);border-radius:var(--r);
  color:var(--muted);font-size:var(--t-2xs);font-weight:700;
  transition:border-color .15s var(--ease),background-color .15s var(--ease)}
.cp-shot{width:100%;aspect-ratio:1/1;border-radius:var(--r-xs);overflow:hidden;
  background:var(--bg-deep);display:flex;align-items:center;justify-content:center}
.cp-shot img{width:100%;height:100%;object-fit:cover;display:block}
.cp-blank{color:var(--faint)}
.cp-art{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cp-mark{position:absolute;top:4px;right:4px;background:var(--accent);color:#fff;
  border-radius:50%;width:20px;height:20px;display:flex;align-items:center;
  justify-content:center;box-shadow:0 1px 3px rgba(13,23,41,.3)}
/* The chosen tile keeps its tint on hover — a later plain :hover would
   otherwise win on equal specificity (see the trap noted in MEMORY.md). */
.cp-tile.on button{border-color:var(--accent);background:var(--accent-soft);
  color:var(--accent)}
@media (hover:hover){
  .cp-tile:not(.on) button:hover{border-color:var(--accent-line);background:var(--bg)}
}

.tag-alt{background:var(--alt-bg);color:var(--alt);border-color:var(--alt-line)}

/* On a phone the hamburger menu already lists every staff page, so the tab
   strip is pure noise; bring it back on wider screens. */
@media (max-width:639px){ .tabs{display:none} }

/* ============================================================
   10. Tablet and desktop
   ============================================================ */
@media (min-width:640px){
  .wrap{padding:0 var(--s4)}
  main{padding-top:var(--s5)}
  .page-head h1{font-size:var(--t-2xl)}

  .browse{margin:0 calc(var(--s4) * -1) var(--s4);padding:10px var(--s4) var(--s2)}
  .sheet{left:var(--s4);right:auto;min-width:300px;max-width:420px}

  .grid{grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:var(--s4)}

  .pbody{padding:var(--s5)}
  .pbody h1{font-size:var(--t-3xl)}
  .spec-tiles{grid-template-columns:repeat(4,minmax(0,1fr))}
  .shot{flex-basis:74px;width:74px;height:74px}
  .detail{grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);gap:var(--s6)}

  .action-bar,.submit-bar,.bulk-bar{justify-content:flex-end}
  .ab-btn{flex:0 0 auto;padding:0 26px}
  .ab-call{flex:0 0 auto}
  .btn-wa{width:auto}

  .panel{padding:var(--s5)}
  .fields{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}
  .cells{grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}
  .img-grid{grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:var(--s3)}
  .shot-sm{width:68px;height:68px;flex-basis:68px}
  .add-images,.add-master{flex-direction:row;align-items:center}
  .add-images .btn,.add-master .btn{width:auto}
  .add-master input{flex:1 1 240px;width:auto}
  .actions .btn{flex:0 0 auto}
  .m-cards{grid-template-columns:repeat(auto-fill,minmax(330px,1fr))}
  .g-cards{grid-template-columns:repeat(auto-fill,minmax(250px,1fr))}
  .bb-group{flex:0 1 auto}
  .bb-group .mini-input{flex:0 0 auto;width:170px}
  .mini{min-height:38px;padding:7px 12px}
  .mini.ico{width:38px;height:38px}
  .mini-select,.m-merge .mini-select{max-width:170px;min-height:38px;padding:8px 10px}
  .mini-input{min-height:38px;padding:8px 11px}
  .tbl{min-width:720px;font-size:var(--t-sm)}
  .toast{left:auto;right:24px;min-width:340px;max-width:470px}
}
@media (min-width:900px){
  .browse{position:static;background:none;border-bottom:0;margin:0 0 var(--s4);
    padding:0;-webkit-backdrop-filter:none;backdrop-filter:none}
  .sheet{position:absolute}
  .grid{gap:var(--s4)}
  .more-rail{scroll-snap-type:none}
}
@media (min-width:1024px){
  .grid{grid-template-columns:repeat(auto-fill,minmax(215px,1fr))}
}

/* Footer line doubles as the hidden staff entrance (five taps) — it must still
   look like an ordinary copyright, so only the cursor and tap behaviour change. */
#foot-mark span{cursor:default;-webkit-user-select:none;user-select:none}

/* Required vs optional fields.
   Red asterisk = must be filled in; grey "optional" chip = can be left blank. */
.req{color:var(--bad);font-weight:800;font-size:1.05em;line-height:1}
.opt{color:var(--muted);background:var(--bg-deep);border-radius:var(--r-pill);
  padding:1px 7px;font-size:var(--t-2xs);font-weight:700;text-transform:uppercase;
  letter-spacing:.05em;vertical-align:1px}
.legend{margin:-2px 0 var(--s3)}
.legend .req{margin-right:2px}
.row-label .req,.row-label .opt{margin-left:3px}
.row-label .opt{padding:0 5px;font-size:9.5px}

/* Field labels flow as text, so the caption and its required/optional marker
   stay on one line — as a flex column each was becoming its own row. */
.fields label{display:block}
.fields label > input,
.fields label > select,
.fields label > textarea,
.fields label > .new-input{display:block;width:100%;margin-top:5px}
.fields label > .pick{display:flex;width:100%;margin-top:5px}
/* Must come last: a display rule above would otherwise beat the hidden
   attribute and reveal the "+ New" boxes that should stay closed. */
.fields label > [hidden]{display:none}
.fields label > .field-hint{display:block;margin-top:4px}
.fields .self-end{display:flex;align-items:flex-end}
.fields .self-end > .btn{width:100%}

/* ============================================================
   Brands / Categories / Sizes / Remarks each get their own colour,
   so the sections stay apart at a glance. One --sec variable per
   section drives the bar, heading, cards and Add button.
   ============================================================ */
.master-brand   {--sec:#1d4ed8; --sec-soft:#eef4ff; --sec-line:#c7dbfe}  /* blue   */
.master-category{--sec:#7c3aed; --sec-soft:#f5f3ff; --sec-line:#ddd6fe}  /* violet */
.master-size    {--sec:#0f766e; --sec-soft:#ecfdf5; --sec-line:#a7f3d0}  /* teal   */
.master-remark  {--sec:#b45309; --sec-soft:#fffbeb; --sec-line:#fde68a}  /* amber  */

.master-sec{border-top:3px solid var(--sec);position:relative}
.master-sec > h2{color:var(--sec)}
.master-sec > h2 .count{color:var(--sec);opacity:.75}
.master-sec .m-card{background:var(--sec-soft);border-color:var(--sec-line);
  border-left:3px solid var(--sec)}
.master-sec .m-card.off{background:var(--bg);border-left-color:var(--line-2)}
.master-sec .add-master .btn{background:var(--sec)}
.master-sec .m-name{color:var(--sec)}
.master-sec .m-card.off .m-name{color:var(--muted)}

/* Sizes carry an extra "order" box, and on a 375px screen the name + order +
   save row pushed the whole page sideways. Let the name shrink and keep the
   order box narrow; the card can never be wider than its column. */
.m-card{max-width:100%;overflow-x:hidden}
.m-rename{flex-wrap:nowrap;min-width:0}
.m-rename input[type=text]{flex:1 1 auto;min-width:0}
.m-order{flex:0 0 56px;width:56px;padding-left:6px;padding-right:6px;text-align:center}

/* ============================================================
   Collapsible master sections. Closed by default, so the page opens
   as four headings instead of a long wall of cards.
   ============================================================ */
details.master-sec{padding:0;overflow:hidden}
details.master-sec > summary{
  list-style:none;cursor:pointer;user-select:none;-webkit-user-select:none;
  display:flex;align-items:center;gap:var(--s2);
  padding:var(--s3) var(--s4);min-height:52px;
  font-weight:800;font-size:var(--t-lg);letter-spacing:-.02em;color:var(--sec);
  background:var(--sec-soft);
}
details.master-sec > summary::-webkit-details-marker{display:none}
details.master-sec > summary:hover{filter:brightness(.985)}
details.master-sec > summary .sec-title{flex:1;min-width:0}
details.master-sec > summary .count{
  background:var(--sec);color:#fff;border-radius:var(--r-pill);
  padding:1px 9px;font-size:var(--t-xs);font-weight:800;opacity:1}
details.master-sec > summary .chev{flex:0 0 auto;transition:transform .18s var(--ease)}
details.master-sec[open] > summary .chev{transform:rotate(180deg)}
details.master-sec[open] > summary{border-bottom:1px solid var(--sec-line)}

/* Everything after the summary is the body, so it needs the padding the
   panel used to provide. */
details.master-sec > *:not(summary){margin-left:var(--s4);margin-right:var(--s4)}
details.master-sec > .add-master{margin-top:var(--s4)}
details.master-sec > .m-cards{margin-bottom:var(--s4)}
details.master-sec > .empty{margin-bottom:var(--s4)}

/* ============================================================
   Import row. It used to reuse the "chip" style, which made the
   checkbox a huge tick and wrapped the label over three lines.
   ============================================================ */
.import-form{display:grid;gap:var(--s2);align-items:center}
.file-input{width:100%;padding:11px 12px;font-size:var(--t-sm);
  border:1px dashed var(--line-2);border-radius:var(--r-sm);background:var(--card);
  color:var(--ink-2)}
.file-input::file-selector-button{
  margin-right:10px;padding:7px 12px;border:0;border-radius:var(--r-xs);
  background:var(--accent-soft);color:var(--accent);font:inherit;font-size:var(--t-sm);
  font-weight:700;cursor:pointer}
.file-input::file-selector-button:hover{background:var(--accent-line)}
.check-line{display:flex;align-items:center;gap:9px;min-height:34px;
  font-size:var(--t-sm);font-weight:600;color:var(--ink-2);cursor:pointer}
.check-line input{flex:0 0 18px;width:18px;height:18px;margin:0;accent-color:var(--accent)}
.check-line span{line-height:1.3}
.import-form .btn{justify-self:start}

@media (min-width:720px){
  /* file | checkbox | button, all on one line */
  .import-form{grid-template-columns:minmax(0,1fr) auto auto;gap:var(--s3)}
}

/* "No results, here's the closest" banner */
.no-match{background:var(--warn-bg);border:1px solid var(--warn-line);
  border-left:4px solid var(--warn);border-radius:var(--r);
  padding:var(--s3) var(--s4);margin-bottom:var(--s3);font-size:var(--t-sm)}
.no-match strong{display:block;color:var(--warn);font-size:var(--t-base)}
.no-match > span{color:var(--ink-2)}
.did-you-mean{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:var(--s2)}
.dym-label{font-size:var(--t-xs);font-weight:700;color:var(--muted);
  text-transform:uppercase;letter-spacing:.05em}
.dym-chip{display:inline-flex;align-items:center;min-height:32px;padding:4px 12px;
  border-radius:var(--r-pill);background:var(--card);border:1px solid var(--warn-line);
  color:var(--warn);font-size:var(--t-sm);font-weight:700}
.dym-chip:hover{background:var(--warn);color:#fff;border-color:var(--warn)}

/* ============================================================
   Staff shortcuts on a product card. The card is an <article> so the
   buttons can sit beside the link rather than inside it — a button
   nested in an <a> is invalid and every tap would open the product.
   ============================================================ */
.card{position:relative}
.card-link{display:flex;flex-direction:column;height:100%;color:inherit}
.card-actions{position:absolute;top:7px;right:7px;z-index:3;display:flex;gap:6px}
.card-actions .ca-form{margin:0;display:block}
.ca-btn{width:34px;height:34px;border:0;border-radius:50%;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  background:rgba(255,255,255,.94);color:var(--ink-2);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  box-shadow:0 2px 6px rgba(13,23,41,.22);
  transition:transform .15s var(--ease),background-color .15s var(--ease),
             color .15s var(--ease)}
.ca-btn:active{transform:scale(.9)}
.ca-edit:hover{background:var(--info);color:#fff}
.ca-hide:hover{background:var(--warn);color:#fff}

/* Fingers are wide — give them a bit more room on a phone. */
@media (max-width:420px){
  .ca-btn{width:36px;height:36px}
}
.tag-owner{background:var(--alt-bg);color:var(--alt);font-weight:800}

/* The Images help text is one line; the server limits hide behind a toggle. */
.hint.one-line{margin:0 0 var(--s3);line-height:1.5}
.hint-toggle{border:0;background:none;padding:0 0 0 4px;font:inherit;
  font-size:var(--t-sm);font-weight:700;color:var(--accent);cursor:pointer;
  text-decoration:underline;text-underline-offset:2px}
.hint-toggle.on{color:var(--ink-2)}
.limits-line{margin:-6px 0 var(--s3);padding:var(--s2) var(--s3);
  background:var(--bg-deep);border-radius:var(--r-xs)}
.warn-inline{color:var(--warn)}

/* Cover photo: obvious badge, and a labelled button on the others. */
.cover-badge{display:inline-flex;align-items:center;gap:4px}
.cover-badge svg{display:block}
.set-cover{padding:6px 9px;font-size:var(--t-2xs);gap:4px;white-space:nowrap}
.inline-ico{display:inline-flex;vertical-align:-2px}
.inline-ico svg{display:block}

/* ============================================================
   Listing code, upload / updated stamps, and the first-run empty state
   ============================================================ */

/* The 16-character listing code. Monospace and letter-spaced so it can be
   read off a screen and typed back in without guessing at the characters. */
.lcode{font-family:var(--font-mono);font-size:var(--t-xs);letter-spacing:.06em;
  color:var(--ink-2);font-weight:600;overflow-wrap:anywhere}
.tbl .lcode{display:block;margin-top:3px;color:var(--muted);font-size:var(--t-2xs)}
.tbl .nowrap{white-space:nowrap}
.tbl .faint{color:var(--faint)}

/* Edit page: code and dates, above the fields and not editable. */
.idstrip{display:flex;flex-wrap:wrap;gap:var(--s2) var(--s5);
  background:var(--card-2);border:1px solid var(--line);border-radius:var(--r);
  padding:var(--s3) var(--s4);margin-bottom:var(--s4)}
.idbit{min-width:0}
.idbit span{display:block;color:var(--muted);font-size:var(--t-2xs);
  text-transform:uppercase;letter-spacing:.07em;font-weight:800;margin-bottom:2px}
.idbit b{font-size:var(--t-sm);color:var(--ink);font-weight:700}

/* Product page: the reference line under the remarks. */
.p-ref{margin-top:var(--s3);padding-top:var(--s3);border-top:1px solid var(--line);
  display:flex;flex-wrap:wrap;gap:var(--s1) var(--s4);
  color:var(--muted);font-size:var(--t-xs)}
.p-ref .lcode{font-size:var(--t-xs);color:var(--ink-2)}

/* Nothing in the catalog yet: staff get the two ways to fix that. */
.empty-start{display:flex;flex-direction:column;align-items:center;gap:var(--s2)}
.es-icon{width:64px;height:64px;border-radius:50%;display:flex;
  align-items:center;justify-content:center;color:var(--accent);
  background:var(--accent-soft);border:1px solid var(--accent-line);
  margin-bottom:var(--s1)}
.es-title{margin:0;font-size:var(--t-lg);font-weight:800;color:var(--ink);
  letter-spacing:-.02em}
.es-sub{margin:0;max-width:44ch;color:var(--muted);font-size:var(--t-sm);
  line-height:1.6}
.es-sub strong{color:var(--ink-2)}
.es-acts{display:flex;flex-wrap:wrap;gap:var(--s2);justify-content:center;
  margin:var(--s3) 0 0}
.es-note{margin:var(--s2) 0 0;font-size:var(--t-xs);color:var(--faint)}
.es-note a{color:var(--muted)}

/* "+ Add product" and "Bulk" travel together above the grid. */
.add-pair{display:inline-flex;gap:var(--s2);margin-left:auto}

/* ============================================================
   Drop zone for photos (bulk.php, product-add.php)
   ------------------------------------------------------------
   The file input is stretched invisibly across the box, so the whole
   area is clickable, accepts a native drop, and still tab-focuses.
   ============================================================ */
.drop{position:relative;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:2px;text-align:center;
  padding:var(--s6) var(--s4);border:2px dashed var(--line-2);
  border-radius:var(--r-lg);background:var(--card-2);color:var(--muted);
  transition:border-color .15s var(--ease),background-color .15s var(--ease)}
.drop-ico{color:var(--accent);margin-bottom:var(--s1)}
.drop-main{font-size:var(--t-base);font-weight:750;color:var(--ink-2)}
.drop-sub{font-size:var(--t-xs);color:var(--muted)}
.drop #picker{position:absolute;left:0;top:0;width:100%;height:100%;
  margin:0;padding:0;border:0;opacity:0;cursor:pointer}
.drop:focus-within{border-color:var(--accent);box-shadow:var(--ring)}
@media (hover:hover){ .drop:hover{border-color:var(--accent)} }

/* Dragging files anywhere over the page lights this up. */
.drop.over{border-color:var(--accent);border-style:solid;background:var(--accent-soft)}
.drop.over .drop-main{color:var(--accent)}
.drop.over .drop-ico{transform:translateY(-2px)}

/* What a drop left out — never leave that unsaid. */
.drop-note{margin:var(--s2) 0 0;color:var(--warn);font-weight:650}

/* ============================================================
   Back button (product.php)
   ------------------------------------------------------------
   Was a small text link. It is the most-used control on the page, so it
   is a real button now: 44px tall, its own surface, thumb-sized.
   ============================================================ */
.back-row{display:flex;align-items:center;gap:var(--s2);margin:0 0 var(--s3)}
/* Highlighted, not plain: this is the way out of a product or an edit and
   the control people reach for most, so it carries the accent rather than
   disappearing into the page as another white pill. */
.btn-back{display:inline-flex;align-items:center;gap:6px;min-height:44px;
  padding:0 16px 0 12px;border-radius:var(--r-pill);
  background:var(--accent-soft);border:1.5px solid var(--accent-line);
  box-shadow:0 1px 2px rgba(29,78,216,.10);
  color:var(--accent);font-size:var(--t-sm);font-weight:800;line-height:1;
  white-space:nowrap;
  transition:border-color .15s var(--ease),background-color .15s var(--ease),
             color .15s var(--ease),transform .12s var(--ease),
             box-shadow .15s var(--ease)}
.btn-back svg{display:block;flex:0 0 auto}
.btn-back:active{transform:scale(.97)}
@media (hover:hover){
  .btn-back:hover{background:var(--accent-grad);border-color:transparent;
    color:#fff;box-shadow:0 2px 8px rgba(29,78,216,.28)}
}
/* The secondary link beside it stays quiet and sits at the far end, so the
   highlight above reads as the one obvious way back. */
.btn-back-alt{margin-left:auto;background:var(--card);color:var(--muted);
  border-color:var(--line-2);font-weight:700;box-shadow:var(--sh-1)}
@media (hover:hover){
  .btn-back-alt:hover{background:var(--accent-soft);color:var(--accent);
    border-color:var(--accent-line);box-shadow:none}
}

/* ============================================================
   Choosing the cover photo
   ------------------------------------------------------------
   It used to be a bare star squeezed between the arrows in the button
   row. It is a labelled tap target on the photo now, in the same corner
   the Cover badge occupies, so the two read as one control: whichever
   photo shows the filled badge is the cover.
   ============================================================ */
.cover-pick{position:absolute;top:8px;left:8px;margin:0;z-index:2}
.cover-dot{display:inline-flex;align-items:center;gap:5px;
  min-height:34px;padding:0 12px;border-radius:var(--r-pill);cursor:pointer;
  font-family:inherit;font-size:var(--t-2xs);font-weight:800;line-height:1;
  letter-spacing:.02em;color:#fff;
  background:rgba(13,23,41,.5);border:1px solid rgba(255,255,255,.42);
  -webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);
  box-shadow:0 2px 6px rgba(13,23,41,.28);
  transition:background-color .15s var(--ease),border-color .15s var(--ease),
             transform .12s var(--ease)}
.cover-dot svg{display:block}
.cover-dot:active{transform:scale(.94)}
@media (hover:hover){
  .cover-dot:hover{background:var(--accent);border-color:rgba(255,255,255,.7)}
}

/* Same choice on the add page, before anything is saved: the cover is
   whichever photo is first, so this just moves one to the front. */
.cov-row{margin-top:6px}
.cov-tag{display:inline-flex;align-items:center;gap:4px;padding:4px 10px;
  border-radius:var(--r-pill);background:var(--accent-soft);color:var(--accent);
  border:1px solid var(--accent-line);
  font-size:var(--t-2xs);font-weight:800;line-height:1}
.cov-set{display:inline-flex;align-items:center;min-height:32px;padding:0 11px;
  border-radius:var(--r-pill);cursor:pointer;
  background:var(--card);border:1px solid var(--line-2);color:var(--muted);
  font-family:inherit;font-size:var(--t-2xs);font-weight:750;line-height:1;
  transition:border-color .15s var(--ease),color .15s var(--ease),
             background-color .15s var(--ease)}
@media (hover:hover){
  .cov-set:hover{border-color:var(--accent);color:var(--accent);
    background:var(--accent-soft)}
}

/* ============================================================
   Multiline remarks
   ------------------------------------------------------------
   A remark is a note, not a label: washing instructions and packing
   notes run to several lines. Every box that takes one is a textarea,
   so they all need the same borders, focus ring and sizing the single
   line inputs beside them already have.
   ============================================================ */
.cell textarea,.new-input,.add-master textarea,.m-rename textarea{
  border:1px solid var(--line-2);border-radius:var(--r);background:var(--card);
  font-family:inherit;color:var(--ink);font-weight:500;
  box-shadow:0 1px 2px rgba(13,23,41,.04);
  resize:vertical;line-height:1.5;
  transition:border-color .15s var(--ease),box-shadow .15s var(--ease)}
.cell textarea:focus,.add-master textarea:focus,.m-rename textarea:focus{
  outline:none;border-color:var(--accent);box-shadow:var(--ring)}
.cell textarea::placeholder,.add-master textarea::placeholder{color:var(--faint)}

/* One row of the bulk grid: shorter than a full field, still two lines. */
.cell textarea{padding:10px 12px;width:100%;font-size:var(--t-md);min-height:62px}

/* The "+ New" reveal under a picker — same width as the select above it. */
textarea.new-input{padding:11px 13px;width:100%;font-size:var(--t-md);
  min-height:74px}

/* Masters page: adding and renaming a saved remark. */
.add-master textarea{flex:0 0 auto;width:100%;padding:12px 13px;
  font-size:var(--t-md);min-height:62px}
.m-rename textarea{flex:1 1 auto;min-width:0;padding:11px 12px;
  font-size:var(--t-md);min-height:58px}
/* The name already comes through nl2br, so no white-space rule here — the
   two together would double every line break. */

/* ============================================================
   Coming back from an edit
   ------------------------------------------------------------
   Saving closes the edit view and returns to the listing, landing on
   the product just changed: #p<id> scrolls to it, ?hl=<id> rings it.
   ============================================================ */

/* The app bar and the browse bar are both sticky, so an anchor would park
   the card underneath them. Reserve their height above the scroll target —
   measured at 112px on the catalog, so this clears them with a gap. */
.card,.tbl tr{scroll-margin-top:calc(var(--appbar-h) + 72px)}

.card.hl{border-color:var(--accent);
  box-shadow:0 0 0 3px var(--accent-line),var(--sh-2);
  animation:hl-land 2.6s var(--ease) both}
.tbl tr.hl td{background:var(--accent-soft) !important;
  box-shadow:inset 0 0 0 1px var(--accent-line);
  animation:hl-land 2.6s var(--ease) both}

/* Bright for a moment, then settles into a quiet ring that stays put —
   long enough to catch the eye, not so long that it nags. */
@keyframes hl-land{
  0%   {box-shadow:0 0 0 3px var(--accent),0 0 0 10px rgba(29,78,216,.22)}
  35%  {box-shadow:0 0 0 3px var(--accent),0 0 0 14px rgba(29,78,216,0)}
  100% {box-shadow:0 0 0 3px var(--accent-line),var(--sh-2)}
}
@media (prefers-reduced-motion:reduce){
  .card.hl,.tbl tr.hl td{animation:none}
}

/* ============================================================
   install.php — the report of what ran
   ============================================================ */
.steps{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;
  gap:var(--s2)}
.step{border:1px solid var(--line);border-radius:var(--r);background:var(--card-2);
  padding:var(--s3) var(--s3) var(--s3) var(--s4);border-left-width:4px}
.step.ok{border-left-color:var(--ok)}
.step.bad{border-left-color:var(--bad);background:var(--bad-bg)}
.step-head{display:flex;align-items:center;gap:8px;
  font-weight:800;font-size:var(--t-base);color:var(--ink)}
.step-mark{display:inline-flex;align-items:center;justify-content:center;
  width:22px;height:22px;flex:0 0 22px;border-radius:50%;color:#fff}
.step.ok .step-mark{background:var(--ok)}
.step.bad .step-mark{background:var(--bad)}
.step-mark svg{display:block}
.step-lines{margin:6px 0 0;padding-left:30px;color:var(--muted);
  font-size:var(--t-sm);line-height:1.55}
.step-lines li{margin-bottom:2px}
.step.bad .step-lines{color:#911616;white-space:pre-line}
.notice ol{margin:0 0 var(--s2);padding-left:20px}
.notice li{margin-bottom:4px}
/* A step that finished but could not do one part of its job. */
.step.warn{border-left-color:var(--warn);background:var(--warn-bg)}
.step.warn .step-mark{background:var(--warn)}
.step-lines .line-bad{color:var(--bad);font-weight:650}
.step.warn .step-lines .line-bad{color:#8a4708}

/* ============================================================
   Sign in: the button and "stay signed in" share a row
   ------------------------------------------------------------
   The tick used to sit above the button as a full-width chip. Worse,
   `.stack input` sizes every input in this form to 100% wide and 48px
   tall — which caught the checkbox and turned it into a huge blue
   square. Both are fixed here.
   ============================================================ */
.signin-row{display:flex;align-items:center;gap:var(--s3);flex-wrap:wrap;
  margin:var(--s4) 0 0}
.signin-row .btn{flex:0 0 auto}
.remember{display:inline-flex;align-items:center;gap:8px;min-height:44px;
  cursor:pointer;user-select:none;
  font-size:var(--t-sm);font-weight:700;color:var(--ink-2);line-height:1.3}
/* Beats .stack input on specificity, so the tick stays a tick. */
.remember input[type=checkbox]{flex:0 0 auto;
  width:17px;height:17px;min-height:0;padding:0;margin:0;
  accent-color:var(--accent);box-shadow:none;border-radius:4px}
@media (hover:hover){ .remember:hover{color:var(--accent)} }

/* ============================================================
   Activity log (csv.php, owner only)
   ------------------------------------------------------------
   A short feed of who did what. Only the newest ACTIVITY_KEEP entries
   exist at all, so this is never a long list.
   ============================================================ */
.act-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;
  gap:1px;background:var(--line);border:1px solid var(--line);
  border-radius:var(--r);overflow:hidden}
.act{display:flex;align-items:baseline;flex-wrap:wrap;gap:var(--s1) var(--s2);
  padding:10px var(--s3);background:var(--card);font-size:var(--t-sm);
  border-left:3px solid var(--line-2)}
.act-ok{border-left-color:var(--ok)}
.act-info{border-left-color:var(--info)}
.act-warn{border-left-color:var(--warn)}
.act-bad{border-left-color:var(--bad)}
.act-alt{border-left-color:var(--alt)}
.act-who{font-weight:800;color:var(--ink);white-space:nowrap}
.act-you{font-size:var(--t-2xs);font-weight:800;text-transform:uppercase;
  letter-spacing:.06em;color:var(--accent);background:var(--accent-soft);
  border-radius:var(--r-pill);padding:1px 6px;margin-left:2px}
.act-what{color:var(--ink-2);font-weight:650}
.act-detail{color:var(--muted);min-width:0;overflow-wrap:anywhere}
.act-when{margin-left:auto;color:var(--faint);font-size:var(--t-xs);
  white-space:nowrap;font-variant-numeric:tabular-nums}
