/* css-main-product.css — styles that only matter when one of these sections is on the page:
 * main-product.
 * Linked from each of them (see the first line of the section file); theme.css
 * holds the common styles. */

/* ---- Combo groups: slots, contents, quantities ---------------------------
 *
 * A selection group is a combo's slots — "pick two" is two questions, not one
 * list with two ticks — and its items carry photographs, because choosing
 * between two burgers is a choice between two pictures. A package group is the
 * same tiles with nothing to decide. Both can hang their own questions off a
 * chosen item, and those appear underneath it.
 */
/* Top and bottom padding equal: a single-slot group (one "select 1", no
 * step-through) used to start its first tile flush against the header's
 * rule above it, with only the bottom getting room — asymmetric for no
 * reason once there was somewhere for that top space to come from. */
.combo-slot { padding: 0.9rem 0; }

.combo-slot + .combo-slot { padding-top: 0.3rem; }

.combo-slot__label {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  margin: 0.5rem 0 0.75rem;
  font-family: var(--font-display);
  font-size: 0.9rem;
  font-weight: 600;
}

/* "1 of 2" — a position in a sequence, which is what a combo's slots are. */
.combo-slot__n { color: var(--ink); }

.combo-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: 0.5rem;
}

.combo-grid--fixed { padding-bottom: 0.9rem; }

/* The cell, not the tile, is the grid item: a picked tile's own questions sit
 * under it inside the same cell — so the cell stacks them, and the tile sizes
 * to itself.
 *
 * `height: 100%` on the tile was the bug behind the overlap: the cell grows
 * when a pick reveals its questions, 100% of that taller cell is the tile, and
 * the tile then covered the very questions that made the cell tall — their
 * headings surfacing on top of the next slot's label. */
.combo-cell { min-width: 0; display: flex; flex-direction: column; }

.combo-tile {
  position: relative;                 /* the visually-hidden input's anchor */
  display: flex;
  flex-direction: column;
  flex: none;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
  cursor: pointer;
  transition: border-color 0.15s var(--ease);
}

.combo-tile:hover { border-color: var(--line-2); }

/* The radio itself is not drawn — the tile is the control, and a checked tile
 * says so with its border and its tick. Kept in the layout (not `display:
 * none`) so it stays focusable and reachable by keyboard. */
.combo-tile input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.combo-tile__media {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  background: linear-gradient(160deg, var(--surface-3), var(--line-2));
}

.combo-tile__media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.combo-tile__ph {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
}

.combo-tile__body {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 0.5rem 0.6rem 0.6rem;
}

/* Catalogue names run long and arrive without spaces
 * ("LaptopLaptopLaptop3333"), and an unbroken word does not wrap — it ran
 * straight out through the tile's edge. Break anywhere, then stop at two
 * lines so tiles in a row stay close in height. */
.combo-tile__name {
  font-size: 0.88rem;
  font-weight: 600;
  line-height: 1.25;
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

.combo-tile__price { color: var(--muted); font-size: 0.8rem; }

.combo-tile:has(input:checked) {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}

/* The tick, so a chosen tile is obvious at grid size without reading it. */
.combo-tile:has(input:checked) .combo-tile__media::after {
  content: "";
  position: absolute;
  top: 0.4rem;
  inset-inline-end: 0.4rem;
  width: 1.15rem;
  height: 1.15rem;
  border-radius: var(--radius-pill);
  background: var(--accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12.5 4.5 4.5L19 7.5'/%3E%3C/svg%3E") center / 0.75rem no-repeat;
}

.combo-tile:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

.combo-tile--fixed { cursor: default; }

/* A picked item's own questions. Hidden until it is picked — an unpicked
 * item's options are not questions anyone has been asked yet, and the server
 * ignores them too (it only walks into items that were chosen).
 *
 * A sibling of the tile grid, so it gets the group's full width; the rules
 * that open the right one are written per item by the snippet. */
.combo-sub {
  display: none;
  margin: 0.6rem 0.7rem 0.2rem;
  padding-inline-start: 0.7rem;
  border-inline-start: 2px solid var(--line-2);
}

.combo-sub--open { display: block; }

.cfg--nested { margin: 0; }

.cfg--nested > .cfg__head { padding: 0.6rem 0; }

.cfg--nested .cfg__title { font-size: 0.92rem; }

/* An `options` group's row: name, unit price, stepper. */
.cfg__row--qty { cursor: default; }

.cfg__row--qty .cfg__name { flex: 1; }

/* Small, square, and out of the way: the picture is here to identify the
 * thing, not to sell it — the row is a line on a list, not a card. */
.cfg__thumb {
  position: relative;
  flex: none;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--surface-2);
}

.cfg__thumb img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* A pill is a word; a thumbnail inside one turns it into a badge. */
.cfg__row--pill .cfg__thumb { display: none; }

.stepper--sm { transform: scale(0.85); transform-origin: right center; }

[dir="rtl"] .stepper--sm { transform-origin: left center; }

/* ---- Product page: stock, running total, action bar ----------------------
 *
 * The shape follows the PLUS app's item screen — stock on the photograph, the
 * price that answers "what will this cost me" rather than "what does the base
 * item cost", and quantity beside the button that uses it.
 */
.pdp__media { position: relative; }

.pdp__stock {
  position: absolute;
  top: 1rem;
  inset-inline-start: 1rem;
  z-index: 1;
  padding: 0.35rem 0.7rem;
  border-radius: var(--radius-pill);
  background: var(--dark);
  color: var(--on-dark);
  font-family: var(--font-display);
  font-size: 0.75rem;
  font-weight: 600;
}

.pdp__stock--out { background: var(--danger); }

.pdp__actions {
  display: flex;
  gap: 1rem;
  align-items: center;
  flex-wrap: wrap;
}

/* "Your plate" — what the shopper has actually built so far, in one line,
 * written where the button already is rather than requiring a scroll back
 * through the groups to check it. `flex-basis: 100%` puts it on its own row
 * inside `.pdp__actions` whether that's the ordinary inline layout or the
 * fixed phone bar below, with no separate rule needed for either. Empty
 * while nothing is chosen yet — a size product with one modifier group may
 * never have anything to say here. */
.pdp__plate {
  flex: 1 1 100%;
  margin: 0;
  font-size: 0.82rem;
  color: var(--ink-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.pdp__plate:empty { display: none; }

.pdp__submit {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
}

/* The total is a second figure inside one button, so it is set apart rather
 * than run on: at a glance it reads as the price, not as part of the label. */
.pdp__submit-total {
  padding-inline-start: 0.6rem;
  border-inline-start: 1px solid currentColor;
  opacity: 0.85;
  font-variant-numeric: tabular-nums;
}

.pdp__submit-total:empty { display: none; }

.pdp__submit-total.is-changed {
  animation: total-tick 0.28s var(--ease);
}

@media (max-width: 63.99rem) {
  /* Fixed to the bottom on a phone. The groups make this page taller than the
     * screen, and the decision to order is taken while reading them — not after
     * scrolling back past them. */
  .pdp__actions {
      position: fixed;
      inset-inline: 0;
      bottom: 0;
      z-index: 40;
      margin: 0;
      padding: 0.7rem var(--gutter);
      padding-bottom: calc(0.7rem + env(safe-area-inset-bottom));
      background: var(--surface);
      border-top: 1px solid var(--line);
      box-shadow: 0 -6px 20px rgb(0 0 0 / 8%);
      /* Wrap, not nowrap.
       *
       * `.pdp__plate` is `flex-basis: 100%` precisely so it takes its own line;
       * `nowrap` overruled that and squeezed it into the row beside the stepper,
       * where a plate of two choices rendered as "C." and told nobody anything.
       * It costs one line of bar height to answer "what am I ordering" without
       * scrolling back through the groups. */
      flex-wrap: wrap;
      row-gap: 0.35rem;
    }
  /* Quieter and tighter in the bar than in the page: it is a reminder here,
     * not a heading. */
  .pdp__plate { font-size: 0.78rem; line-height: 1.3; }
  /* Clears the bar, so the last group is reachable rather than sitting under
     * it — the same reason the menu page pads for its order bar. */
  .pdp { padding-bottom: 5.5rem; }
  /* Label and total on one line: "Add to / cart" broken over two lines beside
     * the figure made the bar look cramped and the button ambiguous. */
  .pdp__submit { padding-inline: 0.9rem; font-size: 0.95rem; white-space: nowrap; }
  .pdp__submit-total { padding-inline-start: 0.5rem; }
}
/* ---- Product detail --------------------------------------------------------
 * "The Pass" — the counter where the kitchen hands the dish over. The photo
 * reads as the room, not a portrait in a circle: full height, full width of
 * its column, and on a wide screen it bleeds past --maxw to the viewport's
 * own edge, because a window onto the food is not supposed to feel gridded.
 * The copy card then rides up over its lower third — the interface arriving
 * after the food, not beside it. `.pdp__stage`/`.pdp__card` carry that
 * relationship; `.pdp__body` is only a card ON MOBILE, where the two stack.
 */
.pdp { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 clamp(2rem, 5vw, 4rem); align-items: start; }

.pdp__stage { order: 2; }

.pdp__card { order: 1; margin-top: 0; }

.pdp__body { padding: 0; }

@media (min-width: 64rem) {
  /* Desktop: room between the hours strip and the dish name, which otherwise
   * sat flush against it. Phones lay this column out on their own. */
  .pdp__card { padding-top: clamp(1.5rem, 3vw, 2.5rem); }
}
.pdp__media {
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-lg);
  /* The trailing edge always ends flush with the viewport (see the bleed
   * margin below) — a round corner there would leave a sliver of the page's
   * own ground showing past the photo, which is the one thing a "window onto
   * the room" cannot do at its own sill. */
  border-start-end-radius: 0;
  border-end-end-radius: 0;
  overflow: hidden;
  background: linear-gradient(160deg, var(--surface-3), var(--line-2));
  box-shadow: var(--lift-1);
  /* Bleeds the trailing column to the viewport's own edge: the container's
   * own inset (its centring gap over --maxw, plus --gutter) pulled back out.
   * `max()` floors the centring term at 0 so this still resolves to exactly
   * --gutter on a screen narrower than --maxw, where there is no gap to add. */
  margin-inline-end: calc(-1 * (var(--gutter) + max(0px, (100vw - var(--maxw)) / 2)));
}

.pdp__media img { width: 100%; height: 100%; object-fit: cover; }

.pdp__media::after {
  /* Warmth under whatever sits on the photograph — the stock badge reads
   * without a chip drawn under it, because the scrim already carries the
   * contrast, not a solid ground. */
  content: "";
  position: absolute;
  inset: 0;
  background: var(--scrim-warm);
  pointer-events: none;
}

/* The track: one frame wide, snapping to each photo. `scroll-snap-type` plus
 * an id per slide is the whole mechanism — the thumbnails below are anchors
 * into it, so paging works with the script off and from the keyboard. */
.pdp__track {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
}

.pdp__track::-webkit-scrollbar { display: none; }

.pdp__track:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.pdp__slide {
  flex: none;
  width: 100%;
  height: 100%;
  scroll-snap-align: center;
  /* A slide scrolled to by anchor would otherwise land under the sticky
   * header. */
  scroll-margin-top: var(--stuck-top, 0px);
}

.pdp__slide img { width: 100%; height: 100%; object-fit: cover; }

.pdp__thumbs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(3.5rem, 1fr));
  gap: 0.6rem;
  margin-top: 1rem;
  list-style: none;
  padding: 0;
}

