/* ==========================================================================
   TrolleyMate — design system
   Green on white. System fonts only, no build step, no frameworks.

   Sections
     1. Tokens
     2. Reset + base
     3. Typography
     4. Layout primitives — page, nav, hero, stat-row, feature, closing, footer
     5. Components — btn, card, table
     6. Motion
     7. Dark mode  (token overrides only — components never restate colours)
     8. App page
     9. Assistant panel
    10. Landing search block — search bar, category chips, brand cluster
    11. App search composition — price table, savings card

   Every component reads its colour from a token, so dark mode is a single
   token flip at the bottom of section 7. Styles appended after section 7
   inherit both themes for free.
   ========================================================================== */

/* ==========================================================================
   1. Tokens
   ========================================================================== */

:root {
  color-scheme: light;

  /* Brand. Green on white — never purple, never a gradient.
     Measured against --paper (#FFF): --accent 5.01:1, --accent-deep 5.42:1,
     --accent-bright 3.30:1. Anything that carries *text* on white therefore
     uses --accent or --accent-deep; --accent-bright is reserved for fills and
     marks, and lifts in dark mode where it becomes the text green. */
  --ink: #1A1D21;
  --accent: #15803D;
  --accent-deep: #1B7A2F;
  /* Reserved for large-text uses only (3.30:1 on white) — owner accepted the
     darker --accent/--accent-deep eyebrow for small-size contrast instead.
     Do NOT use --accent-bright at small text sizes. */
  --accent-bright: #16A34A;
  --accent-pale: #ECFDF3;
  --paper: #FFFFFF;
  --line: #E5E7EB;
  --muted: #6B7280;

  /* The text-weight accent. Kept as its own token because dark mode has to
     lift it off near-black while the fills below stay put. */
  --accent-ink: #15803D;

  /* A hairline that has to be *seen*, not just felt: the edge of a control.
     Holds 3:1 against --paper in both themes, so an outlined button reads as
     a button without being filled. */
  --line-strong: #9CA3AF;

  /* Retailer wordmarks. Styled text, never a trademark image. */
  --coles: #E01A22;
  --woolworths: #178841;
  --aldi: #00457C;

  /* Derived surfaces. --on-accent stays white in both themes: it is the
     foreground of the accent fill, not a background. */
  --surface: #FFFFFF;
  --wash: #F6F8F7;
  --on-accent: #FFFFFF;
  --accent-hover: #166534;

  /* Type */
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;

  /* Space */
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 24px;
  --s6: 32px;
  --s7: 48px;
  --s8: 64px;
  --s9: 96px;

  /* Shape */
  --radius-sm: 8px;
  --radius: 12px;
  --radius-lg: 16px;

  /* Measure */
  --page-max: 1120px;
  --page-pad: clamp(20px, 5vw, 48px);
  --nav-h: 64px;
}

/* ==========================================================================
   2. Reset + base
   ========================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1,
h2,
h3,
h4,
p,
figure,
blockquote,
dl,
dd {
  margin: 0;
}

ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

img,
svg {
  display: block;
  max-width: 100%;
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

table {
  border-collapse: collapse;
}

[hidden] {
  display: none !important;
}

::selection {
  background: var(--accent);
  color: var(--on-accent);
}

:focus-visible {
  outline: 2px solid var(--accent-ink);
  outline-offset: 3px;
  border-radius: 3px;
}

.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;
  border: 0;
}

.skip-link {
  position: absolute;
  left: var(--s4);
  top: -100px;
  z-index: 100;
  padding: 10px var(--s4);
  background: var(--ink);
  color: var(--paper);
  border-radius: var(--radius-sm);
  text-decoration: none;
  font-size: 0.875rem;
}

.skip-link:focus {
  top: var(--s3);
}

/* ==========================================================================
   3. Typography
   ========================================================================== */

h1 {
  font-size: clamp(2.375rem, 5.2vw, 3.75rem);
  line-height: 1;
  letter-spacing: -0.035em;
  font-weight: 700;
  text-wrap: balance;
}

h2 {
  font-size: clamp(1.5rem, 3vw, 2.125rem);
  line-height: 1.12;
  letter-spacing: -0.028em;
  font-weight: 600;
  text-wrap: balance;
}

h3 {
  font-size: 1.0625rem;
  line-height: 1.35;
  letter-spacing: -0.015em;
  font-weight: 600;
}

a {
  color: var(--accent-ink);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}

a:hover {
  color: var(--accent);
}

strong,
b {
  font-weight: 600;
  color: var(--ink);
}

/* Small uppercase mono labels — column heads, stat captions. The only second
   "face" on the page, and it earns its place on data. */
.mono-label {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The eyebrow is the one green line above a headline. Set in the body face at
   its heaviest rather than in mono: it announces, it does not caption.
   --accent-deep, not --accent-bright: 5.42:1 on white against 3.30:1. */
.eyebrow {
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-deep);
}

.num {
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   4. Layout primitives
   ========================================================================== */

/* --- page ---------------------------------------------------------------- */

.page {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--page-pad);
}

/* --- nav ----------------------------------------------------------------- */

.nav {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--paper);
  border-bottom: 1px solid var(--line);
}

.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s5);
  min-height: var(--nav-h);
}

/* Brand cluster: the glyph and name, then what this site is, in the same
   breath. The tagline is the site's only self-description above the fold. */
.brand {
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}

.brand__tag {
  font-size: 0.8125rem;
  line-height: 1.3;
  color: var(--muted);
  white-space: nowrap;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--ink);
  font-size: 1.0625rem;
  font-weight: 700;
  letter-spacing: -0.022em;
  text-decoration: none;
}

.wordmark:hover {
  color: var(--ink);
}

.wordmark .mark {
  color: var(--accent);
  flex: none;
}

.nav__links {
  display: flex;
  align-items: center;
  gap: clamp(14px, 2.5vw, 28px);
}

.nav__link {
  color: var(--muted);
  font-size: 0.9375rem;
  text-decoration: none;
}

.nav__link:hover {
  color: var(--ink);
}

/* --- hero ---------------------------------------------------------------- */

/* Centred: headline, then the search box that answers it. Nothing competes
   with the field for the middle of the page. */
.hero__inner {
  max-width: 860px;
  padding-block: clamp(48px, 7.5vw, 92px) clamp(24px, 3.4vw, 40px);
  text-align: center;
}

.hero__title {
  margin-top: var(--s4);
  font-size: clamp(2rem, 6.2vw, 3.5rem);
  line-height: 1.08;
  letter-spacing: -0.02em;
  font-weight: 700;
}

/* The three names are the product. They carry the accent; the ampersand stays
   ink so the line still reads as one sentence rather than a logo strip. */
.hero__stores {
  color: var(--accent-deep);
}

.hero__amp {
  color: var(--ink);
}

/* --- proof --------------------------------------------------------------- */

.proof__inner {
  padding-block: clamp(24px, 3.4vw, 40px) clamp(40px, 6vw, 68px);
}

/* The ledger: the hero's claim, rendered as the thing itself. */
.ledger {
  max-width: 720px;
  margin-inline: auto;
}

.ledger__verdict {
  margin-top: var(--s4);
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--muted);
}

