/* ==========================================================================
   Aurum — jewelry inventory ledger
   Aesthetic: a jeweler's paper ledger. Warm ivory stock, ink lettering,
   hairline rules, engraved gold. Refined, quiet, high-contrast.
   ========================================================================== */

:root {
  /* paper & ink */
  --paper:        #f6f1e8;
  --paper-2:      #fbf8f2;
  --card:         #fffdf9;
  --ink:          #1a1611;
  --ink-2:        #4a4239;
  --ink-3:        #857a6c;
  --hair:         #e2d8c7;
  --hair-2:       #d3c6b0;

  /* gold */
  --gold:         #ab8322;
  --gold-2:       #c9a227;
  --gold-3:       #e7d29a;
  --gold-wash:    #f3ead2;

  /* category tints: text / wash / edge.
     One muted hue per category, all held to the same low saturation so nine
     badges read as one set rather than a colour wheel. The badge always
     carries its name in text, so the tint groups — it never has to identify. */
  --c-rings-t:     #8d3f56;  --c-rings-w:     #f7e8ec;  --c-rings-e:     #eccfd8;
  --c-necklaces-t: #5f4a7a;  --c-necklaces-w: #efeaf6;  --c-necklaces-e: #ded4ec;
  --c-earrings-t:  #94542e;  --c-earrings-w:  #f9ece3;  --c-earrings-e:  #f0d8c7;
  --c-bracelets-t: #3f5580;  --c-bracelets-w: #e9edf6;  --c-bracelets-e: #d2dcee;
  --c-diamonds-t:  #2f6479;  --c-diamonds-w:  #e6f0f4;  --c-diamonds-e:  #cbe0e8;
  /* Lab grown sits one step green of mined — adjacent enough to read as the
     same family, separable at a glance in a list of both. */
  --c-lab-t:       #2f6f5e;  --c-lab-w:       #e4f1ec;  --c-lab-e:       #c8e2d8;
  --c-watches-t:   #4b4f56;  --c-watches-w:   #ecedef;  --c-watches-e:   #d8dade;
  --c-gold-t:      #8a6414;  --c-gold-w:      #f7ecd2;  --c-gold-e:      #ecdbb0;
  --c-silver-t:    #566a73;  --c-silver-w:    #eaeff1;  --c-silver-e:    #d3dee2;
  --c-other-t:     #4d6349;  --c-other-w:     #eaf0e7;  --c-other-e:     #d5e0d1;

  --danger:       #9c2f26;
  --danger-w:     #fbeae8;

  /* sold: deliberately the most desaturated tint in the set — a sold card
     should read as archive, not as a tenth category. */
  --sold-t:       #6f6558;
  --sold-w:       #efeade;
  --sold-e:       #ddd2be;

  --shadow-1: 0 1px 2px rgba(41,32,18,.05), 0 6px 18px -10px rgba(41,32,18,.22);
  --shadow-2: 0 24px 70px -22px rgba(30,23,10,.42), 0 2px 8px rgba(30,23,10,.10);

  --serif: "Marcellus", "Hoefler Text", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  --sans:  "Archivo", "Helvetica Neue", Helvetica, "Segoe UI", Arial, sans-serif;

  --r-sm: 6px;
  --r-md: 10px;
  --r-lg: 18px;

  --tally-h: 62px;
  --step: cubic-bezier(.2,.7,.2,1);

  color-scheme: light;
}

/* Dark palette. There is exactly one dark block in this file and it hangs off
   `[data-theme="dark"]`, never off the media query: the inline script in
   <head> resolves the user's preference (auto | light | dark) against
   `prefers-color-scheme` and stamps the answer on <html> before first paint.
   So "Auto + system dark" and "Dark" arrive here as the same attribute, and
   "Light" simply never sets it — no rule has to be written twice, and no
   media query can out-vote an explicit choice. */
:root[data-theme="dark"] {
  --paper:      #12100c;
  --paper-2:    #17140f;
  --card:       #1c1813;
  --ink:        #f3ece0;
  --ink-2:      #c3b8a6;
  --ink-3:      #8d8272;
  --hair:       #2e281f;
  --hair-2:     #3d3527;

  --gold:       #d9b657;
  --gold-2:     #e2c469;
  --gold-3:     #6b5620;
  --gold-wash:  #241e11;

  --c-rings-t:     #e2a3b6;  --c-rings-w:     #2b1a20;  --c-rings-e:     #43262f;
  --c-necklaces-t: #c4aee2;  --c-necklaces-w: #211a2c;  --c-necklaces-e: #342844;
  --c-earrings-t:  #e0a87f;  --c-earrings-w:  #271a12;  --c-earrings-e:  #3e2a1d;
  --c-bracelets-t: #a9bde6;  --c-bracelets-w: #171b26;  --c-bracelets-e: #262d3f;
  --c-diamonds-t:  #97cbdf;  --c-diamonds-w:  #14232a;  --c-diamonds-e:  #213b46;
  --c-lab-t:       #92d3bd;  --c-lab-w:       #132420;  --c-lab-e:       #1f3b33;
  --c-watches-t:   #bcc2cb;  --c-watches-w:   #1d1f22;  --c-watches-e:   #2f3238;
  --c-gold-t:      #e8c777;  --c-gold-w:      #262009;  --c-gold-e:      #40340f;
  --c-silver-t:    #b0c2ca;  --c-silver-w:    #181d1f;  --c-silver-e:    #293135;
  --c-other-t:     #a8c6a0;  --c-other-w:     #172016;  --c-other-e:     #273524;

  --danger:     #e58a80;
  --danger-w:   #2b1613;

  --sold-t:     #a49a89;
  --sold-w:     #201d16;
  --sold-e:     #342f24;

  --shadow-1: 0 1px 2px rgba(0,0,0,.5), 0 8px 22px -12px rgba(0,0,0,.7);
  --shadow-2: 0 26px 80px -20px rgba(0,0,0,.8), 0 2px 10px rgba(0,0,0,.5);

  color-scheme: dark;
}

/* ── reset ─────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body, h1, h2, h3, p, figure { margin: 0; }
button, input, select, textarea { font: inherit; color: inherit; }
img, svg { display: block; max-width: 100%; }
table { border-collapse: collapse; }

body {
  min-height: 100dvh;
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink);
  background-color: var(--paper);
  background-image:
    radial-gradient(115% 70% at 50% -18%, var(--gold-wash) 0%, rgba(0,0,0,0) 62%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.4'/%3E%3C/svg%3E");
  background-attachment: fixed, fixed;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}
:root[data-theme="dark"] body { background-blend-mode: normal, soft-light; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.skip {
  position: absolute; left: 12px; top: -60px; z-index: 100;
  background: var(--ink); color: var(--paper); padding: 10px 16px;
  border-radius: var(--r-sm); text-decoration: none;
}
.skip:focus { top: 12px; }

:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
  border-radius: 3px;
}

/* ── masthead ──────────────────────────────────────────────────────── */
.rule-top {
  height: 3px;
  background: linear-gradient(90deg, var(--gold-3), var(--gold-2) 26%, var(--gold) 50%, var(--gold-2) 74%, var(--gold-3));
}

.masthead { padding: 0 clamp(16px, 5vw, 40px); }
.masthead__inner {
  max-width: 1180px; margin: 0 auto;
  display: flex; align-items: flex-end; justify-content: space-between; gap: 16px;
  padding: clamp(18px, 4vw, 30px) 0 clamp(12px, 2vw, 18px);
}

.lockup { display: flex; align-items: center; gap: 13px; }
.lockup__gem { width: 30px; height: 30px; flex: none; }
.lockup__gem path { fill: none; stroke: var(--gold); stroke-width: 1.6; stroke-linejoin: round; }
.lockup__gem path:first-child { fill: color-mix(in srgb, var(--gold) 14%, transparent); }
.lockup__facet { stroke-width: 1; opacity: .65; }

