/* ============================================================
   ASH & SIGNAL
   The app is grey until Encore has money or a deadline to report.
   Greys are strictly achromatic (R=G=B) so the discipline is greppable;
   exactly ONE hue exists, and it means "there is something to act on".
   Money and a closing window share that hue and are told apart by the
   RAIL PATTERN — solid vs dashed — because pattern survives colour
   blindness and a second hue would not.
   ============================================================ */
:root, :root[data-theme='dark'] {
  --ground: #0a0a0a;   /* the page */
  --raise:  #121212;   /* cards, slabs, editors */
  --sunk:   #191919;   /* input wells, thumbnail backing */
  --glow:   #131313;

  --rule:   #2e2e2e;   /* hairline between rows */
  --rule-2: #3d3d3d;   /* section and card edges */
  --edge:   #6a6a6a;   /* a real control boundary: 3.6:1 on every ground */

  --ink-1:  #ededed;   /* names, live figures      15.0:1 */
  --ink-2:  #a6a6a6;   /* sub-lines                 7.7:1 */
  --ink-3:  #8a8a8a;   /* micro-labels              5.7:1  (was 2.72:1) */

  /* The one hue. */
  --sig:       #ff8a3d;
  --sig-money: #ff8a3d;  /* worth cash now: below what you paid, refund owed, back in stock */
  --sig-time:  #ff8a3d;  /* a window narrowing: same hue, DASHED rail */
  --sig-shut:  #6a6a6a;  /* past it: grey, because there is nothing to act on */
  --sig-tint:  #2a1a10;  /* pre-computed flat, not color-mix: a tint that snaps
                            mid-crossfade at dusk is the tell */
  --on-sig:    #0a0a0a;

  --rail: 3px;
  --gut: 12px;
  --r: 2px;
}

:root[data-theme='light'] {
  /* Not an inversion. Dark is an emissive display and its signal glows;
     light is ink on paper and its signal is a printing ink. */
  --ground: #f0f0f0;   /* not white, so slabs read as paper on a desk */
  --raise:  #ffffff;
  --sunk:   #e9e9e9;
  --glow:   #f6f6f6;

  --rule:   #dcdcdc;
  --rule-2: #c6c6c6;
  --edge:   #767676;

  --ink-1:  #151515;   /* 15.9:1 */
  --ink-2:  #565656;   /*  6.5:1 */
  --ink-3:  #646464;   /*  5.0:1 */

  --sig:       #b4530a;
  --sig-money: #b4530a;  /* 4.3:1 on the page, 5.0:1 on a card */
  --sig-time:  #b4530a;
  --sig-shut:  #6e6e6e;
  --sig-tint:  #fbeee4;
  --on-sig:    #ffffff;
}

/* Legacy names, so app.js's inline var(--accent) and every rule below keep
   working without a thousand-line find-and-replace. */
:root, :root[data-theme='dark'], :root[data-theme='light'] {
  --bg: var(--ground);
  --panel: var(--raise);
  --panel-2: var(--sunk);
  --ivory: var(--ink-1);
  --ink: var(--ink-1);
  --muted: var(--ink-2);
  --faint: var(--ink-3);
  --line: var(--rule);
  --line-strong: var(--rule-2);
  --instock: var(--sig-money);
  --low: var(--sig-time);
  --accent: var(--sig-money);

  --serif: 'IBM Plex Sans Condensed', 'Helvetica Neue', Arial, sans-serif;
  --sans: 'IBM Plex Sans Condensed', 'Helvetica Neue', Arial, sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', monospace;
}

:root, [data-theme='dark'] {
  color-scheme: dark;

  --ground: #1f1e1d;
  --raise: #262624;
  --sunk: #30302e;
  --glow: #262624;

  --rule: #3a3937;
  --rule-2: #4a4844;
  --edge: #6f6c64;

  --ink-1: #faf9f5;
  --ink-2: #b8b5ad;
  --ink-3: #9c9890;

  /* ONE hue. Orange means "there is something to do here" and nothing else
     in the app is coloured at all — so the states cannot be told apart by
     colour, and are not: money is a SOLID rail, a narrowing window is a
     DASHED one, and a shut window has no rail and a quieter row. That is
     stronger than three hues, because it survives colour blindness, a
     greyscale screenshot, and a phone in sunlight equally. */
  --sig: #d97757;        /* Claude's coral */
  --sig-money: var(--sig);
  --sig-time: var(--sig);
  --sig-shut: var(--ink-3);   /* nothing to act on: not a signal, just quiet */

  --sig-money-tint: #33241f;
  --sig-time-tint: #33241f;
  --sig-shut-tint: transparent;
  --on-sig: #1f1e1d;

  --focus: #d97757;
  --grain: 0;
}

[data-theme='light'] {
  color-scheme: light;

  --ground: #faf9f5;   /* Claude's warm off-white, never pure white */
  --raise: #ffffff;
  --sunk: #f0eee6;
  --glow: #ffffff;

  --rule: #e5e2d9;
  --rule-2: #d3cfc4;
  --edge: #8b8781;

  --ink-1: #1f1e1d;
  --ink-2: #5c5951;
  --ink-3: #6b6862;

  --sig: #b8532f;
  --sig-money: var(--sig);
  --sig-time: var(--sig);
  --sig-shut: var(--ink-3);

  --sig-money-tint: #f7ebe5;
  --sig-time-tint: #f7ebe5;
  --sig-shut-tint: transparent;
  --on-sig: #ffffff;

  --focus: #b8532f;
  --grain: 0;
}

/* Legacy names, aliased rather than chased through 1200 lines and three JS
   files — and it keeps the contrast test measuring what the app really paints. */
:root, [data-theme='dark'], [data-theme='light'] {
  --bg: var(--ground);
  --panel: var(--raise);
  --panel-2: var(--sunk);
  --ivory: var(--ink-1);
  --muted: var(--ink-2);
  --faint: var(--ink-3);
  --line: var(--rule);
  --line-strong: var(--rule-2);
  --instock: var(--sig-money);
  --low: var(--sig-time);
  --accent: var(--sig-shut);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* The evening changeover.
   Only while the theme is actually being swapped — applyTheme() adds this class
   and takes it off again. Left on permanently it would put a lazy fade on every
   hover and focus in the app, which reads as lag rather than as polish. */
.theming, .theming * {
  transition: background-color 0.35s ease, color 0.35s ease, border-color 0.35s ease !important;
}
@media (prefers-reduced-motion: reduce) {
  .theming, .theming * { transition: none !important; }
}

[hidden] { display: none !important; }

html { color-scheme: dark; }

body {
  background:
    radial-gradient(120% 60% at 50% -10%, var(--glow) 0%, transparent 60%),
    var(--bg);
  color: var(--ivory);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.55;
  min-height: 100vh;
}

.grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  /* Tokenised: full strength on an emissive dark screen, off entirely on
     paper, where noise over white is just dirt. */
  opacity: var(--grain);
  z-index: 10;
  background-image: 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='0.9' numOctaves='2'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncA type='linear' slope='0.05'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

.page {
  max-width: 1020px;
  margin: 0 auto;
  /* No top padding: the masthead is now a sticky strip that supplies its own. */
  padding: 0 var(--pad) 80px;
}

/* ---------- lock screen ---------- */

.lock {
  position: fixed;
  inset: 0;
  z-index: 50;
  background:
    radial-gradient(120% 60% at 50% -10%, var(--glow) 0%, transparent 60%),
    var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
}

.lock-card { width: min(360px, 86vw); text-align: center; }

.lock-title {
  font-family: var(--sans);
  font-weight: 600;
  font-size: 20px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
}

.lock-sub {
  margin: 8px 0 26px;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  font-size: 10px;
  color: var(--muted);
}

.lock-passkey { width: 100%; margin-bottom: 14px; padding: 14px; }

.lock form { display: flex; gap: 10px; }

.lock input {
  flex: 1;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--line-strong);
  color: var(--ivory);
  font-family: var(--mono);
  font-size: 14px;
  padding: 10px 2px;
  outline: none;
}
.lock input:focus { border-color: var(--ivory); }

/* ---------- masthead ---------- */

