/* Bonfino - bright and plain, from the brand board of 31 October 2026.
   Paper ground, white cards, ink text, and Plus Jakarta Sans on every
   headline and every price figure. Two accents, each with one job: green is
   the app speaking (actions, totals, confirmations, the active tab), apricot
   is something wanting your attention (a promotion, a watched product).
   Apricot is too light to read as text on white, so text in that role uses
   --sun, and only fills use --sun-fill. Decoration stays neutral. */

:root {
  --bone: #F6F7F4; --paper: #FFFFFF;
  --ink: #0E1411; --ink-2: #5B6660; --ink-3: #66716B;
  --line: #E6E9E3; --line-2: #D3D8D0;
  --green: #0A7A47; --green-soft: #E7F5EC; --green-line: #BFE3CD;
  --sun: #A84B0C; --sun-soft: #FFF1E6; --sun-fill: #FF9A4D; --on-sun: #0E1411;
  /* Text and washes that sit on a filled green panel. */
  --on-accent: #FFFFFF;
  --on-accent-soft: rgba(255, 255, 255, .18);
  --display: "Plus Jakarta Sans", "Inter", ui-sans-serif, system-ui, sans-serif;
  --sans: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --r-card: 24px; --r-hero: 28px; --r-ctl: 14px; --r-mark: 12px; --r-pill: 999px;
  --shadow-card: 0 1px 2px rgba(14, 20, 17, .05), 0 6px 20px rgba(14, 20, 17, .05);
  --shadow-cta: 0 6px 16px rgba(10, 122, 71, .24);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bone: #0E1411; --paper: #161D19;
    --ink: #F2F5F3; --ink-2: #A3AEA8; --ink-3: #8E9A93;
    --line: #232B27; --line-2: #2F3934;
    --green: #3DDC84; --green-soft: #12261B; --green-line: #1F4A33;
    --sun: #FFAA66; --sun-soft: #2A1C10; --sun-fill: #FF9A4D; --on-sun: #0E1411;
    /* The dark accents are light, so what sits on them has to be dark. */
    --on-accent: #0E1411;
    --on-accent-soft: rgba(0, 0, 0, .16);
    --shadow-card: none;
    --shadow-cta: 0 6px 16px rgba(0, 0, 0, .45);
  }
}

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

/* A display rule on an element beats the hidden attribute's default, so make
   [hidden] win explicitly. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

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

/* Every figure lines up in a column. */
.amt, .price, .subtotal, .hero-amt { font-variant-numeric: tabular-nums; }

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: none; }
a { color: inherit; }
/* Focus in the brand green rather than whatever ring the browser draws. */
:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }
:root { accent-color: var(--green); }

/* ---------------------------------------------------------------- type */

.big {
  font-family: var(--display); font-weight: 800; font-size: 36px; line-height: 1.06;
  letter-spacing: -.035em; margin: 14px 0 6px;
}
.big em { font-style: normal; color: var(--green); }

.sub { font-size: 14.5px; color: var(--ink-2); line-height: 1.5; margin-bottom: 20px; }

.slabel {
  font-size: 13.5px; letter-spacing: 0;
  color: var(--ink-2); font-weight: 600; margin: 20px 0 10px;
}

/* ---------------------------------------------------------------- chrome */

.shell { min-height: 100dvh; }
.view { padding: 8px 22px calc(84px + env(safe-area-inset-bottom)); }
/* Views that pin a .sticky call to action need room for it as well. */
.view.with-cta { padding-bottom: calc(158px + env(safe-area-inset-bottom)); }