.ledger__note {
  margin-top: var(--s3);
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--muted);
}

/* --- stat-row ------------------------------------------------------------ */

.stat-row {
  border-top: 1px solid var(--line);
}

.stat-row__inner {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.stat {
  padding-block: clamp(22px, 3.4vw, 34px);
}

.stat + .stat {
  border-left: 1px solid var(--line);
  padding-left: clamp(20px, 3vw, 40px);
}

.stat__value {
  display: block;
  font-size: clamp(1.5rem, 2.6vw, 2rem);
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}

.stat__label {
  display: block;
  margin-top: var(--s2);
}

.stat-live {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding-bottom: clamp(20px, 3vw, 30px);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  line-height: 1.4;
  color: var(--muted);
}

.pulse {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
}

/* --- feature ------------------------------------------------------------- */

.feature {
  border-top: 1px solid var(--line);
}

/* A jump link lands under a sticky nav otherwise. */
.feature[id] {
  scroll-margin-top: calc(var(--nav-h) + var(--s3));
}

.feature__inner {
  display: grid;
  grid-template-columns: 150px minmax(0, 1fr);
  gap: clamp(20px, 4.5vw, 60px);
  align-items: start;
  padding-block: clamp(44px, 7.5vw, 84px);
}

.feature h2 {
  max-width: 22ch;
}

.feature p {
  margin-top: var(--s4);
  max-width: 62ch;
  font-size: 1.0625rem;
  line-height: 1.62;
  color: var(--ink);
}

.feature p + p {
  color: var(--muted);
}

/* --- closing ------------------------------------------------------------- */

.closing {
  border-top: 1px solid var(--line);
}

.closing__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s5);
  padding-block: clamp(40px, 6vw, 72px);
}

.closing h2 {
  max-width: 24ch;
}

/* --- footer -------------------------------------------------------------- */

.footer {
  border-top: 1px solid var(--line);
  background: var(--wash);
}

.footer__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--s5);
  padding-block: clamp(30px, 4.5vw, 52px);
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--muted);
}

.footer__mark {
  color: var(--ink);
  font-weight: 600;
  letter-spacing: -0.02em;
}

.footer p + p {
  margin-top: var(--s2);
}

/* ==========================================================================
   5. Components
   ========================================================================== */

/* --- btn ----------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  padding: 13px 21px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 0.96875rem;
  font-weight: 500;
  letter-spacing: -0.006em;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.16s ease, border-color 0.16s ease,
    color 0.16s ease;
}

.btn--primary {
  background: var(--accent);
  color: var(--on-accent);
}

.btn--primary:hover {
  background: var(--accent-hover);
  color: var(--on-accent);
}

.btn--quiet {
  border-color: var(--line-strong);
  color: var(--ink);
}

.btn--quiet:hover {
  border-color: var(--ink);
  color: var(--ink);
}

.btn--sm {
  padding: 9px 15px;
  font-size: 0.9062rem;
}

.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.45;
  pointer-events: none;
}

/* --- card ---------------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}

.card__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s4);
  padding: 13px 20px;
  background: var(--wash);
  border-bottom: 1px solid var(--line);
  border-radius: calc(var(--radius-lg) - 1px) calc(var(--radius-lg) - 1px) 0 0;
}

.card__body {
  padding: var(--s5) 20px;
}

/* --- table --------------------------------------------------------------- */

.table-scroll {
  overflow-x: auto;
}

.table {
  width: 100%;
  font-size: 0.9375rem;
  font-variant-numeric: tabular-nums;
}

.table th,
.table td {
  padding-block: 11px;
  text-align: left;
  vertical-align: baseline;
}

.table thead th {
  padding-top: 0;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.table tbody th {
  font-weight: 400;
  color: var(--ink);
  padding-right: var(--s3);
}

.table tbody tr + tr th,
.table tbody tr + tr td {
  border-top: 1px solid var(--line);
}

.table .num {
  text-align: right;
  padding-left: var(--s4);
  white-space: nowrap;
  color: var(--muted);
}

/* Naming .num here rather than td outranks `.table .num`, which would
   otherwise keep the totals muted. Every numeric cell in a .table carries
   .num, so the row rule and the padding land on all of them. */
.table tfoot .num,
.table tfoot th {
  border-top: 2px solid var(--line);
  padding-top: 14px;
  font-size: 1rem;
  font-weight: 600;
  color: var(--ink);
}

/* Cheapest cell in a row. Marked, not highlighted — a rule under the
   figure, the way a shelf ticket underlines the price that matters.
   Restated at tfoot weight so the cheapest total keeps the accent. */
.table .is-low,
.table tfoot .is-low {
  color: var(--accent-ink);
  font-weight: 600;
}

.table .is-low > span {
  border-bottom: 2px solid var(--accent);
  padding-bottom: 2px;
}

/* ==========================================================================
   6. Motion
   ========================================================================== */

.rise {
  animation: rise 0.62s cubic-bezier(0.2, 0.7, 0.2, 1) both;
  animation-delay: var(--d, 0ms);
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.pulse {
  animation: pulse 2.6s ease-in-out infinite;
}

@keyframes pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.35;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .rise,
  .pulse {
    animation: none;
  }

  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
  }
}

/* ==========================================================================
   Responsive
   ========================================================================== */

@media (max-width: 720px) {
  .feature__inner {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s4);
  }

  .footer__inner {
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
  }
}

@media (max-width: 660px) {
  .stat-row__inner {
    grid-template-columns: minmax(0, 1fr);
  }

  .stat {
    padding-block: var(--s4);
  }

  .stat + .stat {
    border-left: 0;
    border-top: 1px solid var(--line);
    padding-left: 0;
  }

  .card__body {
    padding: var(--s4);
  }

  .table {
    font-size: 0.8125rem;
  }

  .table .num {
    padding-left: var(--s2);
  }
}

/* The tagline is the first thing to go when the nav row runs out of room: the
   page under it says the same thing at headline size. */
@media (max-width: 1000px) {
  .brand__tag {
    display: none;
  }
}

@media (max-width: 460px) {
  .nav__links {
    gap: var(--s3);
  }

  /* The hero carries the same link two lines further down — the nav does not
     need to overflow to repeat it. */
  .nav__link {
    display: none;
  }
}

/* ==========================================================================
   7. Dark mode — token overrides only
   Near-black paper, the same greens, contrast held above WCAG AA.
   ========================================================================== */

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;

    --ink: #E9EFEA;
    --paper: #0B0F0C;
    --line: #232A25;
    --muted: #98A69E;
    --line-strong: #6C7A71;

    /* Fills keep the light-mode greens — white on #15803D holds 5.01:1 in
       either theme, because it is a foreground on a fill, not on the page.
       Text-weight green lifts to #22C55E (8.50:1 on --paper); #15803D would
       only reach 3.86:1 there. */
    --accent: #15803D;
    --accent-deep: #22C55E;
    --accent-bright: #22C55E;
    --accent-ink: #22C55E;
    --accent-hover: #1B7A2F;
    --accent-pale: #12241A;

    /* Retailer marks lift too: the brand hexes sit at 4.00 / 4.27 / 1.97:1
       on near-black. These tints hold the hue at 7.1 / 8.2 / 8.8:1. */
    --coles: #FF6E74;
    --woolworths: #3FBF6E;
    --aldi: #7FB3E8;

    --surface: #101510;
    --wash: #151B16;
    --on-accent: #FFFFFF;
  }
}