.lockup__eyebrow {
  font-size: 10px; letter-spacing: .22em; text-transform: uppercase;
  color: var(--ink-3); font-weight: 500; margin-bottom: 1px;
}
.lockup__word {
  font-family: var(--serif);
  font-size: clamp(26px, 6.6vw, 37px);
  font-weight: 400;
  letter-spacing: .05em;
  line-height: 1;
}

.masthead__tools { display: flex; align-items: center; gap: 10px; flex: none; }

/* Two classes on purpose. This element also carries .btn, whose
   `display: inline-flex` is declared later in the file and was quietly
   winning the tie — so the phone masthead kept an Add button it was never
   meant to show, and once the theme toggle joined it the pair ran off the
   right edge at 375px. On a phone the FAB is the Add affordance. */
.masthead .masthead__add { display: none; }
@media (min-width: 720px) { .masthead .masthead__add { display: inline-flex; } }

/* theme toggle — a hairline pill, not a second gold button: it is chrome, and
   the Add action has to stay the only loud thing up here. The mode is written
   on it because "which of the three am I in?" is the whole point of the
   control; an icon alone cannot say Auto. */
.themebtn {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 44px;
  padding: 6px 14px 6px 11px;
  border: 1px solid var(--hair-2);
  border-radius: 999px;
  background: color-mix(in srgb, var(--card) 65%, transparent);
  color: var(--ink-2);
  cursor: pointer;
  transition: border-color .18s var(--step), color .18s var(--step);
}
.themebtn:hover { border-color: var(--gold); color: var(--ink); }
.themebtn__text {
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase; font-weight: 600;
}
.themebtn__ico { display: grid; place-items: center; width: 18px; height: 18px; }
.themebtn__ic {
  grid-area: 1 / 1;
  width: 18px; height: 18px;
  display: none;
  fill: none; stroke: var(--gold); stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
}
.themebtn__fill { fill: var(--gold); stroke: none; }
.themebtn[data-mode="auto"]  .themebtn__ic--auto,
.themebtn[data-mode="light"] .themebtn__ic--light,
.themebtn[data-mode="dark"]  .themebtn__ic--dark { display: block; }
@media (min-width: 720px) {
  .themebtn { min-height: 40px; padding: 5px 14px 5px 11px; }
}

.rule-double {
  max-width: 1180px; margin: 0 auto;
  display: grid; gap: 2px;
}
.rule-double span { display: block; height: 1px; background: var(--hair-2); }
.rule-double span:last-child { opacity: .45; }

/* ── notice ────────────────────────────────────────────────────────── */
.notice {
  max-width: 1180px;
  margin: 14px auto 0;
  width: calc(100% - 2 * clamp(16px, 5vw, 40px));
  padding: 10px 14px;
  font-size: 13px;
  color: var(--c-gold-t);
  background: var(--c-gold-w);
  border: 1px solid var(--c-gold-e);
  border-radius: var(--r-sm);
}

/* ── page ──────────────────────────────────────────────────────────── */
.page {
  max-width: 1180px;
  margin: 0 auto;
  padding: clamp(16px, 3.5vw, 26px) clamp(16px, 5vw, 40px) 0;
}
.page__spacer { height: calc(var(--tally-h) + 34px); }

/* ── section switch (In stock · Sold) ──────────────────────────────────
   Not a filter: it chooses which ledger is open. Two segments in a hairline
   track, sized like a chip. It leads the toolbar row — first on phones, first
   in the desktop bar — so the reading order is which ledger → search it →
   how to sort it. */
.sections {
  display: inline-flex;
  gap: 3px;
  padding: 3px;
  justify-self: start;
  border: 1px solid var(--hair-2);
  border-radius: 999px;
  background: color-mix(in srgb, var(--card) 65%, transparent);
}
.section {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 44px;
  padding: 6px 16px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: none;
  color: var(--ink-2);
  font-size: 12px;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background .18s var(--step), color .18s var(--step);
}
.section:hover { color: var(--ink); }
.section[aria-pressed="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper-2);
}
.section__n {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .03em;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.section[aria-pressed="true"] .section__n { color: var(--gold-2); }
/* Segments shrink on desktop so the whole track lands on --toolbar-h and sits
   level with the sort select beside it. */
@media (min-width: 720px) {
  .sections { padding: 2px; }
  .section { min-height: 30px; padding: 5px 14px; font-size: 11.5px; }
}

/* ── controls ──────────────────────────────────────────────────────────
   Every control belongs to a full-width band; nothing is ever parked beside
   a multi-row block. Three arrangements of the same four children, and the
   grid areas keep visual order identical to DOM order at each one, so the
   tab order never disagrees with the eye.

     phone    switch / search / chips / metals / sort   (stacked)
     tablet   switch + search  ·  chips  ·  sort        (the bar won't fit)
     desktop  switch + search + sort  ·  chips          (one toolbar row)

   The three switch, search and sort controls all sit on --toolbar-h, so
   whenever they share a row they share a baseline and a height. */
.controls {
  display: grid;
  gap: 14px;
  margin-bottom: clamp(16px, 3vw, 22px);
}
@media (min-width: 720px) {
  .controls {
    --toolbar-h: 36px;
    grid-template-columns: auto minmax(200px, 1fr);
    grid-template-areas:
      "switch search"
      "chips  chips"
      "sort   sort";
    align-items: center;
    column-gap: clamp(14px, 2.2vw, 24px);
    row-gap: 14px;
  }
  .controls > .sections { grid-area: switch; }
  .controls > .search   { grid-area: search; }
  .controls > .sortbar  { grid-area: sort; }
  .controls > .filters  { grid-area: chips; }
}
/* Wide enough for the switch, a usable search field and the whole sort
   cluster on one line — below this the cluster is a band of its own. */
@media (min-width: 860px) {
  .controls {
    grid-template-columns: auto minmax(220px, 1fr) auto;
    grid-template-areas:
      "switch search sort"
      "chips  chips  chips";
  }
  .sortbar { justify-content: flex-end; }
}

.search { position: relative; display: flex; align-items: center; }
.search__icon {
  position: absolute; left: 2px; width: 17px; height: 17px; pointer-events: none;
  fill: none; stroke: var(--ink-3); stroke-width: 1.6; stroke-linecap: round;
}
.search input {
  width: 100%;
  min-height: 44px;
  padding: 8px 34px 8px 26px;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--hair-2);
  font-size: 15px;
  transition: border-color .18s var(--step);
}
.search input::placeholder { color: var(--ink-3); }
.search input:focus { border-bottom-color: var(--gold); }
.search input::-webkit-search-cancel-button { display: none; }
.search__clear {
  position: absolute; right: -6px;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  background: none; border: 0; cursor: pointer;
  color: var(--ink-3); font-size: 21px; line-height: 1;
  border-radius: 50%;
}
.search__clear:hover { color: var(--ink); }
/* On desktop the field drops to the toolbar height so its underline finishes
   flush with the bottom of the pills either side of it. */
@media (min-width: 720px) {
  .search input { min-height: var(--toolbar-h); padding-block: 6px; font-size: 14px; }
  .search__clear { width: var(--toolbar-h); height: var(--toolbar-h); right: -4px; font-size: 19px; }
}

/* chips — a full-bleed horizontal scroller on phones, a centred wrap on
   desktop. The row bleeds to the viewport edge while its padding keeps the
   chips on the page gutter, so a chip scrolled off-screen leaves under the
   edge rather than stopping at a hard cut. */