/* Phone: the rail is a bottom tab bar. */
.rail {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 10;
  height: calc(64px + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  background: var(--paper); border-top: 1px solid var(--line);
}
.rail .wordmark, .rail .railfoot { display: none; }
.rail .nav { display: grid; grid-template-columns: repeat(4, 1fr); height: 64px; }

.nv {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; font-size: 11px; font-weight: 500; color: var(--ink-3);
  text-decoration: none; position: relative;
}
.nv.on { color: var(--green); }
.nv .ic {
  width: 21px; height: 21px; stroke: currentColor; fill: none;
  stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round;
}
.nv .dot {
  position: absolute; top: 11px; right: calc(50% - 16px);
  width: 7px; height: 7px; border-radius: 50%; background: var(--sun-fill);
}
.nv .badge {
  margin-left: auto; background: var(--sun-fill); color: var(--on-sun);
  border-radius: var(--r-pill); font-size: 11px; font-weight: 700; padding: 1px 7px;
}

.wordmark {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--display); font-size: 22px; font-weight: 800; letter-spacing: -.045em;
}
.wordmark svg { width: 28px; height: 28px; flex: 0 0 28px; }
.wordmark .bg { fill: var(--green); }
.wordmark .rc { fill: var(--paper); }
.wordmark .tk { fill: none; stroke: var(--green); stroke-width: 5; stroke-linecap: round; stroke-linejoin: round; }

/* The per-view header: wordmark or title on the left, a pill on the right.
   It sits above the scrolling view rather than inside it, so it carries its
   own horizontal padding to line up with the cards below. */
.topbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 20px 22px 6px; }
/* On a phone the rail has no room for the wordmark, so the topbar shows it
   and the tab title is redundant beside the tab bar itself. */
.topbar .ttl { display: none; }

.locpill {
  display: inline-flex; align-items: center; gap: 6px; background: var(--paper);
  border: 1px solid var(--line); border-radius: var(--r-pill); padding: 7px 13px;
  font-size: 13px; font-weight: 500; color: var(--ink-2);
  text-decoration: none; min-width: 0; overflow: hidden;
}
/* text-overflow does not apply to a flex container itself, so the ellipsis
   has to go on the text node inside it. */
.locpill span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.locpill .pin { width: 15px; height: 15px; flex: 0 0 15px; fill: none; stroke: currentColor; stroke-width: 2; }
/* No location saved yet is the one thing here worth chasing, so it is the
   one time the pill speaks in the accent. */
.locpill.unset { color: var(--green); border-color: var(--green-line); background: var(--green-soft); }

/* ---------------------------------------------------------------- surfaces */

.card {
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: 6px 4px; margin-bottom: 14px; box-shadow: var(--shadow-card);
}
.card.pad { padding: 18px; }

.row {
  display: flex; align-items: center; gap: 12px;
  padding: 13px 14px; border-bottom: 1px solid var(--line);
}
.row:last-child { border-bottom: none; }
.row .nm { flex: 1; font-size: 15px; font-weight: 500; min-width: 0; }
.row .nm small { display: block; font-weight: 400; font-size: 12.5px; color: var(--ink-3); margin-top: 2px; }

.step {
  display: flex; align-items: center; gap: 9px; background: var(--bone);
  border-radius: var(--r-pill); padding: 4px 6px; border: 1px solid var(--line);
}
.step b { font-size: 14px; min-width: 14px; text-align: center; font-variant-numeric: tabular-nums; }
.step span {
  width: 22px; height: 22px; border-radius: 50%; background: var(--paper);
  border: 1px solid var(--line-2); display: grid; place-items: center;
  font-size: 14px; color: var(--ink-2); line-height: 1; cursor: pointer; user-select: none;
}

.addrow { display: flex; align-items: center; gap: 10px; padding: 14px; color: var(--ink-3); font-size: 15px; }
.addrow .plus {
  width: 26px; height: 26px; border-radius: 50%; border: 1.5px dashed var(--line-2);
  display: grid; place-items: center; color: var(--ink-3); font-size: 15px; flex: 0 0 26px;
}

.shead { display: flex; align-items: center; gap: 11px; padding: 15px 15px 12px; }
.shead .t { flex: 1; min-width: 0; }
.shead .t b { font-size: 15.5px; font-weight: 600; display: block; }
/* :not(.chip) because a chip sits inside the name and must stay a pill. */
.shead .t span:not(.chip) { font-size: 12.5px; color: var(--ink-3); }
.shead .amt { font-family: var(--display); font-size: 19px; font-weight: 700; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }

/* Neutral on purpose. A chain mark signals nothing, and an accent that turns
   up where nothing is being signalled stops working as a signal. */
