/* The system sheet from design/Tokens.dc.html, as something pages can actually use.
   Warm paper and ink, Plex Sans for words and Plex Mono for every figure, white surfaces with one hairline and no
   shadows. Color marks a state and always comes with a word, never on its own. */

:root {
	--paper: #F3EEE5;
	--surface: #FFFDF9;
	--hairline: #E8E1D5;
	--track: #E3DCCF;
	--edge: #D6CDBE;
	/* Behind a product's photograph: white, the ground nearly every one of them was taken on */
	--photo: #FFFFFF;

	--ink: #1B1815;
	--ink-2: #5E574E;
	--ink-3: #8C847A;

	/* A price event */
	--amber: #9A6A12;
	--amber-field: #F6E7C4;
	/* Stocking up, and swaps */
	--blue: #3B6BA5;
	--blue-field: #DCE6F3;
	--blue-deep: #2C5283;

	--sans: 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
	--mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

body {
	margin: 0;
	/* Never sideways: whatever is wider than the screen scrolls inside its own box (the matrix) or shrinks (the
	   header), and a pixel too many somewhere is not a page that slides under a thumb. Clip, not hidden, so the page
	   stays the thing that scrolls and the sticky header stays put */
	overflow-x: clip;
	background: var(--paper);
	color: var(--ink);
	font-family: var(--sans);
	font-size: 15px;
	line-height: 1.45;
	-webkit-font-smoothing: antialiased;
}

a { color: var(--blue); }
a:hover { color: var(--blue-deep); }

/* Every figure is monospaced and tabular, so columns of them line up and a changing number doesn't reflow the row */
.mono {
	font-family: var(--mono);
	font-variant-numeric: tabular-nums;
}

.eyebrow {
	font-size: 12px;
	font-weight: 600;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--ink-2);
}

/* Every page's margins, one set of them: the same distance from the edges and from the header on every page. A page
   with a bar fixed along its foot says how tall it is in --bar-room, and the room is kept under the page for it */
.page { padding: 0 20px calc(48px + var(--bar-room, 0px)); }

/* The one header every page has (header.js), kept at the top of the screen as the page scrolls: the name, the search
   and where you shop, and the ways on. Across the page's whole width over its margins, on the paper, with a hairline
   under it for the page to pass beneath */
header {
	position: sticky; top: 0; z-index: 35;
	display: flex; align-items: center; justify-content: space-between; gap: 16px; min-height: 44px;
	margin: 0 -20px 16px; padding: 8px 20px;
	background: var(--paper); border-bottom: 1px solid var(--hairline);
}
header > #topbar { flex: 1; min-width: 0; }
/* Whatever else sticks to the top stops under the header, and a link to a place on the page lands below it */
html { scroll-padding-top: var(--header-room, 0px); }
.header-right { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; row-gap: 8px; }
/* Where somebody is, marked rather than left out, so no way on moves from one page to the next (header.js) */
.header-right [aria-current="page"] { border-color: var(--ink); color: var(--ink); }

/* How many, one box wherever an amount is set: [ - | 3x | + ] (amount.js). The count is the thing to read */
.amount-box {
	display: inline-flex; align-items: stretch; flex-shrink: 0; height: 30px; box-sizing: border-box;
	border: 1px solid var(--edge); border-radius: 6px; background: var(--surface); overflow: hidden;
}
.amount-box .step {
	width: 30px; border: 0; background: transparent; padding: 0; cursor: pointer;
	color: var(--ink-2); font-size: 15px; line-height: 1;
}
.amount-box .step:hover:not(:disabled), .amount-box .step:focus-visible { background: var(--track); color: var(--ink); }
.amount-box .step:disabled { color: var(--hairline); cursor: default; }
.amount-box .value {
	/* Room for three digits and the x, so 9 to 10 to 100 moves nothing */
	display: flex; align-items: center; justify-content: center; min-width: 5ch; padding: 0 4px;
	font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 500; color: var(--ink);
	white-space: nowrap;
}
/* None of it yet: the count quieter than a count somebody chose */
.amount-box.none .value { color: var(--ink-3); font-weight: 400; }
/* The lines between the parts stop short of the edges, so they read as the box's own seams rather than walls */
.amount-box > * + * { position: relative; }
.amount-box > * + *::before {
	content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 1px; background: var(--hairline);
}

/* The cart and the account's circle: the same outline and height as the header's other ways on, only round */
.header-icon {
	position: relative; display: inline-flex; align-items: center; justify-content: center;
	width: 32px; height: 32px; padding: 0; border: 1px solid var(--edge); border-radius: 999px; box-sizing: border-box;
	background: transparent; color: var(--ink-2); cursor: pointer; text-decoration: none;
	font-size: 13px; font-weight: 600; letter-spacing: 0.02em;
}
.header-icon:hover { background: var(--track); color: var(--ink); }
.header-icon.avatar { background: var(--ink); border-color: var(--ink); color: var(--surface); }
.header-icon.avatar:hover { background: var(--ink); color: var(--surface); }
/* How many are on the list, on the cart's shoulder */
.cart .badge {
	position: absolute; top: -5px; right: -6px; min-width: 18px; height: 18px; padding: 0 5px; box-sizing: border-box;
	border-radius: 999px; background: var(--ink); color: var(--surface);
	font-family: var(--mono); font-size: 11px; font-weight: 600; line-height: 18px; text-align: center;
}