.chips {
  --chips-gutter: clamp(16px, 5vw, 40px);
  display: flex; flex-wrap: nowrap; gap: 7px;
  overflow-x: auto;
  scrollbar-width: none;
  margin-inline: calc(-1 * var(--chips-gutter));
  padding-inline: var(--chips-gutter);
  padding-block: 2px;
  scroll-padding-inline: var(--chips-gutter);
  -webkit-overflow-scrolling: touch;
  /* Fade sized to the gutter, so it sits over the bleed and only bites once
     a chip has actually scrolled into it — no fade when nothing overflows. */
  -webkit-mask-image: linear-gradient(90deg,
    transparent 0, #000 var(--chips-gutter),
    #000 calc(100% - var(--chips-gutter)), transparent 100%);
  mask-image: linear-gradient(90deg,
    transparent 0, #000 var(--chips-gutter),
    #000 calc(100% - var(--chips-gutter)), transparent 100%);
}
.chips::-webkit-scrollbar { display: none; }
@media (min-width: 720px) {
  .chips {
    flex-wrap: wrap; margin-inline: 0; padding-inline: 0;
    justify-content: flex-start; row-gap: 8px;
    overflow-x: visible;
    -webkit-mask-image: none; mask-image: none;
  }
}

.chip {
  flex: none;
  min-height: 44px;
  padding: 6px 15px;
  border: 1px solid var(--hair-2);
  border-radius: 999px;
  background: color-mix(in srgb, var(--card) 65%, transparent);
  color: var(--ink-2);
  font-size: 12.5px;
  letter-spacing: .085em;
  text-transform: uppercase;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  transition: background .18s var(--step), color .18s var(--step), border-color .18s var(--step);
}
@media (min-width: 720px) { .chip { min-height: 34px; padding: 5px 13px; font-size: 11.5px; } }
.chip:hover { border-color: var(--gold); color: var(--ink); }
.chip[aria-pressed="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper-2);
}
.chip[aria-pressed="true"] .chip__n { color: var(--gold-2); }
.chip__n { margin-left: 7px; font-size: 11px; color: var(--ink-3); font-variant-numeric: tabular-nums; }

/* The add-a-category chip opens the row, ahead of "All", because at the end of
   a scrolling row it was off-screen on a phone. Same silhouette as the filters
   so it belongs to the set, but a notch quieter than any of them — dashed
   hairline, muted ink, no fill — so leading the row it reads as the tool that
   edits the set rather than the first thing to filter by. Gold arrives only on
   hover and focus, when it is about to be used. */
.chip--add {
  border-style: dashed;
  border-color: color-mix(in srgb, var(--hair-2) 80%, transparent);
  background: none;
  color: var(--ink-3);
  padding-inline: 14px;
  letter-spacing: .06em;
  gap: 5px;
  display: inline-flex; align-items: center;
}
.chip--add:hover,
.chip--add:focus-visible { border-style: solid; border-color: var(--gold); color: var(--gold); background: var(--gold-wash); }
.chip--add__plus { font-size: 15px; line-height: 1; opacity: .9; }

/* metal row — the secondary cut, so it stays a hairline outline even when
   selected rather than taking the ink fill the category row uses. */
.filters { display: grid; gap: 8px; min-width: 0; }
.chips--karat { padding-block: 0; }
/* Lighter by colour and weight, never by shrinking the target: the phone
   tap area stays the 44px the rest of the app holds to. */
.chip--karat {
  padding: 5px 13px;
  border-color: color-mix(in srgb, var(--hair-2) 65%, transparent);
  background: none;
  color: var(--ink-3);
  font-size: 11.5px;
  letter-spacing: .07em;
  font-weight: 500;
  text-transform: none;
}
@media (min-width: 720px) { .chip--karat { min-height: 30px; padding: 4px 12px; font-size: 11px; } }
.chip--karat:hover { border-color: var(--gold); color: var(--ink-2); }
.chip--karat[aria-pressed="true"] {
  background: var(--gold-wash);
  border-color: var(--gold-3);
  color: var(--gold);
  font-weight: 600;
}
.chip--karat[aria-pressed="true"] .chip__n { color: var(--gold); opacity: .8; }
.chip--karat .chip__n { margin-left: 6px; font-size: 10.5px; }

/* sort bar (primary control on phones; header clicks work on desktop) */
.sortbar { display: flex; align-items: center; gap: 8px; }
.sortbar__label {
  font-size: 10px; letter-spacing: .2em; text-transform: uppercase; color: var(--ink-3);
}
/* Chrome (border, sheen, chevron, focus) is shared with the intake select
   under "selects" below; this only sets the bar's compact metrics. */
.sortbar__select {
  min-height: 44px;
  padding: 6px 34px 6px 12px;
  font-size: 13.5px;
}
.sortbar__dir {
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border: 1px solid var(--hair-2);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--card) 65%, transparent);
  cursor: pointer; font-size: 14px;
  transition: border-color .18s var(--step), color .18s var(--step);
}
.sortbar__dir:hover { border-color: var(--gold); color: var(--gold); }
/* "Select" sits with sort because it is the other thing you do *to the list*
   rather than to an item. Quiet until pressed, when it takes the ink fill the
   category chips use — the list underneath changes behaviour, so the control
   that did it has to look latched. */
.selectbtn {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 44px;
  padding: 6px 14px;
  border: 1px solid var(--hair-2);
  border-radius: 999px;
  background: color-mix(in srgb, var(--card) 65%, transparent);
  color: var(--ink-2);
  font-size: 11.5px; letter-spacing: .12em; text-transform: uppercase; font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: background .18s var(--step), color .18s var(--step), border-color .18s var(--step);
}
.selectbtn:hover { border-color: var(--gold); color: var(--ink); }
.selectbtn__ico {
  width: 16px; height: 16px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.selectbtn__tick { opacity: 0; transition: opacity .16s var(--step); }
.selectbtn[aria-pressed="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper-2);
}
.selectbtn[aria-pressed="true"] .selectbtn__tick { opacity: 1; }

@media (min-width: 720px) {
  .sortbar__select, .sortbar__dir {
    min-height: var(--toolbar-h); height: var(--toolbar-h);
  }
  .sortbar__dir { width: var(--toolbar-h); }
  .selectbtn { min-height: var(--toolbar-h); height: var(--toolbar-h); padding: 5px 13px; font-size: 11px; }
}

/* ── ledger card ───────────────────────────────────────────────────── */
.ledger {
  position: relative;
  background: var(--card);
  border: 1px solid var(--hair);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-1);
}
.tick {
  position: absolute; width: 9px; height: 9px; pointer-events: none;
  border-color: var(--gold); border-style: solid; border-width: 0;
}
.tick--tl { top: -1px; left: -1px;  border-top-width: 2px; border-left-width: 2px;  border-top-left-radius: var(--r-md); }
.tick--tr { top: -1px; right: -1px; border-top-width: 2px; border-right-width: 2px; border-top-right-radius: var(--r-md); }
.tick--bl { bottom: -1px; left: -1px;  border-bottom-width: 2px; border-left-width: 2px;  border-bottom-left-radius: var(--r-md); }
.tick--br { bottom: -1px; right: -1px; border-bottom-width: 2px; border-right-width: 2px; border-bottom-right-radius: var(--r-md); }

.ledger__scroll { border-radius: var(--r-md); }

/* ── the sheet: cards on phones, table on desktop ──────────────────── */
.sheet { width: 100%; }
.sheet thead { display: none; }
.sheet tbody { display: block; }

.row {
  display: grid;
  /* Floor on the name track: a sold card carries three marks in the badge
     column, and without it that column takes its max-content width and
     crushes the name to a two-character ribbon. */
  grid-template-columns: minmax(118px, 1fr) auto 60px;
  grid-template-areas:
    "name  cat   photo"
    "desc  desc  photo"
    "qty   price photo";
  column-gap: 12px;
  row-gap: 3px;
  align-items: center;
  padding: 13px clamp(13px, 4vw, 18px);
  border-top: 1px solid var(--hair);
  cursor: pointer;
  position: relative;
  background: none;
  transition: background .16s var(--step);
}
.sheet tbody .row:first-child { border-top: 0; }
.row:hover { background: color-mix(in srgb, var(--gold-wash) 55%, transparent); }
.row:focus-visible { outline: 2px solid var(--gold); outline-offset: -3px; }