/* A link, not a decorative image: it has a target, a focus ring and a name. */
.pdp__thumb {
  display: block;
  position: relative;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 2px solid transparent;
  transition: border-color 0.15s var(--ease);
}

.pdp__thumb:hover { border-color: var(--line-2); }

.pdp__thumb:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.pdp__thumbs img { aspect-ratio: 1; object-fit: cover; width: 100%; display: block; }

/* Available to any control whose visible label is its picture. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  .pdp__track { scroll-behavior: auto; }
}
@media (max-width: 52rem) {
  /* Below --maxw + 2 × --gutter (nothing left to bleed into) and, more to the
   * point, below the stacking breakpoint: the photo goes full-bleed on BOTH
   * edges and the card rides up over its lower third. */
  .pdp { grid-template-columns: 1fr; }
  .pdp__stage { order: 1; }
  .pdp__card { order: 2; margin-top: -3.75rem; position: relative; z-index: 1; }
  .pdp__media {
      aspect-ratio: auto;
      height: min(65vh, 32rem);
      border-radius: 0;
      margin-inline-start: calc(var(--gutter) * -1);
    }
  .pdp__body {
      background: var(--paper);
      border-radius: var(--radius-lg) var(--radius-lg) 0 0;
      box-shadow: var(--lift-1);
      padding: 1.75rem var(--gutter) 0.25rem;
    }
}
.pdp__price { display: flex; align-items: baseline; gap: 0.7rem; flex-wrap: wrap; font-size: 1.4rem; margin-bottom: 1.25rem; }

.pdp__price s { color: var(--muted); font-size: 1rem; font-weight: 400; }

/* The description reads as a magazine standfirst rather than body copy: the
 * opening paragraph is set larger and looser, and the measure is capped near
 * 42 characters so the line breaks fall where a reader expects them instead of
 * running the width of the card. Everything after the first paragraph drops to
 * body size — a standfirst that never ends is just text. */
.pdp__desc {
  color: var(--ink-soft);
  margin-bottom: 1.75rem;
  max-width: 42ch;
  font-size: 1.02rem;
  line-height: 1.7;
}

.pdp__desc > :first-child {
  font-size: 1.12rem;
  line-height: 1.6;
  color: var(--ink);
}

.pdp__desc > * + * { margin-top: 0.7rem; }

.pdp__form { display: flex; flex-direction: column; gap: 1.1rem; max-width: 30rem; }

.pdp__foot { margin-top: 1.5rem; font-size: 0.85rem; color: var(--muted); }

.badge-save {
  background: var(--danger);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 0.15rem 0.5rem;
  border-radius: var(--radius-sm);
  align-self: center;
}

/* Product options — sizes, modifiers, combo picks.
 *
 * Read as a kitchen docket rather than a web form. A docket is a stack of
 * lines: each one asks something, each one carries its answer in writing, and
 * they are separated by a tear rather than boxed up individually. That gives
 * the page one structural device that means something — a rule between two
 * questions says they are different questions — instead of a card per group
 * whose border says only "this is a group".
 *
 * What was here before was the form vocabulary: a tracked-out capitalised
 * label, a red asterisk, a radio dot in every row. All three describe the
 * mechanism. None of them describe lunch.
 */
.cfg {
  border: 0;
  border-top: 1px dashed var(--line-2);
  border-radius: 0;
  background: none;
  padding: 0;
  margin: 0;
  overflow: visible;
}

.cfg:first-of-type { border-top: 0; }

/* The question stays put while its answers scroll.
 *
 * A group with a dozen modifiers is taller than a phone screen, and once its
 * header has scrolled away the rows underneath are unlabelled — you are
 * ticking boxes without the question in view, and "up to 3" has gone with it.
 * Sticking the header to the top of its own group keeps the question and the
 * allowance visible for exactly as long as its answers are.
 *
 * `top` clears the site header, which is itself sticky: `--stuck-top` is its
 * measured height, published on every page by theme.js. An opaque ground is
 * required — a transparent sticky header lets the rows slide through it. */