/* A status strip, not a cover.
   This is the single biggest change in the redesign and it is what pays for the
   density everywhere else: the old masthead spent a 96px italic Bodoni, a
   tagline, and a bordered meta panel — about 260px — before the first row of
   content. On a 390x844 phone that is a third of the screen, every time you
   open the app, to tell you the app's own name. It is now 34px, it sticks, and
   it carries the three things that are actually live. */

.masthead {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 14px;
  height: 34px;
  margin: 0 calc(var(--pad) * -1);
  padding: 0 var(--pad);
  background: var(--ground);
  border-bottom: 1px solid var(--rule-2);
}

.masthead-title { flex: 0 0 auto; }

.masthead h1 {
  font-family: var(--sans);
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  line-height: 1;
  color: var(--ink-1);
}

/* The red full stop was the only brand colour in the app, and it never told
   you anything. Unearned colour is the one thing this language does not do. */
.accent { display: none; }
.tagline { display: none; }

.masthead-meta {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 16px;
  overflow-x: auto;
  scrollbar-width: none;
  white-space: nowrap;
  border: none;
  padding: 0;
  font-size: 11px;
}
.masthead-meta::-webkit-scrollbar { display: none; }

.meta-row { display: flex; align-items: baseline; gap: 6px; padding: 0; }

.meta-key {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding: 0;
}

.meta-val { font-family: var(--mono); font-size: 11px; color: var(--ink-2); }
.meta-val.ok { color: var(--sig-money); }
.meta-val.warn { color: var(--sig-time); }

/* The buttons leave the strip and become a row under it, so the strip stays
   34px on a phone instead of wrapping to three lines. */
.meta-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin: 12px 0 0;
}

/* Auto / Light / Dark. A segmented control, grey like every other action. */
.theme-switch {
  display: inline-flex;
  margin: 12px 0 0;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  overflow: hidden;
}
.theme-opt {
  background: transparent;
  border: none;
  border-right: 1px solid var(--edge);
  color: var(--ink-3);
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 5px 10px;
  cursor: pointer;
}
.theme-opt:last-child { border-right: none; }
.theme-opt[aria-pressed='true'] { background: var(--ink-1); color: var(--ground); }

/* The double rule was the seam under a magazine masthead. There is no masthead
   to seam any more. */
.rule-double { display: none; }

/* ---------- tabs ---------- */

/* The tab bar is also the alarm panel.
   Six tabs means five of them are off screen at any moment, so this is the one
   place a signal may appear without its row being visible — a tab whose count
   is lit is the only way to learn that money appeared on a tab you are not
   looking at. */

.tabs {
  position: sticky;
  top: 34px;
  z-index: 19;
  display: flex;
  flex-wrap: nowrap;
  gap: 0;
  margin: 16px calc(var(--pad) * -1) 18px;
  padding: 0 var(--pad);
  background: var(--ground);
  border-bottom: 1px solid var(--rule-2);
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.tabs::-webkit-scrollbar { display: none; }

.tab {
  flex: 0 0 auto;
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--ink-3);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  padding: 10px 13px 8px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}
.tab:hover { color: var(--ink-2); }
/* Grey, not coloured. Selection is not a signal — you already know which tab
   you are on, and spending colour on it would spend the one thing that is
   supposed to mean money. */
.tab.selected { color: var(--ink-1); border-bottom-color: var(--ink-1); }
.tab-count { font-family: var(--mono); font-size: 9px; color: var(--ink-3); margin-left: 5px; }

/* ---------- orders ---------- */

.orders-summary {
  /* A hairline grid rather than a row of boxes: the container paints the rules
     and the tiles paint over them, so every separator costs exactly 1px and
     there is no border-collapse arithmetic to get wrong at 390px. */
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  background: var(--rule);
  border: 1px solid var(--rule);
  margin: 14px 0 16px;
}

.summary-tile {
  position: relative;
  min-width: 0;
  border: none;
  background: var(--ground);
  padding: 11px 12px;
}
/* An odd number of tiles leaves the grid's own background showing through the
   empty cell, which reads as a fourth, broken tile. The last one takes the
   whole row instead. */
.summary-tile:last-child:nth-child(odd) { grid-column: 1 / -1; }
.summary-num {
  display: block;
  font-family: var(--mono);
  font-weight: 500;
  font-size: clamp(15px, 4.4vw, 19px);
  line-height: 1.15;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--ink-1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.summary-lbl {
  display: block;
  margin-top: 3px;
  font-family: var(--mono);
  font-size: 9px;
  line-height: 1.25;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* A lit tile: a 2px bar across its top edge and the figure takes the signal.
   Two channels, so the meaning survives without colour vision. */
.summary-tile.win::before,
.summary-tile.marked::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
}
.summary-tile.win::before { background: var(--sig-money); }
.summary-tile.win .summary-num { color: var(--sig-money); }
.summary-tile.marked::before { background: var(--sig); }
.summary-tile.marked .summary-num { color: var(--sig); }

.order-card {
  border: 1px solid var(--line);
  margin-top: 16px;
  background: linear-gradient(180deg, var(--panel-2), transparent);
  animation: fadeUp 0.4s ease both;
}

.order-head {
  display: flex; justify-content: space-between; align-items: baseline; gap: 16px;
  flex-wrap: wrap;
  padding: 14px 18px;
  border-bottom: 1px solid var(--line);
}
.order-id { font-family: var(--mono); font-size: 12px; color: var(--muted); }
.order-date { font-family: var(--mono); font-size: 12px; color: var(--faint); margin-left: 10px; }
.order-status {
  margin-left: 12px; font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em; color: var(--faint);
}
.order-status.ok { color: var(--instock); }
.order-status.warn { color: var(--low); }
.order-total { font-family: var(--mono); font-size: 12px; color: var(--muted); }

.order-lines { padding: 4px 18px 14px; }

.order-line {
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
  transition: opacity 0.15s;
}

/* to-return tick */
.return-tick { position: relative; flex: 0 0 auto; cursor: pointer; align-self: center; }
.return-tick input { position: absolute; opacity: 0; width: 0; height: 0; }
.return-tick span {
  display: block;
  width: 17px; height: 17px;
  border: 1px solid var(--line-strong);
  transition: all 0.15s;
}
.return-tick:hover span { border-color: var(--ivory); }
.return-tick input:checked + span {
  background: var(--accent);
  border-color: var(--accent);
}
.return-tick input:checked + span::after {
  content: '';
  position: absolute;
  left: 6px; top: 2px;
  width: 4px; height: 9px;
  border: solid var(--ivory);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.order-line.to-return .order-line-name { text-decoration: line-through; color: var(--muted); }
.order-line.to-return { opacity: 0.72; }
.order-card.all-marked { border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.summary-tile.marked { border-color: color-mix(in srgb, var(--accent) 55%, transparent); }
.summary-tile.marked .summary-num { color: var(--accent); }
.order-line:last-child { border-bottom: none; }
/* Massimo Dutti sends a thumbnail with each line. Zara's order API sends none,
   so those are filled in from the catalogue during the price sweep — both shops
   land here looking the same. */
.order-thumb {
  width: 42px; height: 56px; object-fit: cover;
  flex: 0 0 auto; align-self: center;
  background: var(--panel-2); filter: saturate(0.92);
}
/* A line whose picture we refused — a colour swatch, or a photograph of a
   neighbouring reference — still holds its place. Without this the row loses a
   column and its name starts where every other row's picture is, which reads
   as a layout fault rather than as a missing photo. */
.order-thumb-none { display: block; }
/* MD's order feed never marks a return, so you clear the line yourself. */
.line-del {
  order: 99;
  background: none; border: none; cursor: pointer;
  color: var(--faint); font-size: 17px; line-height: 1; padding: 0 2px;
  align-self: center;
}
.line-del:hover { color: var(--accent); }
.order-line-main { flex: 1; min-width: 200px; }
.order-line-name { font-family: var(--sans); font-weight: 500; font-size: 15px; line-height: 1.25; display: block; }
/* No underline. In a language with no ornament, a rule under every product name
   is 84 horizontal lines on one page competing with the rules that separate the
   rows — and the name is obviously the link. */
.order-line-name a { color: inherit; text-decoration: none; }
.order-line-name a:hover { text-decoration: underline; text-underline-offset: 2px; }
/* Long Turkish+English names clamp rather than pushing the price off the row. */
.order-line-name a { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.order-line-name a:hover { border-bottom-color: var(--ivory); }
.order-line-sub { font-family: var(--mono); font-size: 11px; color: var(--faint); }
.order-line-price { font-family: var(--mono); font-size: 13px; text-align: right; white-space: nowrap; }
.order-line-price .paid { color: var(--faint); }
.order-line-price .paid::after { content: ' →'; color: var(--faint); }
/* An arrow is a promise that a second number follows. When one does not, the
   arrow has to go with it. */
.order-line-price .paid.solo::after { content: none; }
.order-line-price .now { color: var(--muted); margin-left: 8px; }
.order-line-price .now.down { color: var(--instock); }
.order-line-price .now.unknown { color: var(--faint); font-size: 11px; }

/* ---------- buttons ---------- */

.btn {
  font-family: var(--sans);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 11px;
  cursor: pointer;
  transition: all 0.18s ease;
  border-radius: 0;
}

.btn-ghost {
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--line);
  padding: 7px 12px;
}
.btn-ghost:hover { color: var(--ivory); border-color: var(--line-strong); }

.btn-solid {
  background: var(--ivory);
  color: var(--bg);
  border: 1px solid var(--ivory);
  padding: 12px 26px;
}
.btn-solid:hover { background: var(--accent); border-color: var(--accent); color: var(--ivory); }
.btn[disabled] { opacity: 0.45; pointer-events: none; }

.btn-danger:hover { color: var(--accent); border-color: var(--accent); }

/* ---------- add flow ---------- */

.add-label {
  display: block;
  font-family: var(--sans);
  font-weight: 600;
  font-size: 16px;
  margin-bottom: 12px;
}

.add-bar { display: flex; gap: 12px; }

.add-bar input {
  flex: 1;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--line-strong);
  color: var(--ivory);
  font-family: var(--mono);
  font-size: 13px;
  padding: 12px 2px;
  outline: none;
  transition: border-color 0.2s;
}
.add-bar input:focus { border-color: var(--ivory); }
.add-bar input::placeholder { color: var(--faint); }

.add-error { margin-top: 10px; color: var(--accent); font-size: 13px; }

/* preview card */

.preview {
  margin-top: 28px;
  border: 1px solid var(--line);
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  display: flex;
  gap: 26px;
  padding: 22px;
  animation: fadeUp 0.45s ease both;
}

.preview-img {
  width: 150px;
  min-height: 200px;
  object-fit: cover;
  background: var(--panel-2);
  filter: saturate(0.92);
}

.preview-body { flex: 1; }

.preview-name { font-family: var(--sans); font-weight: 600; font-size: 18px; line-height: 1.2; }

.preview-sub {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--muted);
  margin: 6px 0 16px;
}

.field-label {
  text-transform: uppercase;
  letter-spacing: 0.16em;
  font-size: 10px;
  color: var(--faint);
  margin: 14px 0 8px;
}

.chips { display: flex; flex-wrap: wrap; gap: 8px; }

.chip {
  border: 1px solid var(--line);
  background: transparent;
  color: var(--muted);
  font-family: var(--mono);
  font-size: 12px;
  padding: 6px 13px;
  cursor: pointer;
  transition: all 0.15s;
  position: relative;
}
.chip:hover { border-color: var(--line-strong); color: var(--ivory); }
.chip.selected { background: var(--ivory); color: var(--bg); border-color: var(--ivory); }
.chip.oos { opacity: 0.55; }
.chip .dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; margin-right: 7px; vertical-align: 1px; }
.dot.in_stock { background: var(--instock); }
.dot.low_on_stock { background: var(--low); }
.dot.out_of_stock, .dot.unknown { background: var(--accent); }
.dot.coming_soon, .dot.back_soon { background: var(--muted); }