/* Gold edge marker on hover. Scoped to the card layout only: a ::before on a
   <tr> becomes an anonymous table-cell once the row is display:table-row,
   which would shift every column across by one. */
@media (max-width: 719px) {
  .row::before {
    content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
    background: var(--gold); transform: scaleY(0); transform-origin: 50% 50%;
    transition: transform .22s var(--step);
  }
  .row:hover::before, .row:focus-within::before { transform: scaleY(1); }
}

.row td { display: block; padding: 0; }

/* Metal and each custom attribute are ledger columns, and a phone card has
   no columns to spare — the card carries them as marks beside the category
   badge instead, and the table hides those marks again in favour of the real
   columns. Higher specificity than `.row td` so the block rule cannot win. */
.row .c-karat, .row .c-attr { display: none; }

.c-name  { grid-area: name; }
.c-cat   { grid-area: cat;  justify-self: end; }
.c-desc  { grid-area: desc; }
.c-qty   { grid-area: qty;  }
.c-price { grid-area: price; justify-self: end; }
.c-photo { grid-area: photo; justify-self: end; }

.i-name {
  font-family: var(--serif);
  font-size: 17px;
  font-weight: 400;
  line-height: 1.3;
  letter-spacing: .012em;
  overflow-wrap: anywhere;
}
/* selection mode ──────────────────────────────────────────────────────
   The checkbox rides inside the name cell rather than taking a column of its
   own: the ledger's column set is shared by the card layout, the table and
   the runtime attribute columns, and none of them should have to shift
   because a temporary mode is on. */