.cfg > .cfg__head {
  position: sticky;
  top: var(--stuck-top, 0px);
  z-index: 2;
  background: var(--paper);
}

/* A nested group is already inside a revealed pick and is short by
 * construction; two headers competing for the same edge is one too many. */
.cfg--nested > .cfg__head { position: static; }

/* The opaque ground has to match whatever the group is standing on, or the
 * stuck header is a cream band across a tinted section. */
.section--tint .cfg > .cfg__head { background: var(--surface-2); }

/* Only while open, and only then — a closed header has nothing below it to
 * separate itself from. */
/* The open question is a chit lifted off the pass.
 *
 * Closed groups stay flat on the page, separated by the dashed tear rule, so a
 * stack of answered questions reads as a stack of paper. The one being
 * answered rises onto its own ground with a shadow under it: the page then has
 * exactly one object in the foreground, and it is the question you are on.
 *
 * `--lift-1` and the surface are the same pair the copy card uses over the
 * photograph, so the two lifts on this page are the same gesture at two
 * scales rather than two unrelated effects. */
.cfg[open] {
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  border-top-color: transparent;
  padding-inline: 0.85rem;
  margin-block: 0.35rem;
}

/* The sticky header has to match the ground it is stuck to, or it is a cream
 * band sliding over a white chit. */
.cfg[open] > .cfg__head {
  background: var(--surface);
  box-shadow: 0 1px 0 var(--line);
  /* Inset so the header's rule spans the chit's full width rather than
   * stopping short of its padding. */
  margin-inline: -0.85rem;
  padding-inline: 0.85rem;
}

/* A nested group is already inside a chit; a chit inside a chit is a box
 * inside a box. */
.cfg--nested[open] {
  background: none;
  box-shadow: none;
  padding-inline: 0;
  margin-block: 0;
}

.cfg__head {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  padding: 0.95rem 0.1rem;
  cursor: pointer;
  list-style: none;
}

/* Safari draws its own triangle through `::-webkit-details-marker`, which the
 * standard `list-style: none` does not reach. */
.cfg__head::-webkit-details-marker { display: none; }

.cfg__heading { flex: 1; min-width: 0; }

.cfg__ask { display: flex; align-items: baseline; gap: 0.6rem; }

/* Sentence case, no tracking. A question set in spaced capitals is shouting a
 * label; this one is asking something. */
.cfg__title {
  font-family: var(--font-display);
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
}

/* How many, in words. Sits with the question because it is part of it. */
.cfg__need {
  font-size: 0.82rem;
  color: var(--muted);
  white-space: nowrap;
}

.cfg__need--must { color: var(--accent); }

/* The answer, under the question. */
.cfg__chosen {
  display: block;
  margin-top: 0.2rem;
  color: var(--ink-soft, var(--muted));
  font-size: 0.88rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cfg__chosen:empty { display: none; }

.cfg__caret { flex: none; color: var(--muted); transition: transform 0.18s var(--ease); }

.cfg[open] > .cfg__head .cfg__caret { transform: rotate(180deg); }

.cfg .cfg__list { padding: 0 0 0.9rem; }

/* 0.5rem, not 0.3: adjacent tap targets need 8px between them or a thumb
 * lands on the row above the one it was aimed at. The gap was 4.8px. */
.cfg__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }

/* A handful of short choices read side by side: sizes are compared, not
 * scanned down a column. */
.cfg__list--pills { flex-direction: row; flex-wrap: wrap; gap: 0.5rem; }

.cfg__row {
  position: relative;
  display: flex;
  gap: 0.7rem;
  align-items: center;
  /* 48px is the floor for a row a thumb has to hit. */
  min-height: 3rem;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: pointer;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease);
}

.cfg__row:hover { border-color: var(--line-2); }

/* A real control, drawn by the browser.
 *
 * This was a 1×1 transparent input with the answer marked by a tick at the far
 * end of the row. That pattern is defensible — the input kept focus and the
 * label kept the click — but it throws away the two things the native control
 * gives for free: a shape that says how many answers the group takes, and a
 * state assistive technology and the platform both already understand.
 *
 * `accent-color` tints it with the brand without replacing it, so a radio is
 * round and a checkbox square automatically. The single-vs-multiple question
 * is then answered by the control itself rather than by a class. */
.cfg__row input {
  flex: none;
  width: 1.15rem;
  height: 1.15rem;
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
}

.cfg__name { flex: 1; }

/* Tabular figures so a column of prices lines up on the decimal — three-digit
 * minor units make that visible. */
.cfg__price { color: var(--muted); font-size: 0.9rem; font-variant-numeric: tabular-nums; }

.cfg__flag { color: var(--danger); font-size: 0.78rem; }

/* Chosen: an olive spine at the leading edge and a tick at the trailing one —
 * the two marks a line gets when it is ticked off. */
.cfg__row:has(input:checked) {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 7%, var(--surface));
}

.cfg__row::before {
  content: "";
  position: absolute;
  inset-block: -1px;
  inset-inline-start: -1px;
  width: 3px;
  border-start-start-radius: var(--radius-sm);
  border-end-start-radius: var(--radius-sm);
  background: var(--accent);
  opacity: 0;
  transition: opacity 0.15s var(--ease);
}

.cfg__row:has(input:checked)::before { opacity: 1; }

/* No tick: the control now shows the state, and two marks for one answer is
 * one mark too many. The row keeps its tinted ground and its spine. */
.cfg__row:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Pills: same line, laid out as a chip. The tick would crowd a short label,
 * so a chosen pill is marked by its ground alone. */
.cfg__row--pill {
  padding: 0.5rem 0.9rem;
  border-radius: var(--radius-pill);
  gap: 0.45rem;
}

.cfg__row--pill::before { display: none; }

/* The one place the control stays hidden. A segmented pill IS the control:
 * its label is the accessible name, its ground is the state, and a checkbox
 * floating inside it would read as a second thing to press. The input keeps
 * its place in the tab order and the pill shows the focus ring below. */
.cfg__row--pill input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  margin: 0;
}

.cfg__row--pill:has(input:checked) {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast, #fff);
}

.cfg__row--pill:has(input:checked) .cfg__price { color: inherit; opacity: 0.85; }

.cfg__row--pill .cfg__name { flex: none; }

/* Sold out, or a package's fixed contents: shown because a customer needs to
 * know what is in the box, but not offered as a choice. */
.cfg__row--off {
  color: var(--muted);
  background: var(--surface-2);
  cursor: not-allowed;
  padding-inline-end: 0.85rem;
}

.cfg__row--off:hover { border-color: var(--line); }

.cfg__row--off::after, .cfg__row--off::before { display: none; }

/* ---- All widths ---------------------------------------------------------- */
.pdp__titlerow {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}

.pdp__titlerow h1 { flex: 1; min-width: 0; margin: 0; }

.pdp__qty { flex: none; margin-top: 0.2rem; }

.pdp__label {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
  margin: 1.25rem 0 0.4rem;
}

.pdp__summary { flex: 1 1 100%; min-width: 0; }

.pdp__count { display: none; margin: 0; }

.pdp__submit-bag { display: none; }

/* The photo's own controls exist for the phone, where there is no header
 * beside the photograph to carry them. */
.pdp__topbar { display: none; }