/* What is on the list: a panel down the whole right-hand side, over the page and its header, as a shop's site shows its
   basket. Kept on the page itself rather than in the header (header.js), so it runs from the top of the window to the
   foot whatever the header is doing. The whole width on a phone */
.cart-wrap { position: relative; }
.cart-panel {
	position: fixed; top: 0; right: 0; bottom: 0; z-index: 45; width: min(460px, 100vw);
	overflow: hidden; box-sizing: border-box; display: flex; flex-direction: column; gap: 12px;
	padding: calc(env(safe-area-inset-top, 0px) + 16px) 20px calc(env(safe-area-inset-bottom, 0px) + 16px);
	background: var(--surface); border-left: 1px solid var(--edge);
}
.cart-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.cart-panel[hidden] { display: none; }
/* Only the rows scroll: what it is, how many and the way on stay at the top, and what it comes to at the foot */
.cart-panel > .cart-heading, .cart-panel > .cart-total { flex-shrink: 0; }
.cart-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 16px; font-weight: 600; }
.cart-title { display: flex; align-items: center; gap: 8px; min-width: 0; }
.cart-title .meta { font-weight: 400; }
.cart-name-mark { display: inline-flex; align-items: center; gap: 8px; }
.cart-heading .button { min-height: 36px; height: 36px; padding: 0 14px; font-size: 14px; white-space: nowrap; flex-shrink: 0; }
/* What the cheapest of each comes to, under the rows, figure at the right as a sum is */
.cart-total {
	display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
	padding-top: 10px; border-top: 1px solid var(--ink); font-size: 14px; font-weight: 600;
}
.cart-total:empty { display: none; }
.cart-total-figure { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.cart-rows {
	display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
}
/* Scrolling, the rows keep a little apart from the scrollbar beside them (header.js fitScrollbar) */
.cart-rows.scrolls { padding-right: 12px; }
/* Set columns, the same in every row, so how many, what and what it costs each line up down the list whatever a name's
   length: the box as wide as a box, the price as wide as "from 99,99", the name the rest */
.cart-row {
	display: grid; grid-template-columns: 112px minmax(0, 1fr) 104px; grid-template-areas: "amount name cost";
	align-items: center; column-gap: 12px; padding: 10px 0; border-top: 1px solid var(--hairline); font-size: 14px;
}
.cart-row .amount-box { grid-area: amount; justify-self: start; }
.cart-name { grid-area: name; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; line-height: 1.3; }
.cart-cost { grid-area: cost; justify-self: end; font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 12px; color: var(--ink-2); white-space: nowrap; }
@media (max-width: 480px) {
	/* On a phone the price goes under the name, which gets the width: beside both, a name had room for one word */
	.cart-row { grid-template-columns: 112px minmax(0, 1fr); grid-template-areas: "amount name" "amount cost"; row-gap: 2px; }
	.cart-cost { justify-self: start; }
}

/* What belongs to one page, under the header: a way back, a language, a household */
.page-bar { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 44px; }
.way-back {
	display: inline-flex; align-items: center; min-height: 44px;
	font-family: var(--mono); font-size: 14px; color: var(--ink-2); text-decoration: none;
}
.way-back:hover { color: var(--ink); }

.brand {
	/* The header is 44px tall and this was 20 of it: the rest was header nobody could click */
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	font-family: var(--mono);
	font-size: 14px;
	font-weight: 500;
	letter-spacing: -0.01em;
	color: var(--ink);
	text-decoration: none;
}

h1 {
	font-size: 32px;
	font-weight: 600;
	line-height: 38px;
	letter-spacing: -0.015em;
	margin: 0;
}

h2 {
	font-size: 16px;
	font-weight: 600;
	line-height: 20px;
	margin: 0;
}

/* A section's heading, with room at its right for what controls the section (a range, a toggle). Every one is the
   same height whether or not it has such a control -- the height of the tallest, a row of pills -- and the words sit
   at the bottom, on the thing they head, so headings side by side in two columns line up. */
.section-head {
	display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 8px 12px;
	min-height: 34px; margin-bottom: 8px;
}
.section-head h2 { min-width: 0; }

.meta {
	font-size: 13px;
	color: var(--ink-2);
	line-height: 18px;
	margin: 0;
}

.card {
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: 12px;
	padding: 20px;
}

/* Rows inside a card carry their own hairline, so a list reads as a list rather than as a stack of boxes */
.rows { display: flex; flex-direction: column; }
.rows > * + * { border-top: 1px solid var(--hairline); }

.row {
	display: flex;
	align-items: center;
	gap: 12px;
	min-height: 56px;
	padding: 8px 0;
}

.row .grow { flex: 1; min-width: 0; }
.row .name { font-size: 16px; font-weight: 500; }

/* Controls */
.button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	height: 44px;
	padding: 0 16px;
	border: 1px solid var(--ink);
	border-radius: 10px;
	background: var(--ink);
	color: var(--surface);
	font-family: inherit;
	font-size: 15px;
	font-weight: 500;
	cursor: pointer;
	/* Half of these are anchors, and an anchor keeps its underline however button-shaped it looks */
	text-decoration: none;
}