/* ==========================================================================
   Extension point — app and assistant styles are appended below this line.
   Use the tokens above; do not restate colours per theme.
   ========================================================================== */

/* ==========================================================================
   8. App page  (/app.html)

   The comparison app ships as one script that owns its own markup, so almost
   every class below is a hook that script writes. The names are its; the
   colours, type and spacing are the system's above.

   The through-line from the landing page is the mark on the cheapest figure:
   an accent rule under the number, never a highlighted row. It appears on the
   three-store cells, on the compare totals, and under the current tab.

     8.1  Shell — nav, tab bar, page head
     8.2  Search chrome — field, chips, result meta
     8.3  Results — card grid, image plate, shelf ticket
     8.4  Three-store cells
     8.5  Product sheet — header, price history, actions
     8.6  Views — compare, saved, alerts, settings
     8.7  Overlays — filter sheet, product sheet, swipe deck
     8.8  Motion
   ========================================================================== */

/* --- 8.1 Shell ----------------------------------------------------------- */

/* The app script binds these on boot and the tab bar duplicates both, so they
   are kept in the document and out of the layout. */
.iconbtn {
  display: none;
}

#tabbar {
  display: flex;
  align-items: stretch;
  gap: clamp(0px, 1.2vw, 12px);
}

.tabbtn {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: var(--nav-h);
  padding: 0 var(--s3);
  border: 0;
  background: none;
  color: var(--muted);
  font-family: var(--font-sans);
  font-size: 0.9375rem;
  font-weight: 500;
  letter-spacing: -0.005em;
  cursor: pointer;
  transition: color 0.16s ease;
}

.tabbtn svg {
  display: none;
}

.tabbtn:hover {
  color: var(--ink);
}

.tabbtn.on {
  color: var(--ink);
}

.tabbtn.on::after {
  content: "";
  position: absolute;
  left: var(--s3);
  right: var(--s3);
  bottom: -1px;
  height: 2px;
  background: var(--accent);
}

.tabbtn .bdg {
  display: inline-grid;
  place-items: center;
  min-width: 17px;
  height: 17px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--on-accent);
  font-family: var(--font-sans);
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}

.app {
  padding-block: clamp(26px, 4vw, 44px) clamp(40px, 6vw, 72px);
}

/* --- 8.2 Search chrome --------------------------------------------------- */

#searchHead {
  margin-bottom: var(--s5);
}

/* The field itself is the .bigsearch component in section 10 — shared with the
   landing page, so the two searches are visibly the same control. */

.chips {
  display: flex;
  gap: var(--s2);
  margin-top: var(--s3);
  padding-bottom: 2px;
  overflow-x: auto;
  scrollbar-width: none;
}

.chips::-webkit-scrollbar {
  display: none;
}

.subbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s3);
  margin-top: var(--s4);
  padding-top: var(--s3);
  border-top: 1px solid var(--line);
  overflow: hidden;
  font-size: 0.75rem;
  color: var(--muted);
}

#count {
  flex: none;
}

.subbar .updated {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  padding-left: var(--s3);
  border-left: 1px solid var(--line);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.subbar .updated:empty {
  display: none;
}

.dotpulse {
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  animation: pulse 2.6s ease-in-out infinite;
}

.morebtn {
  position: relative;
  flex: none;
  margin-left: auto;
}

.morebtn svg {
  width: 15px;
  height: 15px;
}

.mdot {
  position: absolute;
  top: -3px;
  right: -3px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
  border: 2px solid var(--paper);
}

/* --- 8.3 Results --------------------------------------------------------- */

.cardgrid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s3);
}

.cardgrid .status {
  grid-column: 1 / -1;
}

.gcard {
  padding: 10px 12px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  cursor: pointer;
  transition: border-color 0.16s ease;
  animation: cardIn 0.36s cubic-bezier(0.2, 0.7, 0.2, 1) both;
  animation-delay: calc(var(--i, 0) * 24ms);
}

.gcard:hover {
  border-color: var(--line-strong);
}

/* The image plate is photo backing, not a page surface: retailer shots are cut
   out on white. It stays white in both themes, so everything painted on top of
   it reads from these three fixed values rather than the tokens that flip. */
.gimg,
.pstile,
.swtile {
  --plate: #ffffff;
  --plate-ink: #1a1d21;
  --plate-muted: #9ca3af;
  position: relative;
  overflow: hidden;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  background: var(--plate);
  color: var(--plate-muted);
  font-weight: 700;
}

.gimg {
  height: 128px;
  margin-bottom: 10px;
  border-radius: var(--radius);
  font-size: 28px;
}

.pstile {
  flex: none;
  width: 88px;
  height: 88px;
  border-radius: var(--radius-lg);
  font-size: 30px;
}

.swtile {
  width: 74px;
  height: 74px;
  margin: 0 auto var(--s3);
  border-radius: var(--radius-lg);
  font-size: 30px;
}

.gpimg {
  position: absolute;
  inset: 6px;
  width: calc(100% - 12px);
  height: calc(100% - 12px);
  object-fit: contain;
  animation: imgIn 0.4s ease;
}

.pstile .gpimg,
.pimg {
  position: absolute;
  inset: 5px;
  width: calc(100% - 10px);
  height: calc(100% - 10px);
  object-fit: contain;
  animation: imgIn 0.4s ease;
}

/* Which shop the headline price came from. */
.sbadge,
.gtag {
  position: absolute;
  top: 7px;
  padding: 3px 7px;
  border-radius: 6px;
  font-family: var(--font-mono);
  font-size: 0.5625rem;
  font-weight: 500;
  letter-spacing: 0.09em;
  line-height: 1.45;
  text-transform: uppercase;
}

.sbadge {
  right: 7px;
  background: var(--plate-ink);
  color: var(--plate);
}

/* Rank flag — only ever on one or two cards in view, so it can be filled. */
.gtag {
  left: 7px;
  background: var(--accent);
  color: var(--on-accent);
}

.plus {
  position: absolute;
  right: 7px;
  bottom: 7px;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border: 1px solid var(--plate-muted);
  border-radius: 50%;
  background: var(--plate);
  color: var(--accent);
  font-size: 19px;
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
}

.plus.in {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 14px;
}

.gname {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.64em;
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 1.32;
}

.gsub {
  margin-top: 3px;
  font-size: 0.6875rem;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.gprice {
  margin-top: var(--s2);
}

.gunit,
.unit {
  margin-top: 3px;
  font-size: 0.6875rem;
  color: var(--muted);
}

.gp.none {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--muted);
}

/* Shelf ticket: big dollars, small raised $ and cents — the catalogue setting,
   built from the system face at its heaviest rather than a display webfont. */
.tkt {
  display: inline-flex;
  align-items: flex-start;
  color: var(--ink);
  font-weight: 700;
  font-variant-numeric: tabular-nums lining-nums;
  letter-spacing: -0.045em;
  line-height: 0.92;
}