.swatch {
  width: 26px; height: 26px;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  cursor: pointer;
  transition: transform 0.15s;
}
.swatch:hover { transform: scale(1.12); }
.swatch.selected { outline: 2px solid var(--ivory); outline-offset: 2px; }

.preview-actions { margin-top: 20px; display: flex; gap: 12px; }

/* ---------- collection ---------- */

.collection { margin-top: 56px; }

.collection-head {
  display: flex;
  align-items: baseline;
  gap: 14px;
  border-bottom: 1px solid var(--line-strong);
  padding-bottom: 10px;
}

/* Section heads are labels, not covers. 32px of display serif wrapped
   "Massimo Dutti orders" onto three lines and pushed the first real row of
   content off a 390px screen. */
.collection-head h2 {
  font-family: var(--sans);
  font-weight: 600;
  font-size: 17px;
  letter-spacing: -0.005em;
}

.count { font-family: var(--mono); font-size: 12px; color: var(--faint); }

/* Section rule for each product type on the watchlist. Deliberately quiet —
   it exists to break the list into readable runs, not to compete with the
   product names underneath it. */
.type-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 34px 0 0;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line-strong);
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.type-head:first-child { margin-top: 4px; }
.type-count {
  font-size: 10px;
  color: var(--faint);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 1px 7px;
}

.item {
  display: grid;
  grid-template-columns: 44px 96px 1fr auto;
  gap: 22px;
  align-items: center;
  padding: 22px 0;
  border-bottom: 1px solid var(--line);
  animation: fadeUp 0.45s ease both;
}

.item.paused { opacity: 0.45; }

.item-index {
  font-family: var(--mono);
  font-style: italic;
  font-size: 26px;
  color: var(--faint);
}

.item-img { width: 96px; height: 128px; object-fit: cover; background: var(--panel-2); filter: saturate(0.92); }

/* Wishlist pictures come from twenty different shops and are framed twenty
   different ways: a square bottle, a wide luggage cover, a 3:4 packshot, a
   lifestyle photo. `cover` fills the tile by cropping, which is right on the
   watchlist — every Zara and Massimo Dutti shot is the same portrait packshot —
   and wrong here, where it beheads the wide ones. `contain` shows the whole
   product and lets the tile absorb the difference. */
.wish-item .item-img {
  object-fit: contain;
  padding: 6px;
  background: var(--panel-2);
  border-radius: 2px;
}

/* A row whose shop will not give us a picture. Its initials, quietly — enough
   to read as deliberate rather than as a failed load. */
.no-img {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 20px;
  letter-spacing: 1px;
  color: var(--muted);
  opacity: 0.45;
}

.item-name { font-family: var(--sans); font-weight: 500; font-size: 15px; line-height: 1.25; }
.item-name a { color: inherit; text-decoration: none; }
.item-name a:hover { color: var(--accent); }

.item-sub { font-family: var(--mono); font-size: 12px; color: var(--muted); margin-top: 3px; }

.item-price { font-family: var(--mono); font-size: 14px; margin-top: 8px; }

.item-sizes { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }

.size-pill {
  font-family: var(--mono);
  font-size: 11px;
  border: 1px solid var(--line);
  padding: 3px 9px;
  color: var(--muted);
}
.size-pill.in_stock { color: var(--instock); border-color: color-mix(in srgb, var(--instock) 45%, transparent); }
.size-pill.low_on_stock { color: var(--low); border-color: color-mix(in srgb, var(--low) 45%, transparent); }

.item-event { font-size: 11px; color: var(--faint); margin-top: 8px; }
.item-event.restock { color: var(--instock); }
.item-event.sale { color: var(--low); }
.item-event.color { color: var(--instock); }

/* margin-left, because letter-spacing puts the gap AFTER the last letter, not
   before the first — so "TRY 2,490.00" and "SAVE" rendered welded together. */
.sale-badge {
  color: var(--low);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 10px;
  margin-left: 8px;
  white-space: nowrap;
}

.mode-cards { display: flex; gap: 10px; flex-wrap: wrap; }

.mode-card {
  flex: 1;
  min-width: 190px;
  text-align: left;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--muted);
  padding: 12px 14px;
  cursor: pointer;
  transition: all 0.15s;
}
.mode-card strong {
  display: block;
  font-family: var(--sans);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ivory);
  margin-bottom: 4px;
}
.mode-card span { font-size: 11px; line-height: 1.4; }
.mode-card:hover { border-color: var(--line-strong); }
.mode-card.selected { border-color: var(--ivory); background: var(--panel-2); }
.mode-card.selected strong { color: var(--instock); }
.item-error { font-size: 11px; color: var(--accent); margin-top: 8px; }

