@import url('/static/fonts.css');

/* ===========================================================================
   Τιμόμετρο - visual identity

   Warm mustard on cream. The palette is deliberately food-market rather than
   fintech: supermarket price data is dry enough without a blue dashboard.

   Mustard is the brand, but it is *never* used to mean "average price" in the
   same view as the price gauge - the gauge owns green/amber/red, so brand
   accents stay in the mustard family and status colours stay semantic.
   =========================================================================== */

:root {
  --bg: #FAF6EC;
  --surface: #FFFFFF;
  --surface-2: #FFFBF1;
  --ink: #241D12;
  --muted: #7C6B52;
  --line: #E9DFC8;

  --brand: #C98A06;          /* dark mustard - passes AA on cream */
  --brand-strong: #936400;   /* links on cream: 4.23:1 before, 4.79:1 now */
  --brand-tint: #FDF2D4;
  --brand-ink: #3B2A05;

  --good: #2F7D52;
  --good-bg: #E6F3EA;
  --warn: #B8860B;
  --warn-bg: #FBF0D2;
  --dear: #B23D22;
  --dear-bg: #FBE9E3;

  /* Green and amber are legible as a fill long before they are legible as
     text. --good on --good-bg measured 4.40:1 and --warn on --warn-bg 2.87:1,
     both under AA, and the file had already worked around the amber one twice
     by hand (#7A5300, #5C4405). So the status colours split in two: --good and
     --warn paint surfaces, marks and borders; the -ink pair is what you set on
     text. --dear needs no partner - it clears 4.99:1 on its own tint already,
     and inventing a token nothing needs is how a palette starts to rot. */
  --good-ink: #276845;       /* 5.8:1 on --good-bg, 6.7:1 on white */
  --warn-ink: #7A5300;       /* 6.0:1 on --warn-bg, 6.9:1 on white */

  --gauge-cheap: #2F7D52;
  --gauge-mid: #E0A61C;
  --gauge-dear: #C0472B;
  --logo-ink: #241D12;
  --logo-bg: #FFFFFF;

  --radius: 13px;
  --shadow: 0 1px 2px rgba(60, 45, 15, .05), 0 8px 22px rgba(60, 45, 15, .07);
  --font: 'Commissioner', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

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

     Two rates, hinged at 18px, because this page does two jobs.

     Up from 18 the steps grow by about a fifth: those sizes are read as
     hierarchy, one at a time, and need to be told apart across a scroll.
     Down from 18 they grow by about an eighth: those sizes are read at a
     glance, several at once, inside a row of a price table, and a fifth
     between them would put a caption and a chain name in different worlds.

     The sizes before this were picked one at a time - thirty-one of them, from
     9px to 42px, with 12, 12.5, 13 and 13.5 all in service at once. Nothing
     below is an eyeballed value.

     --t-2xs is for letter-spaced uppercase labels only. No lower-case text on
     this site is smaller than --t-xs: Greek carries accents that a 10px line
     loses, and every one of these pages is read on a phone. */
  --t-2xs:  11.5px;
  --t-xs:   13px;
  --t-sm:   14.5px;
  --t-base: 16px;
  --t-md:   18px;
  --t-lg:   22px;
  --t-xl:   27px;
  --t-2xl:  33px;
  --t-3xl:  40px;

  /* Line height is part of the size, not a separate decision. Greek sets
     roughly a tenth longer than the English it was spaced against and stacks
     accents above the cap line, so every one of these is looser than the
     Latin-only value would be - most of all at display sizes, where two lines
     of a Greek headline otherwise touch. */
  --lh-display: 1.18;        /* --t-xl and up */
  --lh-head:    1.3;         /* --t-md, --t-lg */
  --lh-body:    1.6;         /* --t-base prose */
  --lh-ui:      1.4;         /* --t-2xs .. --t-sm */

  /* Spacing. Fours and eights, so that a gap, a padding and a margin set on
     different days still line up. Component-level nudges that exist for a
     measured reason keep their literal value and their comment. */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 72px;
}

/* Every figure on this site - a price, a total, a per-unit rate, a percentage,
   a count in a table - carries this. It is one declaration in one place so
   that the day the font can honour it, the whole site lines up at once.

   It cannot honour it today. The self-hosted subsets in static/fonts/ were cut
   without the `tnum` feature: measured in the browser, `1` sets 42.8 units
   where `0` sets 65.3, and turning tabular figures on changes nothing. So a
   column of totals is still ragged and a price that changes still jitters, and
   no amount of CSS can fix that - only a rebuilt subset can. See the report. */
.tnum, .table .num, .rank-body .num, .stop .num,
.stats dd, .index-now strong, .store-stat strong, .ready-price strong,
.rail-price, .spread-pill, .alt-save, .swap-btn .pct, .swap-opt-meta .chip.price {
  font-variant-numeric: tabular-nums lining-nums;
}



* { box-sizing: border-box; }

body {
  color-scheme: light only;
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 var(--t-base)/var(--lh-body) var(--font);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--brand-strong); text-decoration: none; }
a:hover { text-decoration: underline; }
main { max-width: 1140px; margin: 0 auto; padding: var(--s-5) var(--s-5) var(--s-8); }

/* h1 had no size of its own, so any page that did not opt into .hero, .cat-head,
   .product-head or .store-head fell through to the browser's 2em and its 0.67em
   margins - which is how the site ended up with five different h1 sizes. One
   size here, and the three bespoke ones below collapse into it. */
h1, h2, h3 { letter-spacing: -.021em; }
h1 { font-size: var(--t-xl); line-height: var(--lh-display); font-weight: 800;
     margin: 0 0 var(--s-3); }
h3 { font-size: var(--t-md); line-height: var(--lh-head); font-weight: 700;
     margin: var(--s-5) 0 var(--s-2); }

/* ---------------------------------------------------------------- topbar */
.topbar {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  padding: 12px 20px; background: var(--surface);
  border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 30;
}
.brand { display: flex; align-items: center; gap: 9px; color: var(--ink); }
.brand:hover { text-decoration: none; }
.logo-mark { width: 36px; height: 36px; flex-shrink: 0; overflow: visible; }
.brand-text { font-weight: 800; font-size: var(--t-lg); letter-spacing: -.03em; }
/* The wordmark is exempt from AA as a brand name, and was still the least
   legible text on every page at 2.95:1. --brand-strong keeps the two tones
   and reads: 5.2:1. */
.brand-text span { color: var(--brand-strong); }

.search { display: flex; flex: 1; min-width: 0; gap: 8px; }
.search input {
  /* min-width:0 or the input refuses to shrink below its placeholder's
     intrinsic width and pushes the button off the side of a phone. */
  flex: 1; min-width: 0; padding: 9px 14px; border: 1px solid var(--line);
  border-radius: 10px; background: var(--bg); color: var(--ink);
  font: inherit; font-size: var(--t-sm);
}
.search input:focus, .basket-search input:focus, .bitem input:focus,
.form-field:focus {
  outline: 2px solid var(--brand); outline-offset: 1px; border-color: transparent;
}
.search > button {
  padding: 9px 16px; border: 0; border-radius: 10px; font: inherit;
  background: var(--brand); color: var(--brand-ink); font-weight: 700; cursor: pointer;
}
.search > button:hover { background: var(--brand-strong); color: #fff; }

/* Careful here: the desktop shape of this row is no longer set only by this
   file. mobile.css wraps the links in a .nav-links element for its phone
   disclosure and gives that element display:contents at desktop width, with
   order values on the children to put the basket, donate and language buttons
   back where they were. Move these rules, or lose that stylesheet, and the nav
   stacks onto a second row. */
.topnav {
  display: flex; align-items: center; gap: 15px; white-space: nowrap;
  /* Between phone and desktop the nav is wider than the bar but the bar has
     already used its wrap, so it pushed the whole page sideways. Let the nav
     scroll inside itself instead: nothing is lost, and the page never gains a
     horizontal scrollbar. */
  max-width: 100%; overflow-x: auto; scrollbar-width: none;
  padding-bottom: 2px;
}
.topnav::-webkit-scrollbar { display: none; }
.topnav a { font-weight: 600; font-size: var(--t-sm); color: var(--ink); }
.pill {
  display: inline-block; min-width: 22px; padding: 1px 7px; border-radius: 999px;
  background: var(--brand); color: var(--brand-ink); font-size: var(--t-xs);
  text-align: center; font-weight: 700;
}
.donate {
  border: 1px solid var(--line); border-radius: 8px; padding: 4px 9px;
  font-size: var(--t-sm); line-height: 1.2; text-decoration: none;
}
.donate:hover { border-color: var(--brand); background: var(--brand-tint); text-decoration: none; }
.lang {
  border: 1px solid var(--line); border-radius: 8px; padding: 4px 9px;
  font-size: var(--t-xs); color: var(--muted);
}
.lang:hover { border-color: var(--brand); color: var(--brand-strong); text-decoration: none; }

/* ------------------------------------------------------ disclaimer strip */
.disclaimer {
  background: var(--brand-tint); border-bottom: 1px solid var(--line);
  color: var(--brand-ink); font-size: var(--t-xs); line-height: 1.45;
}

.disclaimer div {
  max-width: 1140px; margin: 0 auto; padding: 8px 20px;
  display: flex; gap: 9px; align-items: baseline;
}
.disclaimer b { font-weight: 700; }
.disclaimer a { color: inherit; text-decoration: underline; }

/* ------------------------------------------------------------------ hero */
.hero { margin-bottom: var(--s-6); }
.hero h1 { font-size: var(--t-3xl); line-height: var(--lh-display); margin: 0 0 var(--s-3); font-weight: 800; }
.hero h1 em { font-style: normal; color: var(--brand-strong); }
/* 63ch was measured against English. The same column of Greek runs about a
   tenth longer per sentence, which at 63ch is a line and a half more than the
   eye wants to track back across. */
.lede { font-size: var(--t-md); line-height: var(--lh-head); color: var(--muted);
        max-width: 56ch; margin: 0 0 var(--s-5); }
.lede strong, .lede em { color: var(--ink); font-style: normal; }
.hero-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 26px; }

.stats { display: flex; flex-wrap: wrap; gap: 10px; margin: 0; padding: 0; }
.stats div {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 11px 16px; min-width: 124px;
}
.stats dt { font-size: var(--t-2xs); color: var(--muted); text-transform: uppercase; letter-spacing: .06em; font-weight: 600; }
.stats dd { margin: 3px 0 0; font-size: var(--t-lg); font-weight: 800; }

/* ------------------------------------------------------------------ bits */
.btn {
  display: inline-block; padding: 10px 18px; border-radius: 10px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  font: inherit; font-weight: 700; cursor: pointer; font-size: var(--t-sm);
}
.btn:hover { border-color: var(--brand); text-decoration: none; }
.btn.primary { background: var(--brand); border-color: var(--brand); color: var(--brand-ink); }
.btn.primary:hover { background: var(--brand-strong); border-color: var(--brand-strong); color: #fff; }
.btn:disabled { opacity: .55; cursor: default; }

h2 { font-size: var(--t-lg); line-height: var(--lh-head); margin: var(--s-6) 0 var(--s-3); font-weight: 700; }
.muted { color: var(--muted); }
.empty {
  color: var(--muted); background: var(--surface); border: 1px dashed var(--line);
  border-radius: var(--radius); padding: 22px; text-align: center;
}

.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--s-3); }
.card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--s-4); box-shadow: var(--shadow); display: block; color: var(--ink);
}
.card:hover { border-color: var(--brand); text-decoration: none; }
.cat-name { display: block; font-weight: 700; font-size: var(--t-md); margin-bottom: 2px; }