.mark {
  width: 38px; height: 38px; border-radius: var(--r-mark); background: var(--bone);
  color: var(--ink-2); display: grid; place-items: center; font-weight: 700;
  font-size: 13px; letter-spacing: -.02em; flex: 0 0 38px;
}

.irow { display: flex; align-items: flex-start; gap: 12px; padding: 11px 15px; border-top: 1px solid var(--line); }
/* Rows with a star or a single line of text read better centred. */
.irow.mid { align-items: center; }
.irow:first-child { border-top: none; }
.irow .l { flex: 1; min-width: 0; }
.irow .l b { font-size: 14px; font-weight: 600; display: block; }
/* The description line. :not(.chip) matters: a chip is a span inside the
   name, and this rule would otherwise turn it into a full-width grey block. */
.irow .l span:not(.chip) { font-size: 12.5px; color: var(--ink-3); display: block; margin-top: 2px; }
.irow .p { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Prices are the product, so they get the display face. */
.irow .p b { font-family: var(--display); font-size: 14.5px; font-weight: 700; }
.irow .p s { display: block; font-size: 12px; color: var(--ink-3); margin-top: 1px; }
/* A total for less than the whole list. It is cheaper because it buys less,
   so it does not get the weight of a number you can compare. */
.irow .p.partial b { color: var(--ink-3); font-weight: 500; }

/* In the trolley.

   This is the control pressed most in the shop, one-handed, so it leads the
   row and is a good deal bigger than the star and the cross beside it. The
   check is drawn in CSS rather than typed as a character, because a tick
   glyph renders at a different weight and baseline on every platform. */
.irow .tick, .rline .tick {
  flex: 0 0 auto; width: 24px; height: 24px; border-radius: 50%;
  border: 1.5px solid var(--line-2); cursor: pointer; position: relative;
}
.irow .tick:focus-visible, .rline .tick:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }
.irow .tick[aria-checked="true"], .rline .tick[aria-checked="true"] { background: var(--green); border-color: var(--green); }
.irow .tick[aria-checked="true"]::after, .rline .tick[aria-checked="true"]::after {
  content: ''; position: absolute; left: 8px; top: 4px; width: 5px; height: 10px;
  border: solid var(--on-accent); border-width: 0 2px 2px 0; transform: rotate(45deg);
}
/* A picked row stays readable - you may want to check the price again - but
   it must not compete with what is still to find. */
.irow.picked { opacity: .5; }
.irow.picked .l b { text-decoration: line-through; }

.chip {
  display: inline-block; font-size: 11.5px; font-weight: 600; letter-spacing: 0;
  padding: 2px 8px; border-radius: var(--r-pill);
  margin-left: 6px; vertical-align: 1.5px;
}
/* Apricot for what wants your attention: a promotion, a watched product.
   Green for the app speaking back. */
.chip.promo { background: var(--sun-fill); color: var(--on-sun); }
.chip.star { background: var(--sun-soft); color: var(--sun); }
.chip.g { background: var(--green-soft); color: var(--green); }
/* A match the matcher is not sure about. Outlined rather than filled: it is
   asking the reader to look, not announcing a saving. */
.chip.warn { background: none; border: 1px solid var(--line-2); color: var(--ink-2); }

.seg {
  display: flex; background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r-ctl); overflow: hidden; margin-bottom: 16px;
}
.seg div {
  flex: 1; padding: 13px 12px; font-size: 13px; color: var(--ink-2);
  text-align: center; border-right: 1px solid var(--line);
}
.seg div:last-child { border-right: none; }
.seg div b {
  display: block; font-family: var(--display); font-size: 19px; color: var(--ink);
  font-weight: 700; letter-spacing: -.02em; margin-top: 3px; font-variant-numeric: tabular-nums;
}
.seg div.win { background: var(--green-soft); color: var(--green); }
.seg div.win b { color: var(--green); }
/* The basket each side buys. The two sides are different baskets, so without
   this the side that wins is often just the side with the bigger number. */