.tkt .cur {
  margin: 0.18em 1px 0 0;
  font-size: 0.44em;
  font-weight: 600;
  letter-spacing: 0;
}

.tkt .tc {
  margin: 0.12em 0 0 2px;
  font-size: 0.5em;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.tkt-md {
  font-size: 30px;
}

.tkt-lg {
  font-size: 42px;
}

.tkt-xl {
  font-size: 54px;
}

/* The gap between the dearest and cheapest shop. Outlined, so it reads next to
   a filled rank flag without the two competing. */
.save {
  display: inline-block;
  margin-top: var(--s2);
  padding: 4px 9px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  color: var(--accent-ink);
  font-family: var(--font-mono);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  line-height: 1.3;
  text-transform: uppercase;
}

#loadMore {
  width: 100%;
  margin-top: var(--s5);
}

.status,
.vempty,
.swdone,
.cxload {
  padding: clamp(36px, 7vw, 56px) var(--s4);
  color: var(--muted);
  font-size: 0.9375rem;
  line-height: 1.65;
  text-align: center;
}

.cxload {
  padding: var(--s6) var(--s4);
}

/* The script decorates its empty states with an emoji. The page does not. */
.status .big,
.vempty .big,
.swdone .big {
  display: none;
}

.fineprint {
  margin-top: var(--s6);
  padding-top: var(--s4);
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.75rem;
  line-height: 1.6;
  max-width: 62ch;
}

/* --- 8.4 Three-store cells ----------------------------------------------- */

.grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s2);
}

.cell {
  position: relative;
  padding: var(--s3) var(--s2) 11px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface);
  text-align: center;
}

.cell.best {
  border-color: var(--accent);
  background: var(--wash);
}

.cell .st {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

.cell .amt {
  display: inline-block;
  margin-top: 5px;
  font-size: 1rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Same mark as the landing ledger: a rule under the figure that wins. */
.cell.best .amt {
  padding-bottom: 2px;
  border-bottom: 2px solid var(--accent);
  color: var(--accent-ink);
}

.cell.na .amt {
  border-bottom: 0;
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 500;
  font-style: italic;
}

.cell .was {
  margin-top: 2px;
  font-size: 0.6875rem;
  color: var(--muted);
  text-decoration: line-through;
}

.cell .br {
  margin-top: 3px;
  font-size: 0.6875rem;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cell .flag {
  position: absolute;
  top: -8px;
  left: 50%;
  transform: translateX(-50%);
  padding: 1px 7px;
  border: 1px solid var(--accent);
  border-radius: 999px;
  background: var(--surface);
  color: var(--accent-ink);
  font-family: var(--font-mono);
  font-size: 0.5625rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  white-space: nowrap;
}

.cell .flag.yflag {
  border-color: var(--line-strong);
  color: var(--ink);
}

/* --- 8.5 Product sheet --------------------------------------------------- */

.pshead {
  display: flex;
  gap: var(--s4);
  margin: 6px 0 var(--s4);
}

.psmeta {
  flex: 1;
  min-width: 0;
}

.psname {
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.3;
}

.pssub {
  margin-top: 3px;
  font-size: 0.8125rem;
  color: var(--muted);
}

.psprice {
  margin-top: 7px;
}

.hist {
  margin: var(--s4) 0 var(--s1);
  padding: var(--s3) var(--s4);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--wash);
}

.hist .hrow {
  display: flex;
  align-items: center;
  gap: var(--s3);
}

.spark {
  flex: none;
}

.spark polyline {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.spark .dot {
  fill: var(--accent);
}

.hmeta {
  flex: 1;
  min-width: 0;
}

.hnote {
  font-size: 0.6875rem;
  line-height: 1.5;
  color: var(--muted);
}

/* Buy now, or wait. */
.vpill {
  display: inline-block;
  margin-bottom: 5px;
  padding: 3px 8px;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 0.5625rem;
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.vpill.good {
  background: var(--accent);
  color: var(--on-accent);
}

.vpill.ok {
  box-shadow: inset 0 0 0 1px var(--accent);
  color: var(--accent-ink);
}

.vpill.flat {
  box-shadow: inset 0 0 0 1px var(--line-strong);
  color: var(--muted);
}

.vpill.high {
  background: var(--ink);
  color: var(--paper);
}

.acts {
  display: flex;
  gap: var(--s3);
  margin-top: var(--s4);
}

.addbtn {
  flex: 1;
  padding: 13px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.9375rem;
  font-weight: 500;
  cursor: pointer;
}

.addbtn.in {
  background: var(--wash);
  color: var(--accent-ink);
}

.heart {
  flex: none;
  display: grid;
  place-items: center;
  width: 52px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: none;
  color: var(--muted);
  cursor: pointer;
}

.heart svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
}

.heart.on {
  border-color: var(--accent);
  background: var(--wash);
  color: var(--accent);
}

.heart.on svg {
  fill: currentColor;
}

/* --- 8.6 Views ----------------------------------------------------------- */

/* Compare, alerts and settings are single columns of prose and figures; they
   read at a text measure, not at the full grid width. */
#view-compare,
#view-alerts,
#view-settings {
  max-width: 700px;
}

.vtitle {
  padding-bottom: var(--s1);
  font-size: clamp(1.375rem, 3vw, 1.75rem);
  font-weight: 600;
  letter-spacing: -0.028em;
  line-height: 1.15;
}

.vsub {
  padding-bottom: var(--s1);
  font-size: 0.875rem;
  line-height: 1.55;
  color: var(--muted);
  max-width: 58ch;
}

.vh {
  margin: var(--s6) 0 var(--s2);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
}

.panelcard {
  margin: var(--s3) 0;
  padding: var(--s4) 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
}

.cxhead {
  margin-bottom: var(--s1);
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.cxhead .win {
  color: var(--accent-ink);
}

.cxrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s4);
  padding: 9px 0;
  border-top: 1px solid var(--line);
  font-size: 0.9375rem;
}

.cxrow.first {
  border-top: 0;
}

.cxrow.win {
  font-weight: 600;
}

.cxrow .a {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.cxrow.win .a {
  padding-bottom: 2px;
  border-bottom: 2px solid var(--accent);
  color: var(--accent-ink);
}

.cxrow .na {
  font-size: 0.75rem;
  color: var(--muted);
  white-space: nowrap;
}

.cxfoot,
.ssub,
.setnote,
.pushnote,
.det {
  font-size: 0.75rem;
  line-height: 1.55;
  color: var(--muted);
}

.cxfoot {
  margin-top: var(--s3);
}

/* Smart Split: the one panel that argues for doing something different. */
.split {
  border-color: var(--accent);
  background: var(--wash);
}

.split .stitle {
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--accent-ink);
}

.split .ssub {
  margin: var(--s1) 0 var(--s2);
}

.split .grp {
  margin-top: var(--s3);
}

.split .grpname {
  margin-bottom: var(--s1);
  font-family: var(--font-mono);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--muted);
}

.split .li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  padding: 3px 0;
  font-size: 0.875rem;
}

