/* heyybi — Phase 13 "Sunday morning" redesign.
   Warm cream paper, fresh leaf green, soft morning light.
   The old neutral-grey look is retired. Selector set is unchanged —
   cart / checkout / account / siupin / pay pages inherit the refresh. */

:root {
  color-scheme: light dark;
  --brand: #2F7D52; /* per-page override injected from owner settings */
  --brand-strong: color-mix(in srgb, var(--brand) 84%, #10231A);
  --brand-tint: color-mix(in srgb, var(--brand) 10%, #FFFFFF);
  --brand-tint-hover: color-mix(in srgb, var(--brand) 18%, #FFFFFF);
  --paper: #FAF7EF;
  --card: #FFFFFF;
  --ink: #26302A;
  --muted: #6F7C72;
  --line: #E9E4D4;
  --sun: #F7E9C4;
  /* Phase 27: the footer band follows the brand color at 80% opacity —
     whatever the owner picks in /edit/settings, the footer follows. */
  --foot-bg: color-mix(in srgb, var(--brand) 80%, transparent);
  --foot-ink: #EAF3EC;
  --shadow-sm: 0 2px 10px rgba(64, 78, 64, .06);
  --shadow-md: 0 10px 30px rgba(64, 78, 64, .12);
  --radius-card: 18px;
  --radius-soft: 12px;
  --font-display: "Poiret One", "Chiron Sung HK", "Songti TC", "Noto Serif CJK TC", serif;
  /* Phase 24 (revised — ONE font set for the whole site): Poiret One
     (single-weight, SIL OFL) covers every Latin glyph — site title, section
     headings, product cards, footer, prices. Chinese falls through per-glyph
     to Chiron Sung HK (昭源宋體) — Hong Kong stroke conventions. The body
     aliases the display stack, so cards, footer and paragraphs share the
     same single typeface set. Fallbacks behind the webfonts stay serif. */
  --font-body: var(--font-display);
}
* { box-sizing: border-box; }
::selection { background: color-mix(in srgb, var(--brand) 22%, transparent); }
:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 4px; }

body {
  margin: 0;
  min-height: 100vh;
  font-family: var(--font-body);
  line-height: 1.55;
  background: var(--paper);
  color: var(--ink);
}
.card {
  max-width: 560px;
  margin: 2rem;
  padding: 2.5rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 20px;
  box-shadow: var(--shadow-md);
  text-align: center;
}
h1 { margin: 0 0 .5rem; font-family: var(--font-display); font-weight: 600; font-size: 2.3rem; letter-spacing: -.01em; }
.tagline { font-size: 1.1rem; margin: 0 0 1rem; color: var(--muted); }
.muted { color: var(--muted); font-size: .95rem; }
.button {
  display: inline-block;
  margin-top: 1rem;
  padding: .65rem 1.5rem;
  background: var(--brand);
  color: #fff;
  border: 0;
  border-radius: 999px;
  text-decoration: none;
  font-weight: 600;
  box-shadow: 0 6px 18px color-mix(in srgb, var(--brand) 28%, transparent);
  transition: background .15s ease, transform .15s ease;
  cursor: pointer;
  font-family: inherit;
  font-size: 1rem;
}
.button:hover { background: var(--brand-strong); transform: translateY(-1px); }

/* ---- Storefront (Phase 2) ---- */
/* Phase 25: the base body rule is plain flow now (the centered grid that
   used to center the admin login card is gone — the owner does not want
   centering). With no grid to fight, the Phase 22 body.store
   display:block !important override is retired; the explicit width:100%
   on .store-head / .store-foot stays as cheap full-width insurance. */
/* Phase 12: hamburger menu — checkbox hack, zero JavaScript. */
.store-head {
  position: sticky; top: 0; z-index: 10;
  width: 100%; /* full-width insurance (kept from Phase 22) */
  background: color-mix(in srgb, var(--paper) 82%, transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
.head-inner {
  position: relative;
  /* Phase 20: the header row spans the full viewport — brand pinned left,
     hamburger pinned right, both flush to the screen edges. */
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .7rem 1.4rem;
}
.store-head .brand {
  display: flex; align-items: center; gap: .55rem;
  color: var(--ink); text-decoration: none;
  margin-right: auto; /* heading pins left; hamburger (last flex item) pins right */
}
.store-head .brand .logo { display: inline-flex; align-items: center; font-size: 1.25rem; }
.brand-name {
  font-family: var(--font-display);
  font-weight: 600; font-size: 1.45rem; letter-spacing: -.01em;
  color: var(--ink);
}
/* Phase 13: the new sprout mark (inline SVG; stays crisp at any size). */
.mark { display: block; width: 1.7rem; height: 1.7rem; }
.mark-stem { stroke: var(--brand); stroke-width: 2.4; stroke-linecap: round; fill: none; }
.mark-leaf-l { fill: var(--brand); }
.mark-leaf-r { fill: color-mix(in srgb, var(--brand) 60%, #8ED0A4); }
.navtog { position: absolute; width: 1px; height: 1px; opacity: 0; }
.navbtn {
  flex: none;
  display: inline-grid; place-items: center;
  width: 2.7rem; height: 2.7rem; border-radius: 999px;
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-sm);
  cursor: pointer;
}
.navbtn:hover { background: var(--brand-tint-hover); }
/* Phase 22: shopping-cart button beside the hamburger — same round chip.
   The count badge pins to the icon's top-right corner. */
.navcart {
  flex: none; position: relative;
  display: inline-grid; place-items: center;
  width: 2.7rem; height: 2.7rem; border-radius: 999px;
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-sm);
  color: var(--ink);
}
.navcart:hover { background: var(--brand-tint-hover); color: var(--brand); }
.navcart svg { width: 1.3rem; height: 1.3rem; display: block; }
.navcart .cart-badge { position: absolute; top: -.2rem; right: -.3rem; min-width: 1.05rem; height: 1.05rem; padding: 0 .2rem; font-size: .66rem; }
.navbtn span {
  position: relative; display: block;
  width: 1.25rem; height: 2px; border-radius: 2px;
  background: var(--ink);
  transition: background .15s ease;
}
.navbtn span::before, .navbtn span::after {
  content: ""; position: absolute; left: 0;
  width: 1.25rem; height: 2px; border-radius: 2px;
  background: var(--ink);
  transition: transform .18s ease;
}
.navbtn span::before { top: -7px; }
.navbtn span::after { top: 7px; }
.navtog:checked ~ .navbtn span { background: transparent; }
.navtog:checked ~ .navbtn span::before { transform: translateY(7px) rotate(45deg); }
.navtog:checked ~ .navbtn span::after { transform: translateY(-7px) rotate(-45deg); }
/* Phase 13: the menu is a floating card, no longer a full-width push-down. */
.navmenu {
  display: none;
  position: absolute; right: 0; top: calc(100% + 6px);
  width: min(20rem, calc(100% - 1.8rem));
  flex-direction: column;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: var(--shadow-md);
  padding: .55rem .8rem .8rem;
  z-index: 20;
}
.navtog:checked ~ .navmenu { display: flex; }
.navmenu a {
  padding: .7rem .7rem;
  color: inherit; text-decoration: none; font-weight: 600; font-size: 1.02rem;
  border-radius: 10px;
}
.navmenu a:hover { background: var(--brand-tint-hover); }
.navmenu a.active { color: var(--brand); }
.navmenu .disabled { color: var(--muted); }
.navmenu-sep { height: 1px; background: var(--line); margin: .4rem 0 .3rem; }
.navmenu .lang-switch { padding: .4rem .6rem .1rem; }
.lang-switch { display: flex; gap: .7rem; font-size: .9rem; }
.lang-switch a { opacity: .55; }
.lang-switch a.active { opacity: 1; font-weight: 700; text-decoration: underline; text-underline-offset: 4px; color: var(--brand); }

.store-main { max-width: 1200px; margin: 0 auto; padding: 2rem 1.4rem; }
.store-main h1, .store-main h2 { font-family: var(--font-display); font-weight: 600; letter-spacing: -.01em; }
.store-main h1 { font-size: 2rem; margin: 0 0 1.2rem; }
.store-main h2 { font-size: 1.45rem; margin: 2.6rem 0 1.1rem; }
/* Phase 13: the "Sunday morning" hero — split layout with an illustrated
   balcony scene: sun glow, plants, steaming coffee, birds, dust motes. */
.hero {
  margin: .4rem 0 1rem;
  display: grid; grid-template-columns: 1.05fr .95fr; gap: 2.4rem;
  align-items: center;
  padding: 2.9rem 3rem 2.5rem 3.2rem;
  border: 1px solid var(--line);
  border-radius: 28px;
  background:
    radial-gradient(90% 120% at 88% -10%, var(--sun), transparent 55%),
    radial-gradient(70% 90% at -10% 110%, color-mix(in srgb, var(--brand) 14%, transparent), transparent 60%),
    linear-gradient(168deg, color-mix(in srgb, var(--brand) 10%, var(--paper)), var(--paper) 70%);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}
.hero-copy { position: relative; z-index: 1; }
.hero-eyebrow {
  margin: 0 0 .9rem;
  font-size: .74rem; font-weight: 700; letter-spacing: .22em; text-transform: uppercase;
  color: var(--brand);
}
.hero h1 { color: var(--ink); font-size: 3.3rem; letter-spacing: -.015em; font-weight: 600; margin: .15rem 0 .4rem; }
.hero .tagline { font-size: 1.1rem; color: var(--muted); margin: 0 0 .3rem; max-width: 36ch; }
.hero-logo { display: inline-block; margin-bottom: .2rem; }
.hero-logo .mark { width: 2.9rem; height: 2.9rem; }
.hero-cta { display: flex; align-items: center; gap: 1.1rem; flex-wrap: wrap; margin-top: 1.5rem; }
.hero-cta .button { margin-top: 0; padding: .8rem 1.9rem; }
.hero-secondary {
  font-weight: 600; font-size: .95rem; color: var(--ink); text-decoration: none;
  border-bottom: 2px solid color-mix(in srgb, var(--brand) 35%, transparent);
  padding-bottom: 2px;
  transition: color .15s ease, border-color .15s ease;
}
.hero-secondary:hover { color: var(--brand); border-color: var(--brand); }
.hero-meta {
  margin: 1.7rem 0 0; font-size: .85rem; font-weight: 600; color: var(--muted);
  display: flex; flex-wrap: wrap; align-items: center; gap: .45rem .7rem;
}
.hero-meta .dot { width: 5px; height: 5px; border-radius: 50%; background: var(--brand); opacity: .45; flex: none; }
.hero-scene { position: relative; min-width: 0; }
.hero-scene svg { display: block; width: 100%; height: auto; }
/* Phase 13b: hero promo slideshow — uploaded slides cycle on pure-CSS
   keyframes. The page injects the per-count keyframe percentages in a small
   <style> block and the cycle length via --promo-d. One slide = static;
   promo selected but nothing uploaded = the SVG scene stays. */
.hero-solo { grid-template-columns: 1fr; }
.hero-promo {
  position: relative; min-width: 0; aspect-ratio: 7 / 6;
  border-radius: var(--radius-soft); overflow: hidden;
  box-shadow: var(--shadow-md); background: var(--card);
}
.hero-promo .promo-img { display: block; width: 100%; height: 100%; object-fit: cover; }
.hero-promo .promo-link { display: block; height: 100%; }
.promo-slide { position: absolute; inset: 0; opacity: 0; animation: hb-promo var(--promo-d, 14s) infinite; }
@media (prefers-reduced-motion: reduce) {
  .promo-slide { animation: none; }
  .promo-slide:first-child { opacity: 1; }
}
@media (max-width: 880px) {
  .hero { grid-template-columns: 1fr; padding: 2.6rem 1.6rem 2.2rem; }
  .hero-scene, .hero-promo { order: 2; max-width: 440px; width: 100%; margin: 0 auto; }
  .hero h1 { font-size: 2.6rem; }
}
/* Hero scene — gentle Sunday-morning motion (decorative only). */
.sc-plant, .sc-hang, .sc-leaf { transform-box: fill-box; transform-origin: 50% 100%; }
.sc-plant { animation: hero-sway 6.5s ease-in-out infinite alternate; }
.sc-hang { animation: hero-sway 7.5s ease-in-out infinite alternate-reverse; }
.sc-leaf { animation: hero-sway 5.5s ease-in-out infinite alternate; }
.sc-steam { animation: hero-steam 3.8s ease-in-out infinite; }
.sc-steam.st2 { animation-delay: 1.9s; }
.sc-mote { animation: hero-mote 7s ease-in-out infinite; }
.sc-mote.m2 { animation-delay: -2.2s; }
.sc-mote.m3 { animation-delay: -4.1s; }
.sc-mote.m4 { animation-delay: -1.3s; }
.sc-mote.m5 { animation-delay: -3.2s; }
.sc-bird { animation: hero-drift 9s ease-in-out infinite alternate; }
.sc-bird.b2 { animation-duration: 7s; animation-delay: -3s; }
@keyframes hero-sway { from { transform: rotate(-1.6deg); } to { transform: rotate(1.8deg); } }
@keyframes hero-steam {
  0% { opacity: 0; transform: translateY(8px); }
  30% { opacity: .6; }
  100% { opacity: 0; transform: translateY(-10px); }
}
@keyframes hero-mote {
  0%, 100% { opacity: .2; transform: translate(0, 0); }
  50% { opacity: .65; transform: translate(10px, -16px); }
}
@keyframes hero-drift {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(9px, -5px); }
}
@media (prefers-reduced-motion: reduce) {
  .sc-plant, .sc-hang, .sc-leaf, .sc-steam, .sc-mote, .sc-bird { animation: none; }
}
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 1.2rem; }
.pcard {
  display: block; padding: 1rem;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm);
  color: inherit; text-decoration: none;
  transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}
.pcard:hover {
  transform: translateY(-3px); box-shadow: var(--shadow-md);
  border-color: color-mix(in srgb, var(--brand) 30%, var(--line));
}
.thumb {
  aspect-ratio: 4 / 3; border-radius: var(--radius-soft); margin-bottom: .8rem;
  display: grid; place-items: center;
  background: linear-gradient(135deg, #F2EFE4, #E8E4D4);
  color: #8a8f99; font-weight: 800; font-size: 1.6rem; letter-spacing: .08em;
  overflow: hidden;
}
/* Phase 6: no product photos yet → logo emoji placeholder (never site-name text). */
.thumb-emoji { font-size: 2.4rem; line-height: 1; }
.thumb-emoji-lg { font-size: 4rem; }
.pname { font-weight: 700; margin-bottom: .25rem; }
/* Phase 22: catalog card names reserve exactly three lines — price, stock
   note and the add-to-cart button align across the grid; longer names
   clamp with an ellipsis after line 3. Scoped to .pcard so the cart
   line-item names keep their natural height. */
.pcard .pname {
  line-height: 1.3; min-height: 3.9em;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
  overflow: hidden;
}
.price { font-weight: 700; color: var(--brand); }
.detail {
  max-width: 720px; margin: 0 auto 2rem;
  background: var(--card); border: 1px solid var(--line); border-radius: 20px; padding: 2.2rem;
  box-shadow: var(--shadow-sm);
}
.detail h1 { font-size: 1.7rem; margin-bottom: .4rem; }
.detail .thumb { aspect-ratio: 16 / 10; margin-bottom: 1.4rem; }
.badge {
  display: inline-block; padding: .2rem .7rem;
  border-radius: 999px; background: var(--brand-tint); color: var(--brand);
  font-size: .85rem; font-weight: 700; margin-right: .4rem;
}
.btn-disabled {
  display: inline-block; margin-top: 1.2rem;
  padding: .6rem 1.2rem; border: 0; border-radius: 999px;
  background: #C9CCD4; color: #fff; font-weight: 700; cursor: not-allowed;
}
.backlink { display: inline-block; margin-bottom: 1rem; color: inherit; font-weight: 600; text-decoration: none; }
.backlink:hover { color: var(--brand); }

/* ---- Cart (Phase 3) ---- */
.cart-badge {
  display: inline-grid; place-items: center;
  min-width: 1.35rem; height: 1.35rem; padding: 0 .28rem;
  border-radius: 999px; background: var(--brand); color: #fff;
  font-size: .78rem; font-weight: 700; vertical-align: 2px;
}
.cart-list { display: grid; gap: 1rem; max-width: 720px; margin: 0 auto; }
.cart-item {
  display: grid; grid-template-columns: 84px 1fr auto; gap: 1rem; align-items: center;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); padding: 1rem;
  box-shadow: var(--shadow-sm);
}
.cart-item .thumb { aspect-ratio: 1 / 1; margin: 0; font-size: 1.1rem; }
.cart-item .thumb .thumb-emoji { font-size: 1.8rem; }
.cart-item .pname { color: inherit; text-decoration: none; }
.cart-item .pname:hover { text-decoration: underline; }
.ci-side { display: grid; gap: .5rem; justify-items: end; }
.ci-qty { display: flex; align-items: center; gap: .45rem; }
.ci-qty form { display: inline; }
.qty-btn {
  width: 2rem; height: 2rem; padding: 0;
  border: 1px solid var(--line); background: var(--card); color: inherit;
  border-radius: 10px; font-weight: 700; font-size: 1rem; cursor: pointer;
}
.qty-btn:hover { border-color: var(--brand); color: var(--brand); }
.qty-n { min-width: 1.5rem; text-align: center; font-weight: 700; }
.ci-line { font-weight: 700; }
.cart-remove {
  border: 0; background: none; padding: 0;
  color: #B3563F; font-weight: 600; font-size: .9rem;
  text-decoration: underline; cursor: pointer;
}
.cart-total {
  display: flex; justify-content: space-between; align-items: center;
  max-width: 720px; margin: 1.2rem auto 0; padding: 1rem 1.2rem;
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card);
  font-size: 1.1rem; font-weight: 700;
  box-shadow: var(--shadow-sm);
}
.cart-foot { text-align: center; margin-top: 1.6rem; }
.addform { display: flex; align-items: center; gap: .9rem; margin-top: 1.2rem; flex-wrap: wrap; }
.qty-label { font-weight: 600; }
.qty-input {
  width: 4.2rem; padding: .5rem .6rem;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--card); color: inherit; font-size: 1rem;
}
@media (max-width: 480px) {
  .cart-item { grid-template-columns: 64px 1fr; }
  .ci-side { grid-column: 1 / -1; display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
}

/* ---- Checkout & order (Phase 4) ---- */
.checkout-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 1.4rem;
  max-width: 880px; margin: 0 auto; align-items: start;
}
.panel {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card); padding: 1.6rem;
  box-shadow: var(--shadow-sm);
}
.panel h2, .panel h3 { font-family: var(--font-display); font-weight: 600; margin-top: 0; }
.field { display: grid; gap: .3rem; margin-bottom: 1rem; }
.field label { font-weight: 600; font-size: .95rem; }
.field input {
  padding: .55rem .7rem;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--card); color: inherit; font-size: 1rem; width: 100%;
}
.field textarea {
  padding: .55rem .7rem;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--card); color: inherit; font-size: 1rem; width: 100%;
  font-family: inherit; resize: vertical;
}
.field-help { margin: 0; }
.checkout-actions { display: flex; gap: 1.2rem; align-items: center; margin-top: 1.4rem; }
.checkout-actions .backlink { margin-bottom: 0; }
.checkout-actions .button { margin-top: 0; }
.summary-line { display: flex; justify-content: space-between; gap: 1rem; padding: .35rem 0; }
.summary-total {
  border-top: 1px solid var(--line); margin-top: .6rem; padding-top: .8rem;
  display: flex; justify-content: space-between;
  font-weight: 700; font-size: 1.05rem;
}
.error-box {
  background: #FDECEA; color: #8C2F1D;
  border-radius: 12px; padding: .8rem 1rem; margin-bottom: 1rem;
  font-weight: 600; font-size: .95rem;
}
.order-status {
  display: inline-block; padding: .2rem .8rem;
  border-radius: 999px; background: #FDF3D7; color: #7A5B12;
  font-weight: 700; font-size: .85rem;
}
@media (max-width: 720px) {
  .checkout-grid { grid-template-columns: 1fr; }
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #131A15;
    --card: #1B241E;
    --ink: #E9EFE9;
    --muted: #9AA69C;
    --line: #2B362E;
    --sun: #2A2E1F;
    /* Phase 27: no dark-mode foot-bg override — the footer follows the
       brand color (80% opacity) in both color schemes, blending with the
       dark paper underneath instead of the cream one. */
    --brand-tint: color-mix(in srgb, var(--brand) 20%, #1B241E);
    --brand-tint-hover: color-mix(in srgb, var(--brand) 30%, #1B241E);
    --shadow-sm: 0 2px 10px rgba(0, 0, 0, .25);
    --shadow-md: 0 10px 30px rgba(0, 0, 0, .35);
  }
  .button { background: var(--brand); color: #fff; }
  .thumb { background: linear-gradient(135deg, #232D25, #1D261F); color: #5A5F6A; }
  .error-box { background: #3A1F18; color: #F3B2A0; }
  .order-status { background: #3A2F14; color: #F0D488; }
  .navbtn:hover { background: var(--brand-tint-hover); }
  .hero {
    background:
      radial-gradient(90% 120% at 88% -10%, color-mix(in srgb, #F7E9C4 16%, transparent), transparent 55%),
      radial-gradient(70% 90% at -10% 110%, color-mix(in srgb, var(--brand) 18%, transparent), transparent 60%),
      linear-gradient(168deg, color-mix(in srgb, var(--brand) 12%, var(--paper)), var(--paper) 70%);
  }
  .hero-scene { opacity: .93; }
  .sc-ledge { fill: #232D25; }
  .sc-ledge-top { fill: #2E3A2B; }
  .sc-light { display: none; }
  .sc-cup, .sc-saucer { fill: #E9EFE9; }
  .sc-steam { stroke: #9AA69C; }
  .btn-disabled { background: #34343E; }
  .cart-remove { color: #D98A70; }
  .mark-leaf-r { fill: color-mix(in srgb, var(--brand) 60%, #8ED0A4); }
}

/* ---- Catalog (Phase 5, heyybi style) ---- */
.pcard { padding: 0; overflow: hidden; }
.pcard-link { display: block; padding: 1rem 1rem .6rem; color: inherit; text-decoration: none; }
.pcard-link .thumb { margin-bottom: .7rem; }
.pcard form { padding: 0 1rem 1rem; }
.card-add {
  width: 100%; margin-top: 0; text-align: center;
  border: 0; cursor: pointer; font-family: inherit; font-size: .95rem;
  padding: .55rem .9rem;
  background: var(--brand-tint); color: var(--brand);
  border-radius: 999px; font-weight: 700;
  transition: background .15s ease, color .15s ease;
}
.card-add:hover { background: var(--brand); color: #fff; }
.stock-note {
  display: inline-block; margin-top: .35rem;
  font-size: .85rem; font-weight: 600; color: #4B8A4B;
}
.stock-low { color: #A06A12; }
.stock-out { color: #B3563F; }
.cat-tiles {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 1.2rem; margin-bottom: 1rem;
}
/* Phase 13: image-led category tiles — white card, serif name. */
.cat-tile {
  display: flex; flex-direction: column; justify-content: flex-start; gap: .35rem;
  padding: 1rem; border-radius: var(--radius-card);
  background: var(--card); border: 1px solid var(--line);
  box-shadow: var(--shadow-sm);
  color: inherit; text-decoration: none; font-weight: 700;
  transition: transform .16s ease, box-shadow .16s ease, border-color .16s ease;
}
.cat-tile:hover {
  transform: translateY(-3px); box-shadow: var(--shadow-md);
  border-color: color-mix(in srgb, var(--brand) 30%, var(--line));
}
.cat-name { font-family: var(--font-display); font-weight: 600; font-size: 1.25rem; }
.cat-count { font-size: .9rem; font-weight: 500; color: var(--muted); }
.cat-chips { display: flex; flex-wrap: wrap; gap: .6rem; margin: 0 0 1.6rem; }
.chip {
  padding: .35rem .95rem; border-radius: 999px;
  background: var(--card); color: inherit; text-decoration: none;
  font-weight: 600; font-size: .92rem;
  border: 1px solid var(--line);
  transition: background .15s ease, border-color .15s ease, color .15s ease;
}
.chip:hover { border-color: var(--brand); color: var(--brand); }
.chip.active { background: var(--brand); color: #fff; border-color: var(--brand); }
.chip.active:hover { color: #fff; }

/* Phase 20: zero-JS product-filter dropdown — a native <details> whose
   floating card carries the type + sort chip rows. No JavaScript. */
.filter-dd { position: relative; display: inline-block; margin: 0 0 1.2rem; }
.filter-btn {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .5rem 1rem; border: 1px solid var(--line); border-radius: 999px;
  background: var(--card); color: inherit; font-size: .92rem; font-weight: 600;
  cursor: pointer; list-style: none; box-shadow: var(--shadow-sm);
  -webkit-user-select: none; user-select: none;
}
.filter-btn::-webkit-details-marker { display: none; }
.filter-btn:hover { border-color: var(--brand); color: var(--brand); background: var(--brand-tint); }
.filter-dd[open] > .filter-btn { border-color: var(--brand); color: var(--brand); background: var(--brand-tint); }
.filter-arrow {
  width: .5rem; height: .5rem; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translateY(-.05rem); transition: transform .18s ease;
}
.filter-dd[open] .filter-arrow { transform: rotate(225deg); }
.filter-on .filter-btn::after {
  content: ""; width: .45rem; height: .45rem; border-radius: 50%;
  background: var(--brand);
}
.filter-card {
  position: absolute; left: 0; top: calc(100% + .5rem); z-index: 30;
  display: grid; gap: .55rem; padding: .85rem .95rem;
  min-width: 16rem; max-width: min(24rem, calc(100vw - 2rem));
  background: var(--card); border: 1px solid var(--line);
  border-radius: 14px; box-shadow: var(--shadow-md);
}
.filter-card .cat-chips { margin: 0; }
.detail .stock-note { font-size: .95rem; margin: .3rem 0 0; }

@media (prefers-color-scheme: dark) {
  .stock-note { color: #7FBF7F; }
  .stock-low { color: #D9A84E; }
  .stock-out { color: #D98A70; }
  .cat-count { color: #9AA69C; }
  .toggle-btn:hover { border-color: var(--brand); }
}

/* ---- Brand + responsive + footer (Phase 6 / Phase 13) ---- */
/* Phase 13: full-bleed footer band; Phase 27: the band color follows the
   brand (--foot-bg = brand at 80% opacity, set in :root above). */
.store-foot {
  margin-top: 3.4rem; padding: 2.6rem 1.4rem 2.2rem;
  width: 100%; /* full-width insurance (kept from Phase 22) */
  background: var(--foot-bg);
  color: var(--foot-ink);
}
/* Phase 22: full-width footer — the 1200px cap is gone, the content spans
   edge to edge (.store-foot's own padding keeps it off the screen edges). */
.foot-inner { display: grid; gap: .55rem; }
.foot-links { display: flex; flex-wrap: wrap; gap: 1.1rem; }
.foot-links a { color: var(--foot-ink); text-decoration: none; font-weight: 600; font-size: .95rem; opacity: .85; }
.foot-links a:hover { opacity: 1; text-decoration: underline; text-underline-offset: 4px; }
.foot-note, .foot-copy { margin: 0; font-size: .9rem; color: rgba(234, 243, 236, .68); }
.foot-pays { margin: .5rem 0 0; }

/* WooCommerce-style phone layout: 2 product columns in portrait. */
@media (max-width: 640px) {
  .head-inner { padding: .7rem .9rem; }
  .navmenu { left: 0; right: 0; width: auto; }
  .store-main { padding: 1.4rem .9rem; }
  .grid { grid-template-columns: repeat(2, 1fr); gap: .8rem; }
  .pcard-link { padding: .7rem .7rem .4rem; }
  .pcard form { padding: 0 .7rem .7rem; }
  .thumb { aspect-ratio: 1 / 1; }
  .thumb-emoji { font-size: 1.9rem; }
  .pname { font-size: .92rem; }
  .price { font-size: .95rem; }
  .stock-note { font-size: .78rem; }
  .card-add { padding: .45rem .6rem; font-size: .85rem; }
  .cat-tiles { grid-template-columns: repeat(2, 1fr); gap: .8rem; margin-bottom: 1rem; }
  .cat-tile { padding: .8rem; }
  .cat-name { font-size: 1.08rem; }
  .hero { margin: 0 0 1rem; padding: 2.2rem 1.2rem 2rem; border-radius: 20px; gap: 1.4rem; }
  .hero h1 { font-size: 2.3rem; }
  .hero-logo .mark { width: 2.4rem; height: 2.4rem; }
  .hero-img { width: 3rem; height: 3rem; }
  .hero-eyebrow { letter-spacing: .16em; }
  .hero-cta { flex-direction: column; align-items: flex-start; gap: .9rem; }
  .hero-meta { font-size: .8rem; gap: .35rem .55rem; }
  .hero-scene { max-width: 360px; margin: 0 auto; }
  .detail { padding: 1.4rem; }
  .store-foot { padding: 2rem 1rem 1.8rem; }
  .foot-links { gap: .9rem; }
}

/* ---- Siupin (Phase 7: product CRUD) ---- */
.siupin-nav { display: flex; gap: 1.1rem; margin: 0 0 1.2rem; flex-wrap: wrap; }
.siupin-nav a { color: inherit; text-decoration: none; font-weight: 600; opacity: .8; }
.siupin-nav a:hover { opacity: 1; text-decoration: underline; }
/* Phase 43d: 重新整理 + 登出 pushed to the tail — far right, and they wrap
   to their own bottom line when the menu is too wide (phones). */
.nav-tail { margin-left: auto; display: flex; gap: .8rem; align-items: center; }
.btn-row { display: flex; gap: .6rem; flex-wrap: wrap; align-items: center; }
.btn-row .button { margin-top: 0; flex: 1; text-align: center; }
.copy-src { display: none; }
.panel-wide { max-width: 880px; margin: 0 auto; }
.siupin-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; }
.siupin-head .button { margin-top: 0; }
.siupin-scroll { overflow-x: auto; }
.siupin-table { width: 100%; border-collapse: collapse; font-size: .95rem; }
.siupin-table th { text-align: left; padding: .5rem .7rem; border-bottom: 2px solid var(--line); font-size: .85rem; }
.siupin-table td { padding: .55rem .7rem; border-bottom: 1px solid var(--line); vertical-align: top; }
.siupin-table tr:last-child td { border-bottom: 0; }
.siupin-table .sub { display: block; font-size: .8rem; color: var(--muted); }
.field select {
  padding: .55rem .7rem;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--card); color: inherit; font-size: 1rem; width: 100%;
}
.field .checkbox { display: flex; gap: .5rem; align-items: center; }
.field .checkbox input { width: auto; }
.pill {
  display: inline-block; padding: .15rem .6rem; border-radius: 999px;
  font-size: .78rem; font-weight: 700; white-space: nowrap;
}
.pill-on { background: var(--brand-tint); color: var(--brand); }
.pill-off { background: #F1EEF4; color: #6A6A75; }
/* One-click hide/publish in the products list (Phase 7 follow-up). */
.row-actions { display: flex; gap: .8rem; align-items: center; white-space: nowrap; }
.row-actions form { display: inline; }
.toggle-btn {
  border: 1px solid var(--line); background: var(--card); color: inherit;
  border-radius: 10px; padding: .25rem .65rem;
  font-size: .82rem; font-weight: 600; cursor: pointer; font-family: inherit;
}
.toggle-btn:hover { border-color: var(--brand); color: var(--brand); }

/* Phase 33: SF waybill input inside row action forms (siupin orders). */
.track-in {
  border: 1px solid var(--line); background: var(--card); color: inherit;
  border-radius: 10px; padding: .25rem .5rem;
  font-size: .82rem; width: 9rem; font-family: inherit;
}
/* Phase 33: waybill deep-link on the customer order page. */
.summary-line a { color: var(--brand); font-weight: 700; }

/* Phase 33 fix: copy-waybill button on the order page (the one owner-approved
   storefront JS bit; without JS the button is inert, the link still works). */
.copy-btn {
  border: 1px solid var(--line); background: var(--card); color: inherit;
  border-radius: 10px; padding: .2rem .6rem; margin-left: .4rem;
  font-size: .8rem; font-weight: 600; cursor: pointer; font-family: inherit;
}
.copy-btn:hover { border-color: var(--brand); color: var(--brand); }

/* Phase 43e: the orders status filter uses the .filter-dd dropdown inside
   the .siupin-head flex row — kill the standalone bottom margin there so it
   lines up with the view chips and the action buttons. */
.siupin-head .filter-dd { margin: 0; }

@media (prefers-color-scheme: dark) {
  .pill-off { background: #26262E; color: #9AA69C; }
}

@media (max-width: 640px) {
  /* Phase 43g: the siupin head stays ONE row on phones — the orders head
     (卡片|表格 chips + 篩選・排序 dropdown + 複製) sits side by side (owner
     ask: 4 btn in one row), slightly tightened so a narrow screen fits.
     flex-wrap stays on as the safety valve if anything ever overflows. */
  .siupin-head { flex-wrap: wrap; justify-content: flex-start; gap: .45rem; }
  .siupin-head .cat-chips { flex-wrap: nowrap; gap: .45rem; margin: 0; }
  .siupin-head .chip { padding: .35rem .7rem; font-size: .88rem; white-space: nowrap; }
  .siupin-head .filter-btn { padding: .38rem .75rem; font-size: .88rem; }
  .siupin-head .toggle-btn { padding: .35rem .7rem; font-size: .88rem; }
  .panel-wide { padding: 1.1rem; }
}

/* ---- Payments (Phase 8: FPS 轉數快 + PayMe) ---- */
.pay-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem;
  margin: 1rem 0; align-items: stretch;
}
.pay-block {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius-card);
  padding: 1.2rem; text-align: center;
  box-shadow: var(--shadow-sm);
}
.pay-block h3 { margin: 0 0 .8rem; }
.qr-box {
  background: #fff; border-radius: 10px; padding: .6rem;
  display: inline-block; width: 100%; max-width: 240px;
}
.qr-box svg { width: 100%; height: auto; display: block; }
.pay-meta { margin: .35rem 0; font-size: .95rem; }
.pay-note { margin: 1rem 0 0; font-size: .95rem; }
.pay-block .button { margin-top: .8rem; }
@media (max-width: 640px) {
  .pay-grid { grid-template-columns: 1fr; }
}

@media (prefers-color-scheme: dark) {
  /* The QR box stays white in dark mode — scanners need the contrast. */
  .qr-box { background: #fff; }
}

/* ---- Payments follow-up (Phase 8): bank transfer + paid notification ---- */
/* The bank block has no QR — it spans the payment grid's full width. */
.pay-block--wide { grid-column: 1 / -1; }
.notify-form { margin: 1.2rem 0 0; }
.notify-form .button { margin-top: 0; }
/* Amber "customer reported payment" pill in the owner order list. */
.pill-notify { background: #FDF3D7; color: #7A5B12; }

@media (prefers-color-scheme: dark) {
  .pill-notify { background: #3A2F14; color: #F0D488; }
}

/* ---- Membership (Phase 9: passkey accounts) ---- */
/* The account page reuses existing pieces: .checkout-grid (register / login
   panels), .panel / .field / .error-box (form), .badge (discount), .pill
   (status), .siupin-table + .siupin-scroll (order history). */
.siupin-table td a { color: inherit; font-weight: 700; }
.siupin-table td a:hover { opacity: .75; }

/* ---- Images (Phase 11: R2 uploads served from /img/<slot>) ---- */
/* Product photos fill the existing .thumb grid cell; emoji stays the fallback. */
.pimg { width: 100%; height: 100%; object-fit: cover; border-radius: var(--radius-soft); display: block; }
.pimg-lg { border-radius: var(--radius-soft); }
.pimg-sm { width: 2.4rem; height: 2.4rem; object-fit: cover; border-radius: 8px;
  display: inline-block; vertical-align: middle; margin-right: .5rem; }
.logo-img { width: 1.7rem; height: 1.7rem; object-fit: contain; border-radius: 6px;
  display: block; }
.hero-img { width: 4.2rem; height: 4.2rem; object-fit: contain; border-radius: 50%; }
.cat-img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius-soft);
  margin-bottom: .7rem; }
.pay-icon { height: 1.6rem; margin: 0 .35rem .2rem 0; vertical-align: middle; }
/* /edit/media — one slot per row: label, current thumb, upload + remove forms. */
.media-slot { display: grid; grid-template-columns: 8rem 6rem 1fr auto;
  align-items: center; gap: 1rem; padding: .8rem 0; border-bottom: 1px solid var(--line); }
.media-slot:last-child { border-bottom: 0; }
.media-label { font-weight: 700; }
.media-thumb { width: 6rem; height: 4.5rem; object-fit: cover; border-radius: 10px; }
.media-form { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
/* ---- Phase 13: product gallery (detail page) + footer pay pills ---- */
/* Server-paged gallery — one photo at a time, ‹ › arrows via ?pic=, zero JS. */
.gallery { position: relative; display: flex; align-items: center; justify-content: center;
  margin: 0 auto 1rem; max-width: 560px; }
.g-main { width: 100%; aspect-ratio: 4 / 3; object-fit: contain; border-radius: var(--radius-card);
  background: var(--card); border: 1px solid var(--line); padding: .4rem; }
.g-arrow { position: absolute; top: 50%; transform: translateY(-50%);
  display: grid; place-items: center; width: 2.4rem; height: 2.4rem; border-radius: 999px;
  background: var(--card); border: 1px solid var(--line); box-shadow: var(--shadow-sm);
  color: var(--ink); text-decoration: none; font-size: 1.25rem; line-height: 1; }
.g-arrow:hover { border-color: var(--brand); color: var(--brand); }
.g-prev { left: .4rem; }
.g-next { right: .4rem; }
.g-thumbs { display: flex; gap: .5rem; justify-content: center; margin: 0 0 1.2rem; flex-wrap: wrap; }
.g-thumb img { width: 4rem; height: 3rem; object-fit: cover; border-radius: 10px;
  border: 1px solid var(--line); display: block; }
.g-thumb.cur img { border-color: var(--brand);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--brand) 45%, transparent); }
/* Footer payment pill (methods without an uploaded icon) — sits on the deep-green
   footer band, so a quiet cream-on-green chip instead of a card. */
.pay-pill { display: inline-block; padding: .18rem .55rem; margin: 0 .3rem .2rem 0;
  border: 1px solid rgba(234, 243, 236, .35); border-radius: 999px; background: rgba(234, 243, 236, .12);
  font-size: .78rem; font-weight: 600; color: var(--foot-ink); vertical-align: middle; }
@media (max-width: 640px) {
  .g-arrow { width: 2rem; height: 2rem; font-size: 1.05rem; }
  .g-prev { left: .2rem; }
  .g-next { right: .2rem; }
  .g-thumb img { width: 3.2rem; height: 2.4rem; }
}
@media (max-width: 640px) {
  .media-slot { grid-template-columns: 1fr 1fr; }
  .media-label { grid-column: 1 / -1; }
}

/* ---------- Phase 17: hero-card mini WYSIWYG (admin, /edit/settings) ---------- */
/* One block per locale: toolbar buttons + the contenteditable field.
   Admin-only chrome — nothing here touches the storefront. */
.hb-editor { display: grid; gap: .4rem; }
.hb-toolbar { display: flex; flex-wrap: wrap; gap: .3rem; }
.hb-btn { min-width: 2.2rem; padding: .25rem .45rem; border: 1px solid var(--line);
  border-radius: 8px; background: var(--card, #fff); color: inherit; font-size: .9rem;
  line-height: 1.2; cursor: pointer; }
.hb-btn:hover { border-color: var(--brand); }
.hb-ed { min-height: 7rem; padding: .6rem .75rem; border: 1px solid var(--line);
  border-radius: 10px; background: var(--card, #fff); overflow-wrap: break-word; }
.hb-ed:focus { outline: 2px solid color-mix(in srgb, var(--brand) 55%, transparent);
  outline-offset: 1px; }
.hb-ed img { max-width: 100%; height: auto; }
.hb-ed:empty::before { content: attr(data-ph); color: var(--muted-ink, #888);
  pointer-events: none; }

/* ---------- Phase 26: checkout delivery-method radios ---------- */
/* One card per method (MTR meetup / local flat mail / SF collect). Plain
   radios, zero JS; the owner's note and the per-method hint render inside
   the label, the SF option carries the official pickup-point link. */
.ship-opts { display: grid; gap: .55rem; margin-bottom: 1.1rem; }
.ship-opt { display: block; padding: .75rem .95rem; border: 1px solid var(--line);
  border-radius: var(--radius-soft); background: var(--card, #fff); cursor: pointer; }
.ship-opt:has(input:checked) { border-color: var(--brand);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand) 45%, transparent); }
.ship-opt input { margin-right: .45rem; accent-color: var(--brand); }
.ship-opt .ship-name { font-weight: 600; }
.ship-opt .ship-fee { color: var(--brand); font-weight: 600; }
.ship-opt .ship-note { display: block; margin-top: .3rem; padding-left: 1.55rem;
  font-size: .9rem; color: var(--muted); }
.ship-opt a { color: var(--brand); }

/* ---------- Phase 27: checkout payment-method radios ---------- */
/* Same card pattern as the delivery radios, for FPS / PayMe / bank
   transfer. The −N% tag marks the methods that carry the owner's
   FPS/PayMe payment discount (bank / card gateways never do). */
.pay-opts { display: grid; gap: .55rem; margin-bottom: 1.1rem; }
.pay-opt { display: block; padding: .75rem .95rem; border: 1px solid var(--line);
  border-radius: var(--radius-soft); background: var(--card, #fff); cursor: pointer; }
.pay-opt:has(input:checked) { border-color: var(--brand);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--brand) 45%, transparent); }
.pay-opt input { margin-right: .45rem; accent-color: var(--brand); }
.pay-opt .pay-name { font-weight: 600; }
.pay-opt .pay-off { color: var(--brand); font-weight: 600; }

/* ---------- Phase 28: settings tabs + membership tiers ---------- */
/* Zero-JS radio tabs for /edit/settings (店主設定 / 折扣 / 送貨方式 /
   雙重認證): the same sibling-selector technique as the hamburger menu.
   Four hidden radios sit before the tab bar and the panels; each label
   toggles its radio, each checked radio reveals its panel and marks its
   label. Each tab is its own form, so no nesting and no cross-tab saves. */
.tabset .tab-radio { position: absolute; width: 1px; height: 1px; opacity: 0; }
.tabs { display: flex; flex-wrap: wrap; gap: .35rem; margin-bottom: 1.2rem;
  border-bottom: 2px solid var(--line); }
.tabs label { padding: .55rem 1rem; margin-bottom: -2px; border-bottom: 2px solid transparent;
  border-radius: var(--radius-soft) var(--radius-soft) 0 0; cursor: pointer;
  font-weight: 600; color: var(--muted); }
.tabs label:hover { background: var(--brand-tint-hover); color: var(--brand); }
.tab-panel { display: none; }
#tab-main:checked ~ .tabs label[for="tab-main"],
#tab-discount:checked ~ .tabs label[for="tab-discount"],
#tab-ship:checked ~ .tabs label[for="tab-ship"],
#tab-members:checked ~ .tabs label[for="tab-members"],
#tab-promos:checked ~ .tabs label[for="tab-promos"],
#tab-2fa:checked ~ .tabs label[for="tab-2fa"] {
  color: var(--brand); background: var(--brand-tint); border-bottom-color: var(--brand); }
#tab-main:checked ~ #panel-main,
#tab-discount:checked ~ #panel-discount,
#tab-ship:checked ~ #panel-ship,
#tab-members:checked ~ #panel-members,
#tab-promos:checked ~ #panel-promos,
#tab-2fa:checked ~ #panel-2fa { display: block; }
/* Phase 29: promo-scheme slots (優惠組合 tab) + the checkout code box. */
.promo-slot { border: 1px solid var(--line); border-radius: var(--radius-soft); padding: .9rem 1rem 0; margin-bottom: 1.4rem; }
.promo-slot legend { font-weight: 600; padding: 0 .35rem; }
.code-form { display: flex; gap: .55rem; align-items: flex-end; margin: 0 0 .8rem; }
.code-form .field { flex: 1; margin: 0; }
.code-form .button { flex-shrink: 0; }
/* Phase 30: discount tab as cards, threshold/pct on one row, code slots as
   fieldsets, and the scheme target list capped to a scrollable box. */
.siupin-card { border: 1px solid var(--line); border-radius: var(--radius-soft); padding: .9rem 1rem 1.2rem; margin-bottom: 1.4rem; }
.tier-row { display: flex; gap: .8rem; }
.tier-row .field { flex: 1; }
.code-slot { border: 1px solid var(--line); border-radius: var(--radius-soft); padding: .9rem 1rem 0; margin-bottom: 1.4rem; }
.code-slot legend { font-weight: 600; padding: 0 .35rem; }
.promo-targets { max-height: 260px; overflow-y: auto; border: 1px solid var(--line); border-radius: var(--radius-soft); padding: .5rem .9rem; }

/* Phase 32: product variants — the storefront picker (a plain <select> on
   the product card + add form) and the siupin matrix table. */
.vk-select {
  width: 100%;
  flex-basis: 100%;
  padding: .5rem .6rem;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--card); color: inherit;
  /* Phase 38: match the site — selects don't inherit font by default, so
     the variant picker used to fall back to the browser's system font
     (wrong family and size next to the brand card button). */
  font-family: inherit; font-size: .95rem; line-height: 1.3;
}
.vk-select option { color: #26302A; background: #fff; }
.addform .vk-select { margin: 0 0 .3rem; }
.variant-note { color: var(--muted); font-size: .95em; display: inline-block; margin-bottom: .5rem; }
.v-matrix { width: 100%; }
.v-matrix .v-label { white-space: nowrap; }
.v-matrix td input[type="text"], .v-matrix td input:not([type]) { width: 110px; }
.v-matrix td .checkbox { display: flex; }

/* ---------- Phase 37: sales & group buys (storefront, zero JS) ---------- */
/* /sale page, the home banner, the card stamps, the struck-through original
   price and the group-buy progress bar. The countdown is server-rendered
   static text — nothing here needs JavaScript. */
.sale-banner {
  display: block; margin: 0 0 1.1rem; padding: .45rem 1rem;
  border-radius: 999px; text-align: center;
  background: var(--brand-tint); color: var(--brand);
  font-family: var(--font-display); font-weight: 700; font-size: 1.02rem;
  text-decoration: none;
}
.sale-banner:hover { background: var(--brand-tint-hover); }
.pcard .thumb { position: relative; }
.sale-stamp {
  position: absolute; top: .5rem; right: .5rem; z-index: 2;
  padding: .22rem .6rem; border-radius: 999px;
  background: var(--brand); color: #fff;
  font-family: var(--font-display); font-weight: 700;
  font-size: .72rem; letter-spacing: .12em; text-transform: uppercase;
  box-shadow: var(--shadow-sm);
}
.price-was { color: var(--muted); font-size: .92em; }
.sale-line { margin: 0 0 .6rem; }
.sale-line .badge { background: var(--brand); color: #fff; }
.sale-until { color: var(--muted); font-size: .95em; }
.sale-progress { margin: 0 0 1.1rem; }
.sale-progress .progress {
  height: .5rem; border-radius: 999px; overflow: hidden;
  background: var(--brand-tint);
}
.sale-progress .progress span { display: block; height: 100%; border-radius: 999px;
  background: var(--brand); }
.progress-note { display: inline-block; margin-top: .3rem;
  font-size: .9rem; color: var(--muted); }
@media (prefers-color-scheme: dark) {
  .sale-banner { background: var(--brand-tint); }
}
@media (max-width: 640px) {
  .sale-banner { font-size: .95rem; padding: .4rem .8rem; }
  .sale-stamp { font-size: .64rem; top: .4rem; right: .4rem; }
}

/* ---------- Phase 39: variant picker popup on product cards ---------- */
/* Multi-variant cards keep the grid clean: the Add-to-cart button is a
   zero-JS <details> summary; the variant select pops up from it (upward
   card, top sheet on phones). Single-variant / no-variant cards and
   the product detail page keep the direct inline form. */
.v-pop { position: relative; display: block; padding: 0 1rem 1rem; }
.v-pop > summary { list-style: none; }
.v-pop > summary::-webkit-details-marker { display: none; }
.v-pop[open] > summary { background: var(--brand); color: #fff; }
/* The hover lift (transform) would make the phone sheet's position: fixed
   resolve against the card (and .pcard overflow: hidden clips it) — freeze
   the lift while the popup is open. :has is in the codebase since Phase 26. */
.pcard:has(.v-pop[open]) { transform: none; }
.v-pop > form {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: calc(100% + .45rem); z-index: 30; /* above static + hovered (transformed) sibling cards */
  display: grid; gap: .5rem; padding: .7rem .75rem;
  width: min(15rem, 100%); /* ≤ card width — .pcard overflow: hidden would clip any overhang */
  background: var(--card); border: 1px solid var(--line);
  border-radius: 14px; box-shadow: var(--shadow-md);
}
.v-pop > form .vk-select { margin: 0; }
.v-pop > form .card-add { margin-top: 0; }
@media (max-width: 640px) {
  /* a phone card column is too narrow for an anchored card — the picker
     becomes a fixed sheet at the TOP of the screen (owner ask 2026-11-03),
     tucked under the sticky header (~4.2rem tall) so it never covers
     the brand / cart / menu bar.
     Owner ask #2: the white box never enclosed the select + button —
     it collapsed into a narrow strip BEHIND them. Root cause: the
     desktop popup anchors with bottom: calc(100% + .45rem); the phone
     rule switches to position: fixed / top: 4.75rem but never reset
     bottom, and top + bottom + height: auto over-constrains a fixed
     element (the solve goes negative → height clamps to 0), so only
     padding + border painted. bottom: auto lets the box hug its
     content; the generous metrics (24rem cap, 1rem padding, tap-target
     select + button) then make it a proper big block. */
  .v-pop { padding: 0 .7rem .7rem; }
  .v-pop > form {
    position: fixed; left: 50%; top: 4.75rem; bottom: auto;
    width: min(24rem, calc(100vw - 1.6rem));
    padding: 1.1rem 1rem 1rem;
    gap: .8rem;
    border-radius: 18px;
  }
  .v-pop > form .vk-select { padding: .9rem 1rem; font-size: 1.05rem; }
  .v-pop > form .card-add { padding: .9rem 1.2rem; font-size: 1.05rem; }
}

/* ---------- Phase 39 follow-up 2: product photo viewer (detail page) ---------- */
/* Owner ask: the uploaded photos are low-res, so the big detail photo looked
   blurred. The detail photo now displays small (crisp at ~340px), and
   clicking it opens the full-size photo in a zero-JS checkbox overlay
   (click anywhere to close). The overlay <img> is pointer-events: none with
   the close <label> on top, so a right-click never targets the photo — no
   "save image as", no "open image in new tab", no drag. Determined users
   can still read the URL from the page source — the web cannot fully
   prevent saving. Product cards are untouched. */
.detail .thumb { max-width: 340px; margin: 0 auto 1.4rem; }
.gallery { max-width: 340px; }
.pic-tog { position: absolute; width: 1px; height: 1px; opacity: 0; }
.pic-open { display: block; width: 100%; height: 100%; cursor: zoom-in; }
.pic-open .pimg, .pic-open .g-main {
  pointer-events: none; -webkit-user-drag: none; user-select: none;
}
.pic-view { position: fixed; inset: 0; z-index: 40; display: none; }
.pic-tog:checked ~ .pic-view { display: grid; overflow: auto; }
.pic-view-bg { position: fixed; inset: 0; cursor: zoom-out;
  background: color-mix(in srgb, #10231A 84%, transparent); }
.pic-view-img { position: relative; z-index: 1; margin: auto;
  pointer-events: none; -webkit-user-drag: none; user-select: none;
  max-width: 92vw; max-height: 86vh; border-radius: 10px;
  box-shadow: var(--shadow-md); }
.pic-view-hint { position: fixed; left: 50%; bottom: 1.1rem;
  transform: translateX(-50%); z-index: 2; pointer-events: none;
  color: var(--foot-ink); font-size: .85rem; letter-spacing: .04em;
  background: rgba(16, 35, 26, .45); padding: .25rem .8rem;
  border-radius: 999px; }

/* ---------- Phase 40 follow-up: desktop passkey tips (account register panel) ---------- */
/* Collapsed <details> under the register button — the same native pattern
   as the Phase 20 filter dropdown. Stuck desktop users tap it open; phone
   registrants (the majority) see a clean panel. */
.tip-dd { margin-top: 1rem; }
.tip-dd > summary {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .4rem 1rem; border: 1px solid var(--line); border-radius: 999px;
  background: var(--card); color: var(--muted); font-size: .88rem; font-weight: 600;
  cursor: pointer; list-style: none; box-shadow: var(--shadow-sm);
  -webkit-user-select: none; user-select: none;
}
.tip-dd > summary::-webkit-details-marker { display: none; }
.tip-dd > summary:hover { border-color: var(--brand); color: var(--brand); }
.tip-dd[open] > summary { border-color: var(--brand); color: var(--brand); background: var(--brand-tint); }
.tip-dd[open] { margin-top: 0; }
.tip-dd p { margin: .5rem 0 0; }

/* ---------- Phase 40 follow-up 2: phone QR on the account tip ---------- */
/* Server-rendered QR of the site's own /account URL — the customer scans
   it with the phone camera and registers (or signs in) on the phone
   itself. Works even when the desktop browser offers no cross-device
   passkey option at all (no Bluetooth / old browser). White tile so the
   code stays scannable in dark mode. */
.tip-qr {
  background: #fff; border: 1px solid var(--line); border-radius: 12px;
  padding: .7rem; width: fit-content; margin: .8rem auto 0;
}
.tip-qr svg { display: block; width: 10.5rem; height: 10.5rem; }

/* ---------- Phase 41: email one-time sign-in link (login panel) ---------- */
/* The desktop fallback under the sign-in button: a member whose passkey
   lives on the phone (and whose PC has no Bluetooth, so no cross-device
   passkey option ever appears there) can still sign in here — a one-time
   link arrives by email, valid for 15 minutes. */
.link-login { margin-top: 1.1rem; padding-top: 1rem; border-top: 1px dashed var(--line); }
.link-login .link-form { display: flex; gap: .6rem; align-items: flex-start; flex-wrap: wrap; }
.link-login .link-form .field { flex: 1 1 12rem; margin: 0; }

/* ---------- Phase 42: order cards + sales analytics (admin, zero JS) ---------- */
/* /edit/orders?view=cards — one rounded card per order (order number, date,
   total, customer, status pills, per-status action buttons, bulk checkbox
   via form="bulkForm"). The classic table stays one chip away. */
.ord-cards { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); margin-bottom: 1rem; }
.ocard {
  display: grid; gap: .5rem; align-content: start;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius-card); padding: 1rem 1.1rem;
  box-shadow: var(--shadow-sm);
}
.ocard-top { display: flex; justify-content: space-between; align-items: center; gap: .5rem; }
.ocard-no { font-family: var(--font-display); font-weight: 600; font-size: 1.15rem; }
.ocard-meta { margin: 0; color: var(--muted); font-size: .85rem; }
.ocard-amt { margin: 0; font-family: var(--font-display); font-weight: 700; font-size: 1.35rem; color: var(--brand); }
.ocard-cust { margin: 0; font-weight: 600; }
.ocard-cust .sub { display: block; font-size: .8rem; color: var(--muted); font-weight: 400; }
.ocard-pills { display: flex; flex-wrap: wrap; gap: .3rem; }
.ocard-actions { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; border-top: 1px dashed var(--line); padding-top: .7rem; }
.ocard-actions form { display: flex; gap: .4rem; align-items: center; flex-wrap: wrap; }
.an-view { margin: 0; }

/* /edit/analytics — summary cards, ranking bars, restock chips. */
.an-metric { margin-top: -.8rem; }
.an-sum { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 1rem; margin: 1.1rem 0 1.6rem; }
.an-stat {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius-card); padding: 1rem 1.2rem;
  box-shadow: var(--shadow-sm);
}
.an-stat .k { margin: 0; font-size: .85rem; font-weight: 600; color: var(--muted); }
.an-stat .v { margin: .25rem 0 0; font-family: var(--font-display); font-weight: 700; font-size: 1.55rem; color: var(--brand); }
.an-stat .v.an-warn { color: #A06A12; }
/* Phase 43d: linked summary cards (已付款/待付款/待出貨 → filtered orders). */
a.an-stat { text-decoration: none; color: inherit; }
a.an-stat:hover { border-color: var(--brand); box-shadow: var(--shadow-md); }
.an-chart { display: grid; gap: .55rem; margin-bottom: 1.6rem; }
.an-row { display: grid; grid-template-columns: minmax(6rem, 13rem) 1fr auto; align-items: center; gap: .7rem; }
.an-name { font-weight: 600; font-size: .95rem; overflow-wrap: break-word; }
.an-bar {
  display: block; height: 1.05rem; border-radius: 999px;
  background: var(--brand-tint); overflow: hidden;
}
.an-bar span { display: block; height: 100%; border-radius: 999px; }
.an-val { font-weight: 700; font-size: .92rem; white-space: nowrap; }
.an-restock { display: flex; flex-wrap: wrap; gap: .6rem; }
.rs-card {
  display: grid; gap: .2rem; justify-items: start;
  border: 1px solid var(--line); background: var(--card);
  border-radius: var(--radius-soft); padding: .6rem .95rem;
  box-shadow: var(--shadow-sm); color: inherit; text-decoration: none;
  transition: border-color .15s ease;
}
.rs-card:hover { border-color: var(--brand); }
.rs-card .stock-note { margin: 0; }
@media (max-width: 640px) {
  .ord-cards { grid-template-columns: 1fr; }
  .an-row { grid-template-columns: 1fr; gap: .25rem; }
  .an-val { justify-self: end; }
  .an-stat .v { font-size: 1.3rem; }
}
@media (prefers-color-scheme: dark) {
  .an-stat .v.an-warn { color: #D9A84E; }
}