.seg div i { display: block; font-style: normal; font-size: 11.5px; color: var(--ink-3); margin-top: 2px; }
.seg div.win i { color: var(--green); opacity: .75; }

/* Why the highlighted side won, directly under the numbers it is about. This
   sentence used to sit in Notes, several sections below them. */
.segwhy {
  font-size: 12.5px; color: var(--ink-2); line-height: 1.5;
  margin: -8px 0 16px; padding: 0 2px;
}

/* Asked before planning: "which cheese?". One card per item, a pill per
   kind, and "no preference" first and already chosen, so planning with the
   defaults is the same single press it always was. */
.askwhy { font-size: 13px; color: var(--ink-2); line-height: 1.5; margin: 0 2px 12px; }
.card:has(.ask) { margin-bottom: 12px; }
.ask-item { font-weight: 700; font-size: 15px; margin-bottom: 10px; }
.ask-opts { display: flex; flex-wrap: wrap; gap: 8px; }
.ask-opt {
  display: inline-flex; flex-direction: column; align-items: flex-start; gap: 1px;
  background: var(--paper); color: var(--ink); border: 1px solid var(--line-2);
  border-radius: var(--r-ctl); padding: 8px 12px; font: inherit; font-size: 13.5px;
  cursor: pointer; text-align: left; min-height: 44px; justify-content: center;
}
.ask-opt span { font-size: 11.5px; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.ask-opt[aria-checked="true"] {
  background: var(--green-soft); color: var(--green); border-color: var(--green-line);
}
.ask-opt[aria-checked="true"] span { color: var(--green); opacity: .8; }
.ask-opt:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }

.miss { padding: 14px 15px; font-size: 13.5px; color: var(--ink-2); line-height: 1.5; }
.miss b { color: var(--ink); }

/* ---------------------------------------------------------------- heroes */

/* The total is a white card with the number in ink, and the saving is the
   green part. .sunhero is the same block in apricot. It is used only for
   offers, so it stands alone rather than needing .hero alongside it. */
.hero, .sunhero {
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-hero);
  padding: 22px; color: var(--ink); margin: 12px 0 14px; box-shadow: var(--shadow-card);
}
.sunhero { background: var(--sun-fill); border-color: transparent; color: var(--on-sun); }
.hero .lbl, .sunhero .lbl { font-size: 14px; font-weight: 600; color: var(--ink-2); }
.sunhero .lbl { color: var(--on-sun); opacity: .75; }
.hero .amt, .sunhero .amt {
  font-family: var(--display); font-size: 52px; font-weight: 800; letter-spacing: -.04em;
  line-height: 1; margin: 10px 0 8px; font-variant-numeric: tabular-nums;
}
.hero .meta, .sunhero .meta { font-size: 13.5px; color: var(--ink-2); }
.sunhero .meta { color: var(--on-sun); opacity: .8; }
.hero .saved, .sunhero .saved {
  display: inline-block; margin-top: 14px; background: var(--green-soft); color: var(--green);
  border-radius: var(--r-pill); padding: 6px 13px; font-size: 13px; font-weight: 600;
}
.sunhero .saved { background: rgba(14, 20, 17, .1); color: var(--on-sun); }

/* ---------------------------------------------------------------- controls */

/* The mockup fades this bar out towards the top. That works over the bone
   page, but the plan hero is a filled card, and a bone fade drawn over it
   reads as a grey smear straight through the total. A solid bar with a
   hairline edge behaves the same everywhere, so content passes under a
   defined line instead of through a stain. */
.sticky {
  position: fixed; left: 0; right: 0; z-index: 9;
  bottom: calc(64px + env(safe-area-inset-bottom));
  padding: 14px 22px 16px;
  background: var(--bone); border-top: 1px solid var(--line);
}
.cta {
  width: 100%; background: var(--green); color: var(--on-accent); border: none;
  border-radius: var(--r-pill); padding: 17px; font-family: var(--display);
  font-size: 16.5px; font-weight: 700; letter-spacing: -.01em; display: flex; align-items: center;
  justify-content: center; gap: 10px; box-shadow: var(--shadow-cta);
}
.cta[disabled] { opacity: .55; box-shadow: none; }
.cta .n { background: var(--on-accent-soft); border-radius: var(--r-pill); padding: 2px 9px; font-size: 13px; }