.item-actions { display: flex; flex-direction: column; gap: 8px; align-items: flex-end; }

.item-editor {
  grid-column: 1 / -1;
  margin-top: 16px;
  padding: 18px 20px;
  border: 1px solid var(--line);
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  animation: fadeUp 0.3s ease both;
}
.item-editor .field-label:first-child { margin-top: 0; }
.item-editor input[type="number"],
#target-price {
  width: 160px;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--ivory);
  font-family: var(--mono);
  font-size: 13px;
  padding: 8px 10px;
  outline: none;
}
.item-editor input[type="number"]:focus, #target-price:focus { border-color: var(--line-strong); }

.item-editor input[type="text"] {
  width: 260px;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--ivory);
  font-family: var(--mono);
  font-size: 13px;
  padding: 8px 10px;
  outline: none;
}
.item-editor input[type="text"]:focus { border-color: var(--line-strong); }
.edit-error { margin-top: 10px; }
.item-checked { font-family: var(--mono); font-size: 10px; color: var(--faint); }

/* ---------- massimo dutti (remote) section ---------- */

.md-row {
  display: grid;
  /* thumb · details · edit button, with the editor spanning underneath */
  grid-template-columns: 64px 1fr auto;
  gap: 18px;
  align-items: center;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.md-img { width: 64px; height: 84px; object-fit: cover; background: var(--panel-2); filter: saturate(0.92); }
.md-pending { opacity: 0.75; }

.rebought-badge {
  display: inline-block;
  margin-left: 10px;
  padding: 2px 8px;
  border: 1px solid var(--line);
  color: var(--faint);
  font-family: var(--sans);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

.drop-badge {
  display: inline-block;
  margin-left: 10px;
  padding: 2px 8px;
  background: color-mix(in srgb, var(--instock) 22%, transparent);
  border: 1px solid color-mix(in srgb, var(--instock) 55%, transparent);
  color: var(--instock);
  font-family: var(--sans);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

#md-sizes {
  width: 260px;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--ivory);
  font-family: var(--mono);
  font-size: 13px;
  padding: 8px 10px;
  outline: none;
}
#md-sizes:focus { border-color: var(--line-strong); }
.md-name { font-family: var(--sans); font-size: 15px; font-weight: 500; }
.md-name a { color: inherit; text-decoration: none; }
.md-name a:hover { color: var(--accent); }
.md-row .item-sizes { margin-top: 8px; }
.md-note { margin-top: 14px; font-size: 11px; color: var(--faint); }

/* ---------- empty & footer ---------- */

.empty { padding: 48px 0 24px; max-width: 560px; }

.empty-display {
  font-family: var(--sans);
  font-style: italic;
  font-size: 34px;
  margin-bottom: 18px;
}

.empty ol { padding-left: 20px; color: var(--muted); font-size: 14px; }
.empty li { margin: 8px 0; }
.mono { font-family: var(--mono); font-size: 0.92em; color: var(--ivory); }

.foot {
  margin-top: 70px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
  font-size: 11px;
  color: var(--faint);
  letter-spacing: 0.04em;
}

/* ---------- motion ---------- */

@keyframes fadeUp {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}

.reveal { animation: fadeUp 0.55s ease both; }
.masthead { animation-delay: 0.02s; }
.rule-double { animation-delay: 0.1s; }
.add { animation-delay: 0.18s; }
.collection-head { animation-delay: 0.26s; }
.foot { animation-delay: 0.34s; }

/* ---------- refunds ledger ---------- */

.money-head, .money-row {
  display: grid;
  grid-template-columns: 132px minmax(150px, 1fr) 108px 140px 140px 116px 28px;
  gap: 10px;
  align-items: center;
}
.money-head {
  font-family: var(--mono);
  font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--faint);
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}
.money-row { padding: 8px 0; border-bottom: 1px solid var(--line); transition: opacity 0.15s; }
.money-row.settled { opacity: 0.5; }
.money-row.settled .m-item { text-decoration: line-through; }

.m-field {
  width: 100%;
  background: transparent;
  border: 1px solid var(--line);
  color: var(--ivory);
  font-family: var(--mono);
  font-size: 12px;
  padding: 7px 8px;
}
.m-field:focus { outline: none; border-color: var(--line-strong); background: var(--panel-2); }
.m-amount { text-align: right; }
/* Past the date they said they'd pay — the whole point of the tab. */
.money-row.late .m-field[data-f='expectedBy'] { color: var(--accent); border-color: var(--accent); }
.m-del {
  background: none; border: none; cursor: pointer;
  color: var(--faint); font-size: 18px; line-height: 1;
  padding: 0;
}
.m-del:hover { color: var(--accent); }
.money-actions { display: flex; gap: 10px; margin-top: 18px; }

/* ---------- other-vendor purchases ---------- */

.other-head, .other-row {
  display: grid;
  grid-template-columns: 44px minmax(160px, 1fr) 82px 104px 104px 140px 28px;
  gap: 10px;
  align-items: center;
}
.other-head {
  font-family: var(--mono);
  font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--faint);
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}
.other-row { padding: 10px 0; border-bottom: 1px solid var(--line); }
.other-dot { width: 42px; height: 56px; background: var(--panel-2); display: block; }
.other-main { min-width: 0; }
.other-name {
  font-family: var(--sans); font-weight: 500; font-size: 15px; color: var(--ivory);
  text-decoration: none; display: block;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.other-name:hover { color: var(--accent); }
.other-sub { font-family: var(--mono); font-size: 11px; color: var(--faint); }
.other-sub a { color: var(--muted); }
/* Name and shop are editable in place: a link we could not resolve arrives
   nameless, and host-derived shop names read like "Marksandspencer". */
.other-name-input {
  width: 100%;
  background: transparent; border: 1px solid transparent;
  color: var(--ivory); font-family: var(--sans); font-weight: 500; font-size: 15px;
  padding: 2px 4px; margin-left: -4px;
}
.other-name-input::placeholder { color: var(--muted); font-family: var(--mono); font-size: 12px; }
.other-vendor-input {
  width: 11ch;
  background: transparent; border: 1px solid transparent;
  color: var(--faint); font-family: var(--mono); font-size: 11px;
  padding: 1px 3px; margin-left: -3px;
}
.other-name-input:hover, .other-vendor-input:hover { border-color: var(--line); }
.other-name-input:focus, .other-vendor-input:focus {
  outline: none; border-color: var(--line-strong); background: var(--panel-2);
}
.o-field[readonly] { color: var(--muted); border-style: dashed; cursor: default; }
.o-field.down { color: var(--instock); border-color: color-mix(in srgb, var(--instock) 45%, transparent); }
/* The saving badge sits under the row rather than squeezing the grid. */
.other-drop { grid-column: 2 / -1; justify-self: start; margin-top: 2px; }
/* Past the return date, there is nothing to reclaim — fade it out. */
.other-row.over { opacity: 0.5; }

@media (prefers-reduced-motion: reduce) {
  .reveal, .item, .preview { animation: none; }
}

/* Seven columns do not fit a tablet either, and that is where this bit.
   The refunds grid asks for 132+150+108+140+140+116+28 plus six 10px gaps —
   874px — and .page gives content 56px less than the viewport. So it fits at
   1024 and overflows by ninety-odd at 834, which is an iPad Pro 11 held
   upright. The stacking below was written for phones and gated at 720, leaving
   every tablet width in between with a table wider than the screen.

   That is why the whole site scrolled sideways rather than just this tab: one
   element past the viewport makes the DOCUMENT horizontally scrollable, and on
   iOS any slightly diagonal swipe then pans the page. The tab it happens on
   does not have to be the tab you are looking at. */
/* A field and its label.

   On a wide screen the head row names the columns, so the wrapper must be
   invisible to the grid or it would collapse seven columns into one:
   display:contents lifts the input straight back into the row. On a phone the
   head row is hidden — it cannot fit — and an unlabelled date box beside
   another unlabelled date box is a guessing game, so the label appears and the
   wrapper becomes a real cell. */
.fld { display: contents; }
.fld-l { display: none; }

@media (max-width: 960px) {
  /* Seven fixed columns do not fit, and the head row that names them is the
     first thing to go — so every field carries its own label from here down.

     minmax(0, …) on every track, because `1fr` is really `minmax(auto, 1fr)`
     and an <input>'s min-content width is about twenty characters: the tracks
     could not shrink below that, so the row overflowed and the browser
     resolved the Other-orders grid to FIVE columns, one of them 18px and one
     of them 0px wide. Measured at 390px. */
  .money-head, .other-head { display: none; }
  .fld { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
  .fld-l {
    display: block;
    font-family: var(--mono);
    font-size: 9px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--faint);
    white-space: nowrap;
  }
  .fld .m-field { width: 100%; }
  .m-field { padding: 6px 8px; }

  /* Every field gets a NAMED home. Letting them fall into the next free cell is
     what produced the screenshots: on the Other-orders card the first column is
     the thumbnail's, so auto-placement dropped "Paid" into a 56px-wide track and
     sheared "9349.00" in half, while "Now" got the whole rest of the row. Two
     amounts that exist to be compared, at a five-to-one width ratio. */
  .money-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-areas:
      'vendor del'
      'item   item'
      'amount sent'
      'chase  status';
    gap: 10px;
    padding: 16px 0;
    align-items: end;
  }
  .money-row > select[data-f='vendor'] { grid-area: vendor; }
  .money-row .f-item { grid-area: item; }
  .money-row .f-amount { grid-area: amount; }
  .money-row .f-sent { grid-area: sent; }
  .money-row .f-chase { grid-area: chase; }
  .money-row .f-status { grid-area: status; }
  .money-row .m-del { grid-area: del; justify-self: end; align-self: center; }

  .other-row {
    grid-template-columns: 56px minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas:
      'thumb main main'
      'size  paid now'
      'ret   ret  del'
      'badge badge badge';
    gap: 10px;
    padding: 16px 0;
    align-items: end;
  }
  .other-row > .order-thumb,
  .other-row > .other-dot { grid-area: thumb; align-self: start; width: 56px; height: 74px; }
  .other-main { grid-area: main; min-width: 0; align-self: start; }
  .other-row .f-size { grid-area: size; }
  .other-row .f-paid { grid-area: paid; }
  .other-row .f-now { grid-area: now; }
  .other-row .f-ret { grid-area: ret; }
  .other-row .m-del { grid-area: del; justify-self: end; align-self: center; }
  /* grid-area names the row AND the column, so a `grid-column` after it undoes
     half of what it just did — that is how the saving badge ended up in a 56px
     column setting one word per line. */
  .other-row .drop-badge {
    grid-area: badge;
    justify-self: start;
    max-width: 100%;
    white-space: normal;
  }

  /* A name too long for the card should taper off, not look sawn through at the
     bezel — an input clips flush by default. */
  .other-name-input { font-size: 15px; text-overflow: ellipsis; }
  .other-main .order-status {
    display: block;
    width: fit-content;
    margin-top: 6px;
    font-size: 10px;
    padding: 2px 7px;
    white-space: normal;
  }
}