.namepick { display: flex; align-items: center; gap: 11px; min-width: 0; }
.namepick .i-name { min-width: 0; }
.rowcheck {
  flex: none;
  /* Big enough to hit on a phone even though the box itself is 20px: the
     whole row toggles, so this is a target of last resort, not the only one. */
  width: 22px; height: 22px; margin: 11px 0 11px 2px;
  -webkit-appearance: none; appearance: none;
  border: 1.5px solid var(--hair-2);
  border-radius: 5px;
  background: var(--paper-2);
  cursor: pointer;
  display: grid; place-items: center;
  transition: background .16s var(--step), border-color .16s var(--step);
}
.rowcheck::after {
  content: "";
  width: 11px; height: 6px;
  border-left: 2px solid var(--paper-2);
  border-bottom: 2px solid var(--paper-2);
  transform: rotate(-45deg) translate(1px, -1px);
  opacity: 0;
  transition: opacity .14s var(--step);
}
.rowcheck:checked { background: var(--gold); border-color: var(--gold); }
.rowcheck:checked::after { opacity: 1; border-color: #1c1607; }
/* Two classes so a picked row outranks .row--sold's archive wash below — a
   sold piece that is selected must still read as selected. */
.sheet .row--picked { background: color-mix(in srgb, var(--gold-wash) 88%, transparent); }
.sheet .row--picked:hover { background: var(--gold-wash); }

.i-desc {
  font-size: 12.5px;
  color: var(--ink-3);
  line-height: 1.45;
  overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden;
}
.i-desc:empty::before { content: "—"; }

/* Metal and custom-attribute cells. Values are short stamps, not prose, so
   they take the tabular figures the numeric columns use and never wrap. */
.i-attr {
  font-size: 13px;
  color: var(--ink-2);
  letter-spacing: .03em;
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.i-attr--unset { color: var(--ink-3); }

.i-qty {
  font-size: 12px;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  letter-spacing: .04em;
}
.i-qty b { font-weight: 600; color: var(--ink); }
.i-qty--zero { color: var(--danger); }

.i-price {
  font-size: 15px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -.01em;
  white-space: nowrap;
}

.i-sold {
  font-size: 12px;
  color: var(--ink-3);
  letter-spacing: .04em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.i-sold::before { content: "Sold "; letter-spacing: .1em; text-transform: uppercase; font-size: 10px; }

.i-priceNote {
  font-size: 9.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: 1px;
  white-space: nowrap;
}

/* A sold row is archive: the ink drops back so the in-stock ledger stays
   the loud one, and the gold hover marker is withdrawn. */
.row--sold { background: color-mix(in srgb, var(--sold-w) 42%, transparent); }
.row--sold .i-name { color: var(--ink-2); }
.row--sold .i-price { color: var(--ink-2); font-weight: 500; }
.row--sold .thumb img { filter: saturate(.55) opacity(.82); }
.row--sold:hover { background: color-mix(in srgb, var(--sold-w) 72%, transparent); }
.row--sold:hover::before { background: var(--sold-e); }

/* category badge */
.badges {
  display: flex; flex-wrap: wrap; gap: 4px;
  align-items: center; justify-content: flex-end;
}
@media (min-width: 720px) { .badges { justify-content: flex-start; } }

.badge {
  display: inline-block;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 10.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  font-weight: 600;
  white-space: nowrap;
  border: 1px solid var(--b-edge, var(--hair-2));
  background: var(--b-wash, var(--paper-2));
  color: var(--b-text, var(--ink-2));
}
.badge[data-cat="Rings"]     { --b-text: var(--c-rings-t);     --b-wash: var(--c-rings-w);     --b-edge: var(--c-rings-e); }
.badge[data-cat="Necklaces"] { --b-text: var(--c-necklaces-t); --b-wash: var(--c-necklaces-w); --b-edge: var(--c-necklaces-e); }
.badge[data-cat="Earrings"]  { --b-text: var(--c-earrings-t);  --b-wash: var(--c-earrings-w);  --b-edge: var(--c-earrings-e); }
.badge[data-cat="Bracelets"] { --b-text: var(--c-bracelets-t); --b-wash: var(--c-bracelets-w); --b-edge: var(--c-bracelets-e); }
.badge[data-cat="Diamonds"]  { --b-text: var(--c-diamonds-t);  --b-wash: var(--c-diamonds-w);  --b-edge: var(--c-diamonds-e); }
.badge[data-cat="Lab Grown Diamonds"] { --b-text: var(--c-lab-t); --b-wash: var(--c-lab-w); --b-edge: var(--c-lab-e); }
.badge[data-cat="Watches"]   { --b-text: var(--c-watches-t);   --b-wash: var(--c-watches-w);   --b-edge: var(--c-watches-e); }
.badge[data-cat="Gold"]      { --b-text: var(--c-gold-t);      --b-wash: var(--c-gold-w);      --b-edge: var(--c-gold-e); }
.badge[data-cat="Silver"]    { --b-text: var(--c-silver-t);    --b-wash: var(--c-silver-w);    --b-edge: var(--c-silver-e); }
.badge[data-cat="Other"]     { --b-text: var(--c-other-t);     --b-wash: var(--c-other-w);     --b-edge: var(--c-other-e); }
.badge--sold { --b-text: var(--sold-t); --b-wash: var(--sold-w); --b-edge: var(--sold-e); }

/* metal mark — a hallmark stamped next to the category, not a second
   category: gold ink on the paper wash, no pill fill of its own. */
.karat {
  display: inline-block;
  padding: 2px 6px;
  border-radius: var(--r-sm);
  border: 1px solid var(--gold-3);
  background: var(--gold-wash);
  color: var(--gold);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .06em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* thumbnail */
.thumb {
  width: 54px; height: 54px;
  padding: 0; border: 0;
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--paper-2);
  box-shadow: inset 0 0 0 1px var(--hair-2);
  display: grid; place-items: center;
  cursor: zoom-in;
  transition: box-shadow .18s var(--step), transform .18s var(--step);
}
.thumb:hover { box-shadow: inset 0 0 0 1px var(--gold), 0 4px 14px -6px rgba(0,0,0,.35); transform: translateY(-1px); }
.thumb img { width: 100%; height: 100%; object-fit: cover; }
.thumb--empty { cursor: default; box-shadow: inset 0 0 0 1px var(--hair); }
.thumb--empty:hover { transform: none; box-shadow: inset 0 0 0 1px var(--hair); }
.thumb__ghost { width: 22px; height: 22px; fill: none; stroke: var(--hair-2); stroke-width: 2; stroke-linejoin: round; }

/* entrance */
@media (prefers-reduced-motion: no-preference) {
  .row--in { animation: rise .5s var(--step) backwards; animation-delay: calc(var(--i, 0) * 32ms); }
  @keyframes rise { from { opacity: 0; transform: translateY(9px); } }
}
.row--flash { animation: flash 1.5s ease-out; }
@keyframes flash {
  0%, 45% { background: color-mix(in srgb, var(--gold-2) 26%, transparent); }
  100%    { background: transparent; }
}

/* ── desktop table ─────────────────────────────────────────────────── */
@media (min-width: 720px) {
  .ledger__scroll { overflow-x: auto; }
  /* Details is the one elastic column, so without a floor every column added
     to its left is paid for out of it — one custom attribute was enough to
     clamp a description to a dozen characters. The floor puts the cost on the
     scroll box instead, which is what it is for: the fixed columns come to
     842px, and the rest is a readable one-line Details. --extra-cols is set on
     the table by renderTableHead. Living inside the 720px query, this can
     never reach the phone layout and start the page scrolling sideways. */
  .sheet {
    min-width: calc(1090px + var(--extra-cols, 0) * 116px);
    table-layout: fixed;
  }
  .sheet thead { display: table-header-group; }
  .sheet tbody { display: table-row-group; }

  /* Pinned rather than a percentage: a percentage grows with the table, so
     every pixel bought for Details above was handed straight back. */
  .col-name  { width: 300px; }
  /* Wide enough for the longest category badge ("Lab Grown Diamonds") beside
     a Sold mark without forcing a third line. */
  .col-cat   { width: 164px; }
  /* Sized like a custom-attribute column, not like the finenesses this one
     used to hold: "White Gold" is a word, and 84px clipped it to an ellipsis.
     The width comes out of Details, which is the auto column, so the table's
     min-width — tuned to fit a 1280px screen without a sideways scroll —
     stays exactly where it was. */
  .col-karat { width: 116px; }
  .col-attr  { width: 116px; }
  .col-desc  { width: auto; }
  /* Holds "Aug 26," + "2026" on two lines for a sold row without stealing
     enough width to matter for the plain quantity it usually carries. */
  .col-qty   { width: 90px; }
  .col-price { width: 116px; }
  .col-photo { width: 88px; }

  .sheet thead th {
    padding: 0;
    border-bottom: 1px solid var(--hair-2);
    background: linear-gradient(180deg, var(--paper-2), var(--card));
    text-align: left;
    font-weight: 500;
  }
  .sheet thead th:first-child { border-top-left-radius: var(--r-md); }
  .sheet thead th:last-child  { border-top-right-radius: var(--r-md); }

  .th {
    display: flex; align-items: center; gap: 5px;
    width: 100%;
    padding: 13px 14px;
    background: none; border: 0;
    font-size: 10px; letter-spacing: .19em; text-transform: uppercase;
    color: var(--ink-3); font-weight: 600;
    cursor: pointer; text-align: left;
    transition: color .16s var(--step);
  }
  .th--static { cursor: default; }
  .th--num { justify-content: flex-end; }
  .th--photo { justify-content: flex-end; }
  button.th:hover { color: var(--gold); }
  .th__arrow { font-size: 9px; opacity: 0; transition: opacity .16s var(--step); }
  button.th:hover .th__arrow { opacity: .45; }
  th[aria-sort] .th { color: var(--ink); }
  th[aria-sort] .th__arrow { opacity: 1; color: var(--gold); }
  th[aria-sort="ascending"]  .th__arrow::after { content: "↑"; }
  th[aria-sort="descending"] .th__arrow::after { content: "↓"; }
  button.th:hover .th__arrow:empty::after { content: "↕"; }

  .row {
    display: table-row;
    grid-template-areas: none;
    padding: 0;
    border-top: 0;
  }
  .row td {
    display: table-cell;
    vertical-align: middle;
    padding: 11px 14px;
    border-top: 1px solid var(--hair);
  }
  .sheet tbody .row:first-child td { border-top: 0; }
  .row .c-karat, .row .c-attr { display: table-cell; }
  /* The columns now say it, so the duplicate marks come off the badge set. */
  .badges .karat { display: none; }
  .c-cat, .c-price, .c-photo { justify-self: auto; }
  .c-qty   { text-align: right; }
  .c-price { text-align: right; }
  .c-photo { text-align: right; }
  .c-photo .thumb { margin-left: auto; }

  .row:hover .c-name, .row:focus-within .c-name { box-shadow: inset 2px 0 0 var(--gold); }

  .i-name { font-size: 16.5px; }
  .i-desc { -webkit-line-clamp: 1; font-size: 13px; }
  .i-qty  { font-size: 14.5px; color: var(--ink); }
  .i-qty  .i-qty-label { display: none; }
  /* The Sold badge in the category cell already names the state, so the
     table drops the word and lets the date wrap inside the narrow column. */
  .i-sold { font-size: 12px; white-space: normal; line-height: 1.3; }
  .i-sold::before { content: none; }
  .thumb  { width: 46px; height: 46px; }
  .badge  { font-size: 10px; }
}

/* ── empty state ───────────────────────────────────────────────────── */
.blank { padding: clamp(44px, 11vw, 76px) 24px; text-align: center; }
.blank__gem {
  width: 54px; height: 54px; margin: 0 auto 18px;
  fill: none; stroke: var(--hair-2); stroke-width: 1.5; stroke-linejoin: round;
}
.blank__title {
  font-family: var(--serif); font-size: 21px; font-weight: 400; margin-bottom: 6px;
}
.blank__body { font-size: 14px; color: var(--ink-3); max-width: 34ch; margin: 0 auto; }

/* ── tally bar ─────────────────────────────────────────────────────── */
.tally {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  background: color-mix(in srgb, var(--ink) 96%, transparent);
  color: var(--paper-2);
  border-top: 1px solid color-mix(in srgb, var(--gold) 45%, transparent);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  backdrop-filter: blur(10px);
}
.tally__inner {
  max-width: 1180px; margin: 0 auto;
  min-height: var(--tally-h);
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 10px calc(clamp(16px, 5vw, 40px) + env(safe-area-inset-right, 0px))
           10px calc(clamp(16px, 5vw, 40px) + env(safe-area-inset-left, 0px));
}
.tally__count {
  font-size: 11px; letter-spacing: .17em; text-transform: uppercase;
  color: color-mix(in srgb, var(--paper-2) 62%, transparent);
  font-variant-numeric: tabular-nums;
}
.tally__count span:first-child { color: var(--paper-2); font-weight: 600; }
.tally__sub { display: block; letter-spacing: .1em; font-size: 10px; margin-top: 2px; }
.tally__value { display: flex; align-items: baseline; gap: 10px; }
.tally__valueLabel {
  font-size: 10px; letter-spacing: .19em; text-transform: uppercase;
  color: color-mix(in srgb, var(--paper-2) 55%, transparent);
}
#tallyValue {
  font-family: var(--serif);
  font-size: clamp(19px, 5vw, 25px);
  font-weight: 400;
  color: var(--gold-2);
  font-variant-numeric: tabular-nums;
  letter-spacing: .02em;
}
:root[data-theme="dark"] .tally { background: color-mix(in srgb, #000 68%, transparent); }

/* ── selection bar ─────────────────────────────────────────────────────
   Takes the tally's place at the foot of the screen while selection mode is
   on (see .is-selecting below) rather than stacking on top of it: two fixed
   bars on a phone leaves nothing to look at. */
.selbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 33;
  background: color-mix(in srgb, var(--card) 97%, transparent);
  border-top: 1px solid var(--hair-2);
  box-shadow: 0 -18px 40px -26px rgba(30,23,10,.55);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  backdrop-filter: blur(10px);
}
.selbar__inner {
  max-width: 1180px; margin: 0 auto;
  min-height: var(--tally-h);
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 9px calc(clamp(16px, 5vw, 40px) + env(safe-area-inset-right, 0px))
           9px calc(clamp(16px, 5vw, 40px) + env(safe-area-inset-left, 0px));
}
.selbar__count {
  font-size: 11px; letter-spacing: .17em; text-transform: uppercase;
  color: var(--ink-3); font-variant-numeric: tabular-nums;
}
.selbar__count b { color: var(--ink); font-weight: 600; font-size: 13px; }
.selbar__actions { display: flex; align-items: center; gap: 8px; }
.selbar__actions .btn { min-height: 44px; }
.selbar .btn--danger[disabled] { opacity: .42; cursor: not-allowed; }
.selbar__confirm {
  max-width: 1180px; margin: 0 auto;
  padding: 0 clamp(16px, 5vw, 40px) 12px;
}
/* While the confirm is up the count row stays visible above it, so the number
   being confirmed is never off-screen. */
.selbar__confirm .danger__confirm { margin: 0; }

.is-selecting .tally,
.is-selecting .fab { display: none; }

/* ── buttons ───────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 46px;
  padding: 11px 20px;
  border-radius: 999px;
  border: 1px solid transparent;
  font-size: 13px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  cursor: pointer;
  transition: background .18s var(--step), color .18s var(--step),
              border-color .18s var(--step), transform .12s var(--step);
}
.btn:active { transform: translateY(1px); }

.btn--gold {
  background: linear-gradient(170deg, var(--gold-2), var(--gold));
  color: #1c1607;
  border-color: color-mix(in srgb, var(--gold) 70%, #000);
  box-shadow: 0 1px 0 rgba(255,255,255,.35) inset, var(--shadow-1);
}
.btn--gold:hover { filter: brightness(1.06); }

.btn--ghost {
  background: transparent; border-color: var(--hair-2); color: var(--ink-2);
}
.btn--ghost:hover { border-color: var(--gold); color: var(--ink); }

.btn--quiet {
  background: transparent; border-color: transparent; color: var(--ink-3);
  text-transform: none; letter-spacing: 0; font-weight: 500; font-size: 13px;
  padding: 10px 12px; min-height: 44px;
}
.btn--quiet:hover { color: var(--ink); }

.btn--danger { background: var(--danger); color: #fff; border-color: var(--danger); }
/* The dark palette lightens --danger to stay legible as *text*, which leaves
   white lettering on it at ~2.5:1. On a fill the ink has to go the other way. */
:root[data-theme="dark"] .btn--danger { color: #24110e; }
.btn--danger:hover { filter: brightness(1.08); }
.btn--danger-ghost {
  background: transparent; color: var(--danger);
  border-color: color-mix(in srgb, var(--danger) 35%, transparent);
}
.btn--danger-ghost:hover { background: var(--danger-w); }

.btn--wide { flex: 1; }

.iconbtn {
  width: 44px; height: 44px; flex: none;
  display: grid; place-items: center;
  background: none; border: 1px solid transparent; border-radius: 50%;
  font-size: 25px; line-height: 1; color: var(--ink-3); cursor: pointer;
  transition: color .16s var(--step), border-color .16s var(--step);
}
.iconbtn:hover { color: var(--ink); border-color: var(--hair-2); }

.fab {
  position: fixed; z-index: 31;
  right: calc(18px + env(safe-area-inset-right, 0px));
  bottom: calc(var(--tally-h) + 18px + env(safe-area-inset-bottom, 0px));
  width: 58px; height: 58px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--gold) 70%, #000);
  background: linear-gradient(170deg, var(--gold-2), var(--gold));
  color: #1c1607; font-size: 25px; line-height: 1;
  cursor: pointer;
  box-shadow: 0 1px 0 rgba(255,255,255,.4) inset, 0 12px 30px -10px rgba(80,58,10,.6);
  transition: transform .16s var(--step);
}
.fab:hover { transform: scale(1.05); }
.fab:active { transform: scale(.97); }
@media (min-width: 720px) { .fab { display: none; } }

/* ── drawer (intake / edit sheet) ──────────────────────────────────── */
.scrim {
  position: fixed; inset: 0; z-index: 40;
  background: rgba(26,19,6,.52);
  backdrop-filter: blur(3px);
  animation: fade .22s var(--step);
}
@keyframes fade { from { opacity: 0; } }

.drawer {
  position: fixed; z-index: 41;
  left: 0; right: 0; bottom: 0;
  max-height: 94dvh;
  display: flex; flex-direction: column;
  background: var(--card);
  border-top: 1px solid var(--hair-2);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: var(--shadow-2);
  animation: slideup .34s var(--step);
}
@keyframes slideup { from { transform: translateY(100%); } }
@media (prefers-reduced-motion: reduce) {
  .drawer, .scrim { animation: none; }
}
@media (min-width: 720px) {
  .drawer {
    left: 50%; bottom: auto; right: auto; top: 50%;
    transform: translate(-50%, -50%);
    width: min(560px, calc(100vw - 48px));
    max-height: 88dvh;
    border: 1px solid var(--hair-2);
    border-radius: var(--r-lg);
    animation: pop .28s var(--step);
  }
  @keyframes pop {
    from { opacity: 0; transform: translate(-50%, calc(-50% + 14px)) scale(.985); }
  }
}

.drawer__grip {
  width: 42px; height: 4px; border-radius: 4px;
  background: var(--hair-2);
  margin: 9px auto 0;
}
@media (min-width: 720px) { .drawer__grip { display: none; } }

.drawer__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 16px clamp(18px, 5vw, 26px) 14px;
  border-bottom: 1px solid var(--hair);
}
.drawer__eyebrow {
  font-size: 10px; letter-spacing: .21em; text-transform: uppercase; color: var(--gold);
  font-weight: 600; margin-bottom: 3px;
}
.drawer__title {
  font-family: var(--serif); font-size: 25px; font-weight: 400; line-height: 1.16;
  letter-spacing: .012em;
}

.form { display: flex; flex-direction: column; min-height: 0; }
.form__body {
  padding: clamp(18px, 5vw, 26px);
  display: grid; gap: 18px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.form__foot {
  display: flex; gap: 10px; align-items: center;
  padding: 14px clamp(18px, 5vw, 26px);
  padding-bottom: calc(14px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--hair);
  background: var(--paper-2);
}
@media (min-width: 720px) {
  .form__foot { border-radius: 0 0 var(--r-lg) var(--r-lg); }
}

/* fields */
.field { display: grid; gap: 6px; min-width: 0; }
.field__label {
  display: flex; align-items: baseline; gap: 8px;
  font-size: 10.5px; letter-spacing: .17em; text-transform: uppercase;
  color: var(--ink-2); font-weight: 600;
}
.req { color: var(--gold); letter-spacing: .1em; font-size: 9.5px; }
.hint { color: var(--ink-3); letter-spacing: .1em; font-size: 9.5px; font-weight: 500; }

.field__input {
  width: 100%;
  min-height: 48px;
  padding: 11px 13px;
  background: var(--paper-2);
  border: 1px solid var(--hair-2);
  border-radius: var(--r-sm);
  font-size: 16px; /* keeps iOS from zooming on focus */
  color: var(--ink);
  transition: border-color .16s var(--step), box-shadow .16s var(--step), background .16s var(--step);
}
.field__input:focus {
  outline: none;
  border-color: var(--gold);
  background: var(--card);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--gold) 18%, transparent);
}
.field__input--area { min-height: 84px; resize: vertical; line-height: 1.5; font-size: 16px; }
.field__input[aria-invalid="true"] {
  border-color: var(--danger);
  background: var(--danger-w);
}
.field__input[aria-invalid="true"]:focus {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 18%, transparent);
}