@media (max-width: 63.99rem) {
  /* ---- Phone ---------------------------------------------------------------- */
  /* -- Hero: the dish, edge to edge ------------------------------------------
     * Square and full-bleed, no radius, no lift — the reference opens on the
     * food with nothing framing it. The copy then starts flat underneath rather
     * than riding up over the photo. */
  /* `minmax(0, 1fr)`, not `1fr`. A bare `1fr` is `minmax(auto, 1fr)`, whose
     * floor is the column's min-content width — and the size-tile grid below
     * reports its min-content as every tile side by side (five auto-fit tracks
     * of 4.4rem, ~387px). The column then outgrew the phone, the photo and the
     * copy ran off the right edge, and `overflow-x: clip` on <html> hid the
     * overflow instead of scrolling it, so nothing looked broken except the
     * content itself. */
  .pdp { grid-template-columns: minmax(0, 1fr); }
  .pdp .pdp__stage { position: relative; }
  .pdp .pdp__media {
      margin-inline: calc(-1 * var(--gutter));
      /* Width drives the square, never height. The earlier phone rule gives the
       * frame a viewport-relative height (~63vh), and with a definite height
       * `aspect-ratio` derives the WIDTH from it — a 512px-wide frame on a 375px
       * phone, with the heart button pushed past the edge. Undoing the height
       * hands the ratio back to the stretched width. */
      width: auto;
      height: auto;
      min-height: 0;
      max-height: none;
      aspect-ratio: 1 / 1;
      border-radius: 0;
      box-shadow: none;
    }
  .pdp__topbar {
      display: flex;
      position: absolute;
      z-index: 3;                           /* above the warm scrim */
      inset-block-start: 0.75rem;
      inset-inline: 0.9rem;
      align-items: center;
      justify-content: space-between;
      gap: 0.75rem;
    }
  .pdp__back {
      display: inline-flex;
      align-items: center;
      gap: 0.35rem;
      min-height: 44px;
      color: #fff;
      text-decoration: none;
      font-family: var(--font-display);
      font-size: 1.1rem;
      font-weight: 600;
      /* Contrast over any photograph: a soft shadow under white type rather than
       * a chip, which would put a box on the food. */
      text-shadow: 0 1px 10px rgb(0 0 0 / 55%);
    }
  .pdp__back svg { filter: drop-shadow(0 1px 6px rgb(0 0 0 / 50%)); }
  /* Back points the way reading goes back — the one icon that should mirror. */
  [dir="rtl"] .pdp__back svg { transform: scaleX(-1); }
  .pdp__topbar-actions { display: flex; gap: 0.6rem; }
  .pdp__wl { margin: 0; }
  .pdp__round {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 44px;
      height: 44px;
      border: 0;
      border-radius: 50%;
      background: #fff;
      color: var(--ink);
      box-shadow: var(--shadow);
      cursor: pointer;
    }
  .pdp__round[aria-pressed="true"] { color: var(--danger); }
  .pdp__round:focus-visible,
    .pdp__back:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
  /* Low stock moves to the lower edge so it never sits under the back link. */
  .pdp .pdp__stock { top: auto; bottom: 2.6rem; }
  /* -- Dots -------------------------------------------------------------------
     * The thumbnail row becomes the reference's dots, laid on the photo. They
     * are still anchors into the snap track — paging works with the script off —
     * and each is a 24px target with the 6px dot drawn inside it, because a 6px
     * link is not something a thumb can hit. */
  .pdp .pdp__thumbs {
      position: absolute;
      z-index: 3;
      inset-inline: 0;
      bottom: 0.6rem;
      display: flex;
      justify-content: center;
      gap: 0.1rem;
      margin: 0;
    }
  .pdp .pdp__thumb {
      position: relative;
      width: 24px;
      height: 24px;
      border: 0;
      border-radius: 0;
      background: none;
      overflow: visible;
    }
  .pdp .pdp__thumb img { display: none; }
  .pdp .pdp__thumb::before {
      content: "";
      position: absolute;
      inset: 50% auto auto 50%;
      width: 7px;
      height: 7px;
      border-radius: 999px;
      background: rgb(255 255 255 / 60%);
      transform: translate(-50%, -50%);
      transition: width 0.2s var(--ease), background 0.2s var(--ease);
    }
  .pdp .pdp__thumb.is-active::before { width: 16px; background: #fff; }
  /* -- The sheet --------------------------------------------------------------- */
  /* No z-index: the sheet no longer rides over the photo, and a stacking
     * context here would trap the fixed order bar (z-index 40) inside it — the
     * cards further down the page, hearts and all, then painted over the bar. */
  .pdp .pdp__card { margin-top: 0; position: relative; z-index: auto; }
  .pdp .pdp__body {
      margin-inline: calc(-1 * var(--gutter));
      padding: 1.1rem var(--gutter) 0;
      background: var(--surface);
      border-radius: 0;
      box-shadow: none;
    }
  .pdp__titlerow h1 { font-size: 1.4rem; line-height: 1.25; font-weight: 600; }
  .pdp .pdp__price { font-size: 1.1rem; margin: 0.35rem 0 0; }
  .pdp__qty .stepper__input {
      width: 2.25rem;
      border: 0;
      border-inline: 1px solid var(--line);
      height: 40px;
      text-align: center;
      font-weight: 600;
      background: none;
    }
  /* Description as plain supporting copy, not a standfirst. */
  .pdp .pdp__desc { max-width: none; font-size: 0.92rem; line-height: 1.65; color: var(--muted); }
  .pdp .pdp__desc > :first-child { font-size: inherit; line-height: inherit; color: inherit; }
  /* -- Option groups: one card each ---------------------------------------- */
  .pdp .pdp__form { max-width: none; gap: 0.9rem; margin-top: 1.1rem; }
  .pdp .cfg,
    .pdp .cfg[open] {
      border: 1px solid var(--line);
      border-radius: var(--radius);
      background: var(--surface);
      box-shadow: none;
      padding: 0;
      margin: 0;
    }
  .pdp .cfg > .cfg__head,
    .pdp .cfg[open] > .cfg__head {
      margin: 0;
      padding: 0.85rem 1rem;
      background: var(--surface);
      box-shadow: none;
      border-radius: var(--radius);
    }
  .pdp .cfg[open] > .cfg__head {
      border-bottom: 1px solid var(--line);
      border-end-start-radius: 0;
      border-end-end-radius: 0;
    }
  /* Question above, its terms under it. */
  .pdp .cfg__ask { flex-direction: column; align-items: flex-start; gap: 0.15rem; }
  .pdp .cfg__title { font-size: 1rem; font-weight: 600; }
  .pdp .cfg__need,
    .pdp .cfg__need--must { color: var(--muted); font-size: 0.78rem; }
  .pdp .cfg .cfg__list { padding: 0.9rem 1rem 1rem; }
  .pdp .combo-slot { padding-inline: 1rem; }
  /* A package's contents sit in the same inset as a combo's slots — they ran
     * to the card's edges and against the header's rule, where a slot has its
     * "1 of 3" line to hold them off it. */
  .pdp .combo-grid--fixed { padding: 0.75rem 1rem 0.9rem; }
  /* A nested group sits inside a card already. */
  .pdp .cfg--nested,
    .pdp .cfg--nested[open] { border: 0; border-radius: 0; }
  .pdp .cfg--nested > .cfg__head { padding-inline: 0; border-bottom: 0; }
  .pdp .cfg--nested .cfg__list { padding-inline: 0; }
  /* -- Size tiles ------------------------------------------------------------
     * A short single-answer group — sizes — as a row of equal tiles: picture,
     * name, then the price in its own small pill. Chosen: the tile's border and
     * that pill take the accent, the tile itself stays white. */
  .pdp .cfg__list--pills {
      display: grid;
      /* `min(4.4rem, 100%)` so the track floor can never exceed the list itself,
       * even before the column above has a definite width. */
      /* 4.1rem: four tiles plus gaps must fit the ~306px inside a card on a
       * 375px phone; at 4.4rem they came up 2px short and wrapped to three. */
      grid-template-columns: repeat(auto-fit, minmax(min(4.1rem, 100%), 1fr));
      gap: 0.5rem;
    }
  .pdp .cfg__row--pill {
      flex-direction: column;
      justify-content: flex-start;
      align-items: center;
      gap: 0.35rem;
      min-height: 0;
      padding: 0.75rem 0.35rem 0.6rem;
      border: 1px solid var(--line);
      border-radius: 12px;
      background: var(--surface);
      color: var(--ink);
      text-align: center;
    }
  .pdp .cfg__row--pill .cfg__thumb { display: block; width: 2.3rem; height: 2.3rem; border-radius: 50%; }
  .pdp .cfg__row--pill .cfg__name { flex: none; font-size: 0.85rem; font-weight: 600; line-height: 1.2; }
  .pdp .cfg__row--pill .cfg__price {
      margin-top: auto;
      padding: 0.12rem 0.55rem;
      border: 1px solid var(--line);
      border-radius: 6px;
      color: var(--ink);
      font-size: 0.75rem;
      font-weight: 600;
    }
  .pdp .cfg__row--pill:has(input:checked) {
      background: var(--surface);
      color: var(--ink);
      border-color: var(--accent);
      box-shadow: inset 0 0 0 1px var(--accent);
    }
  .pdp .cfg__row--pill:has(input:checked) .cfg__price {
      background: var(--accent);
      border-color: var(--accent);
      color: var(--accent-contrast, #fff);
      opacity: 1;
    }
  /* -- Topping chips ------------------------------------------------------------
     * A multi-answer group as two columns of rounded chips: name, price, and the
     * checkbox at the trailing end, where the reference puts it. */
  .pdp .cfg__list:has(> li > .cfg__row > input[type="checkbox"]) {
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: 0.55rem;
    }
  .pdp .cfg__row {
      gap: 0.5rem;
      padding: 0.6rem 0.7rem;
      border-radius: 12px;
    }
  .pdp .cfg__row input { order: 9; }
  .pdp .cfg__row::before { display: none; }
  .pdp .cfg__row .cfg__name { font-size: 0.88rem; line-height: 1.25; }
  .pdp .cfg__row .cfg__price { color: var(--ink); font-size: 0.8rem; font-weight: 600; }
  /* -- The bar ------------------------------------------------------------------
     * Floating, rounded, in the accent — the reference's single strongest object
     * below the photo. The accent is the merchant's (settings.accent_color), so
     * the text uses --accent-contrast rather than assuming white will read. */
  .pdp .pdp__actions {
      inset-inline: 0.75rem;
      bottom: calc(0.75rem + env(safe-area-inset-bottom));
      padding-block: 0.55rem;
      padding-inline: 1rem 0.55rem;
      border: 0;
      border-radius: 16px;
      background: var(--accent);
      background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 82%, #fff) 0%, var(--accent) 100%);
      color: var(--accent-contrast, #fff);
      box-shadow: 0 12px 28px -10px color-mix(in srgb, var(--accent) 70%, transparent);
      flex-wrap: nowrap;
      gap: 0.75rem;
    }
  .pdp .pdp__summary { flex: 1 1 auto; }
  .pdp .pdp__count {
      display: block;
      font-family: var(--font-display);
      font-size: 0.95rem;
      font-weight: 600;
      color: inherit;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
  .pdp .pdp__plate { color: inherit; opacity: 0.85; font-size: 0.75rem; }
  .pdp .pdp__submit {
      flex: none;
      min-height: 48px;
      padding: 0.65rem 0.95rem;
      border: 0;
      border-radius: 12px;
      background: rgb(255 255 255 / 22%);
      color: inherit;
      box-shadow: inset 0 0 0 1px rgb(255 255 255 / 28%);
    }
  .pdp .pdp__submit-label {
      position: absolute;
      width: 1px;
      height: 1px;
      margin: -1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }
  .pdp .pdp__submit-total {
      border: 0;
      padding: 0;
      opacity: 1;
      font-size: 1.1rem;
      font-weight: 700;
    }
  .pdp .pdp__submit-bag { display: inline-flex; }
  /* Not everything required is answered yet: the bar goes grey and flat so it
     * reads as "not yet", and the button stops taking presses (theme.js). */
  /* The primary colour at half strength — still the brand's bar, visibly not
     * ready. Mixed into the page colour rather than `opacity`, which would let
     * the content scrolling underneath show through. */
  .pdp .pdp__actions.is-incomplete {
      background: color-mix(in srgb, var(--accent) 50%, var(--surface));
      box-shadow: none;
    }
  .pdp .pdp__actions.is-incomplete .pdp__submit { opacity: 0.7; }
  /* Clears the floating bar — at the END of the page, where it would otherwise
     * cover the footer's last line. On the product itself it only left an empty
     * band before the sections that follow it. */
  /* The breathing room lives inside the white sheet, so the sheet runs to the
     * bottom of the product instead of stopping short on the page ground. */
  .pdp { padding-bottom: 0; }
  .pdp .pdp__body { padding-bottom: 1.5rem; }
}
/* ---- Phone: the dish name and quantity stay on screen ----------------------
 * Pinned under the site header while the options scroll, on white with a rule
 * under it, so "what am I ordering, how many" never scrolls away while the
 * questions below it are being answered. Full-bleed, so the band reads as a bar
 * rather than a box floating inside the gutter.
 *
 * The groups' own sticky headers then have to stop BELOW it rather than at the
 * same line, or the two would stack on top of each other. theme.js publishes
 * this bar's height as --pdp-title-h (0 wherever it isn't sticky), and the
 * focus scroll-padding takes it into account for the same reason. */
/* ---- Price pill and the quantity in the bar (PLUS) --------------------------
 * The pill beside the name is the phone's; wider screens keep the price line. */
.pdp__pill { display: none; }

.pdp__actions .pdp__qty { margin-top: 0; }

@media (max-width: 63.99rem) {
  .pdp__pill {
      display: inline-block;
      flex: none;
      align-self: flex-start;
      margin-top: 0.2rem;
      padding: 0.3rem 0.6rem;
      border-radius: 8px;
      background: var(--accent);
      color: var(--accent-contrast, #fff);
      font-weight: 600;
      font-size: 0.9rem;
      white-space: nowrap;
      font-variant-numeric: tabular-nums;
    }
  /* The pill already says the price: the line below keeps only the sale
     * story, and goes when there is none. */
  .pdp .pdp__price > span:first-child { display: none; }
  .pdp .pdp__price:not(:has(s)) { display: none; }
  /* The stepper at the start of the bar, drawn on the accent. */
  .pdp .pdp__actions { gap: 0.6rem; padding-inline-start: 0.55rem; }
  .pdp .pdp__actions .pdp__qty {
      order: -1;
      flex: none;
      border-color: rgb(255 255 255 / 35%);
      background: rgb(255 255 255 / 14%);
      color: inherit;
    }
  .pdp .pdp__actions .pdp__qty .stepper__btn { width: 34px; height: 40px; background: transparent; color: inherit; }
  .pdp .pdp__actions .pdp__qty .stepper__input {
      width: 2rem;
      border-inline-color: rgb(255 255 255 / 35%);
      background: transparent;
      color: inherit;
    }
  /* Two rows rather than three things squeezed on one: what's on the plate
     * across the top in small type, then — as PLUS has it — the stepper and a
     * wide button that says what it does and what it costs. */
  .pdp .pdp__actions {
      flex-wrap: wrap;
      row-gap: 0.4rem;
      padding: 0.55rem 0.55rem 0.55rem;
    }
  .pdp .pdp__actions .pdp__summary {
      order: -2;
      flex: 1 1 100%;
      display: flex;
      align-items: baseline;
      gap: 0.5rem;
      min-width: 0;
      padding-inline: 0.45rem;
    }
  .pdp .pdp__actions .pdp__count { flex: none; font-size: 0.82rem; }
  .pdp .pdp__actions .pdp__plate { flex: 1 1 auto; min-width: 0; }
  .pdp .pdp__actions .pdp__submit {
      flex: 1 1 auto;
      justify-content: space-between;
      min-height: 44px;
      border-radius: 10px;             /* the stepper's corners */
    }
  .pdp .pdp__actions .pdp__submit-label {
      position: static;
      width: auto;
      height: auto;
      margin: 0;
      overflow: visible;
      clip-path: none;
      font-weight: 600;
    }
  .pdp .pdp__actions .pdp__qty .stepper__btn { height: 44px; }
  .pdp .pdp__actions .pdp__qty .stepper__input { height: 44px; }
  .pdp .pdp__actions .pdp__submit-bag { display: none; }
}
/* Not orderable here: said with room around it, not jammed under the title
 * bar's rule. */
.pdp__unavailable {
  margin: 1rem 0 0.25rem;
  padding: 0.65rem 0.85rem;
  border-radius: 10px;
  background: color-mix(in srgb, var(--danger) 10%, var(--surface));
  color: var(--danger);
  font-weight: 600;
  font-size: 0.9rem;
}

/* The title bar's own back arrow: phone only, and only once the photo — which
 * carries the first one — is out of view (theme.js sets .is-photo-in-view). */
.pdp__titleback { display: none; }

@media (max-width: 63.99rem) {
  .pdp__titleback {
      display: inline-flex;
      flex: none;
      align-items: center;
      justify-content: center;
      width: 2.25rem;
      height: 2.25rem;
      margin-inline-start: -0.5rem;
      color: var(--ink);
      border-radius: var(--radius-pill);
    }
  [dir="rtl"] .pdp__titleback svg { transform: scaleX(-1); }
  .pdp.is-photo-in-view .pdp__titleback { display: none; }
}
/* ---- Phone: the dish full screen -------------------------------------------
 * A food app's detail screen is the dish and nothing else: no store header,
 * no footer. The photo's own bar already has back, share and save, and now
 * the bag — the one thing the header gave this page that it still needs. */
.pdp__cart { display: none; }

@media (max-width: 63.99rem) {
  .pdp__cart { display: inline-flex; position: relative; color: inherit; }
  .pdp__cart-count {
      position: absolute;
      top: -0.2rem;
      inset-inline-end: -0.2rem;
      min-width: 1.15rem;
      padding: 0 0.25rem;
      border-radius: var(--radius-pill);
      background: var(--accent);
      color: var(--accent-contrast, #fff);
      font-size: 0.7rem;
      font-weight: 700;
      line-height: 1.15rem;
      text-align: center;
    }
}
/* The button refuses a press while something required is unanswered. */
.pdp__actions.is-incomplete .pdp__submit { opacity: 0.55; cursor: not-allowed; }

@media (max-width: 63.99rem) {
  .pdp .pdp__titlerow {
      position: sticky;
      top: var(--stuck-top, 0px);
      z-index: 5;
      align-items: center;
      margin-inline: calc(-1 * var(--gutter));
      padding: 0.7rem var(--gutter);
      background: var(--surface);
      border-bottom: 1px solid var(--line);
    }
  .pdp .cfg > .cfg__head { top: calc(var(--stuck-top, 0px) + var(--pdp-title-h, 0px)); }
}
@media all {
  /* ---- Phone: combo picks as rows ---------------------------------------------
   * One option per line — thumbnail at the start, name beside it, the tick at the
   * end — instead of photo tiles two to a row. On a phone the tiles were mostly
   * empty picture frames (many items carry no photo) and a three-slot combo ran
   * to several screens; as rows the whole slot fits in a glance, and the name
   * gets the full width instead of wrapping inside half of it. */
  .pdp .combo-grid {
      grid-template-columns: minmax(0, 1fr);
      gap: 0.5rem;
    }
  .pdp .combo-tile {
      flex-direction: row;
      align-items: center;
      gap: 0.75rem;
      min-height: var(--tap-min, 3rem);
      padding: 0.4rem;
      padding-inline-end: 2.6rem;           /* room for the trailing tick */
      border-radius: 12px;
    }
  .pdp .combo-tile__media {
      flex: none;
      width: 3.25rem;
      aspect-ratio: 1 / 1;
      border-radius: 8px;
      overflow: hidden;
    }
  .pdp .combo-tile__ph svg { width: 18px; height: 18px; }
  .pdp .combo-tile__body { flex: 1; min-width: 0; padding: 0; }
  .pdp .combo-tile__name { font-size: 0.92rem; }
  /* The tick moves off the thumbnail — at 52px it covered most of the picture —
     * to the row's trailing edge, where a chosen line is marked. */
  .pdp .combo-tile:has(input:checked) .combo-tile__media::after { display: none; }
  .pdp .combo-tile:has(input:checked)::after {
      content: "";
      position: absolute;
      inset-inline-end: 0.8rem;
      top: 50%;
      width: 1.3rem;
      height: 1.3rem;
      margin-top: -0.65rem;
      border-radius: 50%;
      background: var(--accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12.5 4.5 4.5L19 7.5'/%3E%3C/svg%3E") center / 0.8rem no-repeat;
    }
}
/* ---- A pick's own questions, under the pick --------------------------------
 * One generic rule replaces the per-item <style> blocks: the questions sit in
 * the same cell as the tile they belong to, and open when that tile is picked. */
.combo-cell > .combo-sub { display: none; margin: 0.5rem 0 0.35rem; }

.combo-cell:has(> .combo-tile input:checked) > .combo-sub { display: block; }

/* A package's contents have no pick to wait for: their questions are open. */
.combo-cell > .combo-sub--open { display: block; }

/* Their options as rows — name, price, tick at the end, one per line — not the
 * size tiles, which inside a nested box folded "+ BHD 1" onto three lines. */
.pdp .cfg--nested .cfg__list--pills {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.pdp .cfg--nested .cfg__row--pill {
  flex-direction: row;
  align-items: center;
  justify-content: flex-start;
  gap: 0.6rem;
  min-height: var(--tap-min, 3rem);
  padding-block: 0.55rem;
  padding-inline: 0.8rem 2.6rem;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  color: var(--ink);
  text-align: start;
  box-shadow: none;
}

/* A nested multi-answer list too: one option per line. The two-column topping
 * chips, inside an indented nested box, left each chip ~110px — "no sauce
 * [en] + BHD 1" folded onto three lines beside its box. */
.pdp .cfg--nested .cfg__list:has(> li > .cfg__row > input[type="checkbox"]) {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding-bottom: 0.5rem;
}

.pdp .cfg--nested .cfg__row:has(> input[type="checkbox"]) {
  align-items: center;
  min-height: var(--tap-min, 3rem);
  padding: 0.55rem 0.8rem;
}

.pdp .cfg--nested .cfg__row:has(> input[type="checkbox"]) .cfg__name { flex: 1; font-size: 0.9rem; font-weight: 500; }

.pdp .cfg--nested .cfg__row:has(> input[type="checkbox"]) .cfg__price { white-space: nowrap; }

.pdp .cfg--nested .cfg__row--pill .cfg__thumb { width: 2rem; height: 2rem; border-radius: 6px; }

.pdp .cfg--nested .cfg__row--pill .cfg__name { flex: 1; font-size: 0.9rem; font-weight: 500; }

.pdp .cfg--nested .cfg__row--pill .cfg__price {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 600;
  white-space: nowrap;
}

.pdp .cfg--nested .cfg__row--pill:has(input:checked) {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 7%, var(--surface));
  color: var(--ink);
  box-shadow: none;
}

.pdp .cfg--nested .cfg__row--pill:has(input:checked) .cfg__price {
  background: none;
  color: var(--ink);
}

.pdp .cfg--nested .cfg__row--pill:has(input:checked)::after {
  content: "";
  position: absolute;
  inset-inline-end: 0.75rem;
  top: 50%;
  width: 1.2rem;
  height: 1.2rem;
  margin-top: -0.6rem;
  border-radius: 50%;
  background: var(--accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12.5 4.5 4.5L19 7.5'/%3E%3C/svg%3E") center / 0.75rem no-repeat;
}

/* ---- Combo slots, one at a time (after the PLUS app) ------------------------
 * The script adds `.is-stepped` to a multi-slot combo and `.is-away` to every
 * slot except the one being answered.
 *
 * `.is-away` hides VISUALLY, never with display:none. The running total, the
 * item count and the plate line only count controls that are rendered (they
 * test offsetParent, to skip the questions of items nobody picked), so a
 * display:none slot would silently drop an already-made pick from the price.
 * Clipped to nothing and taken out of flow, it stays counted and posted. */
.combo-steps { position: relative; }

.combo-steps.is-stepped > .combo-slot.is-away {
  position: absolute;
  top: 0;
  inset-inline-start: 0;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  visibility: hidden;
  pointer-events: none;
}

.combo-slot__actions { display: flex; justify-content: flex-end; margin-top: 0.75rem; }

.combo-slot__add[disabled] { opacity: 0.45; cursor: not-allowed; }

/* The slot that just became current settles in rather than snapping into
 * place — a small cue that the step moved on, not just that the DOM changed.
 * Keyed to the class leaving `.is-away`, so it plays once per step and never
 * replays while the shopper is only answering something inside the slot
 * that's already on screen. */
.combo-steps.is-stepped > .combo-slot:not(.is-away) {
  animation: combo-slot-in 0.32s ease both;
}

@media (prefers-reduced-motion: reduce) {
  .combo-steps.is-stepped > .combo-slot:not(.is-away) { animation: none; }
}
/* The strip of picks already made — tap one to reopen its slot. Bottom
 * padding matches a slot's own (0.9rem): once every slot is answered, this
 * is the last thing in the card, and 0.3rem here — plenty while an open
 * slot followed right after — left the "Your choices" list sitting almost
 * flush against the card's edge. */
.combo-chosen { padding: 0.8rem 1rem 0.9rem; }

.combo-chip__thumb .combo-tile__media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  margin: 0;
  border-radius: 0;
}

@media (max-width: 63.99rem) {
  .pdp .combo-slot__add { width: 100%; min-height: var(--tap-min, 3rem); }
}
/* ---- Product options as the selection rows ----------------------------------------
 * Every modifier group on the dish page — single-answer (the old pills / size
 * tiles) and multi-answer (the topping chips) — drawn the way a combo pick's own
 * options already are (.cfg--nested, above): one option per line, name then
 * price, and the chosen line tinted in the accent with a round tick at its end.
 * Quantity rows (.cfg__row--qty) keep their stepper layout. */
.pdp .cfg__list--pills,
.pdp .cfg__list:has(> li > .cfg__row > input[type="checkbox"]) {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  gap: 0.5rem;
}

.pdp .cfg__row:not(.cfg__row--qty) {
  position: relative;
  flex-direction: row;
  align-items: center;
  justify-content: flex-start;
  gap: 0.6rem;
  min-height: var(--tap-min, 3rem);
  padding-block: 0.55rem;
  padding-inline: 0.8rem 2.6rem;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  color: var(--ink);
  text-align: start;
  box-shadow: none;
}

.pdp .cfg__row:not(.cfg__row--qty) > input { position: absolute; opacity: 0; pointer-events: none; }

.pdp .cfg__row:not(.cfg__row--qty)::before { display: none; }

.pdp .cfg__row:not(.cfg__row--qty) .cfg__thumb { display: block; width: 2rem; height: 2rem; border-radius: 6px; }

.pdp .cfg__row:not(.cfg__row--qty) .cfg__name { flex: 1; font-size: 0.9rem; font-weight: 500; line-height: 1.3; }

.pdp .cfg__row:not(.cfg__row--qty) .cfg__price {
  margin: 0; padding: 0; border: 0; background: none;
  color: var(--muted); font-size: 0.82rem; font-weight: 600; white-space: nowrap; opacity: 1;
}

.pdp .cfg__row:not(.cfg__row--qty):hover { border-color: var(--line-2, var(--accent)); }

.pdp .cfg__row:not(.cfg__row--qty):has(input:checked) {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 7%, var(--surface));
  color: var(--ink);
  box-shadow: none;
}

.pdp .cfg__row:not(.cfg__row--qty):has(input:checked) .cfg__price { background: none; color: var(--ink); }

.pdp .cfg__row:not(.cfg__row--qty):has(input:checked)::after {
  content: "";
  position: absolute;
  inset-inline-end: 0.75rem;
  top: 50%;
  width: 1.2rem;
  height: 1.2rem;
  margin-top: -0.6rem;
  border-radius: 50%;
  background: var(--accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12.5 4.5 4.5L19 7.5'/%3E%3C/svg%3E") center / 0.75rem no-repeat;
}

.pdp .cfg__row:not(.cfg__row--qty):has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---- Product option groups: a card each, closed or open, at every width -----------
 * The phone rules above made each top-level group a card; on a wider screen a
 * closed group was still bare text over a dashed rule and the open one a lifted
 * chit, with its first option flush against the header's rule. Now one card per
 * group everywhere: the header inside it (the rule under it only while open), the
 * options inset from every edge, and an even gap between the cards. Nested groups
 * (inside a pick) stay flat, as before. */
.pdp .cfg:not(.cfg--nested),
.pdp .cfg[open]:not(.cfg--nested) {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: none;
  padding: 0;
  margin: 0 0 0.75rem;
}

.pdp .cfg:not(.cfg--nested):last-of-type { margin-bottom: 0; }

.pdp .cfg:not(.cfg--nested) > .cfg__head {
  margin: 0;
  padding: 0.9rem 1rem;
  background: var(--surface);
  box-shadow: none;
  border-radius: var(--radius);
  align-items: center;
}

.pdp .cfg[open]:not(.cfg--nested) > .cfg__head {
  border-bottom: 1px solid var(--line);
  border-end-start-radius: 0;
  border-end-end-radius: 0;
}

.pdp .cfg:not(.cfg--nested) > .cfg__list { padding: 0.9rem 1rem 1rem; }

.pdp .cfg:not(.cfg--nested) > .combo-sub { padding: 0 1rem 0.75rem; }

/* A package's contents and a combo's slots take the same inset as the options
 * list, at every width (it was phone-only, so on a wide screen the tiles ran to
 * the card's edges). */
.pdp .cfg:not(.cfg--nested) > .combo-grid--fixed { padding: 0.75rem 1rem 0.9rem; }

.pdp .cfg:not(.cfg--nested) > .combo-slot,
.pdp .cfg:not(.cfg--nested) > .combo-steps { padding-inline: 1rem; }

.pdp .cfg:not(.cfg--nested) > .combo-steps .combo-slot { padding-inline: 0; }

html.is-meal .pdp { display: block; }

/* The page's copy card rides up over its photo with its own top padding; with
   no photo here that is just an empty band above the first question. */
html.is-meal .pdp .pdp__card { margin: 0; padding: 0; }

html.is-meal .pdp .pdp__body { margin-inline: 0; padding: 1.25rem 1.25rem 8rem; }

/* Every group is a plain title over a white card; it never folds. The
   doubled `.pdp` is for specificity: the page's own phone rules name
   `.pdp .cfg[open]` and would otherwise win. */
html.is-meal .pdp .pdp__form { max-width: none; margin: 0; gap: 0; }

html.is-meal .pdp .cfg,
html.is-meal .pdp .cfg[open] {
  margin: 0 0 1.25rem;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  scroll-margin-top: 1rem;
}

html.is-meal .pdp .cfg--nested,
html.is-meal .pdp .cfg--nested[open] { margin: 0; }

html.is-meal .pdp .cfg > .cfg__head,
html.is-meal .pdp .cfg[open] > .cfg__head {
  position: static;
  margin: 0;
  padding: 0.4rem 0;
  background: transparent;
  box-shadow: none;
  pointer-events: none;
  cursor: default;
}

html.is-meal .pdp .cfg:not(.cfg--nested) > .cfg__head .cfg__title { font-size: 1.05rem; font-weight: 700; }

html.is-meal .pdp .cfg:not(.cfg--nested) > .cfg__list {
  margin: 0;
  padding: 0.5rem 0.9rem 0.6rem;
  border-radius: 12px;
  background: var(--surface);
}

/* A combo's slots are already rows of their own cards, and "Your choices" is
   its own list: wrapping them in a second card, with the steps' own padding on
   top, indented them past the group's title. They sit flush with it. */
html.is-meal .pdp .cfg:not(.cfg--nested) > .combo-steps,
html.is-meal .pdp .cfg:not(.cfg--nested) > .combo-slot,
html.is-meal .pdp .cfg:not(.cfg--nested) > .combo-grid {
  margin: 0;
  padding: 0;
  background: transparent;
}

html.is-meal .pdp .combo-chosen { padding-inline: 0; }

/* Room round the thumbnail: it sat 6px from the row's edge, so a photo (or its
   alt text while it loads) looked pressed against the border. */
html.is-meal .pdp .combo-tile { padding-inline-start: 0.75rem; }

html.is-meal .pdp .combo-steps > .combo-slot { padding-inline: 0; }

/* Edge to edge, straight under the header: a margin or side padding here read
   as a grey gap between the header rule and the photo. */
html.is-meal.meal-plain .pdp .pdp__stage { display: block !important; width: 100%; order: -1; margin: 0; padding: 0; }

html.is-meal.meal-plain .pdp .pdp__stage { max-height: 15rem; overflow: hidden; }

html.is-meal.meal-plain .pdp .pdp__stage .pdp__media,
html.is-meal.meal-plain .pdp .pdp__stage .pdp__media * { max-height: 15rem; }

html.is-meal.meal-plain .pdp .pdp__stage .pdp__media { width: 100% !important; margin-inline: 0 !important; }

html.is-meal.meal-plain .pdp .pdp__stage .pdp__media img { width: 100% !important; height: 15rem !important; max-width: none !important; object-fit: cover; border-radius: 0; }

html.is-meal.meal-plain .pdp .pdp__topbar { display: none !important; }

html.is-meal.meal-plain .pdp .pdp__titlerow { display: flex !important; }

html.is-meal.meal-plain .pdp .pdp__titleback,
html.is-meal.meal-plain .pdp .pdp__pill { display: none !important; }

html.is-meal.meal-plain .pdp .pdp__price,
html.is-meal.meal-plain .pdp .pdp__desc,
html.is-meal.meal-plain .pdp .pdp__label { display: block !important; }

html.is-meal.meal-plain .pdp .pdp__qty { display: inline-flex !important; }

html.is-meal.meal-plain .pdp { display: flex; flex-direction: column; align-items: stretch; }

html.is-meal.meal-plain .pdp .pdp__card { width: 100%; }

html.is-meal.meal-plain.meal-noimg .pdp .pdp__stage { display: none !important; }

html.is-meal.meal-plain .pdp .pdp__body { margin-inline: auto; max-width: 34rem; padding: 1rem 1.25rem 8rem; }

html.is-meal.meal-plain .pdp .pdp__titlerow h1 { font-size: 1.4rem; }

html.is-meal.meal-plain .pdp .pdp__card,
html.is-meal.meal-plain .pdp .pdp__body { background: transparent; box-shadow: none; border-radius: 0; }

html.is-meal.meal-plain .pdp .pdp__body { padding: 0.5rem 1.25rem 5rem; }

html.is-meal.meal-plain .pdp .pdp__titlerow { position: static; margin-inline: 0; background: transparent; border: 0; padding: 0.75rem 0 0.25rem; }

html.is-meal.meal-plain .pdp .pdp__actions {
  position: fixed;
  inset-inline: 1.25rem;
  bottom: 1.25rem;
  z-index: 4;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.75rem;
  width: auto;
  max-width: 34rem;
  margin-inline: auto;
  padding: 0;
  background: transparent;
  box-shadow: none;
  transform: none;
}

html.is-meal.meal-plain .pdp .pdp__summary { display: none; }

html.is-meal.meal-plain .pdp .pdp__submit {
  flex: 1;
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-contrast, #fff);
}

html.is-meal.meal-plain .pdp .pdp__submit:disabled,
html.is-meal.meal-plain .pdp .pdp__submit[aria-busy="true"] { opacity: 0.55; }

/* The phone bar tints the stepper white-on-olive; on this card it sits on the
   page's own grey, so it gets its own ink. */
html.is-meal.meal-plain .pdp .pdp__actions .pdp__qty {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 12px;
  color: var(--ink);
}

html.is-meal.meal-plain .pdp .pdp__actions .pdp__qty .stepper__btn,
html.is-meal.meal-plain .pdp .pdp__actions .pdp__qty .stepper__input { color: var(--ink); background: transparent; }

@media (min-width: 48.01rem) {
  html.is-meal .pdp .pdp__body { margin-inline: 230px 340px; }
  html.is-meal .pdp .pdp__actions {
      position: fixed;
      inset-inline-end: 1.25rem;
      bottom: 1.25rem;
      z-index: 4;
      display: flex;
      flex-direction: column;
      align-items: stretch;
      gap: 0.6rem;
      width: 300px;
      padding: 0;
      background: transparent;
      box-shadow: none;
    }
  html.is-meal .pdp__summary { text-align: center; }
  html.is-meal .pdp__plate { white-space: normal; text-align: center; color: var(--ink); }
  html.is-meal .pdp__submit {
      display: flex;
      flex-direction: row;
      justify-content: space-between;
      align-items: center;
      width: 100%;
      padding: 0.9rem 1.25rem;
      border-radius: 14px;
      font-weight: 700;
    }
  html.is-meal .pdp__submit-total { order: 1; }
  html.is-meal .pdp__submit-label { order: 2; }
}
@media (max-width: 48rem) {
  /* A step lands just under the sticky strip, not behind it. */
  html.is-meal .pdp .cfg,
    html.is-meal .pdp .cfg[open] { scroll-margin-top: 3.75rem; }
}
/* A nested checkbox row (a pick's own "Select 3-5" list) was left with 13px at
   its end by the nested-group rules, but its tick sits in that space — so the
   price ran underneath it. The room the other rows have, kept for these too. */
.pdp .cfg--nested .cfg__row:not(.cfg__row--qty):has(> input[type="checkbox"]) { padding-inline-end: 2.6rem; }

/* Air between a group's title and its first card. */
html.is-meal .pdp .cfg > .cfg__head,
html.is-meal .pdp .cfg[open] > .cfg__head { margin-bottom: 0.5rem; }

.qty-offer { margin: 0.75rem 0 1rem; display: grid; gap: 0.75rem; }

.qty-offer__title { margin: 0; font-size: 0.78rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }

.qty-offer__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.35rem; }

.qty-offer__tier {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  padding: 0.5rem 0.8rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  font-size: 0.9rem;
}

.qty-offer__tier strong { color: var(--accent); font-variant-numeric: tabular-nums; }

.qty-offer__left {
  display: inline-block;
  justify-self: start;
  padding: 0.35rem 0.8rem;
  border-radius: var(--radius-pill, 999px);
  background: color-mix(in srgb, var(--danger, #b3261e) 10%, var(--surface));
  color: var(--danger, #b3261e);
  font-size: 0.85rem;
  font-weight: 700;
}

.qty-offer-card__sub { margin: 0.3rem 0 0; color: var(--ink-soft, var(--muted)); font-size: 0.88rem; }