/* iOS gives a date input an intrinsic width of its own and centres the text
   inside it, so on a phone "Sent back" reached past the right edge of the
   screen while its own box stayed unfinished. Take the native metrics off. */
input[type='date'].m-field {
  min-width: 0;
  -webkit-appearance: none;
  appearance: none;
}
input[type='date'].m-field::-webkit-date-and-time-value {
  text-align: left;
  margin: 0;
}

@media (max-width: 720px) {
  /* Nothing may push the PAGE sideways. Anything genuinely too wide for a phone
     scrolls inside its own container instead — five tabs never fit 375px, and a
     page that drifts left-right makes every other interaction feel broken. */
  html, body { overflow-x: hidden; max-width: 100%; }
  .tabs { overflow-x: auto; flex-wrap: nowrap; scrollbar-width: none; }
  .tabs::-webkit-scrollbar { display: none; }
  .tab { flex: 0 0 auto; }

  /* Grid and flex children default to min-width:auto, so a long product name or
     a date input refuses to shrink and shoves the row past the viewport. */
  .m-field, .o-field, .order-line-main, .other-main, .item, .money-row, .other-row {
    min-width: 0;
  }
  /* An order line is a picture, a name and a price, and on a phone the flex
     version broke that into four full-width bands: tick and thumbnail orphaned
     on one row, the name on the next, the price floated right on a third with
     nothing beside it. The price stopped reading as belonging to the item above
     it, which is the one relationship the row exists to show.

     A grid keeps the thumbnail and the text on the same line — the shape the
     desktop row already has — and puts the price directly under its own name
     rather than out on the margin. */
  .order-line {
    display: grid;
    grid-template-columns: auto auto minmax(0, 1fr) auto;
    grid-template-areas:
      "tick thumb main  del"
      "tick thumb price price"
      "extra extra extra extra";
    align-items: start;
    column-gap: 12px;
    row-gap: 3px;
    padding: 14px 0;
  }
  .return-tick { grid-area: tick; align-self: start; margin-top: 3px; }
  .order-thumb { grid-area: thumb; align-self: start; }
  .order-line-main { grid-area: main; flex-basis: auto; }
  /* Left, not right: under the name it belongs to, not against the far edge.
     And allowed to wrap — "TRY 2,550.00 → TRY 2,350.00" does not fit beside a
     thumbnail on a 375px screen, and nowrap pushed the second half off it. */
  .order-line-price {
    grid-area: price;
    text-align: left;
    margin-left: 0;
    white-space: normal;
  }
  .order-line-price .now { margin-left: 6px; }
  /* The badge gets a full-width row of its own, and must SPAN every column:
     giving it a two-column area made its text set the width of the tick and
     thumbnail columns, which starved the name column until it wrapped one word
     per line. */
  .drop-badge, .rebought-badge {
    grid-area: extra;
    white-space: normal;
    justify-self: start;
    max-width: 100%;
    margin-top: 8px;
  }
  /* Beside the name, not below the row.
     It used to share the badge's row, so a line with nothing to say about a
     price — most of them — spent a whole row on one 17px glyph floating in
     space. Up here it also sits where it does on a wide screen: last column,
     top right. */
  .line-del { grid-area: del; align-self: start; justify-self: end; margin-top: 0; }
  /* Two tiles to a row leaves each about 172px, and 16px of padding either
     side leaves 140 for the number. "TRY 109,250.00" at 19px mono wants 160,
     so the largest figures on the page — the ones the tile exists for — were
     the ones pressed against its edges. */
  .summary-tile { min-width: 118px; padding: 12px 13px; }
  .summary-num { font-size: 15px; }
  img, .order-thumb, .md-img, .item-img { max-width: 100%; }

  /* A shop title can run to a hundred characters — L'Occitane names a refill in
     English and Turkish and then adds the size. At 22px serif that was five
     lines of heading before the price, and the row below it was off-screen. */
  .item-name {
    font-size: 17px;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  /* The strikethrough and the badge get their own line when they do not fit,
     rather than being sliced by the viewport edge. */
  .item-price { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 2px; }

  /* One token drives the page padding AND the bleed of the two sticky strips.
     They were hardcoded to 28px, so on a phone they hung 10px past each edge
     and made the whole document scrollable sideways. */
  :root { --pad: 18px; }
  .page { padding: 0 var(--pad) 60px; }
  .masthead-meta { border-left: none; padding-left: 0; width: 100%; }
  .item { grid-template-columns: 72px 1fr; }
  .item-index { display: none; }
  .item-img { width: 72px; height: 96px; }
  /* On a phone the actions sit in a row under the card, and four of them do not
     fit 375px — without wrapping, the last button was simply cut off past the
     right edge, which is worse than useless because Remove was the one you
     could not reach. Wrap them, let each keep its label on one line, and let
     the "checked Nm ago" note take a full line of its own rather than eating
     the width every button then has to fight for. */
  .item-actions {
    flex-direction: row;
    flex-wrap: wrap;
    grid-column: 2;
    justify-content: flex-start;
    align-items: center;
    gap: 8px;
    width: 100%;
    min-width: 0;
  }
  .item-actions .btn { white-space: nowrap; }
  .item-checked { flex: 1 0 100%; }
  .preview { flex-direction: column; }
  .preview-img { width: 100%; height: 240px; }
}

/* Delivery line in the order header: an estimate while it is moving, the actual
   date once it lands — that date is what the return window counts from. */
.order-delivery {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
  margin-left: 10px;
}
.order-delivery.late { color: var(--low); }

/* ---------- Wishlist ---------- */
/* Wider than a watchlist row on purpose: no size pills, no event log, so the
   name and the price carry it. */
.wish-row {
  display: grid;
  grid-template-columns: 64px 1fr auto auto;
  gap: 18px;
  align-items: center;
  padding: 18px 0;
  border-bottom: 1px solid var(--line);
}
.wish-main { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
/* Match the watchlist's typography — these are the same kind of object, just
   without the alerting. */
.wish-name { font-family: var(--sans); font-weight: 500; font-size: 15px; line-height: 1.25; color: inherit; text-decoration: none; }
.wish-name:hover { color: var(--accent); }
.wish-sub { font-family: var(--mono); font-size: 11px; color: var(--faint); }
.wish-price { font-family: var(--mono); font-size: 14px; color: var(--muted); white-space: nowrap; }
.wish-actions { display: flex; gap: 8px; }
.wish-actions .btn[disabled] { opacity: 0.35; cursor: not-allowed; }
/* What the price used to be, when the shop says so. Struck through and quiet:
   the number that matters is the one you would pay. */
.wish-was {
  margin-left: 8px;
  font-size: 12px;
  color: var(--muted);
  text-decoration: line-through;
}

/* What it has cost before. Quiet by default — this is context for the price
   above it, not a second price — and only loud in the one state worth acting
   on, which is "you will not do better than today". */
.wish-low {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--faint);
  margin-top: 3px;
}
.wish-low-note { margin-left: 8px; color: var(--faint); }
.low-badge {
  color: var(--instock);
  border: 1px solid color-mix(in srgb, var(--instock) 45%, transparent);
  padding: 1px 7px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 10px;
}

@media (max-width: 720px) {
  .wish-row { grid-template-columns: 48px 1fr; row-gap: 10px; }
  .wish-price { grid-column: 2; justify-self: start; }
  .wish-actions { grid-column: 2; }
}

/* Reference + colour, split out of the product name so the name stays short. */
.md-tag {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--faint);
  margin-top: 2px;
}
/* Long marketing names still happen; clamp rather than truncate the data. */
.md-name { overflow-wrap: anywhere; }
.md-name a { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

/* Edit affordance on a Massimo Dutti watch row. */
.md-row { position: relative; }
.md-actions { align-self: start; }
.md-editor { grid-column: 1 / -1; padding-top: 14px; border-top: 1px solid var(--line); margin-top: 12px; }
.md-editor .field-label { margin-top: 12px; }

@media (max-width: 720px) {
  .md-row { grid-template-columns: 56px 1fr; }
  .md-actions { grid-column: 2; justify-self: start; }
}

/* Massimo Dutti rows reuse the watchlist card, minus the pause/remove controls
   the Mac watcher owns rather than the server. */
.md-item .item-index { color: var(--faint); }
.item-price .was { color: var(--faint); text-decoration: line-through; margin-left: 6px; }
.md-editor { grid-column: 1 / -1; padding-top: 14px; border-top: 1px solid var(--line); margin-top: 12px; }

/* How an order is arriving. Store collection is the intent, so it reads as
   normal; a courier or drop point is flagged because it is the case you would
   want to catch while the order can still be changed. */
.order-method {
  font-family: var(--mono);
  font-size: 11px;
  padding: 2px 7px;
  border: 1px solid var(--line-strong);
  margin-left: 8px;
  white-space: nowrap;
}
.order-method.wanted { color: var(--instock); border-color: color-mix(in srgb, var(--instock) 45%, transparent); }
.order-method.unwanted { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 55%, transparent); }