.crumbs { display: flex; gap: var(--s-2); flex-wrap: wrap; font-size: var(--t-sm); color: var(--muted); margin-bottom: var(--s-3); }
.cat-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.cat-head h1 { margin: 0 0 var(--s-1); }
.cat-head p { margin: 0; }

.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); margin: var(--s-4) 0; }
.chip {
  background: var(--surface); border: 1px solid var(--line); border-radius: 999px;
  padding: 6px 13px; font-size: var(--t-sm); color: var(--ink);
}
.chip span { color: var(--muted); font-size: var(--t-2xs); }
.chip:hover { border-color: var(--brand); text-decoration: none; }
.chips.tight { margin: 0; gap: 5px; }
.chip.static { font-size: var(--t-xs); padding: 4px 10px; }

.toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 18px 0; }
.toggle {
  padding: 7px 14px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--surface); color: var(--muted); font: inherit; font-size: var(--t-sm); cursor: pointer;
}
.toggle.on { background: var(--brand); border-color: var(--brand); color: var(--brand-ink); font-weight: 700; text-decoration: none; }

/* ---------------------------------------------------------------- tables */
/* No overflow here, deliberately. It was clipping the row backgrounds to the
   rounded corner, which .table-wrap already does - it is a scroll container, so
   it clips to its own radius anyway - and an overflow ancestor is exactly what
   stops a position:sticky column from sticking. mobile.css pins the first
   column on a phone; this is the rule that used to make that impossible. */
.table {
  width: 100%; border-collapse: collapse; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow);
}
/* The vertical padding was set against 16px cells. The cells are 14.5 now and
   the total sets the row height, so the row loses the slack it was carrying. */
.table th, .table td { padding: var(--s-2) var(--s-3); text-align: left; border-bottom: 1px solid var(--line); }
.table th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
.table th.sortable:hover { color: var(--brand-strong); }
.table th.sortable:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
/* The caret is always visible so the column reads as sortable before you click,
   and it points the way the data is going once you do. */
/* An arrow glyph, not text - it is sized to sit on the cap line of an 11.5px
   uppercase header without lifting the row. */
.caret { display: inline-block; margin-left: 5px; opacity: .38; font-size: 9px; }
.caret::after { content: '\2195'; }
.table th[aria-sort="ascending"] .caret { opacity: 1; }
.table th[aria-sort="ascending"] .caret::after { content: '\2191'; }
.table th[aria-sort="descending"] .caret { opacity: 1; }
.table th[aria-sort="descending"] .caret::after { content: '\2193'; }
.table th[aria-sort="ascending"], .table th[aria-sort="descending"] { color: var(--brand-strong); }

.table th { font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-weight: 700; }
.table tr:last-child td { border-bottom: 0; }
.table .num { text-align: right; white-space: nowrap; }

/* The comparison table is the product, and everything in a row was shouting at
   once: the chain, the total, the product name in link mustard, the per-unit
   figure and the difference all sat at 16px and competed. A row answers one
   question - what does this cost here - so the total is the only thing set
   above the row's size, and every supporting figure drops a step and loses its
   weight. The product name stays a link because it is one; it no longer
   out-weighs the number it explains. */
.table td { font-size: var(--t-sm); line-height: var(--lh-ui); }
.table .strong { font-size: var(--t-md); font-weight: 800; line-height: 1.2; }
.table td a { font-weight: 600; }

/* Cheapest row. Green alone decides this for everyone except the one man in
   twelve who cannot separate it from the red further down the column, so the
   row also carries a solid edge at the point where the eye enters it - a mark
   in a place nothing else occupies, readable in greyscale and on a printout.
   The tint rides on the row's background shorthand, so a stylesheet restyling
   .best rows for a phone replaces it cleanly. The edge is a separate inset on
   the first cell, because that is the one that survives being replaced - and
   losing it on a phone is losing it on the device most of this is read on. */
.table tr.best {
  background: var(--good-bg) linear-gradient(90deg, var(--good) 0 3px, transparent 3px) no-repeat;
}
.table tr.best > td:first-child { box-shadow: inset 3px 0 0 var(--good); }
.table tr.best .strong { color: var(--good-ink); }
/* This row holds a price we do not trust, and it still has to be readable -
   somebody is checking why it is flagged. Fading the row to .55 dropped the
   muted spans inside it to 2.19:1, because the fade compounds. A recessive
   ground and one ink does the same job at 4.98:1; the row keeps its warning
   tag, which is the signal that does not depend on noticing a shade. */
.table tr.flagged { background: var(--surface-2); }
.table tr.flagged td, .table tr.flagged .strong { color: var(--muted); font-weight: 400; }

.tag {
  display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 5px;
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--muted); font-size: var(--t-2xs); font-weight: 700;
}
/* An offer badge in --dear read as a warning: red on this site means a price
   is high. It is not a status either way, so it takes a brand accent and
   leaves green/amber/red to mean only what they mean. */
.tag.sale { background: var(--brand-tint); border-color: var(--brand); color: var(--brand-ink); }
.tag.warn { background: var(--warn-bg); color: var(--warn-ink); }

/* ---------------------------------------------------------------- images */
.thumb-cell { width: 46px; padding-right: 0 !important; }
.thumb-sm {
  width: 38px; height: 38px; object-fit: contain; background: #fff;
  border: 1px solid var(--line); border-radius: 7px; padding: 2px; display: block;
}
.placeholder { display: grid; place-items: center; background: var(--surface-2); color: var(--muted); font-size: var(--t-sm); }
.product-head { display: flex; gap: 18px; align-items: flex-start; margin-bottom: 8px; flex-wrap: wrap; }
.product-head h1 { margin: 0 0 var(--s-1); }
.thumb {
  width: 112px; height: 112px; object-fit: contain; background: #fff;
  border: 1px solid var(--line); border-radius: var(--radius); padding: 6px;
}
.thumb.placeholder { font-size: var(--t-2xl); }

/* ------------------------------------------------------- price position */
.gauge { margin: 6px 0 2px; }
/* Cheap green to dear red is the one piece of colour on this site that carries
   a meaning nothing else states, and about one man in twelve cannot separate
   those two ends. So the track is cut into its three zones by a pair of hairline
   gaps: where the pin falls then reads as a position between visible boundaries,
   not as a shade, and it still reads on a greyscale printout. The words at each
   end of .gauge-legend say which end is which. */
.gauge-track {
  position: relative; height: 8px; border-radius: 999px; overflow: visible;
  background:
    linear-gradient(90deg, transparent calc(33% - 1px), var(--surface) calc(33% - 1px) calc(33% + 1px),
                    transparent calc(33% + 1px) calc(66% - 1px), var(--surface) calc(66% - 1px) calc(66% + 1px),
                    transparent calc(66% + 1px)),
    linear-gradient(90deg, var(--gauge-cheap) 0 33%,
                    var(--gauge-mid) 33% 66%, var(--gauge-dear) 66% 100%);
}
.gauge-pin {
  position: absolute; top: -4px; width: 3px; height: 16px; border-radius: 2px;
  background: var(--ink); box-shadow: 0 0 0 2px var(--surface); transform: translateX(-50%);
}
.gauge-legend { display: flex; justify-content: space-between; font-size: var(--t-xs); color: var(--muted); margin-top: 5px; }
.verdict { font-weight: 700; font-size: var(--t-xs); }
.verdict.cheap { color: var(--good-ink); }
.verdict.mid   { color: var(--warn-ink); }
.verdict.dear  { color: var(--dear); }

.trend { font-size: var(--t-xs); font-weight: 700; white-space: nowrap; }
.trend.down { color: var(--good-ink); }
.trend.up   { color: var(--dear); }
.trend.flat { color: var(--muted); font-weight: 500; }

/* ------------------------------------------------------- cheaper alt list */
.alts { margin: var(--s-5) 0; }
.alt {
  display: flex; align-items: center; gap: 12px; padding: 10px 14px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); margin-bottom: 8px; color: var(--ink);
}
.alt:hover { border-color: var(--good); text-decoration: none; }
.alt-name { flex: 1; font-weight: 600; }
.alt-unit { font-variant-numeric: tabular-nums; color: var(--muted); }
.alt-save {
  background: var(--good-bg); color: var(--good-ink); font-weight: 800;
  border-radius: 999px; padding: 3px 10px; font-size: var(--t-xs); white-space: nowrap;
}

/* --------------------------------------------------------- ready baskets */
.ready-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr)); gap: 14px; }
.ready { display: flex; flex-direction: column; gap: 12px; }
.ready header { display: flex; gap: 12px; align-items: flex-start; }
.ready h2 { margin: 0 0 3px; font-size: var(--t-md); }
.ready h2 .lasts { font-size: var(--t-xs); font-weight: 600; color: var(--muted); }
.ready p { margin: 0; font-size: var(--t-sm); }
.ready-icon { font-size: var(--t-xl); line-height: 1; }
.ready-price { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin: 0; }
.ready-price strong { font-size: var(--t-lg); color: var(--good-ink); }
.spread { font-size: var(--t-xs); color: var(--dear); }
.ready .btn { margin-top: auto; }

/* ------------------------------------------------------------ basket rows */
.basket-search { position: relative; margin: 20px 0; }
.basket-search input {
  /* 16px literal for the same reason as the header search: below it Safari
     zooms the page the moment the field takes focus. */
  width: 100%; padding: 13px 16px; font: inherit; font-size: 16px; border-radius: 11px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
}
.results {
  position: absolute; z-index: 10; left: 0; right: 0; margin-top: 6px; max-height: 340px;
  overflow-y: auto; background: var(--surface); border: 1px solid var(--line);
  border-radius: 11px; box-shadow: var(--shadow);
}
.results button {
  display: block; width: 100%; text-align: left; padding: 10px 15px; border: 0;
  background: none; color: var(--ink); cursor: pointer; font: inherit; font-size: var(--t-sm);
  border-bottom: 1px solid var(--line);
}
.results button:last-child { border-bottom: 0; }
.results button:hover { background: var(--brand-tint); }
.results small { color: var(--muted); display: block; font-size: var(--t-xs); }
.results-head {
  padding: 7px 15px; font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); background: var(--surface-2); border-bottom: 1px solid var(--line);
}
.results .product-row { display: flex; align-items: center; gap: 10px; }
.results .product-row img {
  width: 30px; height: 30px; object-fit: contain; background: #fff;
  border-radius: 5px; border: 1px solid var(--line);
}

.basket-items { display: flex; flex-direction: column; gap: 8px; }
/* One line of the basket is one row: what it is, how much of it, the way to
   remove it, and the track. Laid out as columns rather than a flex row,
   because the columns have to be the same on every line: with each line
   sizing itself, a long name or a wide figure moved that line's track and
   the basket read as a list of sliders of different lengths. The name gets a
   fixed column and an ellipsis; the figure a fixed column; the track takes
   the rest, and the rest is the same on every line. */