/* Its own text color on hover too: half of these are anchors, and a link's hover color would put dark blue on ink */
.button:hover { background: #2C2722; border-color: #2C2722; color: var(--surface); text-decoration: none; }
.button[disabled] { opacity: 0.45; cursor: default; }

.button.secondary { background: transparent; color: var(--ink); }
.button.secondary:hover { background: var(--track); color: var(--ink); }

.button.quiet {
	height: 32px;
	padding: 0 12px;
	border-color: var(--edge);
	border-radius: 999px;
	background: transparent;
	color: var(--ink-2);
	font-size: 13px;
}

.button.quiet:hover { background: var(--track); color: var(--ink); }

.chips { display: flex; gap: 0; background: var(--track); border-radius: 999px; padding: 3px; }

.chip {
	height: 32px;
	padding: 0 14px;
	border: 0;
	border-radius: 999px;
	background: transparent;
	color: var(--ink-2);
	font-family: inherit;
	font-size: 13px;
	font-weight: 500;
	cursor: pointer;
}

.chip[aria-selected="true"] { background: var(--ink); color: var(--surface); }

/* Fields */
.field { display: flex; flex-direction: column; gap: 6px; }

.field label { font-size: 13px; font-weight: 500; color: var(--ink-2); }

.field input {
	height: 44px;
	padding: 0 12px;
	border: 1px solid var(--edge);
	border-radius: 10px;
	background: var(--surface);
	color: var(--ink);
	font-family: inherit;
	font-size: 15px;
}

.field input:focus {
	outline: 2px solid var(--blue);
	outline-offset: -1px;
	border-color: var(--blue);
}

.field .hint { font-size: 12px; color: var(--ink-3); line-height: 16px; }

.check { display: flex; gap: 10px; align-items: flex-start; }
/* 16px is what a browser draws and about half a fingertip. The label beside it is a target too, but the
   box is what somebody aims at. */
.check input { margin: 2px 0 0; width: 20px; height: 20px; accent-color: var(--ink); flex-shrink: 0; }
.check label { font-size: 14px; font-weight: 500; }

/* A state always comes with a word, so these are readable with the color removed */
.note {
	display: flex;
	gap: 8px;
	padding: 10px 12px;
	border-radius: 10px;
	font-size: 13px;
	line-height: 18px;
}

.note.problem { background: var(--amber-field); color: var(--amber); }
.note.info { background: var(--blue-field); color: var(--blue); }
.note:empty { display: none; }

.tag {
	font-family: var(--mono);
	font-size: 12px;
	font-weight: 500;
	padding: 1px 8px;
	border-radius: 999px;
	background: var(--track);
	color: var(--ink-2);
}

.tag.info { background: var(--blue-field); color: var(--blue); }

/* The slot that says which shop. The design leaves it for each retailer's own mark and shows initials until there
   is one; with a mark in it the tint would be a box drawn round a logo that already has its own edges. */
.mark.logo { background: transparent; padding: 0; overflow: hidden; }
.mark.logo img { width: 100%; height: 100%; object-fit: contain; display: block; }

[hidden] { display: none !important; }

/* What was just taken off a list, left where it was until somebody moves on (undo.js): the name struck through and
   quiet, and the way to put it back beside it */
.gone-name {
	min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
	color: var(--ink-3); text-decoration: line-through; font-weight: 400;
}
/* As tall as the amount box it stands in for, so the row keeps its height */
.button.undo { flex-shrink: 0; height: 30px; padding: 0 12px; border-radius: 8px; font-size: 13px; }
.cart-row.gone { display: flex; align-items: center; justify-content: space-between; gap: 12px; }

/* ── More to say ─────────────────────────────────────────────────────────────────────────────────────────────
   A circled ? beside a thing, and its sentences in a small card under it when pressed (info.js). Quieter than
   anything it sits beside: it is for the few who ask. */
.info { position: relative; display: inline-flex; vertical-align: middle; }
.info-button {
	width: 18px; height: 18px; padding: 0; border: 1px solid var(--edge); border-radius: 999px; background: transparent;
	font-family: var(--sans); font-size: 11px; font-weight: 600; line-height: 1; color: var(--ink-3); cursor: pointer;
	display: inline-flex; align-items: center; justify-content: center;
}
.info-button:hover, .info-button[aria-expanded="true"] { border-color: var(--ink-2); color: var(--ink); }
/* One for the whole page, over everything, placed against the window by info.js */
.info-pop {
	position: fixed; z-index: 60; width: max-content; max-width: min(320px, calc(100vw - 24px));
	padding: 10px 12px; border: 1px solid var(--edge); border-radius: 8px; background: var(--surface);
	font-size: 13px; font-weight: 400; line-height: 1.45; color: var(--ink-2); text-transform: none; letter-spacing: 0;
	white-space: normal; text-align: left;
}
/* Beside a heading, a little apart from its words */
h2 .info, h3 .info, label .info { margin-left: 6px; }
/* The small print that stays on the pages that price things: two words and the ? with the rest */
.small-print { display: flex; align-items: center; gap: 6px; margin-top: 12px; font-size: 12px; color: var(--ink-3); }

/* ── The bar in the header ──────────────────────────────────────────────────────────────────────────────────
   Finding a product, and where you shop, on every page that shows products. Built by topbar.js, put in the header by
   header.js. The search has the width; where you shop is a smaller field beside it that grows when opened, taking the
   room from the search, and gives it back when closed. */

.topbar { display: flex; align-items: flex-start; gap: 8px; }

/* One field and no button beside it: a search field already says what Enter does. What it finds drops down over
   the page rather than pushing it along: the page is still there to go back to, and nothing under the field moves
   while somebody types. */
.finding { position: relative; flex: 1 1 0; min-width: 0; }

/* The bar's two fields, one look: the search leaves room at the end for Clear, the place for its location button */
.finding input, .place-field input {
	width: 100%; min-height: 44px; padding: 10px 44px 10px 14px;
	border: 1px solid var(--edge); border-radius: 10px; background: var(--surface);
	font-family: inherit; font-size: 16px; color: var(--ink); text-overflow: ellipsis;
}
.place-field input { padding-right: 44px; }

.finding input:focus, .place-field input:focus { outline: 0; border-color: var(--ink); }
.finding input::placeholder, .place-field input::placeholder { color: var(--ink-3); }
/* The browser's own clear cross, beside the Clear button that already does it */
.finding input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }

.finding .clear {
	position: absolute; right: 8px; top: 50%; transform: translateY(-50%); width: 28px; height: 28px; padding: 0;
	display: inline-flex; align-items: center; justify-content: center;
	border: 0; border-radius: 999px; background: transparent; color: var(--ink-3); cursor: pointer;
}

.finding .clear:hover { background: var(--track); color: var(--ink); }

.suggestions {
	position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 40;
	border: 1px solid var(--edge); border-radius: 10px; background: var(--surface);
	max-height: min(70vh, 560px); overflow-y: auto;
}

/* A sentence, not a label: .kind is uppercased and a whole sentence in capitals shouts */
.suggestions .instead {
	font-size: 13px; color: var(--ink-2); padding: 10px 14px 6px;
	border-bottom: 1px solid var(--hairline);
}

.suggestions .kind {
	font-size: 12px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
	color: var(--ink-3); padding: 10px 14px 4px;
}

/* An aisle row and a thing row do different things when clicked, so they never sit in one run */
.suggestions button {
	display: flex; align-items: baseline; gap: 10px; width: 100%; text-align: left;
	border: 0; background: transparent; padding: 9px 14px; cursor: pointer;
	font-family: inherit; font-size: 14px; color: var(--ink);
}

.suggestions button + button { border-top: 1px solid var(--hairline); }
.suggestions button:hover, .suggestions button[aria-selected="true"] { background: var(--track); }
.suggestions .where { flex: 1; min-width: 0; color: var(--ink-2); font-size: 13px; }
.suggestions .cost { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 13px; }
.suggestions .price-mark { align-self: center; }

/* A shop that lists it without a price: words where the figure would be, quieter than any price, on every page
   that shows one (the product page, the scanner's sheet). Under body, to outrank each page's own .cost, which comes
   after this sheet */
body .cost.none { font-family: var(--sans); font-size: 13px; font-weight: 400; color: var(--ink-3); }