.field__error { font-size: 12.5px; color: var(--danger); }

.field-pair { display: grid; gap: 18px; grid-template-columns: 1fr 1fr; }

/* ── segmented attribute picker ────────────────────────────────────────
   A handful of choices, so a row of pills beats a dropdown: one tap, no
   picker wheel, and the whole set is legible at a glance behind the counter.
   Past six values the form switches to a native select instead — but a user
   can still push a list to seven pills, so the row wraps rather than
   shaving the tap targets below the thumb. */
.segmented {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 4px;
  border: 1px solid var(--hair-2);
  border-radius: 999px;
  background: var(--paper-2);
}
/* Sized by its own label, not by a shared column: the built-in values are
   words now ("White Gold"), and a fixed 58px basis packed five of them onto
   one line and let the text run out of its own pill. Content basis + wrap
   breaks the row where the words actually stop; grow still fills the line. */
.segmented__opt {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
  padding: 8px 4px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: none;
  color: var(--ink-2);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: .06em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  cursor: pointer;
  transition: background .18s var(--step), color .18s var(--step), border-color .18s var(--step);
}
.segmented__opt:hover { border-color: var(--gold); color: var(--ink); }
.segmented__opt[aria-pressed="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper-2);
}
/* The unset option is the quiet default, not one more value. */
.segmented__opt[data-value=""] { color: var(--ink-3); font-size: 15px; line-height: 1; }
.segmented__opt[data-value=""][aria-pressed="true"] { color: var(--paper-2); }