.bitem {
  display: grid;
  grid-template-columns: 26ch 104px 32px minmax(160px, 1fr);
  grid-template-areas:
    "name qty rm range"
    "swap swap swap swap";
  align-items: center; column-gap: 12px; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius); padding: 8px 12px;
}
/* Two pixels less padding, because the accent border is two pixels thicker
   and the track on a pinned line was starting two pixels later. */
.bitem.pinned { border-left: 3px solid var(--brand); padding-left: 10px; }
/* The picture on a pinned line lives inside the name's cell, not in a column
   of its own: a column would exist only on those lines, and their tracks
   would start 48px later than everyone else's. */
.bitem .bthumb { grid-area: name; justify-self: start; align-self: center; }
.bitem.pinned .name { padding-left: 46px; }
.bitem .name {
  grid-area: name; min-width: 0; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bitem .swapbox-slot { grid-area: swap; }
.bitem .rm { grid-area: rm; border: 0; background: none; color: var(--muted); cursor: pointer; font-size: var(--t-lg); line-height: 1; }
.bitem .rm:hover { color: var(--dear); }
.bitem .hint { font-weight: 400; font-size: var(--t-xs); }
/* Pinned lines carry the product's own photo. A category line has no single
   product to show, so it gets none rather than a placeholder. */
.bthumb { flex: 0 0 36px; width: 36px; height: 36px; }
.bthumb img {
  width: 36px; height: 36px; object-fit: contain; background: #fff;
  border: 1px solid var(--line); border-radius: 7px; display: block;
}
.swap { color: var(--good-ink); }

/* Alternatives to a pinned SKU. Collapsed by default so a basket of ten
   pinned products does not become a wall, but the saving shows on the closed
   summary because that is the reason to open it. */
.swapbox-slot { flex-basis: 100%; order: 5; font-weight: 400; }
.swapbox-slot:empty { display: none; }
.swapbox {
  border-top: 1px dashed var(--line); margin: 2px 0 0; padding: 8px 0 0;
}
.swapbox summary {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px 10px;
  cursor: pointer; font-size: var(--t-xs); list-style: none; padding: 4px 2px;
  border-radius: 8px;
}
.swapbox summary .swap { flex: 1 1 260px; min-width: 0; }
.swapbox summary::-webkit-details-marker { display: none; }
.swapbox summary .swap b { font-weight: 700; }
/* Small enough to sit on the text baseline of the summary without pushing the
   line height around. */
.swap-mini {
  width: 20px; height: 20px; object-fit: contain; background: #fff;
  border: 1px solid var(--line); border-radius: 4px;
  vertical-align: -5px; margin: 0 1px;
}
.swapbox summary::after {
  content: '⌄'; color: var(--muted); font-size: var(--t-base); line-height: 1;
  transition: transform .15s ease;
}
.swapbox[open] summary::after { transform: rotate(180deg); }
.swapbox summary:hover { background: var(--surface-2); }
.swapbox summary:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.swapbox-n { color: var(--muted); font-size: var(--t-2xs); white-space: nowrap; }

/* Accept the recommendation without opening the list. It lives inside the
   <summary>, so its handler has to stop the click from toggling the details. */
.swap-now {
  display: inline-flex; align-items: center; gap: 5px; margin-left: auto;
  padding: 4px 11px; border-radius: 999px; cursor: pointer; white-space: nowrap;
  border: 1px solid var(--good); background: #fff; color: var(--good-ink);
  font: inherit; font-size: var(--t-xs); font-weight: 700;
}
.swap-now svg { width: 13px; height: 13px; flex: 0 0 13px; display: block; }
.swap-now .do { display: inline-flex; align-items: center; gap: 5px; }
.swap-now:hover { background: var(--good); color: #fff; }
.swap-now.done { background: var(--good); color: #fff; }
.swapbox-body { padding: 6px 0 2px; }
.swapbox-note { margin: 0 0 8px; font-size: var(--t-xs); color: var(--muted); line-height: 1.45; }

.swap-opt {
  display: flex; align-items: center; gap: 10px; padding: 8px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface-2);
}
.swap-opt + .swap-opt { margin-top: 6px; }
/* The biggest saving, marked so there is something to act on without reading
   all six. It leads the list because the list is ordered by value. */
.swap-opt.is-default { border-color: var(--good); background: var(--good-bg); }
.swap-opt-flag {
  display: inline-block; margin-top: 3px; padding: 1px 7px; border-radius: 999px;
  background: var(--good); color: #fff; font-size: var(--t-2xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em;
}
.swap-opt.is-default .chip:not(.only):not(.save) { background: #fff; }
.swap-opt img, .swap-opt-noimg {
  width: 38px; height: 38px; flex: 0 0 38px; object-fit: contain;
  background: #fff; border: 1px solid var(--line); border-radius: 7px;
}
.swap-opt-main { flex: 1; min-width: 0; }
.swap-opt-name {
  display: block; font-weight: 600; font-size: var(--t-xs); color: var(--ink);
  text-decoration: none; line-height: 1.35;
}
.swap-opt-name:hover { text-decoration: underline; }
.swap-opt-meta { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }
.swap-opt-meta .chip {
  font-size: var(--t-2xs); padding: 2px 7px; border-radius: 999px;
  background: #fff; border: 1px solid var(--line); color: var(--ink);
  white-space: nowrap;
}
.swap-opt-meta .chip.price { font-weight: 700; }
.swap-opt-meta .chip.same { border-color: var(--good); color: var(--good-ink); }
.swap-opt-meta .chip.save { background: var(--good-bg); border-color: var(--good-bg); color: var(--good-ink); }
.swap-opt-meta .chip.muted-chip { color: var(--muted); }
/* Stocked by one chain only: a caveat, not a feature, so it reads as caution
   rather than sitting in the same neutral grey as the pack size. Amber ink is
   darkened from --warn to clear 4.5:1 on this tint (6.0:1). */
.swap-opt-meta .chip.only {
  background: var(--warn-bg); border-color: #EBD8A0; color: #7A5300; font-weight: 600;
}
.swap-btn {
  flex: 0 0 auto; display: flex; flex-direction: column; align-items: center;
  gap: 1px; min-width: 96px; padding: 6px 10px; cursor: pointer;
  border: 1px solid var(--good); border-radius: 9px;
  background: #fff; color: var(--good-ink);
  font: inherit; font-size: var(--t-xs); font-weight: 700; line-height: 1.15;
}
.swap-btn .pct { font-size: var(--t-xs); }
.swap-btn .do {
  display: flex; align-items: center; gap: 4px;
  font-size: var(--t-xs); font-weight: 600;
}
.swap-btn svg { width: 13px; height: 13px; display: block; flex: 0 0 13px; }
.swap-btn:hover { background: var(--good); color: #fff; }
.swap-btn.done { background: var(--good); color: #fff; border-color: var(--good); }
.swap-btn.done .do { font-size: var(--t-xs); font-weight: 700; opacity: 1; }
.swap-btn.done svg { width: 15px; height: 15px; flex-basis: 15px; }

/* Phones: the button goes full width under the product rather than squeezing
   the name into two characters per line. */
@media (max-width: 560px) {
  .swap-opt { flex-wrap: wrap; align-items: flex-start; }
  .swap-opt-main { flex-basis: calc(100% - 48px); }
  .swap-btn { flex-basis: 100%; flex-direction: row; justify-content: center; gap: 8px; }
  .swap-btn .do { font-size: var(--t-xs); }
  .swap-btn svg { width: 15px; height: 15px; flex-basis: 15px; }
}

/* --------------------------------------------------------------- results */
.rank-card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  margin-bottom: 10px; box-shadow: var(--shadow); overflow: hidden;
}
.rank-card.winner { border-color: var(--good); border-width: 2px; }
/* Was opacity .84, which took the explanatory note under the chain name to
   3.70:1 - the one line on the card that says why the total is partial. */
.rank-card.partial { background: var(--surface-2); }
.rank-head {
  display: flex; align-items: center; gap: 14px; padding: 13px 18px; cursor: pointer;
  width: 100%; text-align: left; color: var(--ink); list-style: none;
}
.rank-head::-webkit-details-marker { display: none; }
.rank-head::after { content: '⌄'; color: var(--muted); font-size: var(--t-md); line-height: 1; transition: transform .15s ease; }
.rank-card[open] .rank-head::after { transform: rotate(180deg); }
.rank-head .pos { font-size: var(--t-md); font-weight: 800; color: var(--muted); min-width: 26px; }
.rank-card.winner .pos { color: var(--good-ink); }
/* The chain, the schemes it takes and why its total reads the way it does are
   three siblings, not one nested lump: on a phone they each need a line of
   their own and a note inside the name could not have one. The name shrinks to
   its text and the note takes the slack, which is where both already sat. */
.rank-head .who { flex: 0 1 auto; font-weight: 700; font-size: var(--t-md); min-width: 0; }
.rank-head .loy-row:empty { display: none; }
.rank-head .cost { font-size: var(--t-lg); font-weight: 800; font-variant-numeric: tabular-nums;
                   white-space: nowrap; }
@media (max-width: 620px) {
  .rank-head { flex-wrap: wrap; }
  .rank-head .shop-btn { order: 5; margin-left: auto; }
}
.rank-card.winner .cost { color: var(--good-ink); }
.rank-head .note { flex: 1 1 auto; font-size: var(--t-xs); color: var(--muted); font-weight: 500; display: inline-block; margin-left: 9px; }
@media (max-width: 620px) { .rank-head .note { display: block; margin: 2px 0 0; } }
.rank-body { border-top: 1px solid var(--line); padding: 2px 0; }
.rank-body table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.rank-body td { padding: 8px 16px; border-bottom: 1px solid var(--line); }
.rank-body tr:last-child td { border-bottom: 0; }
.rank-body .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }

.warn {
  background: var(--warn-bg); border: 1px solid var(--line); color: var(--brand-ink);
  padding: 12px 16px; border-radius: var(--radius); margin: 14px 0; font-size: var(--t-sm);
}


.chart-card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px 18px; margin-bottom: 16px; box-shadow: var(--shadow);
}
.chart-card h3 { margin: 0 0 12px; font-size: var(--t-sm); }
.bar-row { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; font-size: var(--t-sm); }
.bar-label { width: 168px; flex-shrink: 0; color: var(--muted); line-height: 1.25; }
.bar-track { flex: 1; background: var(--surface-2); border-radius: 5px; height: 17px; overflow: hidden; border: 1px solid var(--line); }
.bar-fill { display: block; height: 100%; background: var(--brand); border-radius: 5px 0 0 5px; }
.bar-fill.win { background: var(--good); }
.bar-value { width: 76px; text-align: right; font-variant-numeric: tabular-nums; font-weight: 700; }

/* ------------------------------------------------------------------ ads */
.ad-slot {
  margin: 26px 0; padding: 0; min-height: 90px; display: grid; place-items: center;
  background: var(--surface-2); border: 1px dashed var(--line); border-radius: var(--radius);
  color: var(--muted); font-size: var(--t-2xs); overflow: hidden;
}
.ad-slot[data-filled="1"] { border-style: solid; background: transparent; min-height: 0; }

/* -------------------------------------------------------------- content */
.prose { max-width: 72ch; }
.prose h2 { margin-top: 30px; }
.prose p, .prose li { color: var(--ink); font-size: var(--t-sm); }
.prose li { margin-bottom: 6px; }
.form-field {
  width: 100%; padding: 10px 13px; border-radius: 10px; border: 1px solid var(--line);
  background: var(--surface); color: var(--ink); font: inherit; margin-bottom: 12px;
}
textarea.form-field { min-height: 130px; resize: vertical; }
.notice { padding: 12px 16px; border-radius: var(--radius); margin: 14px 0; font-size: var(--t-sm); }
.notice.ok { background: var(--good-bg); color: var(--good-ink); border: 1px solid var(--good); }

.sensitive {
  background: var(--warn-bg); border: 1px solid #E6C87A; color: #5C4405;
  border-radius: var(--radius); padding: 11px 15px; font-size: var(--t-xs);
  margin: 12px 0; line-height: 1.5;
}
.sensitive.strong-warn { border-left: 4px solid var(--warn); }
.general-note { font-size: var(--t-xs); margin: 8px 0 0; }
.section-lead { margin-top: 8px; }
.hero.compact { margin-bottom: var(--s-5); }
.hero.compact h1 { font-size: var(--t-2xl); }
.home-foot { margin-top: var(--s-7); padding-top: var(--s-2); border-top: 1px solid var(--line); }
.ready h3 { margin: 0 0 3px; font-size: var(--t-md); font-weight: 700; }
.ready h3 .lasts { font-size: var(--t-xs); font-weight: 600; color: var(--muted); }

.conf {
  font-variant-numeric: tabular-nums; font-weight: 700; font-size: var(--t-xs);
  padding: 2px 8px; border-radius: 999px; white-space: nowrap;
  background: var(--line); color: var(--muted);
}
.conf.good { background: var(--good-bg); color: var(--good-ink); }
.conf.mid  { background: var(--warn-bg); color: var(--warn-ink); }
.conf.low  { background: var(--dear-bg); color: var(--dear); }
.conf.sm { font-size: var(--t-2xs); padding: 1px 6px; }
.conf-wrap { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; cursor: help; }
.conf-cap { font-size: var(--t-2xs); color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
@media (max-width: 620px) { .conf-cap { display: none; } }

.chain-logo {
  width: 26px; height: 26px; object-fit: contain; border-radius: 5px;
  background: #fff; border: 1px solid var(--line); padding: 1px; flex-shrink: 0;
}
.rank-head .chain-logo { width: 32px; height: 32px; }
.loy-row { display: inline-flex; gap: 3px; flex-wrap: wrap; vertical-align: middle; margin-left: 6px; }
/* Deliberately tiny: these are a hint that a scheme applies, not a headline.
   Colour carries the brand, the short code carries the meaning, and the full
   reward text lives in the tooltip so rows stay one line high. */
.loy {
  display: inline-block; font-size: var(--t-2xs); font-weight: 700; letter-spacing: .01em;
  padding: 1px 6px; border-radius: 4px; line-height: 1.5;
  border: 1px solid color-mix(in srgb, var(--loy, var(--muted)) 40%, transparent);
  background: color-mix(in srgb, var(--loy, var(--muted)) 11%, #fff);
  color: var(--loy, var(--muted)); text-decoration: none; cursor: pointer;
  white-space: nowrap;
}
.loy:hover { background: var(--loy, var(--brand)); color: #fff; text-decoration: none; }
.loy-filter { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin-top: 12px; }

.filters {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); margin: 16px 0; overflow: hidden;
}
.filters > summary {
  padding: 11px 16px; cursor: pointer; font-weight: 600; list-style: none;
  display: flex; gap: 8px; align-items: baseline;
}
.filters > summary::-webkit-details-marker { display: none; }
.filters > summary::after { content: '⌄'; margin-left: auto; color: var(--muted); }
.filters[open] > summary::after { transform: rotate(180deg); display: inline-block; }
.filter-body { border-top: 1px solid var(--line); padding: 12px 16px; }
.switch { display: flex; align-items: center; gap: 8px; font-size: var(--t-sm); margin-bottom: 12px; flex-wrap: wrap; }
.switch .muted { font-size: var(--t-xs); }
.chain-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(235px, 1fr)); gap: 7px; }
.chain {
  display: flex; align-items: flex-start; gap: 9px; font-size: var(--t-sm);
  padding: 7px 8px; border: 1px solid var(--line); border-radius: 9px;
  background: var(--surface-2);
}
.chain input { margin-top: 3px; }
.chain-meta { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.chain-name { font-weight: 600; line-height: 1.25; }
.chain small { font-size: var(--t-xs); }
.chain .loy-row { margin-left: 0; }
.small { font-size: var(--t-xs); }

.split {
  background: var(--surface); border: 1px solid var(--good);
  border-radius: var(--radius); margin: 10px 0; font-size: var(--t-sm); overflow: hidden;
}
.split-head {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding: 12px 16px; cursor: pointer; list-style: none;
}
.split-head::-webkit-details-marker { display: none; }
.split-head::after { content: '⌄'; color: var(--muted); font-size: var(--t-md); }
.split[open] .split-head::after { transform: rotate(180deg); display: inline-block; }
.split-body { border-top: 1px solid var(--line); padding: 4px 0 8px; }
.stop { padding: 8px 16px; }
.stop + .stop { border-top: 1px dashed var(--line); }
/* Grid rather than space-between: with flex the total drifted left or right
   depending on how long the shop name was, so nothing lined up down the list. */
.stop-head {
  display: grid; grid-template-columns: 1fr auto auto; gap: 12px;
  align-items: center; margin-bottom: 6px;
}
.stop-name { font-weight: 700; }
.stop-total { font-weight: 700; font-variant-numeric: tabular-nums; color: var(--good-ink); }
.stop table { width: 100%; border-collapse: collapse; font-size: var(--t-xs); }
.stop td { padding: 5px 0; border-bottom: 1px solid var(--line); vertical-align: top; }
.stop tr:last-child td { border-bottom: 0; }
/* padding-left, because the cells are flush: «2 x 1,05 €2,10 €» was one
   string with no space in it at every width. */
.stop .num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums;
             padding-left: 14px; }
.split-names { flex: 1; color: var(--muted); }
.split-total { font-weight: 800; font-size: var(--t-md); color: var(--good-ink); font-variant-numeric: tabular-nums; }
.split-save { background: var(--good-bg); color: var(--good-ink); font-weight: 700;
              border-radius: 999px; padding: 2px 10px; font-size: var(--t-xs); }

/* ------------------------------------------------------- single store view */
.chain-logo.big {
  width: 46px; height: 46px; padding: 3px; border-radius: 8px;
}
.store-head { display: flex; gap: 14px; align-items: flex-start; margin-bottom: 6px; flex-wrap: wrap; }
.store-head h1 { margin: 0 0 var(--s-1); }
.store-head p { margin: 0; }
.store-card header { display: flex; gap: 12px; align-items: center; }
.store-card h3 { margin: 0 0 3px; font-size: var(--t-md); }
.store-stat { margin: 10px 0 2px; display: flex; align-items: baseline; gap: 8px; }
.store-stat strong { font-size: var(--t-lg); color: var(--good-ink); }
.thin-overlap {
  margin: 6px 0 0; font-size: var(--t-xs); color: var(--warn-ink);
  background: var(--warn-bg); border-radius: 7px; padding: 5px 9px;
}

/* Compact price gauge for a table row: same colour language as the map and the
   product page, so "green means cheap" means one thing across the whole site. */
.gauge-mini {
  position: relative; height: 6px; width: 116px; border-radius: 999px;
  background:
    linear-gradient(90deg, transparent calc(20% - 1px), var(--surface) calc(20% - 1px) calc(20% + 1px),
                    transparent calc(20% + 1px) calc(50% - 1px), var(--surface) calc(50% - 1px) calc(50% + 1px),
                    transparent calc(50% + 1px)),
    linear-gradient(90deg, var(--gauge-cheap) 0 20%,
                    var(--gauge-mid) 20% 50%, var(--gauge-dear) 50% 100%);
  margin-bottom: 4px;
}
.gauge-mini .gauge-pin {
  position: absolute; top: -3px; width: 3px; height: 12px; border-radius: 2px;
  background: var(--ink); box-shadow: 0 0 0 2px var(--surface); transform: translateX(-50%);
}
.verdict-cell { min-width: 132px; }
.verdict-cell .verdict { font-size: var(--t-xs); }
.verdict-cell .muted { font-weight: 500; }
.gauge-key { display: flex; gap: 16px; flex-wrap: wrap; align-items: center; margin: 10px 0 14px; font-size: var(--t-xs); }
.gauge-key .dot {
  display: inline-block; width: 10px; height: 10px; border-radius: 50%;
  margin-right: 6px; vertical-align: -1px;
}
.gauge-key .dot.cheap { background: var(--gauge-cheap); }
.gauge-key .dot.mid { background: var(--gauge-mid); }
.gauge-key .dot.dear { background: var(--gauge-dear); }

/* ------------------------------------------------------------------- map */
#map {
  height: 62vh; min-height: 400px; border-radius: var(--radius);
  border: 1px solid var(--line); box-shadow: var(--shadow); z-index: 1;
}
.map-legend {
  display: flex; gap: 18px; flex-wrap: wrap; align-items: center;
  margin: 12px 0 4px; font-size: var(--t-sm);
}
.map-legend .dot {
  display: inline-block; width: 12px; height: 12px; border-radius: 50%;
  margin-right: 7px; vertical-align: -1px; border: 2px solid #fff;
  box-shadow: 0 0 0 1px var(--line);
}
.map-legend .dot.cheap { background: var(--gauge-cheap); }
.map-legend .dot.mid   { background: var(--gauge-mid); }
.map-legend .dot.dear  { background: var(--gauge-dear); }
.map-legend .dot.none  { background: #9C8E76; }
.map-note { font-size: var(--t-xs); margin: 10px 0 0; max-width: 72ch; }
.map-attrib { font-size: var(--t-xs); margin: 6px 0 0; }
.leaflet-container { font: inherit; background: var(--surface-2); }

footer {
  border-top: 1px solid var(--line); padding: 24px 20px; color: var(--muted);
  font-size: var(--t-xs); text-align: center; background: var(--surface);
}
footer nav { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; margin-bottom: 10px; }
footer a { color: var(--muted); text-decoration: underline; }

@media (max-width: 640px) {
  .hero h1 { font-size: var(--t-xl); }
  .table th:nth-child(6), .table td:nth-child(6) { display: none; }
  main { padding: 20px 14px 52px; }
  .topbar { gap: 12px; }
  .bar-label { width: 112px; font-size: var(--t-xs); }
}

/* --------------------------------------------- add-to-basket (inline rows) */
.mini {
  display: inline-flex; align-items: center; gap: 5px; white-space: nowrap;
  border: 1px solid color-mix(in srgb, var(--brand) 45%, transparent);
  background: var(--brand-tint); color: var(--brand-strong);
  border-radius: 999px; padding: 6px 13px 6px 10px; font: inherit;
  font-size: var(--t-xs); font-weight: 700; cursor: pointer;
  transition: background .12s ease, color .12s ease, transform .08s ease;
}
.mini::before {
  content: '+'; display: inline-grid; place-items: center;
  width: 17px; height: 17px; border-radius: 50%; flex-shrink: 0;
  background: var(--brand); color: var(--brand-ink);
  font-size: var(--t-sm); font-weight: 700; line-height: 1;
  /* the glyph sits high in its box, so nudge it back onto the optical centre */
  padding-bottom: 1px;
}
.mini:hover { background: var(--brand); color: var(--brand-ink); }
.mini:hover::before { background: var(--brand-ink); color: var(--brand); }
.mini:active { transform: translateY(1px); }
.mini:disabled { opacity: .6; cursor: default; }
/* app.js swaps the label for a tick and disables the button for a moment after
   an add. Without these two the confirmation reads "+✓" at 60% opacity: the
   pseudo-element goes on drawing its plus, and the disabled fade applies to a
   state that is a success, not an unavailable control. */
.mini.added::before, .btn.with-plus.added::before { display: none; }
.mini.added, .btn.with-plus.added { opacity: 1; }

/* ------------------------------------------------------ header autocomplete */
.topbar .search { position: relative; }
.ac {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 40;
  background: var(--surface); border: 1px solid var(--line); border-radius: 11px;
  box-shadow: var(--shadow); max-height: 380px; overflow-y: auto;
}
.ac-head {
  padding: 7px 14px; font-size: var(--t-2xs); text-transform: uppercase;
  letter-spacing: .06em; color: var(--muted); background: var(--surface-2);
  border-bottom: 1px solid var(--line);
}
.ac-row {
  display: flex; align-items: center; gap: 9px; padding: 9px 14px;
  border-bottom: 1px solid var(--line); color: var(--ink); text-decoration: none;
}
.ac-row:last-child { border-bottom: 0; }
.ac-row:hover, .ac-row.on { background: var(--brand-tint); text-decoration: none; }
.ac-row img {
  width: 28px; height: 28px; object-fit: contain; background: #fff;
  border: 1px solid var(--line); border-radius: 5px; flex-shrink: 0;
}
/* Name on its own line, breadcrumb underneath: side by side the long category
   paths squeezed the name down to "Γά...", which is the one thing you need. */
.ac-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.ac-main { font-weight: 600; font-size: var(--t-sm); }
.ac-row small {
  color: var(--muted); font-size: var(--t-xs); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}

/* ------------------------------------------------------- bank scheme logos */
.loy-logo {
  width: 20px; height: 20px; object-fit: contain; border-radius: 5px;
  vertical-align: middle; flex-shrink: 0;
}
.loyalty-info .loy-logo { width: 26px; height: 26px; }
.loyalty-info { margin: var(--s-5) 0; }
.loyalty-info ul { list-style: none; padding: 0; margin: 0 0 8px; }
.loyalty-info li {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 0; border-bottom: 1px solid var(--line); font-size: var(--t-sm);
}
.loyalty-info li:last-child { border-bottom: 0; }
.loyalty-info .muted { font-size: var(--t-xs); }
.near-help { margin: -6px 0 12px; }
.near-help input {
  width: 62px; padding: 3px 7px; border-radius: 7px; border: 1px solid var(--line);
  background: var(--bg); color: var(--ink); font: inherit; font-size: var(--t-xs); text-align: center;
}


/* -------------------------------------------------- chain not in the feed */
.no-data {
  margin: 8px 0 0; font-size: var(--t-xs); font-weight: 600;
  color: var(--dear); background: var(--dear-bg);
  border: 1px solid color-mix(in srgb, var(--dear) 35%, transparent);
  border-radius: 8px; padding: 8px 11px; line-height: 1.45;
}
.no-data.big { font-weight: 500; font-size: var(--t-sm); padding: 12px 15px; margin: 14px 0; }

/* ------------------------------------------------- chain name as a link */
.chain-link {
  display: inline-flex; align-items: center; gap: 7px; color: var(--ink);
  text-decoration: none; font-weight: 600;
}
.chain-link .chain-logo { width: 22px; height: 22px; }
.chain-link:hover { color: var(--brand-strong); text-decoration: none; }
.chain-link:hover span { text-decoration: underline; }

/* Split plan header: fixed columns so the shop names, totals and savings line
   up down the list instead of drifting with the length of each name. */
.split-head {
  display: grid;
  grid-template-columns: minmax(120px, auto) 1fr auto auto 16px;
  align-items: baseline; gap: 12px; padding: 12px 16px; cursor: pointer;
  list-style: none;
}
.split-head > strong { white-space: nowrap; }
.split-names { color: var(--muted); min-width: 0; }
.split-total { text-align: right; }
@media (max-width: 640px) {
  .split-head { grid-template-columns: 1fr auto; row-gap: 4px; }
  .split-names { grid-column: 1 / -1; order: 3; }
}

.stop-name { display: inline-flex; align-items: center; gap: 8px; }


/* ------------------------------------------------------- product price bar */
.gauge-top { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; font-size: var(--t-xs); }
.gauge-marker-row { position: relative; height: 22px; }
.gauge-value {
  position: absolute; transform: translateX(-50%); white-space: nowrap;
  font-size: var(--t-xs); font-weight: 800; padding: 1px 7px; border-radius: 5px;
  background: var(--surface); border: 1px solid var(--line);
}
.gauge-value.cheap { color: var(--good-ink); border-color: var(--good); }
.gauge-value.mid { color: var(--warn-ink); border-color: var(--warn); }
.gauge-value.dear { color: var(--dear); border-color: var(--dear); }
.gauge-median {
  position: absolute; top: -2px; width: 2px; height: 12px;
  background: rgba(0,0,0,.42); transform: translateX(-50%); border-radius: 1px;
}
.gauge-legend { display: flex; justify-content: space-between; gap: 8px; }
.gauge-legend .mid-label { color: var(--muted); }
.gauge-legend b { color: var(--ink); }
.gauge-note { font-size: var(--t-xs); margin: 6px 0 0; }


/* Large variant of the add button, used on the product page where it is the
   primary action rather than a row affordance. */
.btn.primary.with-plus { display: inline-flex; align-items: center; gap: 8px; }
.btn.primary.with-plus::before {
  content: '+'; display: inline-grid; place-items: center;
  width: 20px; height: 20px; border-radius: 50%; flex-shrink: 0;
  background: var(--brand-ink); color: var(--brand);
  font-size: var(--t-base); font-weight: 700; line-height: 1; padding-bottom: 1px;
}
.product-actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.report-link {
  display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-xs);
  color: var(--muted); border: 1px solid var(--line); background: var(--surface);
  border-radius: 999px; padding: 8px 14px; text-decoration: none;
}
.report-link:hover { border-color: var(--dear); color: var(--dear); text-decoration: none; }

.report-subject {
  display: flex; gap: 8px; flex-wrap: wrap; align-items: baseline;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 11px 15px; margin-bottom: 16px; font-size: var(--t-sm);
}


/* ------------------------------------------------------------ data faults */
.issue-badge {
  display: inline-block; font-size: var(--t-2xs); line-height: 1; cursor: help;
  color: var(--warn-ink); background: var(--warn-bg); border-radius: 4px;
  padding: 2px 5px; vertical-align: middle;
}
.issue-badge:focus-visible { outline: 2px solid var(--warn); }
.issue-note {
  background: var(--warn-bg); border: 1px solid #E6C87A; color: #5C4405;
  border-radius: var(--radius); padding: 10px 14px; margin: 8px 0 12px; font-size: var(--t-xs);
}
.issue-note ul { margin: 5px 0 0; padding-left: 18px; }
.issue-note em { font-style: normal; color: #3F2E03; }

.index-section { margin-top: var(--s-6); }
.index-now { margin: 0 0 8px; display: flex; align-items: baseline; gap: 8px; }
.index-now strong { font-size: var(--t-xl); color: var(--brand-strong); }


/* ------------------------------------------------------------- statistics */
.spread-pill {
  display: inline-block; font-weight: 800; font-size: var(--t-xs); padding: 2px 9px;
  border-radius: 999px; background: var(--dear-bg); color: var(--dear);
}
.spread-pill.good { background: var(--good-bg); color: var(--good-ink); }
.stats-page section { margin-bottom: 34px; }
/* The index bars run 80-120, so a 100 chain sits mid-track rather than at zero. */
.index-track { position: relative; }
.index-track::before {
  content: ''; position: absolute; left: 50%; top: -3px; bottom: -3px;
  width: 1px; background: var(--muted); opacity: .5;
}

.donate-buttons { display: flex; gap: 12px; flex-wrap: wrap; margin: 22px 0; }
.donate-btn { min-width: 148px; text-align: center; }
.donate-btn.kofi { background: #D23F3C; border-color: #D23F3C; color: #fff; }
.donate-btn.kofi:hover { background: #B93331; border-color: #B93331; }
.donate-btn.paypal { background: #003087; border-color: #003087; color: #fff; }
.donate-btn.paypal:hover { background: #001F5C; border-color: #001F5C; }


/* ------------------------------------------------------------- separators */
.divider { border: 0; border-top: 1px solid var(--line); margin: var(--s-6) 0 var(--s-5); }
.divider.soft { margin: 22px 0 16px; opacity: .6; }

/* The basket is the point of the site, so it sits apart from the nav links and
   looks like an action rather than another destination. */
.topnav { margin-left: auto; }
.basket-link {
  display: inline-flex; align-items: center; gap: 7px; margin-left: 6px;
  background: var(--brand-tint); border: 1px solid var(--brand);
  color: var(--brand-strong) !important; border-radius: 999px;
  padding: 6px 14px; font-weight: 700;
}
.basket-link:hover { background: var(--brand); color: var(--brand-ink) !important; text-decoration: none; }
.basket-link:hover .pill { background: var(--brand-ink); color: var(--brand); }

/* price provenance */
.source-note { font-size: var(--t-xs); color: var(--muted); display: block; margin-top: 2px; }
.source-note a { color: inherit; text-decoration: underline; }

.shop-btn {
  display: inline-flex; align-items: center; gap: 4px; white-space: nowrap;
  font-size: var(--t-xs); font-weight: 700; padding: 5px 12px; border-radius: 999px;
  border: 1px solid var(--brand); background: var(--surface);
  color: var(--brand-strong); text-decoration: none; flex-shrink: 0;
}
.shop-btn:hover { background: var(--brand); color: var(--brand-ink); text-decoration: none; }
.stop-head .shop-btn { font-size: var(--t-2xs); padding: 3px 10px; }

.donate-block { margin: 20px 0 26px; }
.donate-block h2 { margin: 0 0 10px; font-size: var(--t-md); }
.donate-other { min-width: 120px; text-align: center; }


/* ---------------------------------------------------------------- tooltip */
.tip {
  position: absolute; z-index: 200; max-width: 300px;
  background: var(--ink); color: #FFF9EC; font-size: var(--t-xs); line-height: 1.45;
  padding: 8px 11px; border-radius: 9px; pointer-events: none;
  box-shadow: 0 6px 20px rgba(36, 29, 18, .25);
}
.tip::after {
  content: ''; position: absolute; left: 50%; margin-left: -5px;
  border: 5px solid transparent;
}
.tip[data-placement="top"]::after { top: 100%; border-top-color: var(--ink); }
.tip[data-placement="bottom"]::after { bottom: 100%; border-bottom-color: var(--ink); }
.tip b { color: #fff; }
.tip .tip-src { opacity: .8; }
[data-tip] { cursor: help; }
[data-tip].shop-btn, [data-tip] a, a[data-tip] { cursor: pointer; }

/* A price with a known provenance gets a subtle dotted underline, so it reads
   as "there is more here" without shouting. */
.has-source { border-bottom: 1px dotted var(--muted); cursor: help; }

/* Link to the page a price was read from. Deliberately quiet: it sits in a
   column of numbers people scan, and it is there for the reader who wants to
   check one, not for everyone reading past it. */
.src-link {
  font-size: .78em; text-decoration: none; color: var(--muted);
  vertical-align: super; margin-left: 2px;
}
.src-link:hover, .src-link:focus-visible { color: var(--brand-strong); }


/* -------------------------------------------------------- quantity slider */
/* Two parts of one control: the figure, which you can type into, and the
   track. On a desktop the track is the last column of the line; on a phone,
   where there is no width left beside a Greek shelf name, mobile.css gives
   it a row of its own under the name. */
/* Boxed, on purpose. It was tried as bare bold text with an underline on
   focus, on the theory that a border and a caret read as a form to fill in;
   the owner's answer was that without an edge nothing says it can be tapped
   and changed, and that is the more important thing for it to say. Sized by
   the figure it holds, so the number is never adrift in a box wider than
   itself; one width for every line, so the tracks beside them line up. */
.bitem .qty {
  grid-area: qty; display: inline-flex; align-items: baseline; justify-content: flex-end;
  gap: 4px; width: 104px; box-sizing: border-box; padding: 4px 9px;
  border-radius: var(--r-sm, 8px);
  background: var(--surface-2); border: 1px solid var(--line);
}
.bitem .qty:focus-within { border-color: var(--brand); box-shadow: 0 0 0 2px var(--brand-tint); }
.qty-num {
  width: 4.2ch; field-sizing: content; min-width: 2.5ch; max-width: 6ch; border: 0; padding: 0; background: none; color: var(--ink);
  font: inherit; font-weight: 700; font-variant-numeric: tabular-nums;
  text-align: right;
}
.qty-num::-webkit-outer-spin-button,
.qty-num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.qty-num { -moz-appearance: textfield; appearance: textfield; }
.qty-num:focus-visible { outline: none; }   /* the box around it shows focus */
.qunit { font-size: var(--t-xs, 13px); color: var(--muted); }

.qty-range {
  grid-area: range; width: 100%; margin: 0; height: 22px;
  -webkit-appearance: none; appearance: none; background: none; cursor: pointer;
}
.qty-range::-webkit-slider-runnable-track {
  height: 6px; border-radius: 999px;
  background: linear-gradient(to right, var(--brand) var(--p, 0%),
                              var(--line) var(--p, 0%));
}
.qty-range::-moz-range-track {
  height: 6px; border-radius: 999px;
  background: linear-gradient(to right, var(--brand) var(--p, 0%),
                              var(--line) var(--p, 0%));
}
.qty-range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; margin-top: -7px;
  width: 20px; height: 20px; border-radius: 50%;
  background: var(--surface); border: 3px solid var(--brand);
  box-shadow: 0 1px 3px rgb(0 0 0 / .18); cursor: grab;
}
.qty-range::-moz-range-thumb {
  width: 20px; height: 20px; border-radius: 50%; border: 3px solid var(--brand);
  background: var(--surface); box-shadow: 0 1px 3px rgb(0 0 0 / .18); cursor: grab;
}
.qty-range:active::-webkit-slider-thumb { cursor: grabbing; }
.qty-range:focus-visible { outline: 2px solid var(--brand); outline-offset: 4px; }



/* Logos inside chart labels.
   The flex container has to be whichever element actually wraps the logo and
   the text. On the stats page that is the <a> inside the label, so setting it
   only on .bar-label left the logo sitting on the text baseline and drifting
   when a long name like "ΑΒ Βασιλόπουλος" wrapped to two lines. */
.bar-label, .bar-label > a {
  display: flex; align-items: center; gap: 7px; min-width: 0;
}
.bar-label > a { text-decoration: none; color: inherit; }
.bar-label > a:hover { color: var(--brand-strong); }
.bar-label .chain-logo { width: 20px; height: 20px; flex-shrink: 0; }
.bar-label span, .bar-label > a > span { min-width: 0; overflow-wrap: anywhere; }


/* ------------------------------------------------- ready-basket rail */
.rail-section { margin-top: var(--s-7); padding-top: var(--s-2); border-top: 1px solid var(--line); }
.rail {
  display: flex; gap: 12px; overflow-x: auto; padding: 4px 2px 14px;
  scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch;
}
.rail::-webkit-scrollbar { height: 8px; }
.rail::-webkit-scrollbar-thumb { background: var(--line); border-radius: 999px; }
.rail-card {
  flex: 0 0 auto; width: 178px; scroll-snap-align: start;
  display: flex; flex-direction: column; gap: 3px; text-align: left;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 13px 15px; cursor: pointer;
  font: inherit; color: var(--ink); transition: border-color .12s ease;
}
.rail-card:hover { border-color: var(--brand); }
.rail-card:disabled { opacity: .6; cursor: default; }
.rail-icon { font-size: var(--t-lg); line-height: 1.1; }
.rail-name { font-weight: 700; font-size: var(--t-sm); line-height: 1.25; }
.rail-meta { font-size: var(--t-xs); color: var(--muted); }
.rail-price { font-weight: 800; color: var(--good-ink); margin-top: 3px; }


/* ------------------------------------------------- non-Greek reference rows */
.foreign { margin: 18px 0; }
.foreign > summary {
  cursor: pointer; font-size: var(--t-sm); color: var(--muted); padding: 6px 0;
  list-style: none;
}
.foreign > summary::-webkit-details-marker { display: none; }
.foreign > summary::before { content: '▸ '; }
.foreign[open] > summary::before { content: '▾ '; }
.foreign-table, .foreign-table td { color: var(--muted); }
.foreign-table { box-shadow: none; background: var(--surface-2); }
.foreign-table td { font-weight: 400; }

/* search dropdown: go, or add without leaving the page */
.ac-row { display: flex; align-items: center; }
.ac-row:hover { background: var(--brand-tint); }
.ac-go {
  display: flex; align-items: center; gap: 9px; flex: 1; min-width: 0;
  padding: 9px 4px 9px 14px; color: var(--ink); text-decoration: none;
}
.ac-go:hover { text-decoration: none; }

/* A round chip rather than a bare glyph with a divider: it reads as a button
   at a glance, and the hard border-left made every row look split in two. */
.ac-add {
  flex-shrink: 0; margin: 0 10px 0 6px; width: 30px; height: 30px;
  display: grid; place-items: center; padding: 0; border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--brand) 40%, transparent);
  background: var(--surface); color: var(--brand-strong);
  cursor: pointer; transition: background .12s ease, color .12s ease,
                              border-color .12s ease, transform .08s ease;
}
/* An SVG centres on its own viewBox, so this is exact regardless of font. */
.ac-add svg { width: 15px; height: 15px; display: block; }
.ac-add:hover {
  background: var(--brand); border-color: var(--brand); color: var(--brand-ink);
  transform: scale(1.08);
}
.ac-add:active { transform: scale(0.96); }
.ac-add:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.ac-add.done {
  background: var(--good); border-color: var(--good); color: #fff;
  transform: none;
}


/* ------------------------------------------------------- basket clear tools */
.basket-tools {
  display: flex; justify-content: flex-end; gap: 8px; flex-wrap: wrap;
  margin: 0 0 8px;
}
.basket-tools:empty { display: none; }
.clear-btn {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--line); background: var(--surface); color: var(--muted);
  border-radius: 999px; padding: 5px 12px; font: inherit; font-size: var(--t-xs);
  cursor: pointer;
}
.clear-btn span {
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--muted); border-radius: 999px;
  padding: 0 6px; font-size: var(--t-2xs); font-weight: 700;
}
.clear-btn:hover { border-color: var(--dear); color: var(--dear); }
.clear-btn:hover span { background: var(--dear-bg); color: var(--dear); }
@media (max-width: 520px) { .basket-tools { justify-content: stretch; } }

/* =========================================================================
   Charts (static/charts.js)

   One visual language for every chart on the site. The hover tooltip is a
   single element on <body> rather than one per chart, so it can never be
   clipped by a card's overflow.
   ========================================================================= */

.chart-card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 16px 18px; box-shadow: var(--shadow);
}
.chart-wrap { width: 100%; }
.chart-svg { width: 100%; height: auto; display: block; touch-action: pan-y; }
.chart-svg .hover-catch { cursor: crosshair; }

.chart-legend {
  display: flex; flex-wrap: wrap; gap: 6px 16px; margin-top: 12px;
  font-size: var(--t-xs); color: var(--muted);
}
.chart-legend span { display: inline-flex; align-items: center; gap: 6px; }
.chart-legend i { width: 11px; height: 11px; border-radius: 3px; flex: 0 0 11px; }

.chart-tip {
  position: fixed; z-index: 90; pointer-events: none;
  background: var(--ink); color: #FFF8E7; border-radius: 9px;
  padding: 8px 11px; font-size: var(--t-xs); line-height: 1.5;
  box-shadow: 0 6px 20px rgba(36, 29, 18, .28); max-width: 280px;
}
.chart-tip[hidden] { display: none; }
.chart-tip strong { display: block; font-size: var(--t-xs); opacity: .78; margin-bottom: 3px; font-weight: 600; }
.chart-tip span { display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.chart-tip i { width: 9px; height: 9px; border-radius: 2px; flex: 0 0 9px; }
.chart-tip b { font-weight: 700; font-variant-numeric: tabular-nums; }
.chart-tip em { font-style: normal; opacity: .7; }
.chart-tip .muted { opacity: .72; white-space: normal; }

/* The same numbers as a table, for screen readers and for anyone who would
   rather read them. Off-screen, not display:none, so it stays announceable. */
.chart-data {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.chart-data table { width: auto; }

/* ------------------------------------------------------------ bar rows */
.cbar {
  display: grid; grid-template-columns: minmax(120px, 210px) 1fr auto;
  align-items: center; gap: 12px; padding: 5px 6px; border-radius: 8px;
}
.cbar:hover, .cbar:focus-visible { background: var(--surface-2); outline: none; }
.cbar:focus-visible { box-shadow: 0 0 0 2px var(--brand); }
.cbar-label { font-size: var(--t-sm); min-width: 0; }
.cbar-label a, .cbar-label span {
  display: flex; align-items: center; gap: 7px; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cbar-label a:hover { color: var(--brand-strong); }
.cbar-label img { width: 20px; height: 20px; object-fit: contain; flex: 0 0 20px; border-radius: 3px; }
.cbar-track {
  position: relative; height: 17px; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: 5px; overflow: hidden;
}
.cbar-fill { position: absolute; top: 0; bottom: 0; border-radius: 3px; min-width: 2px; }
.cbar-fill.neutral { background: var(--brand); }
.cbar-fill.up   { background: var(--dear); }
.cbar-fill.down { background: var(--good); }
.cbar-fill.win  { background: var(--good); }
.cbar-zero { position: absolute; top: -1px; bottom: -1px; width: 1px; background: var(--muted); opacity: .5; }
.cbar-value {
  font-weight: 700; font-size: var(--t-sm); font-variant-numeric: tabular-nums;
  min-width: 62px; text-align: right;
}
.cbar-value.up { color: var(--dear); }
.cbar-value.down { color: var(--good-ink); }

@media (max-width: 560px) {
  .cbar { grid-template-columns: 1fr auto; grid-template-areas: "label value" "track track"; gap: 4px 10px; }
  .cbar-label { grid-area: label; }
  .cbar-value { grid-area: value; }
  .cbar-track { grid-area: track; }
}

/* -------------------------------------------------------------- heatmap */
.heat { display: flex; flex-direction: column; gap: 4px; min-width: 420px; }
.heat-row { display: grid; grid-template-columns: minmax(110px, 175px) repeat(var(--cols), 1fr); gap: 4px; }
.heat-label {
  font-size: var(--t-xs); display: flex; align-items: center; gap: 7px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.heat-label a { display: flex; align-items: center; gap: 7px; color: var(--ink); }
.heat-label a:hover { color: var(--brand-strong); }
.heat-label img { width: 19px; height: 19px; object-fit: contain; flex: 0 0 19px; border-radius: 3px; }
.heat-col { font-size: var(--t-2xs); color: var(--muted); text-align: center; font-weight: 600; }
.heat-cell {
  height: 32px; border-radius: 6px; display: grid; place-items: center;
  font-size: var(--t-2xs); font-weight: 700; font-variant-numeric: tabular-nums;
  /* --t is 0..1 along the ramp. The ramp deliberately stops at a mid mustard
     rather than running to full brand-strong: past that point neither dark nor
     cream ink clears 4.5:1 against the tile, and a crossfade between the two
     passes straight through a band where the number is invisible (measured at
     1.12:1 before this). Capped here, one dark ink holds 4.7:1 at every step,
     and the exact value is on hover anyway. */
  --heat-end: #AD8124;
  background: color-mix(in srgb, var(--heat-end) calc(var(--t) * 100%), var(--surface-2));
  color: var(--ink);
  border: 1px solid var(--line); cursor: default;
}
/* Diverging: green for falls, red for rises. Both endpoints are the darkest
   shade at which the dark ink still clears 4.5:1 at full strength (4.96 and
   4.53), measured rather than guessed.

   Green and red are the whole signal here and, below 420px, the numbers come
   out of the cells entirely - which leaves a red-green grid carrying its meaning
   in the one channel some readers do not have. A rise also gets a fine hatch.
   It is quiet enough not to fight the shade that carries the magnitude, and it
   is the only thing on the grid that survives a monochrome screenshot. */
.heat-cell.down { --heat-end: #4E9B70; }
.heat-cell.up   {
  --heat-end: #CB6A4E;
  background-image: repeating-linear-gradient(45deg,
    rgba(36, 29, 18, .13) 0 1.5px, transparent 1.5px 5px);
}
.heat-cell:hover, .heat-cell:focus-visible { outline: 2px solid var(--ink); outline-offset: -2px; }
.heat-scroll { overflow-x: auto; }

/* ------------------------------------------------- statistics headline */
.pulse {
  display: flex; flex-wrap: wrap; align-items: center; gap: 22px 34px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 20px 24px; box-shadow: var(--shadow);
  margin: 22px 0 30px;
}
.pulse-main { display: flex; flex-direction: column; }
.pulse-label { font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); font-weight: 700; }
.pulse-value { font-size: var(--t-3xl); font-weight: 800; line-height: 1.1; letter-spacing: -.03em;
  font-variant-numeric: tabular-nums; }
.pulse-value.up { color: var(--dear); }
.pulse-value.down { color: var(--good-ink); }
.pulse-sub { font-size: var(--t-xs); color: var(--muted); }
.pulse .stats { flex: 1; min-width: 260px; }
.pulse .stats div { background: var(--surface-2); }

.split-charts { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; }
@media (max-width: 780px) { .split-charts { grid-template-columns: minmax(0, 1fr); } }
.chart-title { margin: 0 0 10px; font-size: var(--t-sm); font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em; }
.chart-title.up { color: var(--dear); }
.chart-title.down { color: var(--good-ink); }

/* The measured period, stated under each chart rather than inside the prose.
   Small, muted, and always present: a reader should never have to guess
   whether a figure covers a day or two months. */
.chart-span {
  margin: 7px 2px 0; font-size: var(--t-xs); color: var(--muted);
  display: flex; align-items: center; gap: 6px;
}
.chart-span::before {
  content: ''; width: 14px; height: 1px; background: var(--line); flex: 0 0 14px;
}

/* ------------------------------------------------- quick-add category row */
.quick-row {
  display: flex; gap: 8px; overflow-x: auto; padding: 4px 2px 10px;
  scrollbar-width: thin; -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
}
.quick-row::-webkit-scrollbar { height: 6px; }
.quick-row::-webkit-scrollbar-thumb { background: var(--line); border-radius: 3px; }
.quick-chip {
  display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
  padding: 7px 14px; border-radius: 999px; cursor: pointer; scroll-snap-align: start;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  font: inherit; font-size: var(--t-sm); font-weight: 600; white-space: nowrap;
  min-height: 36px;             /* comfortable touch target */
}
.quick-chip:hover { border-color: var(--brand); background: var(--brand-tint); }
.quick-chip:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.quick-chip.added {
  background: var(--good); border-color: var(--good); color: #fff;
  justify-content: center; font-size: var(--t-base);
}
.quick-icon { font-size: var(--t-sm); line-height: 1; }

/* --------------------------------------------- floating result shortcut */
.result-peek {
  position: fixed; right: 18px; bottom: 18px; z-index: 60;
  display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
  padding: 11px 17px; border-radius: 13px; cursor: pointer; text-align: left;
  border: 1px solid var(--good); background: var(--good); color: #fff;
  font: inherit; box-shadow: 0 6px 22px rgba(47, 125, 82, .32);
  animation: peek-in .22s ease-out;
}
.result-peek[hidden] { display: none; }
.result-peek:hover { background: #276845; border-color: #276845; }
.result-peek:focus-visible { outline: 3px solid var(--brand); outline-offset: 2px; }
.peek-label {
  font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .07em;
  font-weight: 700;
}
.peek-price { font-size: var(--t-base); font-weight: 800; font-variant-numeric: tabular-nums; }
.peek-cta { font-size: var(--t-xs); }
.peek-cta::after { content: ' ↓'; }
.result-peek.points-up .peek-cta::after { content: ' ↑'; }

@keyframes peek-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .result-peek { animation: none; } }

/* On a phone it spans the width: a corner pill competes with the thumb, and
   the price is the thing worth the room. */
@media (max-width: 560px) {
  .result-peek {
    left: 12px; right: 12px; bottom: 12px;
    flex-direction: row; align-items: center; justify-content: space-between;
    gap: 10px; padding: 12px 16px;
  }
  .peek-label { display: none; }
}

/* =========================================================================
   Phone layout

   Measured on a 375x812 screen: the header alone was taking 260px, a third of
   the viewport, before a single word of the page. These rules give that space
   back. Everything here is spacing and type scale; nothing is hidden that a
   phone visitor would need.
   ========================================================================= */
@media (max-width: 560px) {
  /* --- header: three stacked rows, so each one has to be tight */
  .topbar { padding: 8px 14px; gap: 8px; }
  .logo-mark { width: 30px; height: 30px; }
  .brand-text { font-size: var(--t-md); }
  .brand { gap: 7px; }
  .search { min-width: 0; gap: 6px; }
  /* Literal, not --t-base: Safari zooms the page on focus for anything under
     16px, so this number has to survive a change to the type scale. */
  .search input { padding: 8px 12px; font-size: 16px; }
  .search > button { padding: 8px 13px; font-size: var(--t-sm); }
  .topnav { gap: 13px; }
  .topnav a { font-size: var(--t-sm); }
  .donate, .lang { padding: 3px 7px; font-size: var(--t-xs); }

  /* Same size as on a desktop. It was a step smaller here to buy back header
     height, and this is the paragraph that lets the site exist - it is not
     the place to save twenty pixels. */
  .disclaimer { font-size: var(--t-xs); line-height: var(--lh-ui); }
  .disclaimer div { padding: 7px 14px; gap: 7px; }

  /* --- page rhythm: headings and paragraphs were desktop-sized.
     h1 no longer appears here: it is --t-xl everywhere now, which is a size a
     375px screen can carry, and the only heading that still has to come down
     is the hero's. A lede is reading text, so it never drops below body
     size - it was 15.5px against a 16px paragraph, which read as a mistake. */
  main { padding: var(--s-4) 14px var(--s-7); }
  .hero h1 { font-size: var(--t-xl); }
  .hero { margin-bottom: var(--s-5); }
  h2 { font-size: var(--t-md); margin: var(--s-5) 0 var(--s-2); }
  .lede { font-size: var(--t-base); margin-bottom: var(--s-4); }
  section { margin-bottom: var(--s-1); }

  /* --- cards and rails */
  .card { padding: 13px; }
  .chart-card { padding: 13px 12px; }
  .stats div { padding: 9px 13px; min-width: 108px; }
  .stats dd { font-size: var(--t-md); }
  .rail { gap: 10px; }
  .grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
  .chips { gap: 6px; margin: 14px 0; }
  .chip { padding: 6px 11px; font-size: var(--t-xs); }

  /* --- the headline stat block on /stats
     Side by side it left the big number floating in the middle of a tall
     column while four tiles queued up in a 155px-wide strip beside it. On a
     phone the headline takes the full width and the tiles pair off below. */
  .pulse {
    flex-direction: column; align-items: stretch;
    padding: 15px 16px; gap: 14px;
  }
  .pulse-value { font-size: var(--t-2xl); }
  .pulse .stats {
    min-width: 0; width: 100%;
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
  }
  .pulse .stats div { min-width: 0; padding: 9px 12px; }
  .pulse .stats dt { font-size: var(--t-2xs); line-height: 1.3; }
  .pulse .stats dd { font-size: var(--t-md); }

  /* --- footer links were a dense block of 20px-tall taps */
  .site-footer nav { gap: 10px 16px; }
  .site-footer a { padding: 4px 0; display: inline-block; }
}

/* A price table has four or five columns of numbers and cannot honestly be
   made narrower than a phone. It gets its own scroller so it slides sideways
   inside the page instead of dragging the whole page with it. */
.table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border-radius: var(--radius);
}
.table-wrap > .table { min-width: 100%; }
@media (max-width: 560px) {
  /* There used to be a min-width: 460px here, on every table on the site,
     which made a three-column table scroll sideways on a phone that could
     have shown it whole. A table that does not fit overflows its wrap on its
     own; one that fits should be left alone. */
  .table th, .table td { padding: 9px 10px; }
}

/* Product page, "cheaper alternatives" rows. The saving badge is nowrap and
   the sentence inside it is long, so on a phone the row stacks instead of
   pushing the badge off the side. */
@media (max-width: 560px) {
  .alts .alt { flex-wrap: wrap; align-items: flex-start; row-gap: 6px; padding: 11px 12px; }
  .alts .alt-name { flex: 1 1 calc(100% - 46px); font-size: var(--t-sm); }
  .alts .alt-unit { font-size: var(--t-xs); }
  .alts .alt-save { white-space: normal; font-size: var(--t-xs); padding: 3px 9px; }
  .alts .thumb-sm { flex: 0 0 34px; }
}

/* The header search button carries both a label and an icon; each width shows
   whichever fits. On a phone the word alone left the input showing about eight
   characters of its placeholder. */
.search > button { display: inline-flex; align-items: center; gap: 7px; }
.search-icon { width: 17px; height: 17px; display: none; flex: 0 0 17px; }
@media (max-width: 560px) {
  .search-icon { display: block; }
  .search-label { display: none; }
  .search > button { padding: 8px 11px; }
}

/* "Φίλτρα καταστημάτων (όλες οι αλυσίδες)" was breaking mid-phrase into two
   ragged columns. Let the label take the line and the summary sit under it. */
@media (max-width: 560px) {
  .filters > summary { flex-wrap: wrap; row-gap: 2px; }
  /* order:2 puts the chain count after the disclosure arrow in flex order, so
     the arrow stays on the first line beside the label instead of being
     pushed onto a third row of its own. */
  .filters > summary #filter-summary { order: 2; flex-basis: 100%; font-size: var(--t-xs); }
}

/* "cheapest on 2/3" replaces the confidence percentage on a result card. */
.wins-wrap { display: flex; flex-direction: column; align-items: center; gap: 1px; min-width: 74px; }
.wins {
  font-size: var(--t-sm); font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--good-ink); background: var(--good-bg);
  border-radius: 999px; padding: 2px 11px; line-height: 1.35;
}
.wins-cap {
  font-size: var(--t-2xs); text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); font-weight: 700; white-space: nowrap;
}
.line-warn {
  display: inline-grid; place-items: center; width: 20px; height: 20px;
  border-radius: 50%; background: var(--warn-bg); color: #7A5300;
  font-weight: 800; font-size: var(--t-2xs); cursor: help;
}
@media (max-width: 620px) { .wins-cap { display: none; } }

/* ------------------------------------------------------------ add toast */
.toast-host {
  position: fixed; left: 16px; bottom: 16px; z-index: 70;
  opacity: 0; transform: translateY(10px); pointer-events: none;
  transition: opacity .18s ease, transform .18s ease;
}
.toast-host.show { opacity: 1; transform: none; pointer-events: auto; }
.toast {
  display: flex; align-items: center; gap: 11px;
  background: var(--ink); color: #FFF8E7;
  border-radius: 12px; padding: 10px 12px 10px 13px;
  box-shadow: 0 8px 26px rgba(36, 29, 18, .3);
  max-width: min(360px, calc(100vw - 32px));
}
.toast-tick {
  display: grid; place-items: center; width: 22px; height: 22px; flex: 0 0 22px;
  border-radius: 50%; background: var(--good); color: #fff; font-size: var(--t-xs); font-weight: 700;
}
.toast-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
.toast-text b {
  font-size: var(--t-sm); font-weight: 700;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.toast-text small { font-size: var(--t-xs); opacity: .75; }
.toast-undo {
  flex: 0 0 auto; margin-left: 4px; padding: 6px 12px; border-radius: 8px;
  border: 1px solid rgba(255, 248, 231, .35); background: transparent;
  color: #FFF8E7; font: inherit; font-size: var(--t-xs); font-weight: 700; cursor: pointer;
}
.toast-undo:hover { background: rgba(255, 248, 231, .14); }
.toast-undo:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* On a phone the result pill already owns the bottom edge, so the toast sits
   above it rather than on top of it. */
@media (max-width: 560px) {
  .toast-host { left: 12px; right: 12px; bottom: 12px; }
  .toast { max-width: none; }
}
body:has(.result-peek:not([hidden])) .toast-host { bottom: 82px; }
@media (min-width: 561px) { body:has(.result-peek:not([hidden])) .toast-host { bottom: 16px; } }

@media (prefers-reduced-motion: reduce) {
  .toast-host { transition: none; transform: none; }
}

/* ------------------------------------------------------- map, shared block */
.map-tools {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 14px 0 10px;
}
.addr-form { position: relative; display: flex; gap: 7px; flex: 1; min-width: 220px; }
.addr-form input {
  flex: 1; min-width: 0; padding: 9px 13px; border: 1px solid var(--line);
  border-radius: 10px; background: var(--surface); color: var(--ink);
  font: inherit; font-size: var(--t-sm);
}
.addr-form input:focus { outline: 2px solid var(--brand); outline-offset: 1px; border-color: transparent; }
.addr-results {
  position: absolute; top: calc(100% + 5px); left: 0; right: 0; z-index: 40;
  background: var(--surface); border: 1px solid var(--line); border-radius: 11px;
  box-shadow: var(--shadow); overflow: hidden; max-height: 260px; overflow-y: auto;
}
.addr-results[hidden] { display: none; }
.addr-results button {
  display: block; width: 100%; text-align: left; padding: 10px 13px;
  border: 0; border-bottom: 1px solid var(--line); background: none;
  font: inherit; font-size: var(--t-xs); color: var(--ink); cursor: pointer;
}
.addr-results button:last-child { border-bottom: 0; }
.addr-results button:hover { background: var(--brand-tint); }
.addr-note { padding: 10px 13px; font-size: var(--t-xs); color: var(--muted); }
.origin-label { font-size: var(--t-xs); }

.map-block.compact #map { height: 340px; }

/* A field smaller than 16px makes Safari zoom the page the moment it takes
   focus, which on the map means losing the map. Both of these are short or
   numeric, so the larger text costs a little width and nothing else. */
@media (max-width: 560px) {
  .addr-form input, .near-help input { font-size: 16px; }
  .near-help input { width: 74px; }
  .map-tools { gap: 8px; margin: 12px 0 8px; }
  .addr-form { flex-basis: 100%; }
  .map-block.compact #map { height: 280px; }
  .origin-label { flex-basis: 100%; }
}

/* Heatmap on a phone.
   Eleven chains by seven days cannot shrink to 375px, so it scrolls. What
   makes a scrolling grid usable is knowing which row you are on: the chain
   column sticks to the left while the days slide under it. Cells get smaller
   and the numbers drop out — the colour carries the direction, and a tap gives
   the exact figure. */
@media (max-width: 620px) {
  .heat { min-width: 0; gap: 3px; }
  .heat-row { grid-template-columns: 104px repeat(var(--cols), minmax(48px, 1fr)); gap: 3px; }
  .heat-label {
    position: sticky; left: 0; z-index: 2;
    background: var(--surface); font-size: var(--t-2xs); gap: 5px;
    padding-right: 4px;
  }
  .heat-label img { width: 16px; height: 16px; flex-basis: 16px; }
  .heat-head .heat-label { background: var(--surface); }
  .heat-cell { height: 30px; font-size: var(--t-2xs); border-radius: 5px; }
  .heat-col { font-size: var(--t-2xs); }
  /* The label column casts a small edge so it reads as pinned, not overlapping */
  .heat-label::after {
    content: ''; position: absolute; right: 0; top: 0; bottom: 0; width: 6px;
    background: linear-gradient(to right, rgba(36,29,18,.07), transparent);
  }
}
@media (max-width: 420px) {
  .heat-row { grid-template-columns: 88px repeat(var(--cols), minmax(42px, 1fr)); }
  .heat-cell { font-size: 0; }        /* colour only; the tooltip has the number */
}

/* ------------------------------------------------ what changed this week */
.mover {
  display: flex; align-items: center; gap: 11px; padding: 9px 4px;
  border-bottom: 1px solid var(--line); color: var(--ink);
}
.mover:last-child { border-bottom: 0; }
.mover:hover { text-decoration: none; background: var(--surface-2); border-radius: 8px; }
.mover img {
  width: 38px; height: 38px; flex: 0 0 38px; object-fit: contain;
  background: #fff; border: 1px solid var(--line); border-radius: 7px;
}
.mover-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.mover-name {
  font-weight: 600; font-size: var(--t-xs); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mover-prices { font-size: var(--t-xs); color: var(--muted); font-variant-numeric: tabular-nums; }
.mover-prices b { color: var(--ink); }
.mover-pct {
  font-weight: 800; font-size: var(--t-sm); font-variant-numeric: tabular-nums;
  min-width: 56px; text-align: right;
}
.mover-pct.up { color: var(--dear); }
.mover-pct.down { color: var(--good-ink); }

/* ---- consent ---------------------------------------------------------- */
/* Bottom sheet, not a modal: it must never stop someone reading a price, and
   it must not be dismissable by accident either. Both buttons look the same
   weight, because a refusal styled as the quiet option is not a free choice. */
.consent {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 80;
  background: #2f2a22; color: #fff; box-shadow: 0 -8px 26px rgba(0,0,0,.25);
}
.consent-in {
  max-width: 62rem; margin: 0 auto; padding: .85rem 1rem;
  display: flex; gap: 1rem; align-items: center; flex-wrap: wrap;
}
.consent p { margin: 0; flex: 1; min-width: 16rem; font-size: var(--t-sm); line-height: var(--lh-ui); }
.consent a { color: #f0d9a8; }
.consent-btns { display: flex; gap: .5rem; }
.consent button {
  padding: .45rem 1rem; border-radius: 9px; border: 1px solid transparent;
  font: inherit; cursor: pointer; background: var(--brand); color: var(--brand-ink);
}
.consent button.ghost { background: transparent; border-color: rgba(255,255,255,.45); color: #fff; }
@media (max-width: 520px) { .consent-btns { width: 100%; } .consent button { flex: 1; } }
/* A button that has to read as a link: withdrawing consent belongs in the
   sentence that explains it, not in a control that looks like an action. */
.linklike { background: none; border: 0; padding: 0; font: inherit;
  color: var(--brand-strong); text-decoration: underline; cursor: pointer; }
/* The same control inside the dark consent sheet, where brand mustard all but
   disappears. Matches .consent a. */
.consent .linklike { color: #f0d9a8; }


/* ---------------------------------------------------------------- items */
.root-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 12px; margin: 18px 0 28px; }
.root-tile {
  display: flex; flex-direction: column; align-items: center; text-align: center; gap: 6px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 18px 12px; color: var(--ink); box-shadow: var(--shadow);
}
.root-tile:hover { border-color: var(--brand); text-decoration: none; }
.root-tile img { color: var(--brand-strong, #1F6F6B); }
.root-name { font-weight: 700; font-size: var(--t-base); }
.root-block { margin-top: var(--s-6); }
.root-block h2 { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.root-block h2 img { color: var(--brand-strong, #1F6F6B); }
.root-block h2 a { font-weight: 500; margin-left: auto; }
.dept-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 10px; }
.dept { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 0 14px; }
.dept[open] { grid-column: 1 / -1; }
.dept summary { display: flex; align-items: center; gap: 10px; padding: 12px 0; cursor: pointer; list-style: none; }
.dept summary::-webkit-details-marker { display: none; }
.dept summary img { color: var(--brand-strong, #1F6F6B); flex: none; }
.dept-name { font-weight: 700; flex: 1; }
.dept .chips { margin: 0 0 14px; }
@media (max-width: 600px) {
  .root-grid { grid-template-columns: repeat(2, 1fr); }
  .dept-grid { grid-template-columns: 1fr; }
}

/* When a price was last seen, printed under it only when that is not the
   newest day we hold. Quiet by design: it qualifies the number beside it, it
   is not a second number. */
/* The one lower-case thing on the site set below --t-xs, and deliberately: it
   is a date fragment qualifying the number above it, not a sentence, and at
   --t-xs it reads as a second figure. The #6b7785 fallback was from another
   palette and could never fire. */
.stale { display: block; font-size: var(--t-2xs); font-weight: 400; color: var(--muted);
         font-variant-numeric: tabular-nums; }
.disclaimer .asof { opacity: .85; }