/* Whether your store is taking collections. Derived from what Massimo Dutti
   stamps on each order, so it states its own provenance rather than implying a
   live check. */
/* display:block is load-bearing, not tidiness. storeCollectionLine returns a
   <span>, and vertical padding, borders and margins on an INLINE box do not
   grow the line box — so the bordered panel painted over whatever followed it,
   which on the watchlist is the first group heading. */
.store-status {
  display: block;
  font-size: 13px;
  line-height: 1.5;
  padding: 12px 14px;
  margin-bottom: 18px;
  border: 1px solid var(--line-strong);
  border-left-width: 3px;
}
.store-status.ok { border-left-color: var(--instock); }
.store-status.warn { border-left-color: var(--accent); }
.store-status strong { font-family: var(--mono); font-size: 12px; letter-spacing: 0.02em; }
.store-note { display: block; margin-top: 5px; font-size: 11px; color: var(--faint); }

/* The price target sits with the size pills and follows the same grammar:
   grey while today's price is above it, green once it is beaten. */
.price-pill { letter-spacing: 0.02em; }

/* Wishlist rows are watchlist cards without the alerting, so they inherit
   .item wholesale; only the quiet bits differ. */
.wish-item .item-price.muted { color: var(--faint); font-style: italic; }

/* What the price has been before — see wishLow() in format.js. Deliberately
   quieter than the price itself: it is context for a decision, not the number
   you would pay. */
.wish-low {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--muted);
  margin-top: 4px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 8px;
}
.wish-low-note { color: var(--faint); }
.low-badge {
  color: var(--instock);
  border: 1px solid color-mix(in srgb, var(--instock) 45%, transparent);
  padding: 2px 7px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 10px;
}

/* Where the parcel has got to. Sits under the item name on an Other-orders
   row, quiet unless it says something has arrived. */
.order-status { display: inline-block; margin-top: 6px; font-size: 11px; }
/* The 12px indent belongs to the Zara order HEADER, where this class also lives
   and sits after an order number. Here it is the first thing on its line, so
   that indent just knocked the pill out of line with the name above it. */
.other-main .order-status { margin-left: 0; }

/* ============================================================
   THE SIGNAL RAIL
   Every row carries a 3px rail down its left edge. Grey rows have none, so the
   left margin of the page becomes a scanning column: you flick down it with a
   thumb and only the lit rows stop you. This is the mechanic that lets the app
   be dense without being flat — nothing needs to shout in the row itself.

   The state is read from the classes the renderers ALREADY emit, using :has(),
   so no JavaScript changed to light these up. A row is:
     money  it holds a saving badge, a lowest-yet badge, or a price marked down
     time   its deadline is close, or its refund is overdue
     shut   its window has closed, or it is marked to go back

   Three axes carry each meaning at once — hue, rail pattern, and the wording of
   the badge itself — so none of it depends on colour vision.
   ============================================================ */

.item, .order-line, .money-row, .other-row, .wish-item, .md-row {
  position: relative;
}
.item::before, .order-line::before, .money-row::before,
.other-row::before, .wish-item::before, .md-row::before {
  content: '';
  position: absolute;
  left: calc(var(--gut) * -1);
  top: 0;
  bottom: 0;
  width: var(--rail);
  background: none;
  pointer-events: none;
}

/* MONEY — solid rail. */
.order-line:has(.drop-badge)::before,
.other-row:has(.drop-badge)::before,
.wish-item:has(.low-badge)::before,
.item:has(.item-event.restock)::before,
.item:has(.item-event.sale)::before {
  background: var(--sig-money);
}
/* TIME — dashed, because a narrowing window is not a fact but a countdown. */
.money-row.late::before,
.order-line:has(.size-pill.low_on_stock)::before {
  background: repeating-linear-gradient(180deg, var(--sig-time) 0 5px, transparent 5px 9px);
}
/* SHUT — no colour at all. There is nothing left to act on, so it does not get
   to use the one hue the app has. A hairline in the gutter and a quieter row. */
.other-row.over::before,
.order-line.to-return::before,
.money-row.settled::before {
  background: var(--rule-2);
}
.other-row.over, .money-row.settled { opacity: 0.55; }

/* The gutter the rail lives in. Rows already had their own padding; this only
   reserves the strip, so nothing reflows. */
.order-lines, #items, #wish-rows, #other-rows, #money-rows, #md-items {
  padding-left: var(--gut);
}

/* ---------- one pill, four states ---------- */

/* Grey unless it is telling you something. The lit version puts the signal on
   the border at FULL strength — a faded color-mix border sits at about 2:1 and
   stops being a boundary at all. */