.split .li .nm,
.bkitem .nm {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.split .li .a {
  flex: none;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.pc .pcname {
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.012em;
}

.pc .pcsub {
  margin: 1px 0 var(--s2);
  font-size: 0.8125rem;
  color: var(--muted);
}

.pc .pcr {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s4);
  padding: 7px 0;
  border-top: 1px solid var(--line);
  font-size: 0.875rem;
}

.pc .pcr .ps {
  text-align: right;
  white-space: nowrap;
}

.pc .pcr .ps .amt {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.pc .pcr .ps .sub {
  font-size: 0.6875rem;
  color: var(--muted);
}

.pc .pcr .natxt {
  font-size: 0.75rem;
  font-style: italic;
  color: var(--muted);
}

.bkitem {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: 9px 0;
  border-top: 1px solid var(--line);
  font-size: 0.875rem;
}

.bkitem.first {
  border-top: 0;
}

.bkitem button {
  flex: none;
  padding: 2px 6px;
  border: 0;
  background: none;
  color: var(--muted);
  font-size: 19px;
  line-height: 1;
  cursor: pointer;
}

.bkitem button:hover {
  color: var(--ink);
}

.clearall,
.pbtn,
.step button {
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: none;
  color: var(--ink);
  cursor: pointer;
}

.clearall {
  width: 100%;
  margin-top: var(--s3);
  padding: 11px;
  font-size: 0.875rem;
  color: var(--muted);
}

.clearall:hover {
  color: var(--ink);
  border-color: var(--ink);
}

.alert {
  display: flex;
  align-items: flex-start;
  gap: var(--s3);
}

.alert .ic {
  flex: none;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: var(--radius-sm);
  background: var(--wash);
  color: var(--accent-ink);
}

.alert .ic svg {
  width: 18px;
  height: 18px;
}

.alert .msg {
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.012em;
}

.alert .det {
  margin-top: 2px;
}

.alert .save {
  margin-top: var(--s2);
}

.setrow,
.pushrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
}

.setrow .k,
.pushrow .k {
  font-size: 0.9375rem;
  font-weight: 500;
}

.setrow .v {
  font-size: 0.875rem;
  color: var(--muted);
}

.step {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: var(--s1);
}

.step button {
  width: 40px;
  height: 38px;
  font-size: 18px;
  line-height: 1;
}

.step button:hover {
  border-color: var(--ink);
}

.step .val {
  min-width: 62px;
  font-size: 0.9375rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.setnote,
.pushnote {
  margin-top: var(--s3);
  padding-top: var(--s3);
  border-top: 1px solid var(--line);
}

.panelcard.tight .setnote:first-child {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

.pbtn {
  flex: none;
  padding: 10px 16px;
  border-color: var(--accent);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.875rem;
  font-weight: 500;
}

.pbtn.off {
  border-color: var(--line-strong);
  background: none;
  color: var(--ink);
}

/* --- 8.7 Overlays -------------------------------------------------------- */

#sheetBack,
#psheetBack {
  position: fixed;
  inset: 0;
  /* A scrim is a shadow cast over the page, so it stays dark in both themes. */
  background: rgba(6, 8, 12, 0.5);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
}

#sheetBack {
  z-index: 55;
}

#psheetBack {
  z-index: 57;
}

body.sheet-open #sheetBack,
body.psheet-open #psheetBack {
  opacity: 1;
  pointer-events: auto;
}

/* Phone: a sheet up from the bottom edge. Desktop: a centred dialog — see the
   min-width rule below. Closed in either idiom means out of the a11y tree. */
#sheet,
#psheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  max-width: 640px;
  max-height: 86vh;
  margin-inline: auto;
  padding: var(--s2) 18px calc(18px + env(safe-area-inset-bottom));
  border: 1px solid var(--line);
  border-bottom: 0;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: var(--surface);
  overflow-y: auto;
  transform: translateY(110%);
  visibility: hidden;
  pointer-events: none;
  transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1),
    visibility 0s linear 0.3s;
}

#sheet {
  z-index: 56;
}

#psheet {
  z-index: 58;
}

body.sheet-open #sheet,
body.psheet-open #psheet {
  transform: none;
  visibility: visible;
  pointer-events: auto;
  transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1), visibility 0s;
}

.grab {
  width: 40px;
  height: 4px;
  margin: 6px auto var(--s3);
  border-radius: 999px;
  background: var(--line-strong);
}

.sheeth {
  display: flex;
  align-items: center;
  margin-bottom: var(--s1);
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.sheeth .reset {
  margin-left: auto;
  padding: 4px;
  border: 0;
  background: none;
  color: var(--muted);
  font-size: 0.8125rem;
  cursor: pointer;
}

.sheeth .reset:hover {
  color: var(--ink);
}

.sheetsec {
  padding: var(--s3) 0;
  border-top: 1px solid var(--line);
}

.sheetsec:first-of-type {
  border-top: 0;
}

.sheetlbl {
  margin-bottom: var(--s2);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
}

.seg {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

.segbtn {
  flex: 1 1 auto;
  min-width: 66px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 9px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: none;
  color: var(--ink);
  font-size: 0.8125rem;
  cursor: pointer;
}

.segbtn:hover {
  border-color: var(--ink);
}

.segbtn.on {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--on-accent);
}

.segbtn .n {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.segbtn.on .n {
  color: var(--on-accent);
  opacity: 0.75;
}

.tog {
  display: flex;
  align-items: center;
  padding: 9px 0;
  font-size: 0.9375rem;
  cursor: pointer;
}

.tog input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.tog .sw {
  flex: none;
  position: relative;
  width: 44px;
  height: 26px;
  margin-left: auto;
  border-radius: 999px;
  background: var(--muted);
  transition: background-color 0.2s ease;
}

.tog .sw::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  /* --on-accent is white in both themes: it is a foreground, not a surface. */
  background: var(--on-accent);
  transition: transform 0.2s ease;
}

.tog input:checked ~ .sw {
  background: var(--accent);
}

.tog input:checked ~ .sw::after {
  transform: translateX(18px);
}

.tog input:focus-visible ~ .sw {
  outline: 2px solid var(--accent-ink);
  outline-offset: 3px;
}

.pricerange {
  display: flex;
  align-items: center;
  gap: var(--s2);
}

.pricerange input {
  flex: 1;
  min-width: 0;
  padding: 10px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font-size: 16px;
  outline: none;
}

.pricerange input:focus {
  border-color: var(--accent);
}

.pricerange input::-webkit-outer-spin-button,
.pricerange input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.pricerange .dash {
  flex: none;
  color: var(--muted);
}

.sheetswipe,
.sheetdone {
  width: 100%;
}

.sheetswipe {
  margin-top: var(--s3);
}

.sheetswipe svg {
  width: 16px;
  height: 16px;
}

.sheetdone {
  margin-top: var(--s2);
}

#psheet .sheetdone {
  margin-top: var(--s4);
}

#swipe {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: none;
  flex-direction: column;
  background: var(--paper);
}

#swipe.show {
  display: flex;
}

.swtop {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: max(var(--s3), env(safe-area-inset-top)) var(--s4) var(--s3);
  border-bottom: 1px solid var(--line);
}

.swtop .ttl {
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.015em;
}

