/* Toppi Mini App — "Vintage Darkroom" design system (concept 8B).
   Principles kept from Editorial: one ink + ONE accent, a single spacing/type scale,
   grouping by space (hairlines), one minimalist line-icon set. The voice changed:
   a photographer's darkroom — warm near-black, one safelight-red accent, typewriter
   type, aged-ivory polaroid cards on a warm charcoal matte.
   The palette is now the app's own (Telegram's injected theme no longer drives it —
   the darkroom look IS the brand). Dark is the default; systems that prefer light get
   the "lights on" workroom variant below. */
@font-face {
  font-family: "Special Elite";                 /* self-hosted — the app stays dependency-free */
  src: url("./fonts/special-elite.woff2") format("woff2");
  font-display: swap;
}
:root {
  --bg:        #161210;
  --text:      #e8ddcb;
  --hint:      #8d8172;
  --subtitle:  #8d8172;
  --secondary: #282019;
  --destructive: #e0563f;
  --accent: #e2543e;                        /* the single accent: safelight red */
  --paper: #ded4bd;                         /* aged-ivory polaroid frame */
  --paper-edge: rgba(0, 0, 0, .42);
  --matte: #1e1712;                         /* warm charcoal photo matte (never pure black) */
  --stamp: #ff7b1c;                         /* 35mm film date imprint */
  --line: rgba(226, 84, 62, .22);
  --type: "Special Elite", "Courier New", Courier, monospace;
  /* one 8pt spacing scale */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px; --s7: 48px;
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
}
/* lights on — the warm workroom, same voice */
@media (prefers-color-scheme: light) {
  :root {
    --bg: #efe8d9; --text: #3a332b; --hint: #93887a; --subtitle: #93887a;
    --secondary: #e5dcc8; --destructive: #b3402a; --accent: #c2402c;
    --paper: #f3ecdb; --paper-edge: rgba(60, 48, 36, .22); --matte: #2a2118;
    --line: rgba(110, 95, 75, .30);
  }
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--secondary);
  color: var(--text);
  font: 15px/1.55 var(--type);
}

/* app shell — full-height flex column: fixed header, scrolling body, optional bottom tabs.
   The backdrop: a safelight glow pooling from the top + film grain (inline SVG turbulence). */
#app {
  height: var(--tg-viewport-stable-height, 100vh);
  max-width: 480px; margin: 0 auto;
  background:
    radial-gradient(ellipse 90% 42% at 50% 0%, color-mix(in srgb, var(--accent) 9%, transparent), transparent 60%),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .07 0'/></filter><rect width='140' height='140' filter='url(%23n)'/></svg>"),
    var(--bg);
  display: flex; flex-direction: column; overflow: hidden; position: relative;
}

/* the one icon recipe */
svg.ic {
  width: 24px; height: 24px; flex: none; display: block;
  fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}

/* ---------- header ---------- */
.appbar {
  flex: none; background: var(--bg);
  padding: calc(var(--safe-top) + var(--s5)) var(--s5) var(--s3);
  display: flex; align-items: center; gap: var(--s3);
}
.appbar .back { background: none; border: 0; color: var(--text); cursor: pointer; padding: 0; display: grid; place-items: center; }
.appbar .back .big { width: 26px; height: 26px; }
.appbar .brand { font-size: 14px; font-weight: 700; letter-spacing: 3.5px; text-transform: uppercase; flex: 1; }
.appbar .enc { display: inline-flex; align-items: center; gap: 6px; color: var(--subtitle); font-size: 12px; font-weight: 500; }
.appbar .enc svg { width: 15px; height: 15px; stroke: var(--accent); }

/* ---------- scroll body ---------- */
.scroll { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 0 var(--s5) var(--s6); }
.scroll.mb { padding-bottom: 96px; } /* clears the native Telegram MainButton on form screens */

/* ---------- type scale (typed, like a contact-sheet header) ---------- */
.display { font-size: 27px; line-height: 1.2; font-weight: 700; letter-spacing: 3px; text-transform: uppercase; margin: var(--s5) 0 var(--s3); }
.display.sm { font-size: 21px; }
.lede { font-size: 15px; color: var(--subtitle); margin: 0 0 var(--s6); max-width: 38ch; }
.eyebrow { font-size: 11px; font-weight: 700; letter-spacing: 2px; text-transform: uppercase; color: var(--accent); margin: var(--s6) 0 var(--s3); }

/* ---------- focal capture (single emphasis element) ---------- */
.capture {
  width: 100%; border: 0; background: var(--accent); color: #fff; border-radius: 20px;
  padding: var(--s5); display: flex; align-items: center; gap: var(--s4); cursor: pointer; text-align: left;
  font-family: inherit; box-shadow: 0 16px 36px -14px color-mix(in srgb, var(--accent) 80%, transparent);
}
.capture > svg { width: 30px; height: 30px; stroke: #fff; stroke-width: 1.7; }
.capture .cap-tx b { font-size: 18px; font-weight: 700; display: block; }
.capture .cap-tx span { font-size: 13px; opacity: .85; }
.capture .go { margin-left: auto; }
.capture .go svg { width: 22px; height: 22px; stroke: #fff; }

/* outline button rows (alt actions, toolbars) */
.altrow, .actions { display: flex; gap: var(--s3); }
.altrow { margin-top: var(--s3); }
.actions { margin: var(--s4) 0; }
.altrow button, .actions button {
  flex: 1; background: none; border: 1px solid var(--line); color: var(--text); border-radius: 14px;
  padding: var(--s3); font-size: 14px; font-weight: 600; cursor: pointer; font-family: inherit;
  display: flex; align-items: center; justify-content: center; gap: var(--s2);
}
.altrow svg, .actions svg { width: 18px; height: 18px; stroke: var(--subtitle); }

/* ---------- stat line ---------- */
/* wrap: three tiles (products/docs/expiring) don't fit a 375px phone in one row */
.stats { display: flex; flex-wrap: wrap; gap: var(--s5) var(--s7); }
.stat { background: none; border: 0; padding: 0; cursor: pointer; text-align: left; color: inherit; font: inherit; }
.stat .n { font-size: 30px; font-weight: 800; letter-spacing: -1px; line-height: 1; display: block; }
.stat .l { font-size: 13px; color: var(--subtitle); margin-top: var(--s1); display: flex; align-items: center; gap: 6px; }
.stat .l svg { width: 15px; height: 15px; stroke: var(--subtitle); }

/* ---------- lists (hairline separators, no cards) ---------- */
.list { display: flex; flex-direction: column; }
.row {
  display: flex; align-items: center; gap: var(--s4); padding: var(--s4) 0; cursor: pointer;
  border: 0; border-top: 1px solid var(--line); width: 100%; background: none; text-align: left; color: inherit; font: inherit;
}
.list .row:first-child { border-top: 0; }
.row .th { width: 44px; height: 44px; border-radius: 12px; background: var(--secondary); display: grid; place-items: center; flex: none; }
.row .th svg { width: 22px; height: 22px; stroke: var(--text); }
.row .th img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
.row .m { flex: 1; min-width: 0; }
.row .m b { font-weight: 600; font-size: 16px; display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.row .m small { color: var(--subtitle); font-size: 13px; }
.row .pr { font-weight: 700; font-size: 15px; }
.row .chev { color: var(--hint); }
.row .chev svg { width: 18px; height: 18px; stroke: var(--hint); transform: rotate(180deg); }
.row.locked { opacity: .5; cursor: default; }

.chip { font-size: 11px; font-weight: 700; padding: 4px 10px; border-radius: 20px; background: var(--secondary); color: var(--subtitle); margin-left: auto; }
.chip.hi { background: color-mix(in srgb, var(--destructive) 15%, transparent); color: var(--destructive); }
.chip.me { background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent); }

/* ---------- search ---------- */
.search { display: flex; align-items: center; gap: var(--s3); border: 1px solid var(--line); border-radius: 14px; padding: var(--s3) var(--s4); margin-bottom: var(--s3); }
.search svg { width: 19px; height: 19px; stroke: var(--hint); }
.search input { border: 0; background: none; color: var(--text); font-size: 15px; width: 100%; outline: none; font-family: inherit; }

/* ---------- forms / review ---------- */
.badge { display: inline-flex; align-items: center; gap: 7px; color: var(--accent); font-size: 12px; font-weight: 700; letter-spacing: .4px; margin: var(--s3) 0 var(--s5); }
.badge svg { width: 16px; height: 16px; stroke: var(--accent); }
/* the scan came back full — a warning, not a result, so it wraps and reads as a sentence */
.badge.warn { display: flex; align-items: flex-start; color: var(--text); font-weight: 500;
  letter-spacing: 0; line-height: 1.5; margin-top: calc(-1 * var(--s4));
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border-radius: 10px; padding: var(--s3); }
.badge.warn svg { flex: none; margin-top: 2px; }
.revhead { display: flex; gap: var(--s4); align-items: center; margin-bottom: var(--s5); }
.revhead .im { width: 60px; height: 60px; border-radius: 16px; background: var(--secondary); display: grid; place-items: center; flex: none; }
.revhead .im svg { width: 30px; height: 30px; stroke: var(--text); }
.revhead b { font-size: 20px; font-weight: 700; letter-spacing: -.3px; }
.revhead .conf { color: var(--accent); font-size: 12px; font-weight: 600; margin-top: 2px; display: flex; align-items: center; gap: 5px; }
.dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); display: inline-block; }