/* ── attribute fields & list editing ───────────────────────────────────
   The attribute stack is just fields, so it borrows the form's own rhythm
   rather than inventing a container of its own. */
.attrs { display: grid; gap: 18px; }
.attrs:empty { display: none; }

.attrs__foot {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
  margin-top: -6px;
}
/* Quiet by design: a maintenance door, not a step in filling out the form. */
.linkbtn {
  padding: 6px 0;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--gold-3);
  color: var(--gold);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .06em;
  cursor: pointer;
  transition: color .16s var(--step), border-color .16s var(--step);
}
.linkbtn:hover { color: var(--ink); border-bottom-color: var(--gold); }
.attrs__hint { font-size: 11.5px; color: var(--ink-3); }

/* The add row: one text field and its buttons, on one line where there is
   room and stacked where there is not. Shared by the inline "+ New
   category…" row and every add field in the manage sheet. */
.addrow { display: grid; gap: 6px; }
.inlineadd { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.inlineadd .field__input { flex: 1 1 150px; min-height: 46px; }
.inlineadd__go { min-height: 46px; padding-inline: 18px; font-size: 12px; }
.addrow__err { font-size: 12.5px; color: var(--danger); }

/* ── manage sheet ──────────────────────────────────────────────────── */
.manage__body {
  padding: clamp(18px, 5vw, 26px);
  display: grid; gap: 26px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.mgroup { display: grid; gap: 10px; }
.mgroup__title {
  font-family: var(--serif);
  font-size: 17px; font-weight: 400; letter-spacing: .02em;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--hair);
}
.mgroup__note { font-size: 12px; color: var(--ink-3); line-height: 1.5; }
.mgroup__drop {
  justify-self: start;
  padding: 4px 0;
  background: none; border: 0; border-bottom: 1px solid transparent;
  color: var(--danger); font-size: 11.5px; font-weight: 600;
  letter-spacing: .06em; cursor: pointer;
}
.mgroup__drop:hover { border-bottom-color: var(--danger); }

.mlist { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.mlist__empty { font-size: 12.5px; color: var(--ink-3); }
.mitem {
  display: inline-flex; align-items: center; gap: 6px;
  /* Full 44px tall so the remove button has real estate around it, even
     though the × glyph itself reads small. */
  min-height: 44px;
  padding: 5px 6px 5px 12px;
  border: 1px solid var(--hair-2);
  border-radius: 999px;
  background: var(--paper-2);
  font-size: 12.5px;
  color: var(--ink-2);
}
.mitem__name { font-variant-numeric: tabular-nums; }
/* Built-ins say why they have no × rather than just lacking one. */
.mitem__lock {
  font-size: 9px; letter-spacing: .13em; text-transform: uppercase;
  color: var(--ink-3); padding-right: 6px;
}
.mitem__x {
  width: 36px; height: 36px; flex: none;
  display: grid; place-items: center;
  border: 0; border-radius: 50%;
  background: none;
  color: var(--ink-3); font-size: 17px; line-height: 1;
  cursor: pointer;
  transition: background .16s var(--step), color .16s var(--step);
}
.mitem__x:hover { background: var(--danger-w); color: var(--danger); }

.manage__note {
  flex: 1;
  font-size: 11.5px; color: var(--ink-3); line-height: 1.4;
}

/* The sheet stacks over the intake drawer, so both layers sit one step above
   it and the drawer stays visible underneath. */
.scrim--over { z-index: 42; background: rgba(26,19,6,.4); }
.drawer--over { z-index: 43; }

/* ── selects ───────────────────────────────────────────────────────── */
/* Both dropdowns are real <select> elements — only the closed control is
   restyled, so the iOS picker wheel and every platform's own menu are
   untouched. Shared by the intake field and the sort bar; each context
   supplies its own surface and metrics. */
.selectwrap { position: relative; min-width: 0; }
.selectwrap--bar { display: inline-block; }

.selectwrap select {
  --sel-surface: var(--paper-2);
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
  padding-right: 34px; /* clearance for the chevron */
  font-family: var(--sans);
  color: var(--ink);
  border: 1px solid var(--hair-2);
  border-radius: var(--r-sm);
  /* Flat colour plus a top sheen — engraved rather than plastic. */
  background-color: var(--sel-surface);
  background-image: linear-gradient(180deg,
    color-mix(in srgb, var(--card) 85%, transparent) 0%, transparent 58%);
  transition: border-color .16s var(--step), box-shadow .16s var(--step),
              background-color .16s var(--step);
}
.selectwrap--bar select {
  --sel-surface: color-mix(in srgb, var(--card) 65%, transparent);
}
.selectwrap select::-ms-expand { display: none; }
.selectwrap:hover select { border-color: var(--gold); }

.selectwrap select:focus { outline: none; }
.selectwrap select:focus-visible {
  outline: none;
  border-color: var(--gold);
  background-color: var(--card);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--gold) 18%, transparent);
}

.selectwrap__arrow {
  position: absolute; right: 13px; top: 50%;
  width: 11px; height: 11px;
  transform: translateY(-50%);
  pointer-events: none;
  color: var(--gold);
  fill: none; stroke: currentColor; stroke-width: 1.75;
  stroke-linecap: round; stroke-linejoin: round;
  transition: color .16s var(--step), transform .16s var(--step);
}
.selectwrap:hover .selectwrap__arrow { color: var(--gold-2); }
@media (prefers-reduced-motion: no-preference) {
  .selectwrap:hover .selectwrap__arrow { transform: translateY(calc(-50% + 1px)); }
}

.money { position: relative; }
.money__sign {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  color: var(--ink-3); pointer-events: none; font-size: 15px;
}
.field__input--money { padding-left: 27px; font-variant-numeric: tabular-nums; }

/* photo picker */
.photo { display: flex; gap: 14px; align-items: flex-start; }
.photo__frame {
  width: 92px; height: 92px; flex: none;
  border-radius: var(--r-sm);
  background: var(--paper-2);
  box-shadow: inset 0 0 0 1px var(--hair-2);
  display: grid; place-items: center;
  overflow: hidden;
}
.photo__img { width: 100%; height: 100%; object-fit: cover; }
.photo__ghost {
  width: 34px; height: 34px;
  fill: none; stroke: var(--hair-2); stroke-width: 1.6; stroke-linejoin: round;
}
.photo__actions { display: grid; gap: 6px; justify-items: start; min-width: 0; }
.photo__picks { display: flex; flex-wrap: wrap; gap: 8px; }
.photo__pick { min-height: 44px; gap: 7px; padding-inline: 15px; font-size: 12px; letter-spacing: .06em; }
.photo__pickIcon {
  width: 16px; height: 16px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.5;
  stroke-linejoin: round; stroke-linecap: round;
}
/* Both entry points render everywhere. Desktops with a webcam can still use
   the capture input, and hiding it made the feature look missing. */