.swtop .prog {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.swtop .x {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: none;
  color: var(--ink);
  cursor: pointer;
}

.swtop .x svg {
  width: 15px;
  height: 15px;
}

.deck {
  flex: 1;
  position: relative;
  display: grid;
  place-items: center;
  padding: var(--s4);
  overflow: hidden;
}

.swcard {
  width: 100%;
  max-width: 380px;
  padding: var(--s5) 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  text-align: center;
  touch-action: pan-y;
  user-select: none;
}

.swname {
  font-size: 1.125rem;
  font-weight: 600;
  letter-spacing: -0.02em;
}

.swsize {
  margin-top: 2px;
  font-size: 0.8125rem;
  color: var(--muted);
}

.swprice {
  margin-top: var(--s3);
}

.swat {
  margin-top: 3px;
  font-size: 0.8125rem;
  color: var(--muted);
}

.swcard .save {
  margin: var(--s2) auto 0;
}

.swcard .grid {
  margin-top: var(--s4);
}

/* Stamped on the deck, not the card: they hold still while the card slides. */
.swhint {
  position: absolute;
  top: 26px;
  padding: 6px 12px;
  border: 2px solid;
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  opacity: 0;
  transition: opacity 0.1s;
}

.swhint.add {
  right: 26px;
  border-color: var(--accent);
  color: var(--accent-ink);
  transform: rotate(12deg);
}

.swhint.skip {
  left: 26px;
  border-color: var(--line-strong);
  color: var(--muted);
  transform: rotate(-12deg);
}

.swactions {
  display: flex;
  gap: var(--s3);
  width: 100%;
  max-width: 412px;
  margin: 0 auto;
  padding: var(--s3) var(--s4) calc(var(--s4) + env(safe-area-inset-bottom));
}

.swactions .btn {
  flex: 1;
}

/* --- 8.8 Motion ---------------------------------------------------------- */

@keyframes cardIn {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes imgIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes skelPulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.45;
  }
}

@media (prefers-reduced-motion: reduce) {
  .gcard,
  .panelcard,
  .gpimg,
  .pimg,
  .skelrow .sk,
  .dotpulse {
    animation: none;
  }
}

/* --- App responsive ------------------------------------------------------ */

@media (min-width: 640px) {
  .cardgrid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--s4);
  }
}

@media (min-width: 900px) {
  .cardgrid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* Desktop: the sheets become centred dialogs rather than phone drawers. */
@media (min-width: 721px) {
  #sheet,
  #psheet {
    top: 50%;
    bottom: auto;
    max-width: 560px;
    max-height: 82vh;
    padding: var(--s5) var(--s5) var(--s5);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: 0 24px 70px rgba(6, 8, 12, 0.28);
    transform: translateY(-50%) scale(0.97);
    opacity: 0;
    transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1),
      opacity 0.18s ease, visibility 0s linear 0.22s;
  }

  body.sheet-open #sheet,
  body.psheet-open #psheet {
    transform: translateY(-50%) scale(1);
    opacity: 1;
    transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1),
      opacity 0.18s ease, visibility 0s;
  }

  .grab {
    display: none;
  }
}

/* Phone: the tab bar leaves the nav row and docks within thumb reach. */
@media (max-width: 720px) {
  #tabbar {
    position: fixed;
    z-index: 50;
    left: var(--s3);
    right: var(--s3);
    bottom: max(var(--s3), env(safe-area-inset-bottom));
    max-width: 560px;
    margin-inline: auto;
    padding: 6px 4px;
    border: 1px solid var(--line);
    border-radius: 22px;
    background: var(--surface);
    box-shadow: 0 10px 30px rgba(6, 8, 12, 0.16);
  }

  .tabbtn {
    flex: 1;
    flex-direction: column;
    gap: 3px;
    min-height: 0;
    padding: 6px 0 4px;
    font-size: 0.5625rem;
    letter-spacing: 0.06em;
  }

  .tabbtn svg {
    display: block;
    width: 22px;
    height: 22px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
  }

  .tabbtn.on {
    color: var(--accent-ink);
  }

  .tabbtn.on svg {
    stroke-width: 2.3;
  }

  .tabbtn.on::after {
    display: none;
  }

  .tabbtn .bdg {
    position: absolute;
    top: 0;
    left: calc(50% + 6px);
  }

  /* Clear the docked tab bar. */
  .footer__inner {
    padding-bottom: calc(84px + env(safe-area-inset-bottom));
  }
}

@media (max-width: 460px) {
  .panelcard {
    padding: var(--s4);
  }

  .grid {
    gap: 6px;
  }

  .cell {
    padding: var(--s3) 5px 10px;
  }
}

/* ==========================================================================
   9. Assistant panel  (/assistant.js)

   A chat panel that only exists when the backend says it does, so every rule
   here is dead weight on a keyless deploy — kept small and token-only.

   The idiom is the docket, not the chat bubble: each turn is a full-width
   block under a mono micro-label, the same label voice the tab bar and the
   page eyebrows use. There are no avatars and no alternating sides — the one
   accent in the panel is the live dot on "checking prices…", which is the
   app's own marker for real data being read.

   Stacking: the toggle and panel sit above the docked phone tab bar (50) and
   below the filter/product sheet backdrops (55+), so opening a sheet still
   covers the assistant rather than fighting it.
   ========================================================================== */

.asst-toggle,
.asst-panel {
  position: fixed;
  right: var(--s5);
}

.asst-toggle {
  bottom: var(--s5);
  z-index: 52;
  padding: 11px 20px;
  border: 1px solid var(--accent);
  border-radius: 999px;
  background: var(--accent);
  color: var(--on-accent);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 10px 30px rgba(6, 8, 12, 0.18);
  transition: background-color 0.16s ease, border-color 0.16s ease;
}

.asst-toggle:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
}

/* The panel takes the corner while it is open; the header's close button and
   Escape both bring the toggle back. */
body.asst-open .asst-toggle {
  display: none;
}

.asst-panel {
  bottom: var(--s5);
  z-index: 53;
  display: flex;
  flex-direction: column;
  width: 360px;
  max-width: calc(100vw - var(--s5) * 2);
  max-height: 70vh;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: 0 24px 70px rgba(6, 8, 12, 0.28);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateY(8px);
  transition: opacity 0.18s ease,
    transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1),
    visibility 0s linear 0.22s;
}

.asst-panel.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: none;
  transition: opacity 0.18s ease,
    transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1), visibility 0s;
}

/* --- head ---------------------------------------------------------------- */

.asst-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  padding: var(--s3) var(--s3) var(--s3) var(--s4);
  border-bottom: 1px solid var(--line);
}

.asst-head__label {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
}

.asst-x {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--muted);
  cursor: pointer;
  transition: color 0.16s ease, background-color 0.16s ease;
}

.asst-x svg {
  width: 16px;
  height: 16px;
}

.asst-x:hover {
  background: var(--wash);
  color: var(--ink);
}

/* --- transcript ---------------------------------------------------------- */

.asst-log {
  flex: 1 1 auto;
  min-height: 0;
  padding: var(--s4);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.asst-intro__lede {
  margin: 0 0 var(--s4);
  font-size: 0.875rem;
  line-height: 1.55;
  color: var(--muted);
}

.asst-starters {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s2);
}