.size-pill, .chip, .order-status, .order-method, .drop-badge,
.low-badge, .rebought-badge, .price-pill {
  display: inline-block;
  border: 1px solid var(--edge);
  border-radius: var(--r);
  background: transparent;
  color: var(--ink-2);
  font-family: var(--mono);
  font-weight: 500;
  font-size: 10px;
  line-height: 1.35;
  letter-spacing: 0;
  padding: 2px 7px;
  text-transform: none;
}

.size-pill.in_stock, .drop-badge, .low-badge,
.price-pill.in_stock, .o-field.down {
  border-color: var(--sig-money);
  color: var(--sig-money);
  background: var(--sig-money-tint);
}
.size-pill.low_on_stock {
  border-color: var(--sig-time);
  color: var(--sig-time);
  background: var(--sig-time-tint);
}
.size-pill.out_of_stock, .rebought-badge {
  border-color: var(--sig-shut);
  color: var(--sig-shut);
  background: var(--sig-shut-tint);
}
.size-pill.unknown { border-color: var(--rule-2); color: var(--ink-3); background: transparent; }

/* ---------- buttons are grey. always. ---------- */

/* The inverse of the usual dashboard, where the primary action is the loudest
   thing on screen. Here the loudest thing is whatever is worth money; an action
   is something you already came here to do. */
.btn {
  font-family: var(--mono);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 10px;
  cursor: pointer;
  border-radius: var(--r);
  padding: 7px 12px;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.btn-solid { background: var(--ink-1); color: var(--ground); border: 1px solid var(--ink-1); }
.btn-solid:hover { background: var(--ink-2); border-color: var(--ink-2); }
.btn-ghost { background: transparent; color: var(--ink-2); border: 1px solid var(--edge); }
.btn-ghost:hover { color: var(--ink-1); border-color: var(--ink-1); }
.btn-danger { color: var(--sig-shut); border-color: var(--sig-shut); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }

:where(button, input, select, a):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}

/* The row of actions under the status strip. */
.control-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 14px;
}
.control-bar .meta-actions,
.control-bar .theme-switch { margin: 0; }

/* A parcel's status is information, not a signal.
   "Siparişiniz teslim edildi" is true of almost every row, so painting it the
   money colour lit the whole page and spent the one thing that is supposed to
   mean money. Delivery state stays grey; what it UNLOCKS — a return window, a
   price below what you paid — is what gets to be loud. */
.order-status, .order-status.in_stock, .order-status.warn, .order-status.unknown {
  border-color: var(--rule-2);
  color: var(--ink-3);
  background: transparent;
}

/* ============================================================
   Claude's geometry: rounded, soft-edged, low-contrast borders.
   Applied last so it overrides the square treatment underneath rather than
   editing a hundred rules in place.
   ============================================================ */

.order-card, .preview, .item-editor, .md-editor, .store-status,
.mode-card, .lock-card, .empty { border-radius: var(--r); }

.orders-summary { border-radius: var(--r); overflow: hidden; }

.size-pill, .chip, .order-status, .order-method, .drop-badge, .low-badge,
.rebought-badge, .price-pill, .theme-switch, .btn { border-radius: var(--r-sm); }

.m-field, .o-field, .add-bar input, .lock input, .item-editor input,
.other-name-input, .other-vendor-input, select {
  border-radius: var(--r-sm);
}

.order-thumb, .other-dot, .item-img, .md-img, .preview-img, .wish-thumb {
  border-radius: var(--r-sm);
}

/* The primary action is coral — the one place Claude spends its accent on a
   control rather than on a state. Everything else stays neutral, so the page
   still reads as "quiet until something matters". */
.btn-solid {
  background: var(--sig-shut);
  border-color: var(--sig-shut);
  color: #fff;
}
.btn-solid:hover { background: var(--sig-shut); border-color: var(--sig-shut); filter: brightness(1.08); }

.theme-opt[aria-pressed='true'] { background: var(--sig-shut); color: #fff; }

/* Softer than the instrument version: Claude's surfaces separate by tone, not
   by a hard line. */
.order-line, .item, .wish-item, .money-row, .other-row {
  border-bottom-color: var(--rule);
}

/* Inputs read as wells, which is the single most Claude-ish detail after the
   corner radius. */
.m-field, .o-field, .add-bar input, .item-editor input, select {
  background: var(--sunk);
  border-color: transparent;
}
.m-field:focus, .o-field:focus, .add-bar input:focus, select:focus {
  background: var(--raise);
  border-color: var(--edge);
}
.o-field[readonly] { background: transparent; border-color: var(--rule); }

/* The two footers.
   The page-level one said "checks run with a little jitter, so Zara sees a
   polite trickle" — a nice sentence about an implementation detail, printed
   under every tab forever. It is gone.

   The per-tab note stays, because it says something you cannot work out from
   the rows (why MD prices are read on the Mac, why refunds are hand-kept), but
   it stops being a paragraph: one quiet line, marked with a rule rather than
   floating in the margin. */
.foot { display: none; }

.md-note {
  margin-top: 18px;
  padding-left: 10px;
  border-left: 2px solid var(--rule);
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-3);
  max-width: 62ch;
}

/* ============================================================
   ASH & SIGNAL — the design layer.
   Everything above this point is layout: grids, template areas, the
   labelled-field contract, the mobile breakpoints. None of it changes.
   This section changes only how it LOOKS.
   ============================================================ */

/* No ornament. The old language leaned on a film-grain overlay, a display
   serif and a red full stop; none of them told you anything. */
.grain { display: none; }
* { box-shadow: none; }

body {
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.45;
  background: var(--ground);
}

/* Figures are mono everywhere, and monospaced by construction rather than by
   asking a proportional face for `tnum` — the one thing this app cannot afford
   is two prices that do not line up. */
.item-price, .order-line-price, .summary-num, .meta-val, .order-total,
.order-id, .order-date, .m-field, .o-field, .wish-low, .count, .tab-count,
.item-sub, .other-sub, .size-pill, .mono, .money-head, .other-head {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums lining-nums;
  letter-spacing: -0.01em;
}

/* ---- masthead: a status strip, not a cover ----
   The old one spent about 165px of a 844px screen on a 96px italic wordmark
   and a tagline. That is an order line and a half, above the fold, every time
   you open it. */
.masthead {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; gap: 14px;
  height: 38px; margin: -48px calc(-28px) 0; padding: 0 28px;
  background: var(--ground); border-bottom: 1px solid var(--rule-2);
  flex-wrap: nowrap;
}
.masthead-title { flex: 0 0 auto; }
.masthead h1 {
  font-family: var(--sans);
  font-style: normal; font-weight: 600;
  font-size: 15px; letter-spacing: 0.28em; text-transform: uppercase;
  line-height: 1; color: var(--ink-1);
}
.masthead h1 .accent { display: none; }   /* no unearned colour */
.tagline { display: none; }
.masthead-meta {
  flex: 1 1 auto; min-width: 0;
  display: flex; gap: 16px; align-items: center;
  border: none; padding: 0;
  overflow-x: auto; white-space: nowrap; scrollbar-width: none;
}
.masthead-meta::-webkit-scrollbar { display: none; }
.meta-row { display: flex; align-items: baseline; gap: 5px; padding: 0; }
.meta-key { font-size: 9px; letter-spacing: 0.12em; color: var(--ink-3); padding: 0; }
.meta-val { font-size: 11px; color: var(--ink-2); }
.meta-actions, .theme-switch { display: flex; gap: 6px; flex: 0 0 auto; }
.rule-double { display: none; }

/* ---- tabs: the alarm panel ----
   The one place a signal may appear without its row being on screen, because
   five of the six tabs always are not. */
.tabs {
  position: sticky; top: 38px; z-index: 19;
  gap: 0; margin: 0 -28px 14px; padding: 0 28px;
  background: var(--ground);
  border-bottom: 1px solid var(--rule-2);
  overflow-x: auto; scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  flex: 0 0 auto;
  font-family: var(--mono); font-size: 10px; font-weight: 500;
  letter-spacing: 0.1em; padding: 10px 12px 8px;
  color: var(--ink-3); border-bottom: 2px solid transparent;
}
.tab.selected { color: var(--ink-1); border-bottom-color: var(--ink-1); }  /* grey, not colour */
.tab-count { font-size: 9px; color: var(--ink-3); opacity: 1; }
.tab.selected .tab-count { color: var(--ink-2); }