input[type="file"]:focus-visible + .photo__pick {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
  border-color: var(--gold);
  color: var(--ink);
}
.photo__note { font-size: 11.5px; color: var(--ink-3); line-height: 1.45; }

/* ── sold zone ─────────────────────────────────────────────────────────
   Sits above the delete zone and shares its rule-and-inset shape, but in
   the sold tint rather than the danger red: moving a piece to the Sold
   ledger is reversible, and the styling should say so. */
.soldzone {
  border-top: 1px solid var(--hair);
  padding-top: 18px;
  display: grid; gap: 12px; justify-items: start;
}
.soldzone__mark { display: grid; gap: 12px; justify-items: start; width: 100%; }

.soldzone__state {
  width: 100%;
  padding: 14px;
  border-radius: var(--r-sm);
  background: var(--sold-w);
  border: 1px solid var(--sold-e);
  display: grid; gap: 4px; justify-items: start;
}
.soldzone__title {
  font-size: 10px; letter-spacing: .19em; text-transform: uppercase;
  font-weight: 600; color: var(--sold-t);
}
.soldzone__meta {
  font-family: var(--serif);
  font-size: 17px; line-height: 1.3; color: var(--ink);
  font-variant-numeric: tabular-nums;
  margin-bottom: 8px;
}

.soldzone__confirm {
  width: 100%;
  padding: 14px;
  border-radius: var(--r-sm);
  background: var(--gold-wash);
  border: 1px solid var(--gold-3);
  display: grid; gap: 14px;
}
.soldzone__note { font-size: 11.5px; color: var(--ink-3); }
.soldzone__row { display: flex; gap: 8px; justify-content: flex-end; align-items: center; }

/* delete zone */
.danger {
  border-top: 1px solid var(--hair);
  padding-top: 18px;
  display: grid; gap: 12px; justify-items: start;
}
.danger__confirm {
  width: 100%;
  padding: 14px;
  border-radius: var(--r-sm);
  background: var(--danger-w);
  border: 1px solid color-mix(in srgb, var(--danger) 28%, transparent);
  display: grid; gap: 12px;
}
.danger__ask { font-size: 13.5px; color: var(--danger); }
.danger__row { display: flex; gap: 8px; justify-content: flex-end; }

/* ── webcam capture ────────────────────────────────────────────────────
   Sits above the drawer and the manage sheet (41/43) because it is opened
   from inside the form, and above the lightbox (50) for the same reason. */
.camera {
  position: fixed; inset: 0; z-index: 60;
  display: grid; place-items: center;
  padding: 20px;
  background: rgba(14,10,4,.88);
  backdrop-filter: blur(6px);
  animation: fade .2s var(--step);
}
.camera__panel {
  width: min(620px, 100%);
  max-height: 92dvh;
  display: flex; flex-direction: column;
  background: var(--card);
  border: 1px solid var(--hair-2);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
  overflow: hidden;
}
.camera__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 14px clamp(16px, 4vw, 22px) 12px;
  border-bottom: 1px solid var(--hair);
}
.camera__title {
  font-family: var(--serif); font-size: 22px; font-weight: 400; line-height: 1.16;
}
.camera__stage {
  position: relative;
  aspect-ratio: 4 / 3;
  min-height: 0;
  background: #0b0906;
  display: grid; place-items: center;
  overflow: hidden;
}
.camera__video, .camera__shot {
  width: 100%; height: 100%;
  object-fit: cover;
  /* Mirrored, because a self-facing preview that moves the wrong way is
     unusable — the captured frame is drawn unmirrored on the canvas. */
  transform: scaleX(-1);
}
.camera__shot { transform: none; object-fit: contain; background: #0b0906; }
.camera__status {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: min(90%, 34ch); text-align: center;
  font-size: 13px; line-height: 1.5; color: #e7ddc9;
}
.camera__foot {
  display: flex; gap: 10px; align-items: center; justify-content: flex-end;
  padding: 13px clamp(16px, 4vw, 22px);
  padding-bottom: calc(13px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--hair);
  background: var(--paper-2);
}
.camera__foot .btn--quiet { margin-right: auto; }

/* ── lightbox ──────────────────────────────────────────────────────── */
.lightbox {
  position: fixed; inset: 0; z-index: 50;
  display: grid; place-items: center; gap: 14px;
  align-content: center;
  padding: 24px;
  background: rgba(14,10,4,.9);
  backdrop-filter: blur(6px);
  cursor: zoom-out;
  animation: fade .2s var(--step);
}
.lightbox__img {
  max-width: min(100%, 900px);
  max-height: 76dvh;
  object-fit: contain;
  border-radius: var(--r-sm);
  box-shadow: 0 30px 80px -20px rgba(0,0,0,.9);
  animation: zoomin .26s var(--step);
}
@keyframes zoomin { from { opacity: 0; transform: scale(.965); } }
.lightbox__cap {
  font-family: var(--serif);
  font-size: 16px; color: #f4ecdd; text-align: center;
}
.lightbox__close {
  position: absolute; top: 14px; right: 14px;
  width: 46px; height: 46px;
  display: grid; place-items: center;
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,.22);
  background: rgba(0,0,0,.35);
  color: #f4ecdd; font-size: 25px; line-height: 1; cursor: pointer;
}
.lightbox__close:hover { border-color: var(--gold-2); color: var(--gold-2); }

/* ── touch input sizing ────────────────────────────────────────────────
   iOS Safari zooms the viewport whenever a focused control renders below
   16px. The intake form is already 16px; these are the two chrome controls
   that sit below it, bumped only on touch devices so the desktop density
   of the sort bar is preserved. */
@media (pointer: coarse) {
  .search input,
  .sortbar__select { font-size: 16px; }
  /* Right side keeps clearance for the chevron. */
  .sortbar__select { padding-left: 12px; padding-right: 34px; }
}

/* ── demo sign-in gate ───────────────────────────────────────────────
   The ledger stays off-screen until the demo account is accepted. The
   class is stamped on <html> in the head script so the inventory never
   flashes. The paper background and gold rule stay; everything else
   yields to the card. */
html.is-locked {
  overflow: hidden;
}
html.is-locked .skip,
html.is-locked .masthead,
html.is-locked .notice,
html.is-locked .page,
html.is-locked .tally,
html.is-locked .fab,
html.is-locked .selbar,
html.is-locked .scrim,
html.is-locked .drawer,
html.is-locked .camera,
html.is-locked .lightbox {
  display: none !important;
}

.gate { display: none; }
html.is-locked .gate {
  display: grid;
  place-items: center;
  min-height: 100dvh;
  padding: 28px 20px calc(36px + env(safe-area-inset-bottom, 0px));
}

.gate__card {
  width: min(100%, 400px);
  background: var(--card);
  border: 1px solid var(--hair);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
  padding: 28px 26px 26px;
}
.gate__lockup { margin-bottom: 16px; }
.gate__card .rule-double { margin-bottom: 22px; }
.gate__title {
  font-family: var(--serif);
  font-size: 28px;
  font-weight: 400;
  letter-spacing: .04em;
  line-height: 1.1;
  margin: 0 0 8px;
}
.gate__lede {
  color: var(--ink-2);
  font-size: 14.5px;
  margin: 0 0 22px;
}
.gate__form { display: grid; gap: 16px; }
.gate__form .btn { margin-top: 4px; }

.masthead__out {
  padding-left: 8px;
  padding-right: 8px;
}
@media (max-width: 419px) {
  .masthead__out { font-size: 12px; }
}

[hidden] { display: none !important; }