.asst-starter {
  max-width: 100%;
  padding: 9px 14px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: none;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 0.875rem;
  line-height: 1.3;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.16s ease, color 0.16s ease;
}

.asst-starter:hover {
  border-color: var(--accent);
  color: var(--accent-ink);
}

.asst-turn + .asst-turn,
.asst-turn + .asst-status,
.asst-status + .asst-turn {
  margin-top: var(--s4);
}

.asst-turn__who {
  margin: 0 0 5px;
  font-family: var(--font-mono);
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--muted);
}

.asst-turn__text {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--ink);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* What you asked reads back as a quiet quoted block; the answer is set plain
   at full width, so the reply is the thing with weight on the page. */
.asst-turn--user {
  padding: 10px var(--s3);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--wash);
}

.asst-turn--assistant .asst-turn__who {
  color: var(--accent-ink);
}

.asst-turn--error {
  padding-left: var(--s3);
  border-left: 2px solid var(--line-strong);
}

.asst-status {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin: var(--s4) 0 0;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--muted);
}

/* --- composer ------------------------------------------------------------ */

.asst-form {
  display: flex;
  align-items: flex-end;
  gap: var(--s2);
  padding: var(--s3);
  border-top: 1px solid var(--line);
}

.asst-input {
  flex: 1 1 auto;
  min-width: 0;
  max-height: 132px;
  padding: 10px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 16px; /* below 16px iOS zooms the page on focus */
  line-height: 1.4;
  resize: none;
  overflow-y: auto;
}

.asst-input::placeholder {
  color: var(--muted);
}

.asst-send {
  flex: none;
}

/* --- phone: docked above the tab bar ------------------------------------- */

/* Matches the #tabbar breakpoint, not a round number: below it the tab bar is
   fixed to the bottom edge and the panel has to clear it. */
@media (max-width: 720px) {
  :root {
    /* Measured tab bar (62.1px at 390px wide) + its own bottom offset + air.
       66px keeps ~12px of clearance rather than the ~6px a 60px estimate left,
       so a font-size bump in the tab labels does not close the gap. */
    --asst-dock: calc(66px + max(var(--s3), env(safe-area-inset-bottom)) + var(--s2));
  }

  .asst-toggle,
  .asst-panel {
    right: var(--s3);
  }

  .asst-toggle {
    bottom: var(--asst-dock);
  }

  /* Full width within the same gutters and max-width the tab bar uses, so the
     sheet and the bar read as one docked stack. */
  .asst-panel {
    left: var(--s3);
    bottom: var(--asst-dock);
    width: auto;
    max-width: 560px;
    margin-inline: auto;
    max-height: min(70vh, calc(100vh - var(--asst-dock) - var(--nav-h) - var(--s4)));
    max-height: min(70vh, calc(100dvh - var(--asst-dock) - var(--nav-h) - var(--s4)));
    transform: translateY(12px);
  }

  .asst-panel.is-open {
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .asst-panel,
  .asst-panel.is-open {
    transform: none;
  }

  /* Section 6 forces `transition-duration: 0.01ms !important` on every element
     and `transition-property` defaults to `all`, so the panel AND everything
     inside it get a transition on the visibility they inherit. A discrete
     transition still reads as its from-value at t=0, so without this the
     textarea is technically still hidden — and therefore unfocusable — at the
     instant openPanel() calls focus(), and focus falls back to <body>.
     !important is what it takes to outrank that global rule. */
  .asst-panel,
  .asst-panel.is-open,
  .asst-panel * {
    transition: none !important;
  }
}

/* ==========================================================================
   10. Search block — brand marks, the big field, category chips

   One control, two pages. The landing form GETs to /app so a search from the
   home page arrives as /app?q=…; the app form is intercepted by its own
   script. They are deliberately the same object on screen.
   ========================================================================== */

/* --- retailer wordmarks --------------------------------------------------
   Styled text, never a trademark image. Each one restates family, tracking
   and case because it is dropped into table heads that set their own. */

.mark-coles,
.mark-woolworths,
.mark-aldi {
  font-family: var(--font-sans);
  font-size: 0.8125rem;
  line-height: 1.2;
  white-space: nowrap;
}

.mark-coles {
  color: var(--coles);
  font-weight: 800;
  letter-spacing: -0.02em;
  text-transform: lowercase;
}

.mark-woolworths {
  color: var(--woolworths);
  font-weight: 700;
  letter-spacing: -0.015em;
  text-transform: none;
}

.mark-aldi {
  color: var(--aldi);
  font-weight: 800;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

/* --- the field ----------------------------------------------------------- */

.bigsearch,
#searchForm {
  display: flex;
  gap: var(--s3);
  width: 100%;
  max-width: 720px;
  margin-inline: auto;
  text-align: left;
}

.bigsearch {
  margin-top: var(--s6);
}

.bigsearch__field,
.searchbox {
  position: relative;
  flex: 1;
  min-width: 0;
}

.bigsearch__icon,
.searchbox > svg {
  position: absolute;
  left: 16px;
  top: 50%;
  transform: translateY(-50%);
  width: 18px;
  height: 18px;
  color: var(--muted);
  pointer-events: none;
}

.bigsearch__field input,
#q {
  width: 100%;
  padding: 15px 16px 15px 46px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 16px; /* below 16px iOS zooms the page on focus */
  line-height: 1.4;
  appearance: none;
  outline: none;
  /* A shadow, not a glow: it lifts the field off the page by one step. */
  box-shadow: 0 1px 2px rgba(16, 24, 20, 0.05), 0 6px 16px rgba(16, 24, 20, 0.05);
  transition: border-color 0.16s ease, box-shadow 0.16s ease;
}

.bigsearch__field input::placeholder,
#q::placeholder {
  color: var(--muted);
}

.bigsearch__field input:focus,
#q:focus {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}

.bigsearch__field input::-webkit-search-cancel-button {
  appearance: none;
}

.bigsearch .btn,
#searchForm .btn {
  flex: none;
  padding: 15px 20px;
  border-radius: var(--radius-sm);
  font-weight: 600;
}

/* --- chips --------------------------------------------------------------- */

/* Landing: a centred wrapping row. The app keeps its own .chips scroller. */
.chiprow {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s2);
  margin-top: var(--s4);
}

.chip {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 14px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1.3;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: border-color 0.16s ease, color 0.16s ease,
    background-color 0.16s ease;
}

.chip__icon {
  flex: none;
  width: 15px;
  height: 15px;
  color: var(--muted);
  transition: color 0.16s ease;
}

.chip:hover {
  color: var(--ink);
  border-color: var(--line-strong);
}

.chip:hover .chip__icon {
  color: var(--accent-ink);
}

/* The chip whose term is on screen right now. */
.chip.on {
  border-color: var(--accent);
  background: var(--accent-pale);
  color: var(--accent-ink);
  font-weight: 600;
}

.chip.on .chip__icon {
  color: var(--accent-ink);
}

@media (max-width: 560px) {
  .bigsearch,
  #searchForm {
    flex-direction: column;
  }

  .bigsearch .btn,
  #searchForm .btn {
    width: 100%;
  }

  /* The ledger runs three priced columns next to a wrapping item name. The
     marks come down a step so the whole trolley fits a 360px screen without
     the reader having to scroll the table sideways to find ALDI. */
  .mark-coles,
  .mark-woolworths,
  .mark-aldi {
    font-size: 0.6875rem;
  }
}

