/* ==========================================================================
   Halal-Scanner

   Visual idea: a field instrument. Something you pull out under bad supermarket
   lighting, hold in one hand, and read at a glance. Dark by default because the
   camera viewport dominates the screen and a bright chrome around live video is
   unpleasant; light mode follows the system when asked for.

   Rules that are not negotiable here:
   - Verdict is never carried by colour alone. Icon + word + fill pattern, always.
   - Nothing important sits above the thumb line on a phone.
   - Every interactive target is at least 44px on its short edge.
   ========================================================================== */

/* --------------------------------------------------------------- tokens -- */

:root {
  color-scheme: dark light;

  --bg: #0b0c0e;
  --bg-elev: #131519;
  --bg-elev-2: #1a1d23;
  --bg-sunken: #08090b;
  --line: #23272e;
  --line-strong: #343943;
  --ink: #eceae6;
  --ink-dim: #a3a9b3;
  --ink-faint: #6e747d;
  --accent: #d8a340;
  --accent-ink: #17130a;

  --halal: #45a46b;
  --mushbooh: #d99a2b;
  --haram: #e2564d;
  --unknown: #7f858f;

  --halal-wash: rgba(69, 164, 107, 0.14);
  --mushbooh-wash: rgba(217, 154, 43, 0.15);
  --haram-wash: rgba(226, 86, 77, 0.15);
  --unknown-wash: rgba(127, 133, 143, 0.14);

  --font-body: 'IBM Plex Sans', ui-sans-serif, system-ui, sans-serif;
  --font-head: 'IBM Plex Sans Condensed', 'IBM Plex Sans', ui-sans-serif, system-ui, sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'SFMono-Regular', monospace;

  --radius: 6px;
  --radius-lg: 10px;
  --gap: 14px;
  --pad: 16px;
  --topbar-h: 54px;
  --footer-h: auto;

  --shadow: 0 1px 0 rgba(255, 255, 255, 0.03) inset, 0 8px 24px -12px rgba(0, 0, 0, 0.8);
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

@media (prefers-color-scheme: light) {
  :root {
    --bg: #f2f0ec;
    --bg-elev: #ffffff;
    --bg-elev-2: #faf8f5;
    --bg-sunken: #e7e4de;
    --line: #dcd7cf;
    --line-strong: #c2bcb1;
    --ink: #15171b;
    --ink-dim: #5a606a;
    --ink-faint: #868c95;
    --accent: #9a6a10;
    --accent-ink: #fffdf7;

    --halal: #1f7a45;
    --mushbooh: #99680a;
    --haram: #b7362e;
    --unknown: #5d636d;

    --halal-wash: rgba(31, 122, 69, 0.1);
    --mushbooh-wash: rgba(153, 104, 10, 0.12);
    --haram-wash: rgba(183, 54, 46, 0.1);
    --unknown-wash: rgba(93, 99, 109, 0.1);

    --shadow: 0 1px 2px rgba(20, 18, 14, 0.06), 0 8px 24px -16px rgba(20, 18, 14, 0.3);
  }
}

/* ----------------------------------------------------------------- base -- */

*,
*::before,
*::after { box-sizing: border-box; }

/* Most components below set an explicit `display`, which outranks the user-agent
   `[hidden] { display: none }` rule. Without this, toggling `.hidden` in JS does
   nothing at all — so it is restated here, once, with enough weight to win. */
[hidden] { display: none !important; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: none;

  /* Make it behave like an installed app rather than a web page: no caret drag on
     labels, no long-press "Search Google for…" callout, no 300ms double-tap-zoom
     delay. Selection is re-enabled below only where copying is genuinely useful. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  touch-action: manipulation;
}

/* The raw JSON is the one thing people legitimately want to select and copy. */
details.raw pre,
.entry__input,
.textarea {
  -webkit-user-select: text;
  user-select: text;
  -webkit-touch-callout: default;
}

img { -webkit-user-drag: none; user-drag: none; }

/* Flex and grid children default to min-width:auto, which means they refuse to
   shrink below their content's intrinsic width. One long unbreakable string then
   pushes the whole layout wider than the screen and the page starts scrolling
   sideways. Opting every child out of that is the fix; wide content is given its
   own scroll or wrap treatment where it occurs. */
.panel > *,
.view > *,
.card > *,
.match__body > *,
.product > * { min-width: 0; }

details.raw { overflow: hidden; }

.taglist li { overflow-wrap: anywhere; }

/* A faint dot grid, so large dark areas read as a surface rather than a void. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background-image: radial-gradient(circle at 1px 1px, currentColor 1px, transparent 0);
  background-size: 22px 22px;
  color: var(--ink);
  opacity: 0.025;
}

h1, h2, h3, h4 {
  font-family: var(--font-head);
  font-weight: 700;
  letter-spacing: -0.005em;
  margin: 0;
}

p { margin: 0; }
ul { margin: 0; padding: 0; list-style: none; }

a {
  color: var(--ink);
  text-decoration-color: var(--line-strong);
  text-underline-offset: 3px;
}
a:hover { text-decoration-color: currentColor; }

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.sprite symbol { fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }

.icon {
  width: 20px;
  height: 20px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.icon--sm { width: 15px; height: 15px; }
.icon--lg { width: 28px; height: 28px; }

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

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

/* ----------------------------------------------------------------- shell -- */

.app {
  position: relative;
  z-index: 1;
  /* svh, not dvh. The dynamic viewport height changes as Chrome's URL bar collapses
     during scroll, which reflows everything below — and for the camera box that
     reflow re-crops an object-fit: cover video, i.e. it looks like the camera is
     zooming itself in and out. svh is the stable one. */
  min-height: 100svh;
  display: grid;
  /* minmax(0, 1fr), not the implicit `auto` column. An auto-sized grid track takes
     its minimum from its content's min-content width, and that minimum is NOT
     capped by the container — so a single long unbreakable string (a product name
     with white-space: nowrap) inflates this whole shell wider than the screen and
     the page starts scrolling sideways. This is the grid equivalent of putting
     min-width: 0 on a flex child, and it fixes the class of bug rather than one
     instance of it. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto auto;

  /* Safety net, not a fix: if anything ever gets wider than the screen again, clip
     it rather than letting the whole page scroll sideways. `clip` and not `hidden`
     on purpose — `hidden` would make this a scroll container and break the sticky
     top bar. The real fixes are min-width: 0 on flex children and never leaving
     `white-space: nowrap` on an inline box. */
  overflow-x: clip;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 6px;
  height: calc(var(--topbar-h) + env(safe-area-inset-top));
  padding: env(safe-area-inset-top) max(10px, env(safe-area-inset-right)) 0 max(10px, env(safe-area-inset-left));
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 1px solid var(--line);
}

.topbar__title {
  display: flex;
  align-items: center;
  gap: 9px;
  min-width: 0;
  flex: 1;
}

.topbar__title h1 {
  font-size: 17px;
  letter-spacing: 0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A small aperture mark: two arcs and a dot. Reads as "optical instrument". */
.topbar__mark {
  width: 18px;
  height: 18px;
  flex: none;
  border: 2px solid var(--accent);
  border-radius: 50%;
  position: relative;
}
.topbar__mark::after {
  content: '';
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0.35;
}

.topbar__actions { display: flex; gap: 2px; }

.icon-btn {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  color: var(--ink-dim);
  cursor: pointer;
  transition: color 0.15s var(--ease), background 0.15s var(--ease);
}
.icon-btn:hover { color: var(--ink); background: var(--bg-elev); }
.icon-btn:active { transform: scale(0.94); }
.icon-btn[aria-current='true'] { color: var(--accent); }

.views { position: relative; min-height: 0; min-width: 0; }

.view {
  display: flex;
  flex-direction: column;
  min-height: 100%;
  min-width: 0;
  animation: view-in 0.22s var(--ease) both;
}
.view[hidden] { display: none; }

@keyframes view-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

.panel {
  padding: var(--pad);
  padding-left: max(var(--pad), env(safe-area-inset-left));
  padding-right: max(var(--pad), env(safe-area-inset-right));
  display: flex;
  flex-direction: column;
  gap: var(--gap);
}

.page-head { font-size: 24px; letter-spacing: -0.01em; }
.lead { color: var(--ink-dim); font-size: 14px; }

.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  font-family: var(--font-head);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.disclaimer {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 9px max(12px, env(safe-area-inset-left)) calc(9px + env(safe-area-inset-bottom)) max(12px, env(safe-area-inset-right));
  border-top: 1px solid var(--line);
  background: var(--bg-sunken);
  color: var(--ink-faint);
  font-size: 11px;
  line-height: 1.4;
}
.disclaimer .icon { margin-top: 1px; color: var(--ink-faint); }

/* --------------------------------------------------------------- buttons -- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 46px;
  padding: 0 16px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--bg-elev);
  color: var(--ink);
  font: 600 15px/1 var(--font-body);
  cursor: pointer;
  transition: transform 0.12s var(--ease), background 0.15s var(--ease), border-color 0.15s var(--ease);
}
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

.btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}
.btn--primary:hover { filter: brightness(1.07); }

.btn--ghost { background: transparent; color: var(--ink-dim); }
.btn--ghost:hover { background: var(--bg-elev); color: var(--ink); }

.btn--danger-ghost { background: transparent; color: var(--haram); border-color: color-mix(in srgb, var(--haram) 45%, transparent); }
.btn--danger-ghost:hover { background: var(--haram-wash); }

.btn--block { width: 100%; }
.btn--lg { min-height: 52px; font-size: 16px; }

.link-btn {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 6px 2px;
  border: 0;
  background: none;
  color: var(--ink-dim);
  font: 600 11px/1 var(--font-head);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  cursor: pointer;
}
.link-btn:hover { color: var(--ink); }
.link-btn .icon { transform: rotate(-90deg); }

.chip-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 40px;
  padding: 0 13px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  background: rgba(10, 11, 13, 0.66);
  backdrop-filter: blur(8px);
  color: #f2f0ec;
  font: 600 13px/1 var(--font-body);
  cursor: pointer;
}
.chip-btn[aria-pressed='true'] { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }

/* -------------------------------------------------------------- viewport -- */

.viewport {
  position: relative;
  aspect-ratio: 4 / 3;
  max-height: 52svh;
  background: #000;
  border-bottom: 1px solid var(--line);
  overflow: hidden;
  display: grid;
  place-items: center;
}

.viewport__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #000;
}

.viewport__idle {
  position: relative;
  z-index: 2;
  display: grid;
  justify-items: center;
  gap: 14px;
  padding: 20px;
  text-align: center;
}
.viewport__idle-icon {
  width: 40px; height: 40px;
  fill: none; stroke: var(--ink-faint); stroke-width: 1.4;
  stroke-linecap: round; stroke-linejoin: round;
}
.viewport__engine { color: var(--ink-faint); font: 400 11.5px/1.4 var(--font-mono); }

.reticle {
  position: absolute;
  z-index: 2;
  width: min(74%, 320px);
  aspect-ratio: 5 / 3;
  pointer-events: none;
}
.reticle__corner {
  position: absolute;
  width: 26px; height: 26px;
  border: 2.5px solid var(--accent);
}
.reticle__corner--tl { top: 0; left: 0; border-right: 0; border-bottom: 0; }
.reticle__corner--tr { top: 0; right: 0; border-left: 0; border-bottom: 0; }
.reticle__corner--bl { bottom: 0; left: 0; border-right: 0; border-top: 0; }
.reticle__corner--br { bottom: 0; right: 0; border-left: 0; border-top: 0; }
.reticle__beam {
  position: absolute;
  left: 6%; right: 6%;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  animation: sweep 2.6s var(--ease) infinite;
  opacity: 0.85;
}
@keyframes sweep {
  0%, 100% { top: 12%; }
  50%      { top: 88%; }
}
@media (prefers-reduced-motion: reduce) {
  .reticle__beam { animation: none; top: 50%; }
  .view { animation: none; }
}

.viewport__hint {
  position: absolute;
  z-index: 3;
  bottom: 12px;
  padding: 5px 12px;
  border-radius: 999px;
  background: rgba(10, 11, 13, 0.7);
  backdrop-filter: blur(6px);
  color: #e9e7e3;
  font-size: 12.5px;
}

.viewport__controls {
  position: absolute;
  z-index: 4;
  top: 10px;
  right: 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-end;
}

/* ----------------------------------------------------------------- entry -- */

.entry { display: grid; gap: 7px; }
.entry__label {
  font-family: var(--font-head);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.entry__row { display: flex; gap: 8px; }

.entry__input,
.textarea {
  flex: 1;
  min-width: 0;
  padding: 12px 13px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--bg-sunken);
  color: var(--ink);
  font: 500 16px/1.4 var(--font-mono);
  letter-spacing: 0.04em;
}
.entry__input::placeholder,
.textarea::placeholder { color: var(--ink-faint); letter-spacing: normal; }
.entry__input:focus,
.textarea:focus { outline: none; border-color: var(--accent); }

.textarea {
  width: 100%;
  font-family: var(--font-body);
  /* Never drop below 16px. Mobile browsers zoom the whole page in when a focused
     field is smaller than that, and the user cannot zoom back out. */
  font-size: 16px;
  letter-spacing: normal;
  resize: vertical;
  min-height: 150px;
}

.entry__note { min-height: 1em; font-size: 12.5px; color: var(--ink-faint); }
.entry__note[data-tone='warn'] { color: var(--mushbooh); }
.entry__note[data-tone='error'] { color: var(--haram); }

/* ---------------------------------------------------------------- verdict -- */

.verdict {
  position: sticky;
  top: calc(var(--topbar-h) + env(safe-area-inset-top));
  z-index: 20;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 14px;
  align-items: center;
  padding: 18px max(var(--pad), env(safe-area-inset-left));
  border-bottom: 1px solid var(--line);
  overflow: hidden;
  /* Opaque base. The state washes below are translucent by design, and this bar is
     sticky — without something solid underneath, the match list scrolls through it. */
  background-color: var(--bg);
}

/* Each state gets a different fill *texture* as well as a different colour and
   icon, so the four banners still separate from one another on a greyscale
   screen or for a colour-blind reader:
     haram    steep hatch     mushbooh  shallow hatch
     halal    flat wash       unknown   dot field                              */
.verdict--haram {
  color: var(--haram);
  background-image:
    repeating-linear-gradient(135deg, color-mix(in srgb, currentColor 22%, transparent) 0 3px, transparent 3px 9px),
    linear-gradient(var(--haram-wash), var(--haram-wash));
}
.verdict--mushbooh {
  color: var(--mushbooh);
  background-image:
    repeating-linear-gradient(45deg, color-mix(in srgb, currentColor 20%, transparent) 0 2px, transparent 2px 10px),
    linear-gradient(var(--mushbooh-wash), var(--mushbooh-wash));
}
.verdict--halal {
  color: var(--halal);
  background-image: linear-gradient(var(--halal-wash), var(--halal-wash));
}
.verdict--unknown {
  color: var(--unknown);
  background-image:
    radial-gradient(circle at 1.5px 1.5px, color-mix(in srgb, currentColor 30%, transparent) 1.5px, transparent 0),
    linear-gradient(var(--unknown-wash), var(--unknown-wash));
  background-size: 9px 9px, auto;
}

.verdict__icon { grid-row: 1 / 3; width: 40px; height: 40px; stroke-width: 1.8; }
.verdict__word {
  font-family: var(--font-head);
  font-size: clamp(28px, 9vw, 40px);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.015em;
  text-transform: uppercase;
}
.verdict__sub { color: var(--ink-dim); font-size: 13.5px; }

.verdict__why {
  grid-column: 1 / -1;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid color-mix(in srgb, currentColor 25%, transparent);
  color: var(--ink-dim);
  font-size: 13px;
  line-height: 1.45;
}

/* ------------------------------------------------------------ halal claim -- */

/* Sits immediately under the verdict banner and is meant to compete with it for
   attention, because when a packet says halal and the rules still found something,
   that tension IS the answer — not a footnote to it. */
.claim {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 15px max(var(--pad), env(safe-area-inset-left));
  border-bottom: 1px solid var(--line);
  border-left: 4px solid var(--halal);
  background: var(--halal-wash);
}
.claim__head { display: flex; align-items: center; gap: 9px; }
.claim__icon { width: 22px; height: 22px; flex: none; color: var(--halal); }
.claim__title {
  font-family: var(--font-head);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--halal);
}
.claim__lead { font-size: 14.5px; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.claim__body { font-size: 13px; line-height: 1.5; color: var(--ink-dim); }

/* ------------------------------------------------------------------ cards -- */

.card {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--bg-elev);
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.card__head {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.card__body { font-size: 14px; color: var(--ink-dim); }
.card__actions { display: flex; flex-wrap: wrap; gap: 8px; }

.notice {
  display: flex;
  gap: 10px;
  padding: 12px 13px;
  border: 1px solid var(--line);
  border-left-width: 3px;
  border-radius: var(--radius);
  background: var(--bg-elev);
  font-size: 13.5px;
  line-height: 1.45;
}
.notice .icon { flex: none; margin-top: 1px; }
.notice--warn  { border-left-color: var(--mushbooh); color: var(--ink-dim); }
.notice--warn .icon { color: var(--mushbooh); }
.notice--info  { border-left-color: var(--accent); color: var(--ink-dim); }
.notice--info .icon { color: var(--accent); }
.notice--halal { border-left-color: var(--halal); color: var(--ink-dim); }
.notice--halal .icon { color: var(--halal); }
.notice strong { color: var(--ink); font-weight: 600; }

/* ------------------------------------------------------------- match list -- */

.matches { display: flex; flex-direction: column; gap: 9px; }

.match {
  border: 1px solid var(--line);
  border-left: 3px solid var(--unknown);
  border-radius: var(--radius);
  background: var(--bg-elev);
  overflow: hidden;
}
.match--haram    { border-left-color: var(--haram); }
.match--mushbooh { border-left-color: var(--mushbooh); }
.match--halal    { border-left-color: var(--halal); }

.match__summary {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 13px;
  cursor: pointer;
  list-style: none;
  min-height: 48px;
}
.match__summary::-webkit-details-marker { display: none; }
.match__summary .icon:first-child { flex: none; }
.match--haram    .match__summary > .icon:first-child { color: var(--haram); }
.match--mushbooh .match__summary > .icon:first-child { color: var(--mushbooh); }
.match--halal    .match__summary > .icon:first-child { color: var(--halal); }

/* Title above, badges below and wrapping. See ui.js for why this is two rows. */
.match__head {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 7px;
  align-items: flex-start;
}
.match__title {
  min-width: 0;
  font-weight: 600;
  font-size: 14.5px;
  line-height: 1.3;
  overflow-wrap: anywhere;
}
.match__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  max-width: 100%;
}
.match__chevron { color: var(--ink-faint); transition: transform 0.2s var(--ease); }
.match[open] .match__chevron { transform: rotate(180deg); }

.match__body {
  padding: 0 13px 13px;
  display: flex;
  flex-direction: column;
  gap: 11px;
  border-top: 1px solid var(--line);
  padding-top: 12px;
  margin-top: -1px;
}
.match__reason { font-size: 13.5px; color: var(--ink-dim); line-height: 1.5; }

.evidence { display: flex; flex-direction: column; gap: 6px; }
.evidence__row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 12.5px;
}
.evidence__text {
  font-family: var(--font-mono);
  font-size: 12.5px;
  padding: 2px 6px;
  border-radius: 3px;
  background: var(--bg-sunken);
  border: 1px solid var(--line);
  word-break: break-word;
}

.tier {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 7px;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  font: 600 10.5px/1.6 var(--font-head);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-dim);
  white-space: nowrap;
}
.tier--1 { border-color: color-mix(in srgb, var(--accent) 60%, transparent); color: var(--accent); }
.tier--2 { border-color: var(--line-strong); color: var(--ink-dim); }
.tier--3 { border-style: dashed; color: var(--ink-faint); }
.tier__pips { display: inline-flex; gap: 2px; }
.tier__pips i { width: 4px; height: 4px; border-radius: 50%; background: currentColor; opacity: 0.3; }
.tier__pips i.on { opacity: 1; }