/* ---- the signal rail ----
   Every row lights its own left margin, so you flick down the left edge with
   your thumb and only the rows that want something stop you. Derived from
   classes the renderers already emit — :has() means no JS change, and the row
   cannot disagree with its own badge. */
.item, .order-line, .money-row, .other-row, .wish-item, .md-row {
  position: relative;
  padding-left: var(--gut);
}
.item::before, .order-line::before, .money-row::before,
.other-row::before, .wish-item::before, .md-row::before {
  content: ''; position: absolute; left: 0; top: 6px; bottom: 6px;
  width: var(--rail); background: none;
}
/* money: solid */
.order-line:has(.drop-badge)::before,
.other-row:has(.drop-badge)::before,
.wish-item:has(.low-badge)::before,
.money-row:has(option[value='refunded']:checked)::before {
  background: var(--sig-money);
}
/* a closing window: SAME hue, dashed. Pattern is what distinguishes them,
   because a second hue would not survive deuteranopia. */
.money-row.late::before,
.item:has(.size-pill.low_on_stock)::before {
  background: repeating-linear-gradient(180deg, var(--sig-time) 0 5px, transparent 5px 9px);
}
/* shut: grey. There is nothing to act on, so it does not get the hue. */
.other-row.over::before, .order-line.to-return::before { background: var(--sig-shut); }

/* ---- one pill, four states ---- */
.size-pill, .chip, .order-status, .drop-badge, .low-badge,
.rebought-badge, .price-pill {
  display: inline-block;
  border: 1px solid var(--edge); border-radius: var(--r);
  background: transparent; color: var(--ink-2);
  font-family: var(--mono); font-size: 10px; font-weight: 500;
  letter-spacing: 0; padding: 2px 7px; text-transform: none;
}
.size-pill.in_stock, .order-status.in_stock, .low-badge, .drop-badge, .price-pill.in_stock {
  color: var(--sig-money); border-color: var(--sig-money); background: var(--sig-tint);
}
.size-pill.low_on_stock { color: var(--sig-time); border-color: var(--sig-time); }
.size-pill.out_of_stock, .size-pill.unknown { color: var(--ink-3); border-color: var(--rule); }
.o-field.down { color: var(--sig-money); border-color: var(--sig-money); }
.order-line-price .now.down { color: var(--sig-money); }

/* ---- summary tiles: a hairline grid, not a row of boxes ---- */
.orders-summary {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px; background: var(--rule); border: 1px solid var(--rule);
  margin: 14px 0; flex-wrap: initial;
}
.summary-tile {
  flex: initial; min-width: 0; border: 0; border-radius: 0;
  background: var(--ground); padding: 10px 12px;
}
.summary-num {
  font-family: var(--mono); font-size: clamp(15px, 4.4vw, 19px); font-weight: 500;
  color: var(--ink-1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.summary-lbl { font-size: 9px; letter-spacing: 0.1em; color: var(--ink-3); }
.summary-tile.win, .summary-tile.marked { border: 0; }
.summary-tile.win .summary-num, .summary-tile.marked .summary-num { color: var(--sig-money); }
.summary-tile.win { box-shadow: inset 0 2px 0 0 var(--sig-money) !important; }
.summary-tile.marked { box-shadow: inset 0 2px 0 0 var(--sig-shut) !important; }

/* ---- surfaces ---- */
.order-card, .preview, .item-editor, .md-editor {
  background: var(--raise); border: 1px solid var(--rule-2); border-radius: 0;
}
.order-head { border-bottom: 1px solid var(--rule); }
.order-card.all-marked { border-color: var(--edge); }
.item-img, .order-thumb, .md-img, .other-dot, .order-thumb-none {
  background: var(--sunk); border-radius: 0; filter: none;
}

/* ---- type ---- */
.item-name, .order-line-name, .other-name, .wish-name, .md-name, .preview-name {
  font-family: var(--sans); font-style: normal; font-weight: 500;
  font-size: 15px; line-height: 1.25; letter-spacing: 0;
}
.item-name { font-size: 16px; }
.collection-head h2 {
  font-family: var(--sans); font-style: normal; font-weight: 600;
  font-size: 17px; letter-spacing: -0.005em;
}
.type-head {
  font-family: var(--mono); font-size: 10px; font-weight: 500;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-3);
}
.add-label, .field-label, .fld-l { font-family: var(--mono); color: var(--ink-3); }

/* ---- controls ---- */
.btn {
  font-family: var(--mono); font-weight: 500; font-size: 10px;
  letter-spacing: 0.1em; border-radius: var(--r);
  border: 1px solid var(--edge); background: transparent; color: var(--ink-2);
  padding: 7px 12px;
}
.btn:hover { color: var(--ink-1); border-color: var(--ink-1); }
/* Actions are grey; only STATES get the hue. The inverse of every dashboard,
   where the primary button is the loudest thing on screen — here the loudest
   thing is always something the app is telling you. */
.btn-solid { background: var(--ink-1); color: var(--ground); border-color: var(--ink-1); }
.btn-solid:hover { background: var(--ink-2); border-color: var(--ink-2); color: var(--ground); }
.btn-danger { color: var(--ink-3); }
.btn-danger:hover { color: var(--sig-shut); border-color: var(--sig-shut); }
.m-field, .o-field, .lock input, .add-bar input {
  background: var(--sunk); border: 1px solid var(--rule-2); border-radius: var(--r);
  color: var(--ink-1); font-family: var(--mono);
}
.m-field:focus, .o-field:focus, .add-bar input:focus { border-color: var(--edge); background: var(--sunk); }
.return-tick span { border-radius: var(--r); border-color: var(--edge); }
.return-tick input:checked + span { background: var(--ink-1); border-color: var(--ink-1); }
.return-tick input:checked + span::after { border-color: var(--ground); }

/* the theme control */
.theme-opt {
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.1em;
  text-transform: uppercase; padding: 5px 8px; cursor: pointer;
  background: transparent; border: 1px solid var(--rule-2); border-radius: var(--r);
  color: var(--ink-3);
}
.theme-opt[aria-pressed='true'] { color: var(--ink-1); border-color: var(--edge); }

.empty, .md-note, .foot { color: var(--ink-3); font-size: 13px; }
.lock-title { font-family: var(--sans); font-style: normal; font-weight: 600;
  font-size: 22px; letter-spacing: 0.28em; text-transform: uppercase; }
.lock-title .accent { display: none; }

@media (max-width: 720px) {
  .masthead { margin: -32px -18px 0; padding: 0 18px; }
  .tabs { margin: 0 -18px 12px; padding: 0 18px; }
  .item-name { font-size: 15px; }
}

/* The strip is 38px of status. Controls do not fit inside it and were being
   sheared off by it, so they get their own line underneath — which is also
   where a toolbar belongs on a phone: near the thumb, not under the title. */
.control-bar {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  padding: 12px 0 14px; border-bottom: 1px solid var(--rule);
}
.control-bar .theme-switch { margin-left: auto; }
.tabs { margin-top: 0; }
#tab-watch > .add, #tab-wish > .add, #tab-other > .add,
.collection { margin-top: 0; }
.collection-head { margin-bottom: 10px; }

/* The sticky strip must not eat the row below it.
   It used `margin-top: -32px` to cancel .page's top padding and sit flush to
   the viewport edge — but a negative margin takes that space out of the FLOW
   as well, so the control bar started 18px higher than the strip's bottom and
   the tops of Check now / Test alert / Add Face ID were painted over.
   Zero the page's top padding instead: same flush strip, no stolen space. */
.page { padding-top: 0; }
.masthead { margin-top: 0; }

/* Leftovers from the editorial language that the reskin did not reach. */
.empty-display {
  font-family: var(--sans); font-style: normal; font-weight: 600;
  font-size: 17px; letter-spacing: -0.005em; color: var(--ink-2);
}
.empty ol { margin-top: 10px; padding-left: 18px; }
.empty li { margin-bottom: 6px; }
.add { margin-bottom: 18px; }
.collection-head { margin-top: 4px; }
.lock-title { font-size: 20px; }