.sr-only {
	position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
	overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Where you shop: a field of its own beside the search, so an address can be typed straight in. Small until it is
   opened, then as wide as the address and its branches need. What it finds drops down over the page like the
   search's suggestions. */
/* Sized by its width alone, as the cart beside it is: the box around the two is as wide as they say they are */
.place-field { position: relative; flex: none; width: 240px; min-width: 0; transition: width 0.18s ease; }
.place-field.open { width: min(440px, 40vw); }
.place-field .pin { display: none; }
@media (prefers-reduced-motion: reduce) { .place-field, header { transition: none; } }

/* The halves that join a field (header.js): the way to the shelves on the search's left, the cart on the place's
   right. Built for this spot rather than restyled into it: the field's height, border and surface, rounded on the
   outer side only, and between the two the amount box's seam, a hairline that stops short of the edges */
.join-part {
	position: relative; display: inline-flex; align-items: center; justify-content: center; gap: 8px; flex: none;
	height: 44px; box-sizing: border-box; border: 1px solid var(--edge); background: var(--surface); color: var(--ink-2);
	font-family: inherit; font-size: 13px; font-weight: 500; text-decoration: none; cursor: pointer;
}
.join-part:hover { background: var(--track); color: var(--ink); }
.join-part[aria-current="page"] { color: var(--ink); }
.join-part::before { content: ""; position: absolute; top: 8px; bottom: 8px; width: 1px; background: var(--hairline); }
.join-start { padding: 0 14px; border-right: 0; border-radius: 10px 0 0 10px; }
.join-start::before { right: 0; }
.join-end { width: 48px; padding: 0; border-left: 0; border-radius: 0 10px 10px 0; }
.join-end::before { left: 0; }
.join-end .badge { top: -6px; right: -6px; }
@media (max-width: 700px) {
	.join-start { padding: 0 12px; }
	.join-start .label { display: none; }
}

/* The way to the shelves and the search as one control. Typing, the field's whole border is the seam */
.search-join { display: flex; align-items: stretch; flex: 1 1 0; min-width: 0; }
.search-join .finding { flex: 1 1 0; }
.search-join .finding input { border-top-left-radius: 0; border-bottom-left-radius: 0; border-left: 0; }
.search-join:has(input:focus) .finding input { border-left: 1px solid var(--ink); }
.search-join:has(input:focus) .join-start::before { display: none; }

/* The place and the cart as one control. Open, the field has its own whole border, which is the seam */
.place-join { display: flex; align-items: stretch; flex: none; min-width: 0; }
.place-join .cart-wrap { display: flex; }
.place-join .place-field .summary, .place-join .place-field input { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.place-join .place-field:not(.open) .summary { border-right: 0; }
.place-join .place-field.open + .cart-wrap .join-end { margin-left: -1px; border-left: 1px solid var(--edge); }
.place-join .place-field.open + .cart-wrap .join-end::before { display: none; }

/* Closed, the field is two lines: the address, and under it a small box with a shop and how many. Open, it is the
   field to type in, with the location button at its end */
.place-field .with-button { display: none; }
.place-field.open .with-button { display: flex; }
.place-field.open .summary { display: none; }
.place-field .summary {
	display: flex; align-items: center; gap: 10px;
	width: 100%; height: 44px; padding: 0 5px 0 12px; box-sizing: border-box;
	border: 1px solid var(--edge); border-radius: 10px; background: var(--surface);
	font: inherit; color: var(--ink); text-align: left; cursor: pointer;
}
.place-field .summary:hover { border-color: var(--ink-3); }
.place-field .summary .where { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.place-field .summary .street, .place-field .summary .town {
	overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.25;
}
.place-field .summary .street { font-size: 14px; }
.place-field .summary .town { font-size: 12px; color: var(--ink-2); }
.place-field .summary .where.none .street { color: var(--ink-3); }
/* How many shops, at the right end inside the field's own border, as tall as the field allows */
.place-field .summary .shops {
	display: inline-flex; align-items: center; gap: 5px; flex-shrink: 0; height: 32px; padding: 0 9px;
	border: 1px solid var(--hairline); border-radius: 7px; background: var(--paper); color: var(--ink-2);
	font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 13px; line-height: 1;
}

/* Wide enough for a branch's name and address on one line each; anchored to the field's right edge, so on a wide
   screen it opens leftward over the page rather than off it */
.place-panel {
	position: absolute; top: calc(100% + 6px); right: 0; z-index: 40;
	width: min(640px, calc(100vw - 32px));
	display: flex; flex-direction: column; gap: 12px; padding: 16px;
	border: 1px solid var(--edge); border-radius: 12px; background: var(--surface);
	max-height: min(80vh, 720px); overflow-y: auto;
}

/* The distance, as a choice rather than a button to press until it is far enough */
.place-panel .within { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 14px; }
.place-panel .within select {
	min-height: 36px; padding: 0 10px; border: 1px solid var(--edge); border-radius: 8px;
	background: var(--surface); font-family: var(--mono); font-size: 14px; color: var(--ink);
}

/* The field owns the location button: it sits inside the box, at the end, where a clear or reveal control would */
.with-button { position: relative; display: flex; }
.with-button input { flex: 1; }
.with-button .inside {
	position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
	width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
	border: 0; border-radius: 8px; background: transparent; color: var(--ink-2); cursor: pointer;
}
.with-button .inside:hover { background: var(--track); color: var(--ink); }
.with-button .inside[disabled] { opacity: 0.4; cursor: default; }

.place-panel .results { display: flex; flex-direction: column; border: 1px solid var(--hairline); border-radius: 10px; overflow: hidden; }
.place-panel .results:empty { display: none; }
.place-panel .result {
	display: flex; align-items: center; gap: 10px; padding: 10px 12px;
	border: 0; border-bottom: 1px solid var(--hairline); background: var(--surface);
	font-family: inherit; font-size: 14px; text-align: left; cursor: pointer;
}
.place-panel .result:last-child { border-bottom: 0; }
.place-panel .result:hover, .place-panel .result:focus { background: var(--track); }
.place-panel .result .grow { flex: 1; min-width: 0; }

/* One branch in the chooser, scoped to it: the browse rail and the trip page have a .branch of their own. A tick,
   how far, whose it is, and what it is called. How far comes first and largest, since which of
   two branches somebody goes to is mostly which is nearer. The address is what tells two of a chain apart. */
.place-panel .branch { display: flex; align-items: center; gap: 12px; min-height: 56px; padding: 8px 0; cursor: pointer; }
.place-panel .branch input { width: 18px; height: 18px; accent-color: var(--ink); flex-shrink: 0; }
.place-panel .branch .far {
	font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 15px; font-weight: 600;
	color: var(--ink); white-space: nowrap; min-width: 64px; text-align: right; flex-shrink: 0;
}
.place-panel .branch .what { flex: 1; min-width: 0; }
.place-panel .branch .name { font-size: 15px; font-weight: 500; }
.place-panel .branch .where { font-size: 13px; color: var(--ink-2); }

.place-panel .actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* The chains that clearly do not deliver here, folded away under the ones that do */
.place-panel .others { margin-top: 8px; }
.place-panel .others summary { font-size: 13px; color: var(--ink-2); cursor: pointer; padding: 4px 0; }

/* The online shops, as many to a row as fit: each the tick, the mark and the name, and under the name the way to ask
   the chain whether it delivers here */
.place-panel .online-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 4px 16px; margin-top: 6px; }
.place-panel .branch.online {
	display: grid; grid-template-columns: auto auto minmax(0, 1fr); grid-template-areas: "tick mark name" ". check check";
	align-items: center; column-gap: 10px; row-gap: 2px; min-height: 0; padding: 6px 0;
}
/* A tick box's own size: the fields' height would otherwise make every cell 44 pixels tall at its first line */
.place-panel .branch.online input { grid-area: tick; min-height: 0; height: 18px; margin: 0; }
.place-panel .branch.online .mark { grid-area: mark; width: 24px; height: 24px; border-radius: 6px; }
.place-panel .branch.online .name { grid-area: name; min-width: 0; font-size: 14px; }
.place-panel .branch.online .check { grid-area: check; min-width: 0; font-size: 12px; line-height: 1.3; color: var(--ink-2); }
.place-panel .branch.online .check:hover { color: var(--ink); }

/* A city has sixty branches within five kilometres. They scroll inside the panel, so what confirms them stays on
   screen rather than sixty rows further down. Never squeezed by what is under them: the panel scrolls instead */
.place-panel #nearby { max-height: min(40vh, 360px); overflow-y: auto; flex-shrink: 0; }
/* Rows keep their height in a box that is shorter than them all; squeezed, their two lines drew over each other */
.place-panel #nearby > * { flex-shrink: 0; }
.place-panel #nearby:empty { display: none; }

/* On a phone the header takes two rows, the name and the ways on above, the bar under them. Where you shop is a pin
   beside the search until it is opened, and then the whole row: side by side, neither field is wide enough to read
   what is in it */
@media (max-width: 700px) {
	header { flex-wrap: wrap; row-gap: 8px; transition: transform 0.2s ease; }
	header > #topbar { order: 3; flex-basis: 100%; }
	/* Scrolling down, the first row goes up out of sight and the search stays (header.js tuckOnScroll) */
	header.tucked { transform: translateY(var(--tuck, -52px)); }
	.place-field { width: 44px; }
	.place-field .summary { align-items: center; justify-content: center; padding: 0; }
	.place-field .summary .where, .place-field .summary .shops { display: none; }
	.place-field .summary .pin { display: flex; color: var(--ink-2); }
	.place-field.open { width: auto; }
	/* Open, the place and its cart take the row the search gave up */
	.topbar:has(.place-field.open) .place-join { flex: 1 1 auto; }
	.topbar:has(.place-field.open) .place-field { flex: 1 1 auto; }
	.topbar:has(.place-field.open) .search-join { display: none; }
	.place-panel { left: 0; right: 0; width: auto; }
}

/* ── The product card ────────────────────────────────────────────────────────────────────────────────────────
   One thing, as a card: the retailer's photograph, whose shop it is, what it is called and what it costs. Built by
   card.js and laid out by .cards, on the front page and the catalogue alike: a page that sized its own columns
   would be showing a different card, whatever the markup inside it. */

/* Columns as wide as the room allows at no less than 200px, so a card's picture is the same size wherever it is.
   Two across on a phone, because one of these per row is a list and three is too small to read. */
.cards {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
	gap: 12px;
	margin-top: 16px;
}
.cards:empty { display: none; }
@media (max-width: 600px) {
	.cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.pick {
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding: 14px;
	background: var(--surface);
	border: 1px solid var(--hairline);
	border-radius: 12px;
	color: inherit;
	text-decoration: none;
	min-height: 148px;
}

.pick:hover { border-color: var(--edge); }

/* Every photograph's frame, wherever it is (images.js photograph): white, because nearly every product photograph is
   taken on white, and on a tint each of them showed as a white box within it. The frame is the picture's ground, so a
   photograph and its frame read as one */
.shot { background: var(--photo); }

/* Inside the card's padding like everything else on it, with corners of its own. Contained, never cropped — they come
   at every aspect ratio a marketing department has ever chosen. No shadow, like everything else here. */
.pick .shot {
	margin: 0 0 8px;
	border-radius: 8px;
	display: flex;
	align-items: center;
	justify-content: center;
	overflow: hidden;
}

/* Square, and as wide as the card: nearly every one of these photographs is square, and a short frame showed them at
   half the width they had room for */
.pick .shot { aspect-ratio: 1 / 1; }
/* Filling the frame, not capped at it: a cap left every picture smaller than the frame at its own pixel size, so a
   chain that serves 200px pictures showed smaller ones than a chain that serves 1000px. Contain scales each up or
   down to the same square, and still never crops */
.pick .shot img { width: 100%; height: 100%; object-fit: contain; display: block; }

/* The link behind the photograph draws no box of its own: the frame inside it is the box. Mouse-only anyway: it is
   aria-hidden with tabindex -1, because the name below it is the link that counts. */
.pick > a[aria-hidden="true"] { display: contents; }
/* The shop's mark on a card, here rather than in a page: a page's own .mark is for its own lists, and a card that
   took its mark from whichever page it was on is how the two drifted apart */
.pick .mark, .place-panel .branch .mark {
	width: 24px; height: 24px; border-radius: 6px; display: flex; align-items: center; justify-content: center;
	font-size: 10px; font-weight: 600; letter-spacing: 0.02em; color: var(--ink); flex-shrink: 0;
}
/* The tile's color is for initials. A shop's own logo brings its own ground: behind one the tile showed through
   wherever the image stops short of the edge -- two thin lines either side of Albert Heijn's */
.pick .mark:not(.logo), .place-panel .branch .mark:not(.logo) { background: var(--track); }
.pick .who { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; row-gap: 6px; }

/* Whose price it is, in a row of a list: the card's mark, smaller (card.js priceMark). A card sizes its own */
.price-mark {
	width: 20px; height: 20px; border-radius: 5px; display: flex; align-items: center; justify-content: center;
	font-size: 9px; font-weight: 600; color: var(--ink); flex-shrink: 0;
}
.price-mark:not(.logo) { background: var(--track); }

/* Every shop that sells an alternative, small enough to sit in a line of text; what each charges is on hover */
.marks { display: inline-flex; align-items: center; gap: 3px; vertical-align: middle; cursor: default; }
.marks .mark {
	width: 18px; height: 18px; border-radius: 4px; display: flex; align-items: center; justify-content: center;
	font-size: 8px; font-weight: 600; background: var(--track); color: var(--ink); flex-shrink: 0;
}
/* Three lines at most: a card's name is there to recognise the thing among others, and past three it is mostly the
   pack size the line under it already gives. The whole of it is the tooltip and the product's own page */
.pick .name {
	font-size: 15px; font-weight: 500; line-height: 19px;
	display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
/* The card used to be one link and now holds one, because there is a button in it too. It should not
   start looking like prose with a link in the middle of it. */
.pick .name a { color: inherit; text-decoration: none; }
.pick .name a:hover { text-decoration: underline; }
.pick .pack { font-size: 12px; color: var(--ink-3); }
.pick .figures { display: flex; align-items: flex-end; gap: 8px; margin-top: auto; }
/* The shop the price is from, at the end of the price's line */
.pick .figures > .mark { margin-left: auto; flex-shrink: 0; }
.pick .prices { display: flex; align-items: baseline; gap: 0 8px; flex-wrap: wrap; min-width: 0; }
.pick .now { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 19px; font-weight: 500; }
.pick .per { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 12px; color: var(--ink-2); }
.pick .note-line { font-size: 12px; color: var(--ink-2); line-height: 16px; }

/* The move itself, which is the only thing on these cards worth a color, and it comes with the number */
.move { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 12px; font-weight: 500; padding: 1px 8px; border-radius: 999px; white-space: nowrap; }
.move.down { background: var(--amber-field); color: var(--amber); }
.move.up { background: var(--track); color: var(--ink-2); }

/* ── The list being built ────────────────────────────────────────────────────────────────────────────────────
   Finding something and putting it on a list is one motion, so the way onto a list looks the same wherever
   something is found. What is on it is the header's cart, on every page (header.js). */

/* How many of a product are on the list, wherever it can be put there: [ - | 0x | cart+ ] (basket.js addButton).
   The cart with a plus puts it on; it and the - are one weight, so neither end outweighs the count between them */
.add-group { display: inline-flex; flex-shrink: 0; }
/* Both ends one width, so the count sits in the middle of the box */
.add-group .amount-box .step { width: 34px; }
.add-group .amount-box .more { color: var(--ink-2); display: inline-flex; align-items: center; justify-content: center; }
/* On a product's page or the scanner's sheet, the box is a page button's height */
.add-group:not(.compact) .amount-box { height: 44px; border-radius: 10px; }
.add-group:not(.compact) .amount-box .step { width: 44px; font-size: 18px; }
.add-group:not(.compact) .amount-box .more svg { width: 24px; height: 24px; }
.add-group:not(.compact) .amount-box .value { font-size: 15px; }
/* A card's last line, the whole width of it: the card's action rather than a small box beside nothing, the count in
   the middle, a little taller than elsewhere and a little apart from the shop line above it */
.pick > .add-group { display: flex; justify-content: flex-end; margin-top: 6px; }
.pick > .add-group .amount-box { flex: 1; height: 36px; border-radius: 8px; }
/* None of it on the list yet: only the way to put it there, the cart, rather than a box of nothing on every card.
   The box grows out of it once there is one */
.add-group .amount-box.none > .fewer, .add-group .amount-box.none > .value, .add-group .amount-box.none > .more::before { display: none; }
.pick > .add-group .amount-box.none { flex: 0 0 auto; }
.pick > .add-group .value { flex: 1; }
.pick > .add-group .step { width: 40px; }

/* How long since anybody charged less, as a badge: the product page's and the scanner's sheet's. A low is the one
   thing worth a color, and it comes with the word */
.verdict {
	display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600;
	padding: 2px 9px; border-radius: 999px; background: var(--track); color: var(--ink-2);
}
.verdict.low { background: var(--amber-field); color: var(--amber); }

/* A shop's lines put straight into its own online cart, from the heading of its card (retailers.js orderOnline) */
.order-online {
	display: inline-flex; align-items: center; gap: 6px; margin-left: auto; height: 32px; padding: 0 12px;
	border: 1px solid var(--ink); border-radius: 999px; background: var(--surface); color: var(--ink);
	font-size: 13px; font-weight: 500; text-decoration: none; white-space: nowrap; flex-shrink: 0;
}
.order-online:hover, .order-online:focus-visible { background: var(--ink); color: var(--surface); }
.order-online + .sum, .no-order-online + .sum { margin-left: 12px; }
/* In the order button's place, where a chain takes no list from here: said quietly, in words */
.no-order-online {
	margin-left: auto; display: inline-flex; align-items: center; gap: 6px;
	font-size: 12px; color: var(--ink-3); white-space: nowrap; flex-shrink: 0;
}
/* A product's page at its chain, beside it: an outgoing arrow, quiet until it is pointed at */
.product-link {
	flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px;
	border-radius: 999px; color: var(--ink-3);
}
.product-link:hover, .product-link:focus-visible { color: var(--ink); background: var(--track); }
/* A phone's heading has no room for the words beside the shop's name and its total: the cart alone, its label still
   saying the whole of it */
@media (max-width: 480px) {
	.order-online { height: 36px; padding: 0; }
	.order-online, .no-order-online { width: 36px; justify-content: center; }
	.order-online .words, .no-order-online .words { display: none; }
}

/* A shop's offer, wherever its price is shown (offers.js): a line under its name saying what the offer is, and what it
   was struck above the price */
.offer-line { font-size: 12px; color: var(--ink-2); margin-top: 2px; }
.offer-tag { background: var(--amber-field); color: var(--amber); font-weight: 600; padding: 1px 6px; border-radius: 3px; font-size: 11px; }
.cost-stack { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.15; }
.cost-stack .was { font-family: var(--mono); font-size: 12px; color: var(--ink-3); }

/* The scanner in the corner of every page (header.js): on a phone it opens full screen, at a desk as a panel in that
   corner (scanner.css) */
:root { --fab-bottom: calc(env(safe-area-inset-bottom, 0px) + 16px); }
.scan-fab {
	display: flex; position: fixed; right: 16px; bottom: var(--fab-bottom); z-index: 11;
	width: 56px; height: 56px; border-radius: 999px; border: 0; padding: 0; cursor: pointer;
	align-items: center; justify-content: center; background: var(--ink); color: var(--surface);
}
/* Where it is always there, the page ends below it rather than under it */
@media (hover: none), (max-width: 899px) {
	.page { padding-bottom: calc(120px + var(--bar-room, 0px)); }
}
/* At a desk it is seldom wanted, and there is a mouse to find it with: tucked past the edge but for a sliver, out
   when the pointer comes near or the keyboard reaches it. A wider reach than the sliver, so it is not a target to aim
   for */
@media (hover: hover) and (min-width: 900px) {
	.scan-fab { transform: translateX(58px); transition: transform 0.18s ease; }
	.scan-fab::before { content: ""; position: absolute; inset: -16px -16px -16px -28px; }
	.scan-fab:hover, .scan-fab:focus-visible { transform: none; }
}
@media (prefers-reduced-motion: reduce) { .scan-fab { transition: none; } }