/* Phase 15: the product photo on the review/edit card */
.pimg-wrap { margin-bottom: var(--s4); border-radius: 16px; overflow: hidden; background: var(--secondary); }
.pimg { display: block; width: 100%; max-height: 260px; object-fit: cover; }

.fields { margin-top: var(--s4); }
.fld { padding: var(--s4) 0; border-top: 1px solid var(--line); }
.fld:first-child { border-top: 0; }
.fld label { font-size: 11px; font-weight: 700; letter-spacing: .6px; text-transform: uppercase; color: var(--subtitle); display: flex; justify-content: space-between; align-items: center; }
.fld label .ai { color: var(--accent); letter-spacing: .3px; }
.fld input, .fld textarea {
  width: 100%; border: 0; background: none; color: var(--text); font-size: 17px; outline: none;
  margin-top: 6px; font-family: inherit; resize: vertical; line-height: 1.4;
}
/* Simple mode: the editor's secondary fields fold behind this disclosure. Styled like a
   .fld row so the closed summary reads as one more line of the same form. */
.morefields { border-top: 1px solid var(--line); }
.morefields > summary {
  cursor: pointer; user-select: none; padding: var(--s4) 0;
  font-size: 11px; font-weight: 700; letter-spacing: .6px; text-transform: uppercase; color: var(--subtitle);
}
.morefields > summary::after { content: " ▸"; }
.morefields[open] > summary::after { content: " ▾"; }
.morefields > .fld:first-of-type { border-top: 0; padding-top: 0; }

.warnbox { font-size: 13px; line-height: 1.45; padding: 10px 12px; border-radius: 10px; background: rgba(255, 159, 10, .12); border: 1px solid rgba(255, 159, 10, .35); margin: 0 0 var(--s5); }
.note { font-size: 13px; color: var(--subtitle); display: flex; align-items: center; gap: 7px; margin-top: var(--s5); }
.note svg { width: 15px; height: 15px; stroke: var(--accent); }
.meta-line { color: var(--subtitle); font-size: 14px; margin-bottom: var(--s4); }
pre { white-space: pre-wrap; word-break: break-word; background: var(--secondary); padding: var(--s4); border-radius: 14px; max-height: 40vh; overflow: auto; font-size: 14px; line-height: 1.5; margin: 0; }

/* ---------- privacy ---------- */
.privlede { font-size: 20px; font-weight: 700; letter-spacing: -.3px; margin: var(--s3) 0 var(--s5); line-height: 1.3; }
.pli { padding: var(--s5) 0; border-top: 1px solid var(--line); display: flex; gap: var(--s4); }
.pli:first-of-type { border-top: 0; }
.pli .in { width: 36px; height: 36px; flex: none; display: grid; place-items: center; }
.pli .in svg { width: 22px; height: 22px; stroke: var(--accent); }
.pli b { font-size: 15px; font-weight: 700; }
.pli p { margin: var(--s1) 0 0; font-size: 14px; color: var(--subtitle); line-height: 1.5; }
.pli a { color: var(--accent); font-weight: 600; text-decoration: none; }

/* destructive */
.reset { margin-top: var(--s6); width: 100%; background: none; border: 1px solid var(--destructive); color: var(--destructive); border-radius: 14px; padding: var(--s4); font-size: 15px; font-weight: 600; cursor: pointer; font-family: inherit; }
.danger-btn { border-color: var(--destructive) !important; color: var(--destructive) !important; }
.danger-btn svg { stroke: var(--destructive) !important; }

/* ---------- empty + skeleton ---------- */
.empty { text-align: center; padding: 56px 24px; color: var(--subtitle); }
/* Simple mode: the empty state carries the primary action itself (empty state = onboarding). */
.empty .capture { margin-top: var(--s5); text-align: left; }
.empty .empty-ic { width: 44px; height: 44px; stroke: var(--hint); margin: 0 auto; opacity: .6; }
.empty b { display: block; color: var(--text); font-size: 17px; font-weight: 700; margin: 14px 0 4px; }
.empty p { margin: 0; font-size: 14px; line-height: 1.5; }