.flag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 7px;
  border-radius: 999px;
  font: 600 10.5px/1.55 var(--font-head);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* Not nowrap: these chips carry sentences like "durch Vegetarisch-Kennzeichnung
     entschärft", and refusing to wrap is what broke the card layout. */
  max-width: 100%;
  overflow-wrap: anywhere;
}
.flag--edited { background: color-mix(in srgb, var(--accent) 20%, transparent); color: var(--accent); }
.flag--vegan  { background: var(--halal-wash); color: var(--halal); }

.override { display: flex; flex-direction: column; gap: 7px; padding-top: 10px; border-top: 1px dashed var(--line); }
.override__label {
  font: 600 10.5px/1.6 var(--font-head);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.segmented {
  display: flex;
  gap: 0;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  overflow: hidden;
}
.segmented button {
  flex: 1;
  min-height: 42px;
  padding: 0 8px;
  border: 0;
  border-right: 1px solid var(--line-strong);
  background: var(--bg-elev-2);
  color: var(--ink-dim);
  font: 600 13px/1 var(--font-body);
  cursor: pointer;
}
.segmented button:last-child { border-right: 0; }
.segmented button[aria-pressed='true'] { background: var(--ink); color: var(--bg); }
.segmented button[data-status='halal'][aria-pressed='true']    { background: var(--halal); color: #fff; }
.segmented button[data-status='mushbooh'][aria-pressed='true'] { background: var(--mushbooh); color: #1a1305; }
.segmented button[data-status='haram'][aria-pressed='true']    { background: var(--haram); color: #fff; }

/* ------------------------------------------------------------ ingredients -- */

.ingredients {
  font-size: 14.5px;
  line-height: 1.75;
  word-break: break-word;
  padding: 13px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-elev);
}

/* Highlights carry an underline as well as a wash, so they survive greyscale.
   They are <button>s so a tap opens the rule; `display: inline` keeps them flowing
   inside the sentence rather than sitting as blocks. */
.mark {
  display: inline;
  margin: 0;
  border: 0;
  padding: 2px 3px;
  border-radius: 3px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-weight: 600;
  text-align: left;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 2px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.mark:disabled { cursor: default; }
.mark:active:not(:disabled) { filter: brightness(1.35); }

.mark[data-status='haram']    { background: var(--haram-wash);    text-decoration-color: var(--haram); }
.mark[data-status='mushbooh'] { background: var(--mushbooh-wash); text-decoration-color: var(--mushbooh); }
.mark[data-status='halal']    { background: var(--halal-wash);    text-decoration-color: var(--halal); }

/* Brief pulse on the card a highlight points at, so it is obvious where you landed. */
@keyframes match-flash {
  0%, 100% { background: var(--bg-elev); }
  30%      { background: var(--bg-elev-2); box-shadow: inset 0 0 0 1px var(--accent); }
}
.match--flash { animation: match-flash 1.1s var(--ease); }
@media (prefers-reduced-motion: reduce) {
  .match--flash { animation: none; box-shadow: inset 0 0 0 1px var(--accent); }
}

/* ---------------------------------------------------------------- product -- */

.product {
  display: grid;
  grid-template-columns: 76px 1fr;
  gap: 13px;
  align-items: start;
  padding: 13px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--bg-elev);
}
.product__image {
  width: 76px;
  aspect-ratio: 1;
  object-fit: contain;
  border-radius: var(--radius);
  background: var(--bg-sunken);
  border: 1px solid var(--line);
}
.product__noimage {
  display: grid;
  place-items: center;
  width: 76px;
  aspect-ratio: 1;
  border-radius: var(--radius);
  background: var(--bg-sunken);
  border: 1px dashed var(--line-strong);
  color: var(--ink-faint);
  font-size: 10.5px;
  text-align: center;
  padding: 4px;
}
.product__name { font-size: 17px; font-weight: 600; line-height: 1.25; font-family: var(--font-head); }
.product__meta { color: var(--ink-dim); font-size: 13px; margin-top: 2px; }
.product__code { font-family: var(--font-mono); font-size: 12px; color: var(--ink-faint); margin-top: 6px; letter-spacing: 0.05em; }

.freshness {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 9px;
  font-size: 12px;
  color: var(--ink-faint);
}
.freshness button { min-height: 34px; padding: 0 11px; font-size: 12.5px; }

.taglist { display: flex; flex-wrap: wrap; gap: 5px; }
.taglist li {
  font-family: var(--font-mono);
  font-size: 11.5px;
  padding: 3px 7px;
  border-radius: 3px;
  border: 1px solid var(--line);
  background: var(--bg-sunken);
  color: var(--ink-dim);
}

details.raw { border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg-elev); }
details.raw > summary {
  padding: 13px;
  cursor: pointer;
  font: 600 12px/1 var(--font-head);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  list-style: none;
  min-height: 46px;
  display: flex;
  align-items: center;
  gap: 8px;
}
details.raw > summary::-webkit-details-marker { display: none; }
details.raw pre {
  margin: 0;
  padding: 13px;
  border-top: 1px solid var(--line);
  background: var(--bg-sunken);
  font: 400 11.5px/1.55 var(--font-mono);
  color: var(--ink-dim);
  /* Wrap rather than scroll sideways. Raw JSON has lines far wider than a phone,
     and any element wider than the viewport makes the whole page scroll
     horizontally — which reads as "the app suddenly went desktop". */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  word-break: break-word;
  overflow-y: auto;
  max-height: 46svh;
}

/* ---------------------------------------------------------------- history -- */

.history-list { display: flex; flex-direction: column; gap: 7px; }

.history-item {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  min-height: 58px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--unknown);
  border-radius: var(--radius);
  background: var(--bg-elev);
  color: inherit;
  text-align: left;
  font: inherit;
  cursor: pointer;
}
.history-item--haram    { border-left-color: var(--haram); }
.history-item--mushbooh { border-left-color: var(--mushbooh); }
.history-item--halal    { border-left-color: var(--halal); }
.history-item:hover { background: var(--bg-elev-2); }
.history-item__body { flex: 1; min-width: 0; display: block; }

/* These MUST be block. They are <span>s, and on an inline box `overflow: hidden`
   and `text-overflow: ellipsis` do nothing at all — so `white-space: nowrap` left
   the full product name as the element's min-content width, and one long name
   pushed the entire page wider than the screen. */
.history-item__name,
.history-item__meta {
  display: block;
  min-width: 0;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.history-item__name { font-weight: 600; font-size: 14.5px; }
.history-item__meta { font-size: 11.5px; color: var(--ink-faint); font-family: var(--font-mono); }
.history-item__verdict { flex: none; }
.history-item--haram    .history-item__verdict { color: var(--haram); }
.history-item--mushbooh .history-item__verdict { color: var(--mushbooh); }
.history-item--halal    .history-item__verdict { color: var(--halal); }
.history-item--unknown  .history-item__verdict { color: var(--unknown); }

.noscript {
  max-width: 62ch;
  margin: 0 auto;
  padding: 32px 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  line-height: 1.6;
}
.noscript h1 { font-size: 26px; }
.noscript strong { color: var(--accent); }

.empty { color: var(--ink-faint); font-size: 14px; text-align: center; padding: 26px 0; }

/* "Scan the next one", as its own row of the app shell rather than something
   sticky inside the scrolling result. A grid row is always exactly where it says it
   is; sticky inside a clipped, nested container is a fight not worth having. Bottom
   of the screen, because that is where a thumb is. */
/* "Scan the next one" stays on screen while you read the result — the shop loop is
   verdict, next product, verdict, next product, and that button must never be a
   scroll away. Fixed rather than sticky: as a grid row it would sit at the bottom
   of the *document*, and sticky inside the nested, clipped result container gave
   inconsistent offsets. The :has() rule below reserves the space it covers. */
.result-actions {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  padding: 10px max(var(--pad), env(safe-area-inset-left))
           calc(10px + env(safe-area-inset-bottom)) max(var(--pad), env(safe-area-inset-right));
  background: color-mix(in srgb, var(--bg) 94%, transparent);
  backdrop-filter: blur(14px) saturate(140%);
  border-top: 1px solid var(--line);
}

@media (min-width: 720px) {
  .result-actions { left: 50%; transform: translateX(-50%); width: min(780px, 100%); }
}
@media (min-width: 1040px) {
  .result-actions { width: min(900px, 100%); }
}

/* Reserve the height the fixed bar covers, so the footer disclaimer and the end of
   the ingredient list stay reachable instead of hiding underneath it. */
.app:has(> .result-actions:not([hidden])) { padding-bottom: 74px; }

.warnings { display: flex; flex-direction: column; gap: 5px; font-size: 12px; color: var(--mushbooh); }
.warnings li { font-family: var(--font-mono); line-height: 1.4; }

/* ------------------------------------------------------------------ toast -- */

.toast-host {
  position: fixed;
  z-index: 60;
  left: 50%;
  bottom: calc(16px + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 7px;
  width: min(94vw, 420px);
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 11px 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--bg-elev-2);
  color: var(--ink);
  font-size: 13.5px;
  box-shadow: var(--shadow);
  animation: toast-in 0.24s var(--ease) both;
  pointer-events: auto;
}
.toast--error { border-color: color-mix(in srgb, var(--haram) 55%, transparent); }
.toast--error .icon { color: var(--haram); }
.toast--ok .icon { color: var(--halal); }
.toast button { margin-left: auto; }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(10px) scale(0.98); }
  to   { opacity: 1; transform: none; }
}

.spinner {
  width: 16px; height: 16px;
  border: 2px solid color-mix(in srgb, currentColor 30%, transparent);
  border-top-color: currentColor;
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  flex: none;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* -------------------------------------------------------------- responsive -- */

@media (min-width: 720px) {
  :root { --pad: 22px; --gap: 16px; }

  .app { max-width: 780px; margin-inline: auto; box-shadow: 0 0 0 1px var(--line); }
  .viewport { aspect-ratio: 16 / 9; max-height: 46svh; }
  .verdict { padding: 22px; }
  .disclaimer { font-size: 12px; }
}

@media (min-width: 1040px) {
  .app { max-width: 900px; }
}