button.secondary {
  background: var(--paper); border: 1px solid var(--line-2); border-radius: var(--r-ctl);
  padding: 11px 18px; font-size: 14.5px; font-weight: 600; color: var(--ink);
}

.scan {
  border: 1.5px dashed var(--line-2); border-radius: var(--r-card); padding: 26px 20px;
  text-align: center; background: var(--paper); margin-bottom: 14px;
}
.scan .ic {
  width: 40px; height: 40px; margin: 0 auto 12px; color: var(--green); display: block;
  stroke: currentColor; fill: none; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round;
}
.scan b { font-size: 16px; display: block; margin-bottom: 5px; }
.scan span { font-size: 13px; color: var(--ink-2); line-height: 1.45; display: block; }
.scan button {
  margin-top: 14px; background: var(--green); color: var(--on-accent); border: none;
  border-radius: 13px; padding: 12px 22px; font-size: 14.5px; font-weight: 600;
}

/* Receipts: the review that takes the scan card's place, and the list. */
.scan .rwait { margin-top: 14px; }
.scan button.secondary { background: var(--paper); color: var(--ink); border: 1px solid var(--line-2); }
.review .guess { display: block; font-size: 12px; font-weight: 400; color: var(--sun); margin-top: 2px; }
.review input[type="date"] {
  font: inherit; font-size: 14px; color: var(--ink); background: var(--paper);
  border: 1px solid var(--line-2); border-radius: 10px; padding: 6px 8px;
}
.review select { max-width: 55%; }
.rcheck { padding: 11px 15px; font-size: 13px; color: var(--green); border-top: 1px solid var(--line); }
.rcheck.gap { color: var(--sun); }
.rline {
  display: flex; align-items: flex-start; gap: 12px; padding: 11px 15px;
  border-top: 1px solid var(--line);
}
.rline.suspect { background: var(--sun-soft); }
.rline.off .l, .rline.off .rprice { opacity: .45; }
.rline .l { flex: 1; min-width: 0; }
.rline .rname {
  width: 100%; font: inherit; font-size: 14px; font-weight: 600; color: var(--ink);
  background: transparent; border: none; border-bottom: 1px dashed var(--line-2); padding: 2px 0;
}
.rline small { display: block; font-size: 12px; color: var(--ink-3); margin-top: 3px; overflow-wrap: anywhere; }
.rline small.was { color: var(--sun); }
.rline .rprice {
  flex: 0 0 72px; width: 72px; text-align: right; font: inherit; font-size: 14px;
  font-variant-numeric: tabular-nums; color: var(--ink); background: var(--paper);
  border: 1px solid var(--line-2); border-radius: 10px; padding: 6px 8px;
}
.rline .rprice.bad { border-color: var(--sun); }
.ractions { display: flex; gap: 10px; padding: 14px 15px; border-top: 1px solid var(--line); }
.ractions .save {
  flex: 1; background: var(--green); color: var(--on-accent); border: none;
  border-radius: 13px; padding: 12px 18px; font-size: 14.5px; font-weight: 600;
}
.ractions button[disabled] { opacity: .6; }

.rrow { border-top: 1px solid var(--line); }
.rrow:first-child { border-top: none; }
.rrow .rhead {
  display: flex; align-items: baseline; gap: 10px; width: 100%; padding: 13px 15px;
  background: none; border: none; text-align: left; font: inherit; color: var(--ink); cursor: pointer;
}
.rrow .rhead b { font-weight: 600; }
.rrow .rhead span { flex: 1; min-width: 0; font-size: 13px; color: var(--ink-3); }
.rrow .rhead em { font-style: normal; font-weight: 600; font-variant-numeric: tabular-nums; }
.rrow .rbody { padding: 0 15px 12px; }
.rrow .rl { display: flex; justify-content: space-between; gap: 12px; padding: 4px 0; font-size: 13px; color: var(--ink-2); }
.rrow .rl span { min-width: 0; overflow-wrap: anywhere; }
.rrow .rl b { font-weight: 500; color: var(--ink); font-variant-numeric: tabular-nums; }
.rrow .linkish { margin-top: 8px; color: var(--sun); }