.sk { border-radius: 7px; background: linear-gradient(90deg, var(--secondary), color-mix(in srgb, var(--hint) 16%, var(--secondary)), var(--secondary)); background-size: 200% 100%; animation: sh 1.3s infinite; }
@keyframes sh { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
.sk-row { pointer-events: none; }
.sk-row .m { display: flex; flex-direction: column; }
.sk-l, .sk-s { display: block; }
.sk-l { width: 60%; height: 13px; margin-bottom: 6px; }
.sk-s { width: 35%; height: 11px; }

/* ---------- analyzing state ---------- */
.analyzing-inline { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 56vh; text-align: center; }
.scanbox { width: 150px; height: 150px; border-radius: 24px; background: var(--secondary); position: relative; overflow: hidden; display: grid; place-items: center; }
.scanbox .scan-ic { width: 60px; height: 60px; stroke: var(--hint); stroke-width: 1.3; }
.scanline { position: absolute; left: 12px; right: 12px; height: 2px; background: var(--accent); box-shadow: 0 0 14px var(--accent); animation: scan 1.5s ease-in-out infinite; }
@keyframes scan { 0%, 100% { top: 14%; } 50% { top: 82%; } }
.analyzing-inline b { font-size: 19px; margin-top: var(--s5); font-weight: 700; }
.analyzing-inline p { color: var(--subtitle); font-size: 14px; margin-top: var(--s1); }

/* ---------- web login + web main button (plain browser, outside Telegram) ---------- */
.login { min-height: 70vh; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; }
.login .lede { margin-bottom: var(--s5); }
.web-main-btn {
  position: fixed; left: 50%; transform: translateX(-50%); z-index: 10;
  bottom: calc(var(--safe-bottom) + var(--s4));
  width: min(432px, calc(100% - 2 * var(--s5))); /* app shell is max 480px wide, minus its side padding */
  background: var(--accent); color: #fff; border: 0; border-radius: 16px;
  padding: var(--s4); font: inherit; font-size: 16px; font-weight: 700; cursor: pointer;
  box-shadow: 0 12px 28px -12px color-mix(in srgb, var(--accent) 80%, transparent);
}
.web-main-btn[hidden] { display: none; }

/* ---------- multi-item scan review (Phase 20) ---------- */
.mrow { display: flex; align-items: center; gap: var(--s3); padding: var(--s3) 0; border-top: 1px solid var(--line); }
.mrow:first-child { border-top: 0; }
.mthumb { width: 56px; height: 56px; object-fit: cover; border-radius: 12px; flex: none; background: var(--secondary); }
.mfields { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.mi { border: 0; background: none; color: var(--text); font-size: 15px; font-weight: 600; outline: none; width: 100%; font-family: inherit; }
.mi.sm { font-size: 13px; font-weight: 400; color: var(--subtitle); }
.mcheck { width: 20px; height: 20px; flex: none; accent-color: var(--accent); }

/* ---------- catalog ("Curator", concept 4E): ledge grid / category shelves + filter sheet ---------- */
.cat-toolbar { display: flex; align-items: center; gap: var(--s2); margin-bottom: var(--s3); }
.cat-toolbar .search { flex: 1; margin-bottom: 0; }
.vbtn { border: 1px solid var(--line); background: none; color: var(--hint); border-radius: 12px; padding: 10px; cursor: pointer; }
.vbtn.on { color: var(--text); border-color: var(--text); }
.vbtn svg { width: 19px; height: 19px; }
.fbtn { position: relative; border: 1px solid var(--line); background: none; color: var(--text); border-radius: 12px; padding: 10px 12px; font-family: inherit; font-size: 13px; font-weight: 700; cursor: pointer; display: flex; gap: 6px; align-items: center; }
.fbtn svg { width: 17px; height: 17px; stroke: var(--subtitle); }
.fbtn .badge-n { position: absolute; top: -6px; right: -6px; background: var(--accent); color: #fff; font-size: 10px; font-weight: 800; border-radius: 50%; width: 16px; height: 16px; display: grid; place-items: center; }
.fchips { display: flex; gap: var(--s2); flex-wrap: wrap; margin-bottom: var(--s3); }
.fchip { border: 0; background: var(--secondary); color: var(--text); padding: 5px 11px; border-radius: 14px; font-size: 12px; font-weight: 600; cursor: pointer; font-family: inherit; }
.fchip span { color: var(--hint); margin-left: 4px; }

/* "Vintage darkroom" polaroids (concepts 7D + 8B): every card the SAME square polaroid —
   aged-ivory frame, warm charcoal matte window, the photo letterboxed with object-fit:
   contain so the object is NEVER cropped or distorted; the label is typed on the chin.
   minmax(0,1fr) columns + min-width:0 let the nowrap labels ellipsize instead of
   widening the grid; the tilt is a paint-only transform (it can't cause sideways scroll). */
.plankrow { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s3); margin-bottom: var(--s4); }
.print { min-width: 0; border: 0; cursor: pointer; font: inherit; color: inherit;
  display: flex; flex-direction: column; background: var(--paper); padding: 6px 6px 0; border-radius: 2px;
  box-shadow: 0 0 0 1px var(--paper-edge), 0 2px 6px rgba(0, 0, 0, .4), 0 12px 16px -8px rgba(0, 0, 0, .55);
  transition: transform .15s ease, box-shadow .15s ease; }
/* the tilt keys on the plank SLOT: the phone puts .print straight in the row, the desktop
   wraps it in .printwrap (so the pencil can overlay a button without nesting buttons) —
   :nth-child on the direct child covers both. */
.plankrow > :nth-child(3n+1) .print, .plankrow > .print:nth-child(3n+1) { transform: rotate(-1.4deg); }
.plankrow > :nth-child(3n+2) .print, .plankrow > .print:nth-child(3n+2) { transform: rotate(.6deg); }
.plankrow > :nth-child(3n) .print, .plankrow > .print:nth-child(3n) { transform: rotate(1.3deg); }
.plankrow .print:hover, .print:hover { transform: rotate(0) translateY(-4px);
  box-shadow: 0 0 0 1px var(--paper-edge), 0 4px 10px rgba(0, 0, 0, .45), 0 20px 24px -10px rgba(0, 0, 0, .65); }
/* desktop selection: the polaroid straightens and takes an accent ring; the pencil is
   hover-revealed (always visible where hover doesn't exist). `editing` marks the item
   currently open in the panel — a different question than "selected", so a different mark. */
.printwrap { position: relative; min-width: 0; }
.print { width: 100%; user-select: none; }
.print.sel { outline: 3px solid var(--accent); outline-offset: 3px; }
.plankrow .print.sel { transform: rotate(0); }   /* outranks the tilt's 3-class selector */
.print.editing { box-shadow: 0 0 0 1px var(--paper-edge), 0 0 0 3px color-mix(in srgb, var(--stamp) 60%, transparent), 0 2px 6px rgba(0, 0, 0, .4); }
.cedit { position: absolute; top: 10px; right: 10px; z-index: 2; width: 30px; height: 30px;
  display: grid; place-items: center; border: 0; border-radius: 50%; cursor: pointer;
  background: rgba(20, 14, 9, .78); opacity: 0; transition: opacity .12s ease; }
.cedit svg { width: 14px; height: 14px; stroke: var(--paper); }
.printwrap:hover .cedit, .cedit:focus-visible { opacity: 1; }
@media (hover: none) { .cedit { opacity: 1; } }
/* the matte window is a HARD square: the photo is absolutely positioned inside it so no
   image aspect can stretch the card; the inner vignette dissolves the photo's edge into
   the matte instead of a hard bright rectangle. */
.print .paper { width: 100%; aspect-ratio: 1; background: var(--matte); position: relative; overflow: hidden; }
.print .paper::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  box-shadow: inset 0 0 18px rgba(22, 14, 8, .55); }
.print .paper .pph { position: absolute; inset: 0; display: grid; place-items: center; }
.print .paper .pph img { width: 100%; height: 100%; object-fit: contain;
  filter: sepia(.16) saturate(.82) contrast(1.02) brightness(.94); }   /* toned into the palette */
.print .paper .pph svg { width: 34px; height: 34px; stroke: var(--hint); opacity: .5; }   /* no-photo placeholder */
/* the 35mm date imprint, burned into the corner of every frame */
.print .dstamp { position: absolute; right: 7px; bottom: 5px; z-index: 1; pointer-events: none;
  font-size: 9px; letter-spacing: 1px; color: var(--stamp); text-shadow: 0 0 5px rgba(255, 123, 28, .8); }
/* the chin: typed name + price. Special Elite has no true bold — the synthesized bold,
   larger size and near-black ink on the ivory paper are what keep it readable. */
.print .chin { height: 44px; display: flex; flex-direction: column; justify-content: center; gap: 2px; text-align: center; padding: 0 3px; }
.print .chin b { font-size: 11px; font-weight: 700; letter-spacing: .4px; text-transform: uppercase; color: #221c14;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.print .chin small { font-size: 11px; font-weight: 700; letter-spacing: .5px; color: #a83b28;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* SHELVES view: the same planks, grouped under category labels */
.shelfsec { margin-bottom: var(--s4); }
.shelf-label { display: flex; align-items: baseline; gap: var(--s2); margin-bottom: var(--s4); }
.shelf-label b { font-size: 12px; font-weight: 800; letter-spacing: 1.6px; text-transform: uppercase; }
.shelf-label small { color: var(--subtitle); font-size: 11px; }

.cat-count { margin-top: var(--s6); text-align: center; font-size: 11px; letter-spacing: 1.4px; text-transform: uppercase; color: var(--subtitle); }
.cat-count b { color: var(--accent); }

/* the sort & filter sheet */
.scrim { position: absolute; inset: 0; background: rgba(0, 0, 0, .35); opacity: 0; pointer-events: none; transition: opacity .2s; z-index: 20; }
.scrim.open { opacity: 1; pointer-events: auto; }
.fsheet { position: absolute; left: 0; right: 0; bottom: 0; background: var(--bg); border-radius: 18px 18px 0 0; box-shadow: 0 -18px 40px rgba(0, 0, 0, .3); padding: var(--s4) var(--s5) calc(var(--safe-bottom) + var(--s6)); transform: translateY(105%); transition: transform .22s; z-index: 21; }
.fsheet.open { transform: none; }
.fsheet .grab { width: 38px; height: 4px; border-radius: 2px; background: var(--line); margin: 0 auto var(--s3); }
.fsheet .eyebrow { margin: var(--s4) 0 var(--s2); }
.optrow { display: flex; gap: var(--s2); flex-wrap: wrap; }
.opt { border: 1px solid var(--line); background: none; color: var(--text); padding: 7px 13px; border-radius: 18px; font-size: 13px; font-weight: 600; cursor: pointer; font-family: inherit; }
.opt.on { background: var(--text); color: var(--bg); border-color: var(--text); }
.range { display: flex; gap: var(--s3); align-items: center; }
.range input { width: 90px; border: 1px solid var(--line); background: none; color: var(--text); border-radius: 10px; padding: 8px 10px; font-size: 14px; outline: 0; font-family: inherit; }
.range span { color: var(--subtitle); font-size: 13px; }
.apply { width: 100%; margin-top: var(--s5); border: 0; background: var(--accent); color: #fff; font-size: 15px; font-weight: 700; padding: 13px; border-radius: 12px; cursor: pointer; font-family: inherit; }

/* ---------- keep/leave verdict, shown wherever an item is ----------
   Green for keep, the destructive red for leave — the same pairing the deck buttons use,
   so the badge reads as "the side you swiped". */
.sbadge { font-size: 10px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase; padding: 3px 8px; border-radius: 20px; white-space: nowrap; }
.sbadge.keep { background: rgba(74, 157, 106, .9); color: #fff; }
.sbadge.leave { background: var(--destructive); color: #fff; }
/* On a polaroid the badge pins to the photo's top-left, opposite the date imprint. */
.paper .sbadge { position: absolute; top: 7px; left: 7px; z-index: 2; box-shadow: 0 2px 6px rgba(0, 0, 0, .45); }
/* Food gallery: expiry urgency in the same badge slot the catalog uses for verdicts. */
.sbadge.fexp { background: rgba(90, 90, 96, .85); color: #fff; }
.sbadge.fexp.me { background: var(--accent); color: #fff; }
.sbadge.fexp.hi { background: var(--destructive); color: #fff; }
.dtable .muted { color: var(--hint); opacity: .6; }

/* the edit forms' 3-way verdict picker */
.segrow { display: flex; gap: var(--s2); }
.seg { flex: 1; border: 1px solid var(--line); background: none; color: var(--hint); border-radius: 10px; padding: 9px 6px; font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; }
.seg:hover { border-color: var(--hint); color: var(--text); }
.seg.on { border-color: var(--accent); color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }

/* ---------- declutter game ("Sort") ----------
   Setup screen, then a card stack. Deliberately ONE layout for phone and desktop: a swipe
   deck is already a single-column, one-thing-at-a-time screen, so the 1024px workspace
   split the catalog needs would only make the cards smaller. */
.gfilters { display: flex; gap: var(--s3); margin-bottom: var(--s3); }
.gf { flex: 1; display: flex; flex-direction: column; gap: 5px; }
.gf span { font-size: 12px; color: var(--subtitle); font-weight: 600; }
.gf input { border: 1px solid var(--line); background: none; color: var(--text); border-radius: 10px; padding: 9px 11px; font-size: 15px; outline: 0; font-family: inherit; width: 100%; }
.gf input:focus { border-color: var(--accent); }
.gchips { display: flex; gap: var(--s2); flex-wrap: wrap; margin-bottom: var(--s4); }
.grow-check { display: flex; align-items: center; gap: var(--s2); font-size: 13px; color: var(--subtitle); margin-bottom: var(--s5); cursor: pointer; }
.grow-check input { accent-color: var(--accent); width: 17px; height: 17px; }

.deck { display: flex; flex-direction: column; min-height: 100%; }
.deckhead { display: flex; align-items: center; justify-content: space-between; font-size: 12px; font-weight: 700; letter-spacing: 1.5px; text-transform: uppercase; color: var(--subtitle); margin-bottom: var(--s4); }
.linkish { background: none; border: 0; color: var(--accent); font: inherit; font-size: 12px; font-weight: 700; letter-spacing: 1.5px; text-transform: uppercase; cursor: pointer; padding: 0; }

/* The stack is a positioning context: cards overlap, the live one on top. */
.stack { position: relative; flex: 1; min-height: 400px; }
.gcard {
  position: absolute; inset: 0; display: flex; flex-direction: column; gap: var(--s3);
  background: var(--secondary); border: 1px solid var(--line); border-radius: 18px;
  padding: var(--s4); box-shadow: 0 18px 40px -24px rgba(0, 0, 0, .7);
  touch-action: pan-y;              /* let the page scroll vertically; we own horizontal */
  user-select: none; cursor: grab;
}
.gcard.behind { transform: scale(.94) translateY(10px); opacity: .5; pointer-events: none; }
.gcard.live { transition: transform .18s ease-out; }
.gcard.dragging { transition: none; cursor: grabbing; }
.gph { flex: 1; min-height: 0; display: grid; place-items: center; background: #0d0a09; border-radius: 12px; overflow: hidden; }
.gph img { width: 100%; height: 100%; object-fit: contain; }   /* contain: never crop the item */
.gph svg { width: 46px; height: 46px; stroke: var(--hint); opacity: .5; }
.gmeta b { font-size: 19px; font-weight: 700; display: block; line-height: 1.25; }
.gsub { font-size: 13px; color: var(--subtitle); margin-top: 3px; }
.gwas { font-size: 12px; color: var(--accent); margin-top: 5px; font-weight: 600; }
.gwhy { display: flex; gap: var(--s2); }
.whybtn { flex: 1; border: 1px solid var(--line); background: none; color: var(--hint); border-radius: 10px; padding: 9px; font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; }
.whybtn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.whybtn:disabled { opacity: .5; cursor: default; }
.gout { font-size: 14px; line-height: 1.5; color: var(--text); background: var(--bg); border-radius: 10px; padding: var(--s3); border: 1px solid var(--line); }

/* Drag feedback: the whole card tints toward the side it's heading for. */
.gcard.to-keep { border-color: #4a9d6a; box-shadow: 0 0 0 2px rgba(74, 157, 106, .35), 0 18px 40px -24px rgba(0, 0, 0, .7); }
.gcard.to-leave { border-color: var(--destructive); box-shadow: 0 0 0 2px color-mix(in srgb, var(--destructive) 35%, transparent), 0 18px 40px -24px rgba(0, 0, 0, .7); }
.gcard.fly-keep { transform: translateX(130%) rotate(18deg); opacity: 0; }
.gcard.fly-leave { transform: translateX(-130%) rotate(-18deg); opacity: 0; }

.deckacts { display: flex; gap: var(--s3); margin-top: var(--s4); }
.swipebtn { flex: 1; display: flex; align-items: center; justify-content: center; gap: var(--s2); border: 1px solid var(--line); background: none; border-radius: 14px; padding: 14px; font: inherit; font-size: 15px; font-weight: 700; cursor: pointer; color: var(--text); }
.swipebtn svg { width: 20px; height: 20px; }
.swipebtn.keep { color: #4a9d6a; border-color: rgba(74, 157, 106, .45); }
.swipebtn.keep svg { stroke: #4a9d6a; }
.swipebtn.leave { color: var(--destructive); border-color: color-mix(in srgb, var(--destructive) 45%, transparent); }
.swipebtn.leave svg { stroke: var(--destructive); }
.swipebtn:hover { background: var(--secondary); }

/* Respect reduced-motion: the card still leaves, it just doesn't fly. */
@media (prefers-reduced-motion: reduce) {
  .gcard.live { transition: none; }
  .gcard.fly-keep, .gcard.fly-leave { transform: none; }
}

/* ---------- bottom tab bar ---------- */
.tabbar { flex: none; background: var(--bg); border-top: 1px solid var(--line); padding: var(--s2) var(--s3) calc(var(--safe-bottom) + var(--s2)); display: flex; }
.tab { flex: 1; background: none; border: 0; color: var(--hint); cursor: pointer; padding: 6px 0; font-family: inherit; display: flex; flex-direction: column; align-items: center; gap: 4px; font-size: 11px; font-weight: 600; }
.tab svg { width: 22px; height: 22px; }
.tab.active { color: var(--accent); }

/* ==================================================================================
   DESKTOP (concept 6D). The phone UI above is untouched: below 1024px the sidebar and
   editor panel are display:none and #app stays the 480px flex column. At ≥1024px the
   shell becomes a grid (sidebar | appbar/scroll | editor panel), the tab bar hides,
   and renderProducts() switches to the desktop screen (gallery / table / places).
   ================================================================================== */

/* sidebar — hidden on phones; the tab bar's desktop replacement */
.sidebar { display: none; }

/* desktop-only components (only ever rendered by the desktop screens, so these rules
   are global — a mid-resize frame still looks right until the re-render lands) */
.seg { display: flex; background: var(--secondary); border-radius: 12px; padding: 3px; }
.seg button { display: flex; align-items: center; gap: 6px; padding: 7px 13px; border: 0; border-radius: 9px; font: inherit; font-size: 13px; font-weight: 600; color: var(--hint); background: none; cursor: pointer; }
.seg button svg { width: 16px; height: 16px; }
.seg button.on { background: var(--bg); color: var(--text); box-shadow: 0 1px 4px rgba(0, 0, 0, .12); }

.dtoolbar { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; padding: var(--s3) 0; position: sticky; top: 0; background: var(--bg); z-index: 3; border-bottom: 1px solid var(--line); margin-bottom: var(--s3); }
.dtoolbar .search { flex: 1 1 240px; min-width: 200px; margin-bottom: 0; }
.dtoolbar .search kbd { font: 600 11px/1 inherit; color: var(--hint); border: 1px solid var(--line); border-radius: 5px; padding: 3px 6px; }
.dtoolbar .dsort { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--subtitle); }
.dtoolbar select { background: var(--secondary); border: 0; border-radius: 10px; padding: 9px 10px; font: inherit; font-size: 13px; font-weight: 600; color: var(--text); outline: 0; }
.dtoolbar .actions { margin: 0; }
.dtoolbar .actions button { flex: none; padding: 9px 14px; }

.dchips { display: flex; gap: var(--s2); flex-wrap: wrap; align-items: center; margin-bottom: var(--s4); }
.dchips .lab { font-size: 11px; font-weight: 700; letter-spacing: 1.2px; text-transform: uppercase; color: var(--subtitle); margin-right: var(--s1); }
.dchip { border: 1px solid var(--line); background: none; color: var(--hint); border-radius: 20px; padding: 5px 13px; font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; }
.dchip:hover { border-color: var(--hint); }
.dchip.on { background: color-mix(in srgb, var(--accent) 12%, transparent); border-color: var(--accent); color: var(--accent); }
/* Advisory chip: a phone-only filter is set but not applied in this view (click to clear). */
.dchip.warn { margin-left: auto; border-style: dashed; border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); color: var(--subtitle); font-weight: 500; }
.dchip.warn:hover { color: var(--accent); border-color: var(--accent); }
/* Second tier: the selected group's leaves. Full-width so it wraps onto its own row, and
   recessed so the eye reads group → leaf rather than one undifferentiated pile of chips. */
.dsubchips { flex-basis: 100%; display: flex; gap: var(--s2); flex-wrap: wrap; align-items: center;
  margin-top: var(--s2); padding-left: var(--s3); border-left: 2px solid var(--line); }
.dsubchips .dchip { font-size: 12px; padding: 4px 11px; font-weight: 500; }
/* "Needs review" counter — items the taxonomy migration could only file to a group. */
#dreview b { font-variant-numeric: tabular-nums; margin-left: 4px; }

/* Category picker: grouped <select> + the "＋ New category…" escape hatch it reveals. */
.catsel, .catnew { width: 100%; }
.catnew { margin-top: var(--s2); }

/* bulk action bar — appears in table view when rows are checked */
/* Floating action bar: FIXED, never in flow — appearing mid-interaction must not shift
   the grid under the cursor (it used to swallow the second click of every double-click). */
.bulkbar { position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%); z-index: 40;
  max-width: min(92vw, 720px); display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  padding: var(--s2) var(--s3); font-size: 13px; border-radius: 12px;
  background: color-mix(in srgb, var(--accent) 12%, var(--bg));
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  box-shadow: 0 10px 28px -8px rgba(0, 0, 0, .55); }
.bulkbar[hidden] { display: none; }
.bulkbar b { color: var(--accent); }
.bulkbar label { display: flex; align-items: center; gap: 6px; color: var(--subtitle); }
.bulkbar select { background: var(--bg); border: 1px solid var(--line); border-radius: 8px; padding: 5px 8px; font: inherit; font-size: 13px; font-weight: 600; color: var(--text); }
.bulkbar .spacer { flex: 1; }
.bulkbar .bulkdel { border: 0; background: none; color: var(--destructive); font: inherit; font-size: 13px; font-weight: 700; cursor: pointer; }
.bulkbar .bclear { border: 0; background: none; cursor: pointer; display: grid; place-items: center; padding: 4px; }
.bulkbar .bclear svg { width: 14px; height: 14px; stroke: var(--subtitle); }
.bulkbar .bclear:hover svg { stroke: var(--text); }

/* data table */
.dtable { width: 100%; border-collapse: collapse; font-size: 14px; }
.dtable thead th { position: sticky; top: 0; background: var(--bg); z-index: 2; text-align: left; font-size: 11px; font-weight: 700; letter-spacing: 1.1px; text-transform: uppercase; color: var(--subtitle); padding: 10px var(--s3); border-bottom: 1px solid var(--line); white-space: nowrap; user-select: none; }
.dtable thead th[data-sort] { cursor: pointer; }
.dtable thead th[data-sort]:hover { color: var(--text); }
.dtable thead th .dir { color: var(--accent); }
.dtable tbody td { padding: 8px var(--s3); border-bottom: 1px solid var(--line); vertical-align: middle; }
/* rows are click-to-select targets now, so a Shift+click must not smear a text selection */
.dtable tbody { user-select: none; }
.dtable tbody tr { cursor: pointer; }
.dtable tbody tr:hover { background: color-mix(in srgb, var(--hint) 8%, transparent); }
/* .sel = in the multi-select; .editing = open in the panel (opposite edges, so both read) */
.dtable tbody tr.sel { background: color-mix(in srgb, var(--accent) 9%, transparent); box-shadow: inset 3px 0 0 var(--accent); }
.dtable tbody tr.editing { box-shadow: inset -3px 0 0 var(--stamp); }
.dtable tbody tr.sel.editing { box-shadow: inset 3px 0 0 var(--accent), inset -3px 0 0 var(--stamp); }
.dtable .rowedit { border: 0; background: none; cursor: pointer; display: grid; place-items: center;
  width: 28px; height: 28px; border-radius: 8px; opacity: 0; transition: opacity .12s ease; }
.dtable .rowedit svg { width: 14px; height: 14px; stroke: var(--subtitle); }
.dtable .rowedit:hover { background: color-mix(in srgb, var(--hint) 14%, transparent); }
.dtable .rowedit:hover svg { stroke: var(--text); }
.dtable tbody tr:hover .rowedit, .dtable .rowedit:focus-visible { opacity: 1; }
@media (hover: none) { .dtable .rowedit { opacity: 1; } }
.dtable .tthumb { width: 38px; height: 38px; border-radius: 8px; background: var(--secondary); display: grid; place-items: center; overflow: hidden; }
.dtable .tthumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.dtable .tthumb svg { width: 18px; height: 18px; stroke: var(--hint); opacity: .5; }
.dtable .nm { font-weight: 600; }
.dtable .catchip { display: inline-block; font-size: 12px; font-weight: 600; color: var(--subtitle); border: 1px solid var(--line); border-radius: 14px; padding: 2px 9px; white-space: nowrap; }
.dtable td.place { color: var(--subtitle); white-space: nowrap; }
.dtable td.price { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.dtable td.added { color: var(--subtitle); white-space: nowrap; font-size: 13px; }
.dtable input[type=checkbox] { width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; }

/* places board — rooms as drop targets, items as frameless vignette thumbs (concept 5C) */
.dropstrip { border: 1.5px dashed color-mix(in srgb, var(--hint) 45%, transparent); border-radius: 16px; display: flex; align-items: center; justify-content: center; gap: var(--s3); padding: var(--s3) var(--s4); margin-bottom: var(--s5); color: var(--subtitle); font-size: 13px; transition: border-color .15s ease, background .15s ease; }
.dropstrip svg { width: 22px; height: 22px; stroke: var(--accent); }
.dropstrip b { color: var(--text); }
.dropstrip.over { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); color: var(--text); }
.tray { border: 1.5px dashed color-mix(in srgb, var(--accent) 55%, transparent); border-radius: 16px; padding: var(--s4) var(--s5); margin-bottom: var(--s5); background: color-mix(in srgb, var(--accent) 5%, transparent); }
.tray .rhead b { color: var(--accent); }
/* masonry columns, not a grid: each room card is exactly as tall as its contents, so a
   one-item room no longer stretches to match a hundred-item room in the same row */
.board { columns: 320px; column-gap: var(--s5); }
.room { border: 1px solid var(--line); border-radius: 16px; padding: var(--s4) var(--s5); transition: border-color .15s ease, background .15s ease; break-inside: avoid; margin-bottom: var(--s5); }
.room.over, .tray.over { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
.rhead { display: flex; align-items: baseline; gap: var(--s2); margin-bottom: var(--s3); }
.rhead svg { width: 15px; height: 15px; stroke: var(--hint); align-self: center; }
.rhead b { font-size: 14px; font-weight: 700; }
.rhead .cnt { color: var(--subtitle); font-size: 12px; font-weight: 600; }
.rhead .rhint { margin-left: auto; color: var(--subtitle); font-size: 11px; }
.ritems { display: flex; flex-wrap: wrap; gap: var(--s3); min-height: 64px; align-items: flex-start; }
.vthumb { width: 88px; cursor: grab; text-align: center; position: relative; background: none; border: 0; padding: 0; font: inherit; color: inherit; }
.vthumb:active { cursor: grabbing; }
.vthumb .vph { width: 88px; height: 88px; display: grid; place-items: center; pointer-events: none; }
.vthumb .vph img { width: 88px; height: 88px; object-fit: cover; display: block;
  -webkit-mask-image: radial-gradient(ellipse 78% 78% at 50% 46%, #000 52%, transparent 78%);
          mask-image: radial-gradient(ellipse 78% 78% at 50% 46%, #000 52%, transparent 78%); }
.vthumb .vph svg { width: 32px; height: 32px; stroke: var(--hint); opacity: .5; }
.vthumb .vlbl { display: block; font-size: 9.5px; font-weight: 700; letter-spacing: .5px; text-transform: uppercase; color: var(--subtitle); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 2px; }
.vthumb:hover .vlbl { color: var(--text); }
.vthumb.dragging { opacity: .4; }
/* places-board thumbs are part of the catalog multi-select, same accent ring as cards */
.vthumb.sel .vph { outline: 3px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.vthumb.sel .vlbl { color: var(--accent); }
.roomempty { color: var(--subtitle); font-size: 12px; font-style: italic; padding: var(--s3) 0; }

/* editor panel — grid column 3; width animates open/closed */
.dpanel { display: none; }

/* toast — transient confirmations for desktop actions (save, move, bulk edits) */
.toast { position: fixed; bottom: 22px; left: 50%; transform: translate(-50%, 80px); background: var(--text); color: var(--bg); padding: 10px 18px; border-radius: 12px; font-size: 13px; font-weight: 600; transition: transform .25s ease; z-index: 30; pointer-events: none; }
.toast.on { transform: translate(-50%, 0); }

@media (min-width: 1024px) {
  /* the shell: un-cap the 480px column, place sidebar | appbar+scroll | editor panel */
  #app {
    max-width: none; height: 100vh;
    display: grid;
    grid-template-columns: 236px minmax(0, 1fr) auto;
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas: "side appbar appbar" "side scroll panel";
  }
  /* login / fatal error render without chrome — keep them a centered column */
  #app.bare { display: flex; flex-direction: column; }
  .appbar { grid-area: appbar; }
  .scroll { grid-area: scroll; padding: 0 var(--s6) var(--s6); }
  .tabbar { display: none; }

  .sidebar { grid-area: side; display: flex; flex-direction: column; border-right: 1px solid var(--line); padding: var(--s5) var(--s4) var(--s4); }
  .sidebar .sbrand { font-size: 15px; font-weight: 800; letter-spacing: 2.5px; text-transform: uppercase; padding: 0 var(--s3) var(--s5); }
  .sidebar nav { display: flex; flex-direction: column; gap: 2px; }
  .nitem { display: flex; align-items: center; gap: var(--s3); padding: 9px var(--s3); border: 0; border-radius: 10px; background: none; color: var(--hint); font: inherit; font-weight: 600; font-size: 14px; text-align: left; width: 100%; cursor: pointer; }
  .nitem:hover { background: var(--secondary); color: var(--text); }
  .nitem.active { color: var(--text); background: var(--secondary); }
  .nitem.active svg { stroke: var(--accent); }
  .nitem svg { width: 20px; height: 20px; }
  .sidebar .grow { flex: 1; }
  .scan-cta { display: flex; align-items: center; justify-content: center; gap: var(--s2); border: 0; background: var(--accent); color: #fff; border-radius: 12px; padding: 12px; font: inherit; font-weight: 700; font-size: 14px; cursor: pointer; margin-bottom: var(--s4); box-shadow: 0 6px 14px -8px var(--accent); }
  .scan-cta:hover { filter: brightness(1.06); }
  .scan-cta svg { width: 20px; height: 20px; stroke: #fff; }
  .sidebar .sfoot { display: flex; align-items: center; gap: 6px; color: var(--subtitle); font-size: 12px; padding: var(--s3); border-top: 1px solid var(--line); }
  .sidebar .sfoot svg { width: 14px; height: 14px; stroke: var(--accent); }

  /* keep prose-and-form screens readable — don't let inputs stretch to 1400px */
  .scroll > .fields, .scroll > .list, .scroll > form, .scroll > .capture, .scroll > .altrow,
  .scroll > .stats, .scroll > .note, .scroll > pre, .scroll > .pimg-wrap, .scroll > .actions,
  .scroll > .badge, .scroll > .revhead, .scroll > .warnbox, .scroll > .pli, .scroll > .privlede,
  .scroll > .reset, .scroll > #mlist, .scroll > .search, .scroll > .optrow, .scroll > .meta-line,
  .scroll > #fbody, .scroll > .gout { max-width: 720px; }

  /* the editor panel occupies grid column 3 and animates its width */
  .dpanel { display: flex; grid-area: panel; width: 0; overflow: hidden; border-left: 1px solid transparent; transition: width .22s ease; }
  .dpanel.open { width: 372px; border-left-color: var(--line); }
  .dpanel .dinner { width: 372px; flex: none; overflow-y: auto; padding: var(--s5); }
  .dpanel .dtop { display: flex; align-items: center; margin-bottom: var(--s3); }
  .dpanel .dtop .eyebrow { margin: 0; }
  .dpanel .dtop .pclose { margin-left: auto; width: 30px; height: 30px; border: 0; border-radius: 8px; background: none; color: var(--hint); display: grid; place-items: center; cursor: pointer; }
  .dpanel .dtop .pclose:hover { background: var(--secondary); color: var(--text); }
  .dpanel .dtop .pclose svg { width: 16px; height: 16px; }
  .dpanel .pimg-wrap { margin-bottom: var(--s3); }
  .dpanel .fields { margin-top: 0; }
  .dpanel .dactions { display: flex; gap: var(--s3); margin-top: var(--s5); }
  .dpanel .psave { flex: 1; border: 0; background: var(--accent); color: #fff; border-radius: 12px; padding: 12px; font: inherit; font-weight: 700; cursor: pointer; }
  .dpanel .psave:hover { filter: brightness(1.06); }
  .dpanel .pdel { border: 0; background: none; color: var(--destructive); font: inherit; font-weight: 700; cursor: pointer; padding: 12px var(--s4); }

  /* the pinned save button sits over the content column, not the viewport center */
  .web-main-btn { left: calc(50% + 118px); }
}

/* ============================================================================
   PLACES — concept 9A: nested frames, with an optional tree.
   Desktop-only, like the rest of the catalog workspace. Before places had a
   parent this was one flat card per place; a frame inside a frame is the whole
   point, so containment is DRAWN rather than implied by a label.
   ========================================================================== */
@media (min-width: 1024px) {
  .pbar { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; margin-bottom: var(--s4); }
  .pbar .phint { color: var(--subtitle); font-size: 11.5px; margin-left: auto; }
  .seg button[aria-pressed="true"] { background: var(--bg); color: var(--text); box-shadow: 0 1px 4px rgba(0, 0, 0, .12); }
  .dbtn { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--line); background: none;
    color: var(--hint); border-radius: 10px; padding: 8px 13px; font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; }
  .dbtn:hover { border-color: var(--hint); color: var(--text); }
  .dbtn svg { width: 15px; height: 15px; }
  .dbtn.accent { border-color: var(--accent); color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
  .dbtn.danger { border-color: color-mix(in srgb, var(--destructive) 50%, transparent); color: var(--destructive); }

  /* ---- frames ---- */
  /* NOT masonry columns like the old flat board: a frame can contain a frame, and
     column-breaking a nested structure puts a child in a different column from its
     parent — which is precisely the relationship the frame exists to show. */
  .pboard { display: block; }
  .pframe { position: relative; border: 1px solid var(--line); border-radius: 14px;
    padding: var(--s5) var(--s4) var(--s4); margin: var(--s5) 0 0;
    background: color-mix(in srgb, var(--text) 2%, transparent);
    transition: border-color .15s ease, background .15s ease; }
  .pframe[data-depth="1"] { background: color-mix(in srgb, var(--text) 4%, transparent); }
  .pframe[data-depth="2"] { background: color-mix(in srgb, var(--text) 6%, transparent); }
  .pframe.over { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
  /* the name straddles the border, as drawn in the concept sketch */
  .ptag { position: absolute; top: -13px; left: var(--s4); display: inline-flex; align-items: center; gap: var(--s2);
    max-width: calc(100% - 96px); background: var(--bg); border: 1px solid var(--line); border-radius: 8px;
    padding: 3px 11px; font: inherit; font-size: 14px; color: var(--text); cursor: pointer; }
  .pframe[data-depth="0"] > .ptag { font-size: 15px; font-weight: 700; letter-spacing: .4px; }
  .ptag:hover { border-color: var(--accent); }
  .ptag .nm { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .ptag .car { color: var(--subtitle); font-size: 10px; flex: none; }
  .ptag .cnt { color: var(--subtitle); font-size: 12px; font-weight: 600; flex: none; }
  .ptag .cnt b { color: var(--accent); }
  .pedit { position: absolute; top: -13px; right: var(--s4); background: var(--bg); border: 1px solid var(--line);
    border-radius: 8px; padding: 3px 8px; color: var(--subtitle); cursor: pointer; display: grid; place-items: center; }
  .pedit:hover { border-color: var(--accent); color: var(--accent); }
  .pedit svg { width: 14px; height: 14px; }
  /* how much of this place's total is held by the places inside it */
  .psplit { height: 2px; background: var(--line); margin: 0 0 var(--s3); border-radius: 2px; }
  .psplit i { display: block; height: 2px; background: var(--accent); opacity: .75; border-radius: 2px; }
  .pkids { display: grid; gap: var(--s5); margin-top: var(--s4); }
  .rmore { align-self: center; color: var(--subtitle); font-size: 12px; font-weight: 600; }

  /* a place with no children starts compact and promotes to a frame on click */
  .pzones { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: var(--s2); margin-top: var(--s3); }
  .pzone { display: flex; align-items: center; gap: var(--s2); text-align: left; font: inherit; color: inherit;
    border: 1px solid var(--line); border-radius: 10px; padding: 9px var(--s3); background: none; cursor: pointer; }
  .pzone:hover, .pzone.over { border-color: var(--accent); }
  .pzone.over { background: color-mix(in srgb, var(--accent) 10%, transparent); }
  .pzone .zt { min-width: 0; flex: 1; }
  .pzone .zn { display: block; font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .pzone .zc { color: var(--subtitle); font-size: 11px; }
  .pzone .zc b { color: var(--accent); }

  /* ---- tree (style: guides) ---- */
  /* Indentation is a grid SPACER COLUMN, not padding per depth: it reserves the space
     whether or not a chevron exists, so parent and leaf rows line up and the hover/drop
     target still spans the FULL row width. */
  .ptree { --ind: 18px; border-top: 1px solid var(--line); }
  .trow { position: relative; display: grid; align-items: center;
    grid-template-columns: calc(var(--level) * var(--ind)) 22px 1fr auto;
    border-bottom: 1px solid color-mix(in srgb, var(--line) 55%, transparent); min-height: 38px; }
  .trow:hover { background: color-mix(in srgb, var(--text) 4%, transparent); }
  .trow[aria-selected="true"] { background: color-mix(in srgb, var(--accent) 8%, transparent); }
  .trow.over { background: color-mix(in srgb, var(--accent) 18%, transparent); box-shadow: inset 2px 0 0 var(--accent); }
  .ptree.guides .trow .g { position: absolute; top: 0; bottom: 0; width: 1px;
    background: color-mix(in srgb, var(--hint) 45%, transparent); }
  .trow .sp { grid-column: 1; }
  /* the chevron is its OWN target: it folds and never opens the photos */
  .trow .tw { grid-column: 2; background: none; border: 0; padding: 0; height: 100%; cursor: pointer;
    display: grid; place-items: center; position: relative; z-index: 2; font: inherit; font-size: 11px;
    color: color-mix(in srgb, var(--text) 62%, transparent); }
  .trow .tw:hover { color: var(--accent); }
  .trow .tw[data-leaf] { cursor: default; opacity: .35; }
  .trow .tc { grid-column: 3; display: flex; align-items: center; gap: var(--s3); min-width: 0;
    background: none; border: 0; padding: 5px var(--s3) 5px 0; font: inherit; color: inherit; text-align: left; cursor: pointer; }
  .trow .tnm { flex: 1; font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
  .trow[data-depth="0"] .tnm { font-weight: 700; }
  .trow .tn { flex: none; font-size: 12px; font-weight: 600; color: var(--accent); }
  .trow .tn small { color: var(--subtitle); font-weight: 600; }
  .trow .tbar { flex: none; width: 64px; height: 3px; background: var(--line); border-radius: 2px; overflow: hidden; }
  .trow .tbar i { display: block; height: 3px; background: var(--accent); opacity: .7; }
  .trow .tedit { grid-column: 4; position: static; margin-right: var(--s3); opacity: 0; }
  .trow:hover .tedit, .trow .tedit:focus-visible { opacity: 1; }
  .titems { padding: var(--s3) var(--s4) var(--s4) calc(var(--level) * var(--ind) + 22px);
    border-bottom: 1px solid color-mix(in srgb, var(--line) 55%, transparent); }

  /* ---- the place dialog ---- */
  .pdlg { position: fixed; inset: 0; z-index: 60; background: rgba(0, 0, 0, .5);
    display: grid; place-items: center; padding: var(--s5); }
  .pdlg-card { width: min(430px, 100%); background: var(--bg); border: 1px solid var(--line);
    border-radius: 16px; padding: var(--s5); display: flex; flex-direction: column; gap: var(--s3); }
  .pdlg-card h3 { margin: 0; font-size: 17px; }
  .pdlg-card label { display: flex; flex-direction: column; gap: 5px; font-size: 12px;
    font-weight: 700; letter-spacing: .6px; text-transform: uppercase; color: var(--subtitle); }
  .pdlg-card input, .pdlg-card select { background: var(--secondary); border: 1px solid var(--line);
    color: var(--text); border-radius: 10px; padding: 10px 12px; font: inherit; font-size: 14px;
    font-weight: 400; letter-spacing: 0; text-transform: none; }
  .pdlg-card input:focus, .pdlg-card select:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
  .pdlg-note { margin: 0; color: var(--subtitle); font-size: 12px; line-height: 1.5; }
  .pdlg-row { display: flex; align-items: center; gap: var(--s2); margin-top: var(--s2); }
  .pdlg-row .grow { flex: 1; }
}