/* ==========================================================================
   11. App search composition  (/app.html, search view)

   The results are a price table inside one card, with the savings figure
   parked beside it. Below 1100px the table restacks: every row becomes its
   own card, and each price carries the retailer mark the column head used to.
   ========================================================================== */

/* --- the app's own hero, shown only before the first result --------------- */

/* The app is a tool, not a front page: its search head starts at the same
   edge as the table under it rather than centring like the landing hero. */
#searchForm {
  margin-inline: 0;
}

#chips {
  justify-content: flex-start;
}

.apphero {
  margin-bottom: var(--s5);
}

.apphero__title {
  margin-top: 10px;
  font-size: clamp(1.5rem, 3.6vw, 2.125rem);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.14;
  text-wrap: balance;
}

/* --- two columns --------------------------------------------------------- */

.searchgrid {
  display: grid;
  gap: var(--s5);
}

@media (min-width: 1100px) {
  .searchgrid {
    grid-template-columns: minmax(0, 1fr) 320px;
    gap: 24px;
    align-items: start;
  }
}

.searchgrid__main {
  min-width: 0;
}

/* --- the price card ------------------------------------------------------ */

.pricecard {
  padding: 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}

.pricecard__head {
  margin-bottom: var(--s4);
}

.pricecard__title {
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.pricecard__sub {
  margin-top: 4px;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--muted);
}

/* Column heads only make sense over rows; the "no products match these
   filters" note is not a row, so the card drops them for it. */
.pthead {
  display: none;
}

.pricecard.has-rows .pthead {
  display: grid;
}

.pthead,
.prow,
.skelrow {
  grid-template-columns: minmax(180px, 1fr) repeat(3, minmax(56px, 84px)) 112px;
  gap: 10px;
  align-items: center;
}

.pthead {
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
}

.pth {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  line-height: 1.2;
  text-transform: uppercase;
  color: var(--muted);
}

.pth--best {
  text-align: right;
}

/* --- a product row ------------------------------------------------------- */

.prow {
  display: grid;
  padding: 14px 0;
  cursor: pointer;
  transition: background-color 0.16s ease;
}

.prow + .prow {
  border-top: 1px solid var(--line);
}

.prow:hover {
  background: var(--wash);
}

.pcell-prod {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

/* The thumbnail plate stays white in both themes: retailer shots are cut out
   on white. The initial sits under the photo, so a missing or broken image
   leaves a lettered tile rather than a hole. */
.pthumb {
  position: relative;
  flex: none;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: #ffffff;
  color: #9ca3af;
  font-size: 0.9375rem;
  font-weight: 700;
}

.pthumb img {
  position: absolute;
  inset: 3px;
  width: calc(100% - 6px);
  height: calc(100% - 6px);
  object-fit: contain;
}

.pmeta {
  min-width: 0;
}

.pname {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 1.3;
}

.punit {
  margin-top: 2px;
  font-size: 0.75rem;
  line-height: 1.35;
  color: var(--muted);
}

.pcell-store {
  position: relative; /* the hidden retailer label clips against this cell */
  min-width: 0;
}

/* Which retailer this price belongs to. On desktop the column head says it, so
   the label is hidden from the page but NOT from a screen reader — display:none
   would take the only attribution a row has out of the accessibility tree. */
.pcell-store .lbl {
  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;
  border: 0;
}

.pcell-store .amt {
  font-size: 0.9375rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

.pcell-store .u {
  margin-top: 2px;
  font-size: 0.6875rem;
  line-height: 1.3;
  color: var(--muted);
}

.pcell-store .none {
  font-size: 0.75rem;
  color: var(--muted);
}

.pcell-best {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--s2);
}

/* The one filled green object in the results. White on --accent is 5.01:1 in
   both themes — --accent-bright would only reach 3.30:1 under 12px bold. */
.pbadge {
  display: inline-block;
  padding: 5px 9px;
  border-radius: 6px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  line-height: 1.2;
  text-transform: uppercase;
  white-space: nowrap;
}

.pcell-best .none {
  font-size: 0.75rem;
  color: var(--muted);
}

.pchev {
  flex: none;
  width: 14px;
  height: 14px;
  color: var(--muted);
}

/* Loading: the same row shape, so nothing jumps when the prices land. */
.skelrow {
  display: grid;
  padding: 14px 0;
}

.skelrow + .skelrow {
  border-top: 1px solid var(--line);
}

.skelrow .sk {
  height: 12px;
  border-radius: var(--radius-sm);
  background: var(--line);
  animation: skelPulse 1.4s ease-in-out infinite;
}

.skelrow .sk.tall {
  width: 40px;
  height: 40px;
  border-radius: 6px;
}

/* --- stacked below 1100px ------------------------------------------------- */

@media (max-width: 1099.98px) {
  /* The rows become the cards, so the card around them steps back. */
  .pricecard {
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
  }

  .pricecard__head {
    margin-bottom: var(--s3);
  }

  .pricecard.has-rows .pthead {
    display: none;
  }

  .prow,
  .skelrow {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 8px;
    padding: 14px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
  }

  .prow + .prow,
  .skelrow + .skelrow {
    margin-top: 10px;
  }

  .pcell-prod {
    flex: 1 0 100%;
  }

  .pcell-store {
    flex: 1 1 0;
    padding: 8px 7px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
  }

  /* Stacked: the column head is gone, so the label comes back on screen. */
  .pcell-store .lbl {
    position: static;
    width: auto;
    height: auto;
    margin: 0 0 3px;
    overflow: visible;
    clip: auto;
    clip-path: none;
    font-size: 0.6875rem;
  }

  .pcell-best {
    flex: 1 0 100%;
    justify-content: space-between;
  }
}

/* --- the savings card ---------------------------------------------------- */

.savings {
  align-self: start;
  padding: 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}

.savings__title {
  font-size: 1.125rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.25;
}

.savings__lede {
  margin-top: 10px;
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--muted);
}

/* The number is the point of this card, so it is the largest thing in it. */
.savings__figure {
  margin-top: 4px;
  font-size: 40px;
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.05;
  color: var(--accent-deep);
  font-variant-numeric: tabular-nums;
}

.savings__count {
  margin-top: 4px;
  font-size: 0.8125rem;
  color: var(--muted);
}

.savings__rows {
  margin-top: var(--s4);
  padding-top: var(--s4);
  border-top: 1px solid var(--line);
}

.savings__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s3);
  font-size: 0.875rem;
  color: var(--muted);
}

.savings__row + .savings__row {
  margin-top: var(--s2);
}

.savings__row .a {
  flex: none;
  color: var(--ink);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.savings__panel {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-top: var(--s4);
  padding: 14px;
  border-radius: 10px;
  background: var(--accent-pale);
}

.savings__panel svg {
  flex: none;
  width: 18px;
  height: 18px;
  margin-top: 1px;
  color: var(--accent-ink);
}

.savings__panelTitle {
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--ink);
}

.savings__panelNote {
  margin-top: 3px;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--muted);
}