.setrow {
  display: flex; align-items: center; gap: 12px; padding: 14px 15px;
  border-top: 1px solid var(--line); font-size: 14.5px;
}
.setrow:first-child { border-top: none; }
.setrow .k { flex: 1; font-weight: 500; min-width: 0; }
.setrow .v { color: var(--ink-3); font-size: 13.5px; text-align: right; font-variant-numeric: tabular-nums; }

.toggle {
  width: 42px; height: 25px; border-radius: var(--r-pill); background: var(--green);
  position: relative; flex: 0 0 42px; cursor: pointer;
}
.toggle i { position: absolute; top: 2.5px; right: 2.5px; width: 20px; height: 20px; border-radius: 50%; background: var(--paper); }
.toggle.off { background: var(--line-2); }
.toggle.off i { left: 2.5px; right: auto; }

/* The star glyph on a row. :not(.chip) keeps it clear of "chip star", which
   is a chip and takes the chip's size. */
.star:not(.chip) { font-size: 17px; color: var(--line-2); line-height: 1; cursor: pointer; user-select: none; }
.star.on:not(.chip) { color: var(--sun); }

/* "Not what I meant". Quieter than the star at rest, because rejecting a match
   is the rarer act, and it must never be the thing a thumb finds by accident
   next to it. It states itself on hover and to a screen reader either way. */
.nope { font-size: 14px; color: var(--line-2); line-height: 1; cursor: pointer; user-select: none; margin-left: 10px; opacity: .55; }
.nope:hover, .nope:focus { opacity: 1; color: var(--sun); }

/* ---------------------------------------------------------------- states */

.empty { text-align: center; padding: 26px 20px; }
.empty b { font-size: 16px; font-weight: 600; display: block; }
.empty p { font-size: 14px; color: var(--ink-2); line-height: 1.5; margin: 7px 0 0; }
.empty button { margin-top: 16px; }

.spinner {
  display: inline-block; width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid var(--line-2); border-top-color: var(--green);
  animation: spin .7s linear infinite; vertical-align: -2px;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------------------------------------------------------------- plan */

/* The regular-basket prompt: an icon and two lines of text, the whole card
   clickable. */
.plan-prompt { display: flex; align-items: center; gap: 12px; cursor: pointer; }
.plan-prompt .cnt { flex: 1; font-size: 14px; line-height: 1.4; }
.plan-prompt .cnt b { font-weight: 600; }
.plan-prompt .cnt span { display: block; color: var(--ink-3); font-size: 12.5px; margin-top: 2px; }

/* The list's add-item field. The mockup shows plain text; this is the real
   input it stands in for. */
.addrow input { flex: 1; border: none; background: none; font: inherit; color: inherit; }
.addrow input::placeholder { color: var(--ink-3); }

/* Dropping an item from the list. */
.rm {
  flex: 0 0 22px; width: 22px; height: 22px; border-radius: 50%;
  border: 1px solid var(--line-2); background: var(--paper); color: var(--ink-3);
  font-size: 14px; line-height: 1; display: grid; place-items: center;
}

/* Inline status under the plan button: the empty-list nudge, or a request
   that failed. */
.notice { font-size: 13px; color: var(--ink-2); text-align: center; padding: 10px 4px 0; }
.notice.err { color: var(--sun); }

/* More than one unmatched item, or more than one note, stacked in one card. */
.miss + .miss { border-top: 1px solid var(--line); }

/* ---------------------------------------------------------------- basket */

/* The frequency dropdown on a basket row: a small pill, not a full-size
   native control. The closed value truncates rather than pushing the row's
   other columns around. */
.freqsel {
  border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--bone);
  color: var(--ink-2); font-size: 11.5px; padding: 5px 8px; max-width: 96px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---------------------------------------------------------------- watch */

/* The em dash for a watched product with no known price at all. No deal
   means no data, and this reads as that honest gap rather than a blank. */
.irow .p .unk { color: var(--ink-3); }

/* The inline "alert me under" control on a no-offer row: a small pill in
   the same family as .freqsel, but a number input rather than a select. */
.tgt {
  display: flex; align-items: center; gap: 4px; flex: 0 0 auto;
  background: var(--bone); border: 1px solid var(--line); border-radius: var(--r-pill);
  padding: 5px 10px; font-size: 11.5px; color: var(--ink-3);
}
.tgt input {
  width: 44px; border: none; background: none; font: inherit; color: var(--ink);
  text-align: right; -moz-appearance: textfield;
}
.tgt input::-webkit-outer-spin-button,
.tgt input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.tgt input::placeholder { color: var(--ink-3); }

/* ---------------------------------------------------------------- you */

/* Most setrows are a label and a value on one line. The ones that hold a
   form stack instead, because a text field and three buttons do not fit
   beside a label at phone width. */
.setrow.col { display: block; }
.setrow.col .k { margin-bottom: 4px; }
.setrow .hint { font-size: 12.5px; color: var(--ink-3); line-height: 1.45; margin-bottom: 10px; }
.setrow .notice { text-align: left; padding: 9px 0 0; }
/* A message about the card as a whole rather than about one row, so it sits
   at the foot and lines up with the rows above it. */
.notice.cardfoot { text-align: left; padding: 0 15px 14px; }

/* A count we could not read. Never a zero standing in for it. */
.setrow .v .unk { color: var(--ink-3); font-style: italic; }

/* Fields inside a card sit on the bone ground, so they read as editable
   without a heavy border. `.pform` is named alongside `.setrow` because the
   price form now also opens against a gap in the plan, where there is no
   settings row around it to pick this up from. */
.setrow input[type="text"],
.setrow input[type="number"],
.setrow select,
.setrow textarea,
.pform input,
.pform select {
  background: var(--bone); border: 1px solid var(--line); border-radius: var(--r-ctl);
  padding: 10px 12px; font-size: 14px; color: var(--ink); width: 100%;
}
.setrow input::placeholder, .setrow textarea::placeholder,
.pform input::placeholder { color: var(--ink-3); }
.setrow textarea { resize: vertical; margin-top: 10px; font-size: 13px; }
.setrow.col > button.secondary { margin-top: 10px; }
.setrow select.wide { width: 100%; }

/* A number with its unit beside it: francs before, kilometres after. */
.unit { display: flex; align-items: center; gap: 6px; color: var(--ink-3); font-size: 13.5px; }
.unit input[type="number"] {
  width: 72px; text-align: right; font-variant-numeric: tabular-nums;
  -moz-appearance: textfield;
}
.unit input::-webkit-outer-spin-button,
.unit input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* The town field and its save button share a line; "use my location" sits
   under them, because it is the fallback and not the main path. */
.locedit { display: flex; gap: 8px; }
.locedit input { flex: 1; min-width: 0; }
.locedit button { flex: 0 0 auto; }

/* Padded rather than bare, so it is a comfortable target on a phone even
   though it reads as a link. */
.linkish {
  margin-top: 4px; padding: 9px 0; font-size: 13px; color: var(--green);
  font-weight: 600; text-decoration: underline; text-underline-offset: 3px;
}
.linkish[disabled] { color: var(--ink-3); }

/* Name, shop, price, save. The name takes the width it needs; the other
   three share the line under it. Keyed to classes rather than the You card's
   ids, because the same form now opens against a gap in the plan.

   The price is sized by its grid track, not by a width on the input: the
   shared control rule above sets `width: 100%` from a selector specific
   enough to beat a bare class pair, so a width here would be ignored on the
   You card and obeyed on the plan - the same form, two different shapes. */
.pform { display: grid; grid-template-columns: 1fr 92px auto; gap: 8px; }
.pform .pf-name { grid-column: 1 / -1; }
.pform .pf-price { text-align: right; font-variant-numeric: tabular-nums; }
.pform button { white-space: nowrap; }

/* The bookmarklet is dragged, not pressed, so it looks like something you
   can pick up rather than a button. */
.bmk {
  display: inline-block; border: 1.5px dashed var(--line-2); border-radius: var(--r-ctl);
  padding: 9px 16px; font-size: 14px; font-weight: 600; color: var(--green);
  text-decoration: none; cursor: grab;
}

/* ---------------------------------------------------------------- desktop */

@media (min-width: 900px) {
  .shell { display: flex; }
  .rail {
    position: sticky; top: 0; left: auto; right: auto; bottom: auto;
    width: 216px; flex: 0 0 216px; height: 100dvh; padding: 22px 14px;
    border-top: none; border-right: 1px solid var(--line);
    display: flex; flex-direction: column;
  }
  .rail .wordmark { display: flex; font-size: 23px; padding: 0 10px 22px; }
  .rail .railfoot {
    display: block; margin-top: auto; border-top: 1px solid var(--line);
    padding: 14px 0 0 10px; font-size: 12.5px; color: var(--ink-3); line-height: 1.5;
  }
  .rail .nav { display: flex; flex-direction: column; gap: 2px; height: auto; }
  .nv {
    flex-direction: row; justify-content: flex-start; gap: 11px;
    padding: 11px 12px; border-radius: var(--r-ctl); font-size: 14.5px; color: var(--ink-2);
  }
  .nv.on { background: var(--green-soft); color: var(--green); font-weight: 600; }
  .nv .ic { width: 19px; height: 19px; }
  .nv .dot { position: static; margin-left: auto; }
  .pane { flex: 1; min-width: 0; }
  .view { padding: 0 26px 40px; }
  .view.with-cta { padding-bottom: 40px; }
  .sticky { position: static; padding: 0; background: none; border-top: none; }
  .two-col { display: grid; grid-template-columns: 392px 1fr; gap: 26px; align-items: start; }
  /* The rail carries the wordmark here, so the topbar names the tab instead. */
  .topbar { padding: 22px 26px 10px; border-bottom: 1px solid var(--line); margin-bottom: 14px; }
  .topbar .wordmark { display: none; }
  .topbar .ttl { display: block; font-family: var(--display); font-size: 21px; font-weight: 600; letter-spacing: -.01em; }
  .big { font-size: 30px; margin-top: 4px; }
}

/* The sign-in screen. It replaces the whole shell, so it carries its own
   breathing room rather than relying on the view padding around a tab bar
   that is not there. */
.signin { max-width: 460px; margin: 8vh auto 0; }
.signin .wordmark { margin-bottom: 28px; }
.signin .big { margin-bottom: 10px; }
.signin .send {
  flex: 0 0 auto; background: var(--green); color: var(--on-accent); border-radius: var(--r-pill);
  padding: 0 20px; min-height: 48px; font-family: var(--display); font-size: 15px; font-weight: 700;
}
.signin .send[disabled] { opacity: .55; }
.signin .sub { margin-bottom: 22px; }
.signin .locedit { display: flex; gap: 8px; }
.signin .locedit input { flex: 1; min-width: 0; }
.signin input[type="email"] {
  background: var(--paper); border: 1px solid var(--line-2); border-radius: var(--r-pill);
  padding: 0 18px; min-height: 48px; font-size: 15px; color: var(--ink);
}
.signin input::placeholder { color: var(--ink-3); }
.signin .notice { margin-top: 14px; }
.signin .locedit + .sub, .signin .notice + .sub { margin-top: 16px; }

/* The privacy page: a page of its own, readable before signing in. */
.privacy { max-width: 680px; margin: 0 auto; padding: 40px 16px 64px; }
.privacy h2 { font-family: var(--display); font-weight: 700; letter-spacing: -.02em; font-size: 20px; margin: 28px 0 8px; }
.privacy p { font-size: 15px; line-height: 1.6; color: var(--ink-2); }
.privacy a { color: var(--green); }

/* Under the scan button: what happens to the photo, before it is taken. */
.scan .scan-notice { display: block; margin-top: 12px; font-size: 12px; line-height: 1.45; color: var(--ink-3); }
.scan .scan-notice a { color: var(--green); }
