    /* ============================================================
       Sprout Count — enhancement styles (added on top of the base).
       Text size, easy-reading font, sticky drawer headers,
       recent foods, starter-carb hint, plate-total pill, portion
       buttons, feedback modal, print styles.
       ============================================================ */

    /* Easy-reading (dyslexia-friendly) font toggle */
    body.easy-font, body.easy-font button, body.easy-font input, body.easy-font textarea,
    body.easy-font h1, body.easy-font h2, body.easy-font h3, body.easy-font .display,
    body.easy-font .greeting-text, body.easy-font .meal-item-title {
      font-family: 'Atkinson Hyperlegible', 'Nunito', system-ui, sans-serif !important;
      letter-spacing: 0.012em;
    }
    body.easy-font { word-spacing: 0.06em; }

    /* Compact 3-across segmented control (text size) */
    .seg-chip {
      flex: 1 1 0; min-width: 0; padding: 9px 4px; text-align: center;
      background: var(--bg); border: 2px solid var(--bg2); color: var(--ink);
      border-radius: 12px; font-weight: 800; font-size: 0.82rem; cursor: pointer;
      font-family: 'Nunito', sans-serif;
    }
    body.theme-night .seg-chip { background: #1B2140; border-color: #262C52; }
    .seg-chip.active { border-color: var(--sky); background: var(--sky); color: #fff; }
    .bread-slice-note {
      font-size: 0.68rem; color: var(--ink-soft); font-weight: 700; line-height: 1.4;
      margin: 8px 0 0;
    }

    /* Carb Detective: level selector + play/reveal cards. Reuses the app's
       existing colour tokens throughout (mint/sky/sun/berry) rather than
       introducing a new palette, and the level-locked state mirrors how
       other "not yet available" states already look elsewhere. */
    .detective-level-row {
      display: flex; gap: 6px; margin-bottom: 12px;
    }
    .detective-level-btn {
      flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 2px;
      background: var(--card); border: 2px solid var(--bg2); border-radius: 14px;
      padding: 8px 4px; cursor: pointer; font-family: 'Nunito', sans-serif;
    }
    body.theme-night .detective-level-btn { background: #1B2140; border-color: #262C52; }
    .detective-level-btn.active { border-color: var(--sky); background: rgba(63,169,245,0.14); }
    .detective-level-btn.locked { opacity: 0.55; cursor: not-allowed; }
    .detective-level-icon { font-size: 1.2rem; }
    .detective-level-name { font-weight: 800; font-size: 0.72rem; color: var(--ink); }
    .detective-level-btn.active .detective-level-name { color: var(--sky); }
    .detective-level-progress { font-size: 0.62rem; font-weight: 700; color: var(--ink-soft); }

    .detective-card {
      background: var(--bg); border-radius: 18px; padding: 20px 16px; text-align: center; position: relative;
    }
    .detective-streak-badge {
      position: absolute; top: 10px; right: 12px;
      font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 0.8rem; color: var(--sun-ink);
    }
    .detective-food-emoji { font-size: 3.2rem; line-height: 1; margin-bottom: 4px; }
    .detective-food-name { font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 1.1rem; margin-bottom: 12px; }
    .detective-question { font-size: 0.78rem; font-weight: 700; color: var(--ink-soft); margin-bottom: 8px; }
    .detective-guess-display {
      font-family: 'Baloo 2', sans-serif; font-size: 2rem; font-weight: 800; color: var(--sky); margin-bottom: 4px;
    }
    #detectiveSlider { width: 100%; accent-color: var(--sky); margin: 4px 0 4px; }

    .detective-reveal-badge {
      display: inline-flex; align-items: center; gap: 6px;
      font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 0.9rem;
      padding: 6px 16px; border-radius: 100px; margin-bottom: 14px;
    }
    .detective-reveal-badge.close { background: rgba(0,137,123,0.2); color: #4FD6C4; }
    .detective-reveal-badge.ok { background: rgba(255,201,60,0.2); color: var(--sun-ink); }
    .detective-reveal-badge.off { background: rgba(216,27,96,0.2); color: var(--berry); }
    .detective-reveal-numbers { display: flex; justify-content: center; gap: 24px; margin-bottom: 14px; }
    .detective-reveal-label { font-size: 0.64rem; color: var(--ink-soft); font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em; }
    .detective-reveal-val { font-family: 'Baloo 2', sans-serif; font-size: 1.4rem; font-weight: 800; }
    .detective-reveal-val.yours { color: var(--sky); }
    .detective-reveal-val.real { color: var(--sun-ink); }

    /* Higher or Lower: styled like an actual deck of playing cards rather
       than matching the app's own palette — the point is it reads as
       "cards", so the card faces are cream/white with red or black suit
       pips, and the back is a classic red pattern, deliberately different
       from the app's mint/sky/sun theme used everywhere else. */
    .hol-topbar {
      display: flex; justify-content: space-between; align-items: center;
      font-family: 'Baloo 2', sans-serif; font-weight: 700; font-size: 0.85rem;
      margin-bottom: 12px;
    }
    .hol-streak { color: var(--sun-ink); font-weight: 800; }
    .hol-table {
      display: flex; justify-content: center; align-items: center; gap: 12px;
      margin-bottom: 14px; perspective: 1000px;
    }
    .hol-card-slot { width: 108px; height: 152px; }
    .hol-card {
      width: 100%; height: 100%; position: relative;
      transform-style: preserve-3d; transition: transform 0.6s cubic-bezier(0.4,0.2,0.2,1);
    }
    .hol-card.hol-flipped { transform: rotateY(180deg); }
    .hol-card-face {
      position: absolute; inset: 0; border-radius: 10px;
      backface-visibility: hidden;
      box-shadow: 0 6px 16px rgba(0,0,0,0.35);
      display: flex; flex-direction: column;
    }
    .hol-card-front { background: #FBF8F1; padding: 6px; color: #222; }
    .hol-pip { position: absolute; font-size: 0.95rem; font-weight: 800; line-height: 1; }
    .hol-corner-tl { top: 6px; left: 6px; }
    .hol-corner-br { bottom: 6px; right: 6px; transform: rotate(180deg); }
    .hol-pip.red { color: #D81B60; }
    .hol-pip.black { color: #1F2A44; }
    .hol-card-center {
      flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    }
    .hol-card-emoji { font-size: 2rem; line-height: 1; }
    .hol-card-name { font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 0.68rem; color: #1F2A44; text-align: center; }
    .hol-card-carb-label { font-size: 0.5rem; color: #6B7280; font-weight: 700; text-transform: uppercase; letter-spacing: 0.02em; }
    .hol-card-carb-val { font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 1rem; color: var(--mint); }
    .hol-card-back {
      background:
        repeating-linear-gradient(45deg, rgba(255,255,255,0.08) 0 6px, transparent 6px 12px),
        linear-gradient(135deg, #C41E3A, #8B0000);
      transform: rotateY(180deg);
      display: flex; align-items: center; justify-content: center;
      border: 3px solid rgba(255,255,255,0.5);
      font-size: 1.7rem;
    }
    .hol-hidden-start .hol-card { transform: rotateY(180deg); }
    .hol-hidden-start .hol-card.hol-flipped { transform: rotateY(0deg); }
    .hol-vs { font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 0.78rem; color: var(--ink-soft); }
    .hol-question {
      text-align: center; font-size: 0.8rem; font-weight: 700; color: var(--ink);
      margin-bottom: 10px; line-height: 1.4;
    }
    .hol-question b { color: var(--sun-ink); }
    .hol-result {
      text-align: center; font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 0.82rem;
      padding: 7px; border-radius: 10px; margin-bottom: 10px; min-height: 18px;
    }
    .hol-result.correct { background: rgba(0,137,123,0.2); color: #4FD6C4; }
    .hol-result.wrong { background: rgba(216,27,96,0.2); color: var(--berry); }
    .hol-guess-row { display: flex; gap: 10px; }
    .hol-guess-btn {
      flex: 1; padding: 12px; border-radius: 12px; border: none;
      font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 0.88rem; color: #fff;
      cursor: pointer;
    }
    .hol-guess-btn.hol-higher { background: var(--sky); }
    .hol-guess-btn.hol-lower { background: var(--berry); }
    .hol-guess-btn:active { transform: scale(0.97); }

    /* Sticky section headers inside the settings drawer */
    .drawer .drawer-section-label {
      position: sticky; top: 0; z-index: 6;
      background: var(--card); margin-top: 20px;
      padding-top: 10px;
    }
    /* Keep the drawer close button tappable above sticky headers */
    .drawer .close-btn { z-index: 8; }

    /* Starter-carb "typical value" hint, shown under the food picker */
    .starter-carb-hint {
      display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
      margin: 0 0 14px; padding: 8px 10px;
      background: var(--sprout-green-tint); border: 1.5px dashed rgba(14,140,122,0.4);
      border-radius: 12px;
    }
    .starter-carb-hint[hidden] { display: none; }
    body.theme-night .starter-carb-hint { border-color: rgba(46,196,182,0.45); }

    /* .squash-dilute-* block removed 2026-08 — dead CSS, no matching
       markup anywhere in index.html or any JS file (verified via
       cross-reference audit). Was styling for a squash/dilution-ratio
       helper that was never wired up or was fully removed elsewhere. */
    .starter-carb-hint-text {
      display: flex; align-items: flex-start; gap: 8px;
      flex: 1 1 140px; min-width: 0;
    }
    .starter-carb-hint-icon {
      flex: 0 0 auto; width: 40px; height: 40px; border-radius: 50%;
      background: var(--card); border: 2px dashed #fff;
      box-shadow: 0 0 0 1.5px rgba(14,140,122,0.35);
      display: flex; align-items: center; justify-content: center;
      font-size: 1.4rem; margin-top: 1px;
    }
    body.theme-night .starter-carb-hint-icon { background: #1B2140; }
    .starter-carb-hint-body {
      font-family: 'Nunito', sans-serif; font-weight: 700; font-size: 0.76rem;
      color: var(--ink-soft); line-height: 1.35; min-width: 0; flex: 1 1 auto;
    }
    /* Source line — a quiet button until you want the evidence. */
    .footer-version { font-size: 0.72rem; opacity: 0.85; }
    .footer-report-btn {
      background: none; border: none; padding: 0; cursor: pointer;
      font-family: inherit; font-size: inherit; font-weight: 800;
      color: inherit; text-decoration: underline;
      -webkit-tap-highlight-color: transparent;
    }
    .footer-report-btn:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
    .starter-carb-src {
      display: inline-flex; align-items: center; gap: 4px;
      margin-top: 5px; padding: 3px 9px;
      border: 1.5px solid rgba(14,140,122,0.28); border-radius: 100px;
      background: var(--sprout-green-tint); color: var(--sprout-green-deep);
      font-family: 'Nunito', sans-serif; font-size: 0.63rem; font-weight: 800;
      cursor: pointer; -webkit-tap-highlight-color: transparent;
      transition: border-color 0.15s ease, transform 0.12s ease;
    }
    .starter-carb-src:active { transform: scale(0.96); }
    .starter-carb-src:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
    body.theme-night .starter-carb-src { color: #2EC4B6; border-color: rgba(46,196,182,0.35); }
    body.theme-contrast .starter-carb-src, body.theme-contrast-light .starter-carb-src {
      color: var(--ink); background: var(--bg2); border-color: var(--ink-soft);
    }
    .starter-carb-src-caret { font-size: 0.6rem; transition: transform 0.18s ease; }
    .starter-carb-src[aria-expanded="true"] .starter-carb-src-caret { transform: rotate(180deg); }

    /* The evidence panel itself. */
    .starter-carb-srclist {
      flex: 1 1 100%; width: 100%; margin-top: 8px;
      padding: 10px 12px; border-radius: var(--radius-md);
      background: var(--card); border: 1.5px solid var(--bg2);
      animation: srcOpen 0.22s ease both;
    }
    .starter-carb-srclist[hidden] { display: none; }
    body.theme-night .starter-carb-srclist { background: #1B2140; border-color: #262C52; }
    @keyframes srcOpen {
      from { opacity: 0; transform: translateY(-4px); }
      to { opacity: 1; transform: translateY(0); }
    }
    @media (prefers-reduced-motion: reduce) {
      .starter-carb-srclist { animation: none; }
      .starter-carb-src-caret { transition: none; }
    }
    .starter-src-bar-row {
      display: flex; align-items: flex-start; justify-content: space-between;
      gap: 10px; margin-bottom: 7px;
    }
    .starter-src-head {
      font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 0.76rem;
      color: var(--ink); line-height: 1.3;
    }
    .starter-src-close {
      flex: 0 0 auto; position: relative;
      width: 24px; height: 24px; padding: 0; border-radius: 50%;
      border: 1.5px solid var(--bg2); background: transparent;
      color: var(--ink-soft); font-size: 0.7rem; line-height: 1; cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      transition: background-color 0.15s ease, color 0.15s ease, transform 0.12s ease;
    }
    /* Small circle, comfortable 44px touch area. */
    .starter-src-close::before {
      content: ''; position: absolute; top: 50%; left: 50%;
      width: 44px; height: 44px; transform: translate(-50%, -50%);
    }
    .starter-src-close:hover { background: var(--bg2); color: var(--ink); }
    .starter-src-close:active { transform: scale(0.9); }
    .starter-src-close:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
    .starter-src-actions {
      display: flex; gap: 8px; margin-top: 9px;
    }
    .starter-src-action {
      flex: 1 1 0; padding: 7px 8px; border-radius: 100px; cursor: pointer;
      border: 1.5px solid var(--bg2); background: transparent; color: var(--ink-soft);
      font-family: 'Nunito', sans-serif; font-weight: 800; font-size: 0.7rem;
      -webkit-tap-highlight-color: transparent;
      transition: border-color 0.15s ease, color 0.15s ease, transform 0.12s ease;
    }
    .starter-src-action:hover { border-color: var(--sprout-green); color: var(--ink); }
    .starter-src-action:active { transform: scale(0.96); }
    .starter-src-action:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
    @media (prefers-reduced-motion: reduce) {
      .starter-src-close, .starter-src-action { transition: none; }
    }
    .starter-src-list { list-style: none; margin: 0; padding: 0; }
    .starter-src-caveat {
      font-family: 'Nunito', sans-serif; font-weight: 700; font-size: 0.7rem;
      color: var(--ink-soft); line-height: 1.45; margin-bottom: 9px;
    }
    .starter-src-row {
      display: grid; width: 100%;
      grid-template-columns: 1fr auto;
      grid-template-areas: 'tag tag' 'name val';
      gap: 1px 8px; padding: 9px 4px;
      border-top: 1px solid var(--bg2);
      border-left: none; border-right: none; border-bottom: none;
      background: transparent; text-align: left; cursor: pointer;
      border-radius: 8px;
      -webkit-tap-highlight-color: transparent;
      transition: background-color 0.15s ease, transform 0.1s ease;
    }
    li:first-child > .starter-src-row { border-top: none; padding-top: 2px; }
    .starter-src-row:hover { background: var(--bg2); }
    .starter-src-row:active { transform: scale(0.98); }
    .starter-src-row:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
    @media (prefers-reduced-motion: reduce) { .starter-src-row { transition: none; } }
    .starter-src-tag {
      grid-area: tag;
      display: inline-block; width: fit-content; margin-bottom: 3px;
      padding: 2px 8px; border-radius: 100px;
      background: #FFF4D6; color: #8A5A00;
      font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 0.6rem;
      text-transform: uppercase; letter-spacing: 0.04em;
    }
    body.theme-night .starter-src-tag { background: rgba(255,193,7,0.16); color: #FFD98A; }
    body.theme-contrast .starter-src-tag, body.theme-contrast-light .starter-src-tag {
      background: var(--bg2); color: var(--ink);
    }
    .starter-src-name {
      grid-area: name;
      font-family: 'Nunito', sans-serif; font-weight: 700; font-size: 0.7rem;
      color: var(--ink-soft); line-height: 1.3;
    }
    .starter-src-name-estimate { font-style: italic; }
    .starter-src-val {
      grid-area: val; align-self: center;
      font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 0.8rem;
      color: var(--ink);
    }
    /* .starter-src-more / .starter-src-foot removed 2026-08 — dead CSS,
       unreferenced in any JS or HTML (verified via cross-reference audit). */
    .starter-src-empty {
      font-family: 'Nunito', sans-serif; font-weight: 700; font-size: 0.7rem;
      color: var(--ink-soft); line-height: 1.45;
    }
    .starter-carb-choice-row {
      display: flex; gap: 8px; flex-wrap: wrap; flex: 0 0 auto; width: 100%;
    }
    .starter-carb-use-btn {
      flex: 1 1 auto; background: var(--sprout-green); color: #fff; border: none;
      border-radius: 100px; padding: 8px 14px; font-family: 'Baloo 2', sans-serif;
      font-weight: 800; font-size: 0.82rem; cursor: pointer;
    }
    .starter-carb-use-btn:active { transform: scale(0.95); }
    body.theme-contrast .starter-carb-use-btn, body.theme-contrast-light .starter-carb-use-btn {
      background: var(--ink-soft); color: var(--bg);
    }
    /* Secondary, quieter option next to Use — same size so neither reads as
       "the" answer, since some parents will know their exact packaging
       number and others will want the guess. */
    .starter-carb-own-btn {
      flex: 1 1 auto; background: var(--card); color: var(--ink);
      border: 1.5px solid var(--sprout-green); border-radius: 100px; padding: 8px 14px;
      font-family: 'Nunito', sans-serif; font-weight: 800; font-size: 0.78rem; cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    body.theme-night .starter-carb-own-btn { background: #1B2140; }
    .starter-carb-own-btn:active { transform: scale(0.95); }
    body.theme-contrast .starter-carb-own-btn, body.theme-contrast-light .starter-carb-own-btn {
      background: transparent; color: var(--ink); border-color: var(--ink);
    }
    /* Portion verification — two chips tucked under the weight stepper. */
    .portion-chips { display: flex; gap: 8px; flex-wrap: wrap; margin: 6px 0 10px; }
    .portion-method-btn {
      flex: 1 1 90px; min-width: 0; padding: 7px 6px; border-radius: 100px;
      border: 2px solid #E0A200; background: var(--card); color: var(--ink-soft);
      font-family: 'Nunito', sans-serif; font-weight: 800; font-size: 0.76rem;
      cursor: pointer; -webkit-tap-highlight-color: transparent;
      transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, transform 0.12s ease;
    }
    body.theme-night .portion-method-btn { background: #1B2140; }
    .portion-method-btn:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
    .portion-method-btn:active { transform: scale(0.96); }
    .portion-chips .portion-method-btn.active {
      border-color: var(--sprout-green); background: var(--sprout-green); color: #fff;
    }
    body.theme-contrast .portion-chips .portion-method-btn.active,
    body.theme-contrast-light .portion-chips .portion-method-btn.active {
      background: var(--ink-soft); color: var(--bg); border-color: var(--ink-soft);
    }
    /* Guessing is a legitimate last resort, so it stays — but it shouldn't look
       like an equal third of the row. Dashed and muted, never scolding. */
    .portion-method-btn[data-method="guide"] {
      border-style: dotted; opacity: 0.82; font-weight: 700;
    }
    .portion-chips .portion-method-btn[data-method="guide"].active {
      border-style: dotted; opacity: 1;
    }
    /* Gentle idle nudge while no method has been picked yet — much slower
       and lower-contrast than the skip warning below, just a quiet "this is
       waiting for you" rather than an alert. Stops the instant one is tapped. */
    .portion-chips.unanswered .portion-method-btn {
      animation: portionSoftPulse 2.4s ease-in-out infinite;
    }
    body.theme-night .portion-chips.unanswered .portion-method-btn {
      animation-name: portionSoftPulseNight;
    }
    @keyframes portionSoftPulse {
      0%, 100% { box-shadow: none; }
      50% { box-shadow: 0 0 0 4px rgba(90, 179, 106, 0.38); }
    }
    @keyframes portionSoftPulseNight {
      0%, 100% { box-shadow: none; }
      50% { box-shadow: 0 0 0 4px rgba(120, 200, 135, 0.45); }
    }
    @media (prefers-reduced-motion: reduce) {
      .portion-chips.unanswered .portion-method-btn { animation: none; }
    }
    /* Deliberately as visually prominent as "Add a drink" — same recipe
       (tinted fill, 2px border, brand-blue text) — so escaping the presets
       for a real weighed/label number is never something a person has to
       hunt for in a line of small print. */
    .bread-own-numbers-btn {
      display: flex; width: 100%; align-items: center; justify-content: center; gap: 6px;
      background: rgba(63, 169, 245, 0.08);
      border: 2px solid rgba(63, 169, 245, 0.4);
      border-radius: 12px;
      color: var(--sky);
      padding: 12px 16px;
      font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 0.9rem;
      cursor: pointer; margin-bottom: 16px;
      transition: all 0.2s ease;
      -webkit-tap-highlight-color: transparent;
    }
    .bread-own-numbers-btn[hidden] { display: none; }
    .bread-own-numbers-btn:active { transform: scale(0.98); }
    .bread-own-numbers-btn:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
    body.theme-night .bread-own-numbers-btn {
      background: rgba(63, 169, 245, 0.15);
    }
    /* Same "look here" language, reused for the carbs field right after
       adding a brand-new food, so it's obvious where to enter its numbers. */
    #carbsInput.needs-carbs-highlight {
      animation: portionPulse 0.55s ease 3;
    }
    body.theme-night #carbsInput.needs-carbs-highlight {
      animation-name: portionPulseNight;
    }
    @media (prefers-reduced-motion: reduce) {
      #carbsInput.needs-carbs-highlight { animation: none; box-shadow: 0 0 0 4px rgba(224,162,0,0.3); }
    }
    .portion-chips[hidden] { display: none; }

    .result-est-note {
      margin-top: 10px; padding: 8px 10px; border-radius: 12px;
      background: rgba(255,255,255,0.22);
      font-family: 'Nunito', sans-serif; font-weight: 800; font-size: 0.76rem;
      line-height: 1.4;
    }
    .result-est-note[hidden] { display: none; }
    /* ============================================================
       REX APPROVES STAMP. See the matching comment in index.html above
       #rexApprovesStamp for the full trigger/revert notes. Fires on a
       small random chance per log (not every time — #sproutSays
       already mentions Rex in its own wording now, so this is a rare
       extra surprise rather than a guaranteed second mention).
       Styled as a small stamp/badge rather than matching
       .result-est-note's info-box look, so it reads as a fun aside
       rather than another informational line.
       ============================================================ */
    /* Scoped so the `hidden` attribute still works — see
       .plate-attention-flag's comment in main.css for the full reasoning.
       This element is toggled via .hidden in JS. */
    .rex-approves-stamp:not([hidden]) { display: inline-block; }
    .rex-approves-stamp {
      margin-top: 8px; padding: 5px 12px; border-radius: 999px;
      background: rgba(255,255,255,0.22);
      font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 0.78rem;
      animation: rexApprovesPop 0.3s ease-out;
    }
    .rex-approves-stamp[hidden] { display: none; }
    @keyframes rexApprovesPop {
      0% { opacity: 0; transform: scale(0.85) translateY(4px); }
      100% { opacity: 1; transform: scale(1) translateY(0); }
    }
    @media (prefers-reduced-motion: reduce) {
      .rex-approves-stamp { animation: none; }
    }
    /* END REX APPROVES STAMP */
    .meal-item-range {
      font-size: 0.72rem; color: var(--ink-soft); font-weight: 800; margin-top: 2px;
    }
    /* A guessed carb value or an eyeballed portion — the colour does the
       flagging so there's no extra symbol for a child to decode. */
    button.meal-item-range {
      font-family: 'Nunito', sans-serif; text-align: left; cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    button.meal-item-range:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
    .range-why-cue { text-decoration: underline; opacity: 0.85; margin-left: 2px; }
    .meal-item-why {
      margin-top: 5px; padding: 7px 9px; border-radius: 9px;
      background: #FFF4D6; color: #8A5A00;
      font-family: 'Nunito', sans-serif; font-weight: 700; font-size: 0.72rem;
      line-height: 1.45;
    }
    .meal-item-why[hidden] { display: none; }
    body.theme-night .meal-item-why { background: rgba(255,193,7,0.14); color: #FFD98A; }
    body.theme-contrast .meal-item-why, body.theme-contrast-light .meal-item-why {
      background: var(--bg2); color: var(--ink);
    }
    .meal-item-range.is-guess, .caregiver-item-details.is-guess {
      display: inline-block; color: #8A5A00; background: #FFF4D6;
      border: 1.5px dotted #DC2626; border-radius: 7px;
      padding: 1px 7px; margin-top: 3px;
    }
    body.theme-night .meal-item-range.is-guess { color: #FFD98A; background: rgba(255,193,7,0.16); }
    body.theme-contrast .meal-item-range.is-guess, body.theme-contrast-light .meal-item-range.is-guess {
      color: var(--ink); background: var(--bg2);
    }
    .meal-total-range {
      font-family: 'Nunito', sans-serif; font-weight: 800; font-size: 0.76rem;
      color: var(--ink-soft); text-align: right; margin-top: 4px; line-height: 1.4;
    }

    /* Floating plate-total pill (jump to meal log) */
    /* v1.42.0: was a single-row inline-flex; now a column so the
       optional nudge line (#platePillNudge) can sit below the count
       without needing a second, separate element on the page. When the
       nudge is hidden (fewer than 2 items — see
       updatePlateTotalPill()'s own comment for the threshold), this
       renders identically to the old single-row pill: align-items:
       flex-start plus the inner .plate-pill-row's own flex layout
       reproduces the exact old visual shape, just wrapped one level
       deeper. */
    .plate-total-pill {
      position: fixed; left: 16px; bottom: 22px; z-index: 4;
      display: inline-flex; flex-direction: column; align-items: flex-start; gap: 2px;
      background: linear-gradient(135deg, var(--sprout-green), var(--sprout-green-deep));
      color: #fff; border: none; border-radius: 100px;
      padding: 11px 16px; font-family: 'Baloo 2', sans-serif; font-weight: 800;
      font-size: 0.95rem; cursor: pointer; box-shadow: 0 6px 18px rgba(0,89,78,0.34);
      opacity: 0; visibility: hidden; transform: translateY(8px) scale(0.9);
      transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
      max-width: calc(100vw - 120px);
    }
    .plate-total-pill.visible { opacity: 1; visibility: visible; transform: translateY(0) scale(1); }
    .plate-total-pill:active { transform: scale(0.94); }
    .plate-pill-row { display: inline-flex; align-items: center; gap: 8px; }
    .plate-total-pill .plate-pill-arrow { font-weight: 900; }
    /* Gold/sun accent (not the pill's own green) so the nudge visually
       reads as "a suggestion" distinct from the plain count/total row
       above it — same accent family already used for the saved-foods
       shortcut (.fs-fav-shortcut) elsewhere in the app, for a
       consistent "this is an invitation to tap" visual language.
       Own font-size rather than inheriting the pill's 0.95rem: at the
       same size as the count line, two stacked full-size rows made the
       pill noticeably taller and more visually heavy for what is,
       after all, a secondary hint rather than the pill's main content. */
    .plate-pill-nudge {
      font-size: 0.74rem; font-weight: 700; color: var(--sun);
    }
    /* Brief in-place reaction when an item is added while the pill is
       already visible — confirms the add without moving the page.
       Considered and rejected: auto-scrolling to the meal log on every
       add, per resetAccordionForNextItem()'s own comment in app.js
       about a past "screen feels busy" report from doing exactly that
       kind of thing on this same screen. */
    .plate-total-pill.pulse { animation: platePillPulse 0.5s ease; }
    @keyframes platePillPulse {
      0% { transform: scale(1); }
      40% { transform: scale(1.06); }
      100% { transform: scale(1); }
    }
    body.theme-contrast .plate-total-pill, body.theme-contrast-light .plate-total-pill {
      background: var(--ink-soft); color: var(--bg);
    }
    body.theme-contrast .plate-pill-nudge, body.theme-contrast-light .plate-pill-nudge {
      color: var(--bg);
    }
    @media (prefers-reduced-motion: reduce) {
      .plate-total-pill { transition: opacity 0.15s linear; }
      .plate-total-pill.pulse { animation: none; }
    }


    /* Feedback modal bits */
    .feedback-type-row { display: flex; gap: 8px; margin: 6px 0 14px; }
    .feedback-type-btn {
      flex: 1 1 0; min-width: 0; padding: 10px 4px; border-radius: 12px;
      border: 2px solid var(--bg2); background: var(--bg); color: var(--ink);
      font-weight: 800; font-size: 0.82rem; cursor: pointer; font-family: 'Nunito', sans-serif;
      -webkit-tap-highlight-color: transparent;
      transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease, transform 0.12s ease;
    }
    body.theme-night .feedback-type-btn { background: #1B2140; border-color: #262C52; }
    .feedback-type-btn:hover { border-color: var(--sky); }
    .feedback-type-btn:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
    .feedback-type-btn:active { transform: scale(0.95); }
    /* Selected state. Three classes deep so it always beats the per-theme
       overrides above (which used to win in Night mode and hide the highlight).
       color/ring use --card so the text stays legible in every theme. */
    .feedback-type-row .feedback-type-btn.active {
      border-color: var(--sky); background: var(--sky); color: var(--card);
      box-shadow: inset 0 0 0 2px var(--card), 0 3px 10px rgba(0,0,0,0.18);
    }
    @media (prefers-reduced-motion: reduce) {
      .feedback-type-btn { transition: none; }
      .feedback-type-btn:active { transform: none; }
    }
    /* Close ✕ on the feedback card */
    #feedbackModal .modal-content { position: relative; }
    #feedbackModal .feedback-close-x {
      position: absolute; top: 12px; right: 12px; z-index: 2;
      width: 34px; height: 34px; padding: 0; line-height: 1;
      display: flex; align-items: center; justify-content: center;
      border: none; border-radius: 50%; background: var(--bg);
      color: var(--ink-soft); font-size: 17px; font-weight: 900; cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      transition: background-color 0.15s ease, color 0.15s ease, transform 0.12s ease;
    }
    #feedbackModal .feedback-close-x:hover { background: var(--bg2); color: var(--ink); }
    #feedbackModal .feedback-close-x:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
    #feedbackModal .feedback-close-x:active { transform: scale(0.92); }
    @media (prefers-reduced-motion: reduce) {
      #feedbackModal .feedback-close-x { transition: none; }
      #feedbackModal .feedback-close-x:active { transform: none; }
    }
    .feedback-grownup-note {
      display: flex; align-items: center; gap: 8px; font-size: 0.78rem; font-weight: 800;
      color: var(--mint); background: var(--sprout-green-tint); border-radius: 12px;
      padding: 9px 11px; margin-bottom: 14px; line-height: 1.35;
    }
    .feedback-textarea {
      width: 100%; min-height: 92px; resize: vertical; padding: 11px;
      border-radius: 12px; border: 2px solid var(--bg2); background: var(--bg);
      color: var(--ink); font-weight: 700; font-family: 'Nunito', sans-serif;
      font-size: 0.95rem; outline: none;
    }
    .feedback-textarea:focus { border-color: var(--sky); }
    .feedback-privacy-note { font-size: 0.72rem; color: var(--ink-soft); font-weight: 700; margin: 10px 0 0; line-height: 1.4; }

    /* Barcode-scan chip button — kept static here (rather than injected by
       barcode/scanner.js at runtime) so it renders correctly from the very
       first paint. scanner.js and off-client.js are now lazy-loaded only
       when this chip is first tapped (see index.html + scanner.js
       injectButton()), so their styles can't be relied on to exist yet.
       This is the only rule scanner.js's own injectStyles() used to define
       that needs to exist before that script has loaded. */
    .barcode-scan-chip-pinned{flex:0 0 auto;width:58px;height:64px;border-radius:14px;
      border:2px solid var(--sky);background:linear-gradient(160deg,#E7F5FF,var(--sky));
      color:#08405E;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
      -webkit-tap-highlight-color:transparent;
      box-shadow:0 2px 6px rgba(63,169,245,0.45);transition:transform 0.12s ease;}
    .barcode-scan-chip-pinned .pinned-chip-icon{font-size:23px;line-height:1;}
    .barcode-scan-chip-pinned:active{transform:scale(0.9);}
    .barcode-scan-chip-pinned:focus-visible{outline:3px solid var(--sky);outline-offset:2px;}
    .barcode-scan-chip-pinned:disabled{opacity:0.55;cursor:wait;}
    body.theme-night .barcode-scan-chip-pinned{background:linear-gradient(160deg,#123049,#1E5C86);color:#DCEEFB;}


/* Scanned meal items show a camera marker instead of the rename affordance:
   the product name comes from the barcode and is not editable, so offering
   "tap to name" would be a promise the app no longer keeps. */
.scanned-hint {
  font-size: 0.72rem;
  opacity: 0.75;
  margin-left: 4px;
}


/* An item the app could not put a number on. Deliberately reads as words
   rather than a figure — "0g" would be a claim, and the whole point is that
   no claim can be made. Amber rather than red: it is a caveat, not an error.
   Kept even though nothing can create a NEW uncounted item any more (see
   docs/NOTES.md "Removed the unused uncounted-item entry point" entry) —
   this class still renders correctly for any pre-existing history entry
   already saved with uncounted:true from before that removal, and item-
   schema.js still carries that field through on load for exactly that
   reason. Removing this class would turn an old "we don't know" entry into
   a blank/undefined-looking value on reload. */
.uncounted-val {
  font-size: 0.72rem;
  font-weight: 800;
  color: var(--sunny, #F59E0B);
  white-space: nowrap;
}


/* ====================================================================
   DRINKS STATION v2
   Ported 1:1 from drinks-station-mockup-v2.html (12 Aug 2026).

   The mockup's :root was literally this app's theme-night palette, so
   every var() below is already the app's own token and themes for free.
   The one thing that could NOT come across verbatim is the mockup's
   hardcoded rgba(255,255,255,.04) / rgba(0,0,0,.28) surfaces: those
   assume a dark background and go invisible (or unreadable) in the
   light, rainbow, caregiver and high-contrast themes. They are mapped
   onto three new per-theme tokens declared in css/main.css:
     --dv-surface  faint raised panel
     --dv-line     hairline / dashed rule
     --dv-inset    recessed well (maths box, result panel)
   Same appearance in theme-night as the mockup, correct everywhere else.
   ==================================================================== */

.drinks-v2 { font-weight: 700; }

.drinks-v2 .dv-head-actions { display: flex; align-items: center; gap: 12px; }
.dv-reset-btn {
  background: none; border: none; color: var(--ink-soft); font: inherit;
  font-size: .72rem; font-weight: 800; cursor: pointer;
  text-decoration: underline; padding: 4px;
}

/* ---- steps ---------------------------------------------------------- */
.dv-step { margin-bottom: 14px; }
.dv-step:last-child { margin-bottom: 0; }
.dv-step-head {
  display: flex; align-items: center; gap: 6px; width: 100%;
  background: none; border: none; padding: 0; color: inherit; font: inherit;
  text-align: left; cursor: pointer;
}
.dv-badge {
  flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%;
  background: var(--sprout-green); color: #fff; font-size: .72rem; font-weight: 800;
  display: grid; place-items: center;
}
.dv-badge.done { background: var(--sky); color: var(--card); }
.dv-badge.todo { background: var(--dv-line); color: var(--ink-soft); }
/* v1.37.0: both given explicit font-scale support. .dv-step-title is the
   question itself ("Carbs per 100g", "What are you drinking?") and
   .dv-step-answer is literally the confirmed carbs/weight/amount for that
   step once answered — together these are the core Food/Drinks Station
   numbers, and neither responded to the "Large"/"Larger" text-size
   setting before this. */
.dv-step-title { font-size: calc(.95rem * var(--font-scale, 1)); font-weight: 800; flex: 1; }
.dv-step-answer { font-size: calc(.82rem * var(--font-scale, 1)); font-weight: 800; color: var(--sky); white-space: nowrap; }
/* The open/closed chevron. Was .8rem — a ~10px glyph doing the job of
   telling you whether a step is open, which is the single most important
   piece of state on the screen. Now 1.35rem in a 32px box, which also gives
   the header's tap target a sensible visual anchor at the right-hand edge.
   Colour lifted from --ink-soft to --sky so it reads as a control rather
   than decoration. */
.dv-chev {
  color: var(--sky);
  font-size: 1.35rem;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 26px, not 32: at 32 the extra width tipped "What's on your plate?
     (optional)" onto a second line. The glyph is what needed to grow, not
     the box — a -6px margin-right lets it sit flush to the card edge and
     claw back the space the title lost. */
  width: 26px;
  height: 26px;
  flex: 0 0 26px;
  margin-right: -6px;
  /* The box stays 26px, but the arrow inside it is scaled up. A transform
     is purely visual — it takes no layout space — so the glyph can be made
     properly legible without pushing the title onto a second line, which is
     what capping the box at 26px was working around in the first place.
     At 1.35rem the triangle occupies only a fraction of its em square and
     read as a speck on a phone. */
  transform: scale(1.75);
  transform-origin: center;
  transition: transform .18s ease;
}
/* Rotated when the step is open, so a collapsed step always looks the same
   "tap me to open" way — whether it's still to come, or was answered and
   tucked away, or got closed by an accidental tap on the header. */
/* MUST repeat the scale from the base rule. `transform` is one property,
   not a list that merges — writing rotate() alone here silently discards
   the scale(1.75) above and an open chevron shrinks to a third of a
   collapsed one. Change the scale in the base rule and change it here too. */
.dv-step[data-state="open"] .dv-chev { transform: rotate(180deg) scale(1.75); }
.dv-step-body { margin-top: 10px; }
.dv-step[data-state="collapsed"] .dv-step-body { display: none; }
/* Brief fade-out applied by collapseStepSoftly() in food-station.js —
   data-state passes through "collapsing" for STEP_COLLAPSE_FADE_MS before
   landing on "collapsed" (which is what actually applies display:none
   above) — softens the single-open accordion's force-close of a sibling
   step so it reads as a handoff rather than an instant snap, matching the
   existing fade-IN on open (dvIn keyframe, further down this file).
   Selector keys off data-state="collapsing" specifically, NOT "open" plus
   a class — "collapsing" matches neither the open-state selector below
   (whose dvIn animation would otherwise override this transition on the
   same opacity/transform properties, silently cancelling the fade-out)
   nor the collapsed-state display:none rule above, so the body stays
   visible for exactly the fade's duration. The .fs-step-collapsing class
   JS also adds is a belt-and-braces hook for anything else that might
   want to target the transitional moment; this rule doesn't depend on it.
   Timing (140ms) is set in JS as STEP_COLLAPSE_FADE_MS and must stay
   roughly matched to this transition's duration, or the element will
   either flash unstyled or get cut off mid-fade. Skipped entirely under
   prefers-reduced-motion (checked in JS, not here, since the state flip
   itself needs to skip the delay too, not just the visual). */
.dv-step[data-state="collapsing"] .dv-step-body { opacity: 0; transform: translateY(-3px); transition: opacity .14s ease-in, transform .14s ease-in; }
.dv-step[data-state="locked"] { opacity: .42; pointer-events: none; }
.dv-step[data-state="locked"] .dv-step-body { display: none; }

/* Food Station's own hard gate: a locked step is fully
   invisible, not just dimmed. Scoped to .food-station-step only — Drinks
   Station shares the plain .dv-step[data-state="locked"] rule above
   (faded-but-visible) and that stays untouched. This is what makes Step 3
   genuinely not there until Step 2's Continue button is tapped, matching
   the intended pre-mockup behaviour. */
.food-station-step[data-state="locked"] { display: none; }

/* Drinks Station gets the same hard-hide treatment — a locked step is
   genuinely not there, not just faded. Scoped to .drinks-station-step only, same
   reasoning as the food-only rule above: nothing else shares .dv-step's
   plain faded-locked look by accident. */
.drinks-station-step[data-state="locked"] { display: none; }

/* ---- scrolling picker (same pattern as the food station) ------------ */
.dv-rail { display: flex; gap: 8px; overflow-x: auto; padding: 2px 2px 8px; scrollbar-width: none; }
.dv-rail::-webkit-scrollbar { display: none; }
.dv-chip {
  flex: 0 0 auto; min-width: 76px; padding: 10px 12px; border-radius: 16px;
  background: var(--bg2); border: 2px solid transparent; color: var(--ink);
  font: inherit; font-weight: 800; font-size: .76rem; text-align: center; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; gap: 4px; line-height: 1.15;
}
.dv-chip .dv-emo { font-size: 1.35rem; }
.dv-chip .dv-sub { font-size: .64rem; font-weight: 700; color: var(--ink-soft); }
.dv-chip[aria-pressed="true"] { border-color: var(--sky); background: var(--sprout-green-tint); }
.dv-chip:focus-visible { outline: 3px solid var(--sun); outline-offset: 2px; }
.dv-rail-hint { font-size: .7rem; font-weight: 700; color: var(--ink-soft); margin: 2px 0 0; }

/* ---- "last time" — INFORMATION ONLY, never a shortcut button --------
   Nothing here is tappable and nothing here fills a field in. It is a
   memory jog sitting above the ordinary picker, not a way around it. */
/* display scoped so the `hidden` attribute still works — see
   .plate-attention-flag in main.css for why. */
.dv-lasttime:not([hidden]) { display: flex; }
.dv-lasttime {
   align-items: center; gap: 8px; margin-bottom: 10px; padding: 8px 12px;
  border-radius: 12px; background: var(--dv-surface); border: 1px dashed var(--dv-line);
  font-size: .74rem; font-weight: 700; color: var(--ink-soft);
}
.dv-lasttime b { color: var(--ink); }

/* .dv-prefill carries its own display:flex, so without this line a leftover
   dilution summary would stay visible after switching straight from squash to
   another drink. (.dv-sf, the old sugar-free checkbox, is gone — sugar-free is
   a required chip question now, see .dv-choice below.) */
.dv-dilute[hidden], .dv-maths[hidden],
.dv-stepper[hidden], .dv-prefill[hidden], .dv-lasttime[hidden] { display: none !important; }

/* ---- required questions inside Step 1 --------------------------------
   The sugar-free question and the squash dilution question are the same
   kind of thing: something that has to be answered before Step 1 counts,
   asked as chips rather than a tick. They share a shell and share the
   .dv-ratio chip. Amber stays reserved for the squash warning; the
   sugar-free question is an ordinary question, so it wears sky. */
.dv-choice[hidden] { display: none !important; }
.dv-choice {
  margin-top: 12px; padding: 12px; border-radius: 16px;
  background: var(--sprout-green-tint); border: 1.5px solid var(--sky);
}
.dv-choice h3 { margin: 0 0 3px; font-size: .86rem; font-family: 'Baloo 2', Nunito, sans-serif; font-weight: 800; color: var(--sky); }
.dv-choice p { margin: 0 0 10px; font-size: .74rem; font-weight: 700; color: var(--ink-soft); line-height: 1.4; }
.dv-choice .dv-ratio[aria-pressed="true"] { border-color: var(--sky); background: var(--sprout-green-tint); }

/* ---- nutrition-label pointer (manual-entry path only) -----------------
   Small illustrative "where to look" graphic, brought back from an older
   design for the "I know the carbs" path — pairs a
   plain example nutrition-facts rectangle with the "Carbohydrate" row
   highlighted, alongside the existing pointer copy. The SVG carries no
   real data (30g is a placeholder, not this food's actual figure) so it
   can never be mistaken for a live value. */
.fs-label-hint[hidden] { display: none !important; }
/* display scoped so the `hidden` attribute still works — see
   .plate-attention-flag in main.css for why. */
.fs-label-hint:not([hidden]) { display: flex; }
.fs-label-hint {
   align-items: center; gap: 12px;
  margin: 4px 0 12px; padding: 10px; border-radius: 14px;
  background: var(--dv-surface); border: 1.5px solid var(--dv-line);
}
.fs-label-hint-icon { flex: 0 0 auto; width: 58px; height: auto; }
.fs-label-hint-txt { margin: 0; font-size: .78rem; font-weight: 700; color: var(--ink-soft); line-height: 1.4; }
.fs-label-hint-txt strong { color: var(--ink); }

/* .fs-manual-heading (a plain <p> replacing #fsOwnBtn) was here briefly
   (25 Aug 2026) but removed the same day once the nutrition-label hint
   above was added — its own copy ("See Carbohydrate... Type that number
   below") already said the same thing the heading did, one line down. */

/* ---- Step 1's front door: "what do you know already?" ---------------
   The real first decision, replacing a carousel
   sitting next to a demoted "skip, it's optional" button that three
   rounds of restyling that button couldn't fix — the button was never
   the wrong element, Step 1 was asking the wrong question first. Shares
   .dv-choice's shell/colours for visual consistency with the rest of the
   app's required-question pattern, but sized up: this is the single most
   important decision on the screen, not a minor sub-question, so it gets
   two large equal-weight cards rather than compact inline chips. Neither
   card carries extra visual weight over the other on purpose — this is a
   fork, not a default with an opt-out link beside it. */
.fs-knowledge-choice { margin-top: 0; }
.fs-knowledge-cards { gap: 10px; }
.fs-knowledge-btn.dv-ratio {
  position: relative;
  flex: 1 1 45%; display: flex; flex-direction: column; align-items: center;
  gap: 4px; padding: 18px 10px; min-height: 128px; text-align: center;
}
/* Numbered corner badge ("1" / "2" on the two "What do you know
   already?" cards) removed — the two cards are an
   equal-weight fork (pick whichever matches your situation), not
   sequential steps, and the numbering read as implying an order that
   doesn't exist. An icon-only alternative was mocked up and also
   rejected on looks, so the badge is gone entirely rather than
   replaced. Markup: the <span class="fs-knowledge-badge"> elements
   removed from both cards in index.html. */
.fs-knowledge-icon { font-size: 1.7rem; line-height: 1; margin-bottom: 2px; }
/* Image variant for sprout-thinking.svg / sprout-calculator.svg — sized
   up from smaller values that both read as "too small". Tested 52/64/72/80/88px live: the card has no fixed
   height (flex column, grows to fit content) so nothing hard-clips even
   at 88px, but by 88px the icon starts crowding the card's top border
   and the card grows noticeably taller than the barcode shortcut beneath
   it. 72px is the largest size that still leaves comfortable padding on
   all sides. */
.fs-knowledge-icon-img { width: 72px; height: 72px; object-fit: contain; }
.fs-knowledge-title { font-size: .88rem; font-weight: 800; color: var(--ink); }
.fs-knowledge-btn .dv-r-sub { font-size: .68rem; margin-top: 3px; }
.fs-knowledge-btn[aria-pressed="true"] {
  border-color: var(--sky); background: var(--sprout-green-tint);
}
/* Barcode shortcut — a lighter-weight link under the two knowledge
   cards, not a third fs-knowledge-btn — scanning is
   really the fastest form of EITHER answer above, not a third kind of
   knowledge, so it shouldn't visually compete with the fork itself —
   same demotion the rest of the app gives to "a faster way" affordances
   next to a required question. #barcodeScanChip keeps its id;
   barcode/scanner.js finds and wires it purely by that id, unaffected by
   this class name change from the old .barcode-scan-chip-pinned. */
/* v1.31.0: reverted from the filled/dashed treatment back to a quiet
   outline style. The filled version (matching the two knowledge cards'
   solid look) was originally chosen so this read as equally important
   to the fork above it — but once the favourites shortcut below it was
   ALSO given that same filled/dashed treatment (for the same "equal
   importance" reasoning, independently), the two shortcuts became
   visually identical except for accent hue, reading as one list of
   interchangeable "faster path" options rather than two different
   kinds of tool. Since the favourites shortcut is the one that
   actually carries real personalisation value (the "like 'X'?" line),
   it's the one that should keep the heavier fill; the barcode shortcut
   goes back to a thin solid border with no fill, so it reads as the
   quieter of the two rather than competing with it. Border style
   (solid, not dashed) is deliberately different from the favourites
   shortcut's dashed border below — dashed is now reserved for "this
   also opens something bigger" (matching how .go-btn:disabled uses a
   dashed border for a related "not the active thing yet" meaning). */
.fs-barcode-shortcut {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  width: 100%; margin-top: 10px; padding: 9px 12px; border-radius: 10px;
  background: transparent; border: 1px solid var(--dv-line);
  color: var(--ink); font: inherit; font-weight: 700; font-size: .78rem;
  cursor: pointer;
}
/* v1.49.0: text/icon colour changed from var(--sky) to var(--ink) — Jon
   reported this genuinely hard to read in practice (blue text on the
   Food Station card's own dark navy-blue background). Checked the
   formal WCAG contrast ratio first rather than assuming: var(--sky)
   against var(--card) in the dark theme is 5.24:1, which technically
   PASSES the 4.5:1 AA threshold for normal text — but a passing ratio
   between two similar hues (both blue) doesn't guarantee something
   reads easily at a glance on a real phone screen, and Jon's direct
   report of it being hard to read is the more trustworthy signal here
   than a formula that doesn't fully capture same-hue-family contrast.
   var(--ink) is the app's own body-text colour, deliberately tuned per
   theme for strong contrast against var(--card)/var(--bg) — 12.35:1 in
   the dark theme specifically — and correctly adapts across every
   theme the way a fixed colour (e.g. plain white) would not; a fixed
   white was tried once before for this exact icon (see the comment on
   .fs-barcode-icon below) and rejected for washing out in light themes. */
.fs-barcode-icon {
  width: 20px; height: 15px; flex: 0 0 auto;
  /* v1.31.0: was a flat hardcoded white, tuned specifically against
     the old filled var(--dv-inset) background (see the removed
     comment in git history / NOTES.md v1.31.0 entry for the original
     reasoning and its explicit light-theme caveat). That background no
     longer applies now this button is transparent/outline — reusing
     the old white would risk washing out against a light card in the
     default/rainbow/caregiver themes, exactly the failure mode the old
     comment warned about. Switched to currentColor (var(--sky), same
     as this button's text) so the icon always matches a colour that's
     already confirmed legible against var(--card) in every theme,
     rather than a colour tuned for a background this button no longer
     has.

     v1.49.0: currentColor now resolves to var(--ink) (the button's own
     colour was changed, see .fs-barcode-shortcut's own comment above)
     rather than var(--sky) — no change needed to this rule itself,
     currentColor automatically follows whatever the parent's text
     colour becomes. */
  color: currentColor;
}
.fs-barcode-shortcut:active { transform: scale(0.98); }
.fs-barcode-shortcut:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
.fs-barcode-shortcut:disabled { opacity: .55; cursor: wait; }

/* ============================================================
   FAVOURITES SHORTCUT ON THE FRONT DOOR — see the
   matching comment block in index.html above #favShortcutBtn for the
   full revert note. This block (down to "END") can be deleted as one
   unit; nothing else needs to change. Intentionally does NOT mirror
   .fs-barcode-shortcut's layout properties anymore as of v1.31.0 (it
   did before) — the two are now deliberately different shapes, not
   just different accent colours, so removing this block still cannot
   affect the barcode shortcut's own styling, and vice versa.

   v1.31.0: promoted to a filled pill with a badge-shaped icon and a
   two-line text layout (title + optional personalised subline), up
   from the flat single-line dashed treatment it shared with the
   barcode shortcut before this pass. This is the shortcut that
   actually carries new information (which saved food is being
   referenced), so it's the one that should read as the heavier of the
   two — see the barcode shortcut's own comment above for the other
   side of this reasoning. Dashed border kept and made more prominent
   (1.5px solid-gold-tinted fill behind it, not just an outline) so it
   still reads as "opens something", not as a default/primary CTA —
   this app's real primary actions (.go-btn) are solid-filled with no
   border at all, and this shortcut deliberately stays visually
   distinct from that. */
.fs-fav-shortcut {
  display: flex; align-items: center; gap: 10px;
  width: 100%; margin-top: 8px; padding: 10px 12px; border-radius: 14px;
  background: rgba(255,201,60,0.14); border: 1.5px solid var(--sun);
  color: var(--sun-ink); font: inherit; text-align: left;
  cursor: pointer;
}
.fs-fav-shortcut-badge {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 50%;
  background: rgba(255,201,60,0.25);
  display: flex; align-items: center; justify-content: center;
  font-size: .85rem; line-height: 1;
}
.fs-fav-shortcut-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.fs-fav-shortcut-title {
  font-family: 'Baloo 2', sans-serif; font-weight: 700; font-size: .8rem;
  color: var(--ink);
}
/* Personalised subline (e.g. "like 'Fish fingers'?") — see
   js/favourites/favourites-ui.js for how/when this gets filled in and
   why it's a separate element rather than rewriting the title text.
   Hidden by default via the [hidden] attribute in index.html; the JS
   only ever sets textContent + removes [hidden], never touches the
   title line, so a user with zero saved favourites sees an unchanged,
   accurate title with no empty gap where the subline would go. */
.fs-fav-shortcut-sub {
  font-size: .72rem; font-weight: 700; color: var(--sun-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fs-fav-shortcut:active { transform: scale(0.98); }
.fs-fav-shortcut:focus-visible { outline: 3px solid var(--sun); outline-offset: 2px; }
/* END favourites shortcut on the front door */

/* ---- squash dilution — mandatory, not optional ---------------------- */
.dv-dilute {
  margin-top: 12px; padding: 12px; border-radius: 16px;
  background: rgba(255, 201, 60, .10); border: 1.5px solid rgba(255, 201, 60, .45);
}
.dv-dilute h3 { margin: 0 0 3px; font-size: .86rem; font-family: 'Baloo 2', Nunito, sans-serif; font-weight: 800; color: var(--sun-ink); }
.dv-dilute p { margin: 0 0 10px; font-size: .74rem; font-weight: 700; color: var(--ink-soft); line-height: 1.4; }
.dv-ratios { display: flex; gap: 7px; flex-wrap: wrap; }
.dv-ratio {
  flex: 1 1 auto; padding: 9px 6px; border-radius: 12px; background: var(--dv-inset);
  border: 2px solid transparent; color: var(--ink); font: inherit; font-weight: 800;
  font-size: .76rem; cursor: pointer;
}
.dv-ratio .dv-r-sub { display: block; font-size: .62rem; font-weight: 700; color: var(--ink-soft); margin-top: 2px; }
.dv-ratio[aria-pressed="true"] { border-color: var(--sun); background: rgba(255, 201, 60, .18); }
/* "Not sure" shares its 1:4 factor with a confident direct pick, but must
   never look the same once selected — the caregiver still doesn't know
   the actual ratio, and the UI shouldn't imply otherwise. Dashed instead
   of solid, plus the maths line below carries an explicit "assumed" tag
   (see .dv-assumed-tag). */
.dv-ratio[data-ratio-id="?"][aria-pressed="true"] {
  border-style: dashed;
  border-color: rgba(214, 108, 26, .75);
  background: rgba(214, 108, 26, .12);
}
.dv-maths {
  margin-top: 10px; padding: 9px 11px; border-radius: 11px; background: var(--dv-inset);
  font-size: .78rem; font-weight: 800; line-height: 1.5;
}
.dv-maths .dv-was { color: var(--ink-soft); text-decoration: line-through; }
.dv-maths .dv-now { color: var(--sun-ink); font-size: 1rem; }
.dv-assumed-tag {
  display: inline-block; margin-top: 4px; padding: 3px 8px; border-radius: 999px;
  background: rgba(214, 108, 26, .15); color: #A85416; font-size: .66rem;
  font-weight: 800; letter-spacing: .02em;
}

/* ---- amount --------------------------------------------------------- */
/* display scoped so the `hidden` attribute still works — see
   .plate-attention-flag in main.css for why. */
.dv-stepper:not([hidden]) { display: flex; }
.dv-stepper {  align-items: center; gap: 10px; margin-top: 10px; }
.dv-sbtn {
  width: 46px; height: 46px; border-radius: 14px; border: none; background: var(--sprout-green);
  color: #fff; font-size: 1.3rem; font-weight: 800; cursor: pointer; flex: 0 0 auto;
  display: grid; place-items: center; padding: 0;
}
.dv-valbox { flex: 1; background: var(--bg2); border-radius: 14px; padding: 9px; text-align: center; }
.dv-valbox input {
  width: 100%; background: none; border: none; color: var(--ink);
  font-family: 'Baloo 2', Nunito, sans-serif; font-size: 1.5rem; font-weight: 800; text-align: center;
}
.dv-valbox input:focus { outline: none; }
.dv-u { font-size: .68rem; font-weight: 700; color: var(--ink-soft); }

/* ---- carbs — the "must agree" step ----------------------------------
   .dv-prefill is a real button, not a static box. It must be TAPPED to
   accept the number, every single time. There is no state in which this
   value silently becomes "answered" on its own. */
.dv-linkish {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  width: 100%; padding: 13px 12px; border-radius: 14px;
  background: var(--sprout-green); border: none; color: #fff; font: inherit;
  font-weight: 800; font-size: .92rem; text-align: center; cursor: pointer;
  box-shadow: 0 4px 10px rgba(14, 140, 122, .3);
}
.dv-linkish:active { transform: scale(0.98); }
.dv-linkish-badge {
  font-size: .66rem; font-weight: 800; text-transform: uppercase; letter-spacing: .02em;
  background: rgba(255, 255, 255, .22); padding: 3px 9px; border-radius: 20px;
}
.dv-linkish-main { font-size: .9rem; }
.dv-or-divider { text-align: center; font-size: .68rem; font-weight: 700; color: var(--ink-soft); margin: 10px 0 8px; }
/* Clearly the secondary option next to the solid green "own bottle"
   button, but with real button affordance — a visible border and a
   tap-hint strip, not muted static-looking text. */
/* display scoped so the `hidden` attribute still works — see
   .plate-attention-flag in main.css for why. */
.dv-prefill:not([hidden]) { display: flex; }
.dv-prefill {
   align-items: center; gap: 10px; padding: 12px; border-radius: 14px;
  background: var(--dv-surface); border: 2px solid var(--sky);
  width: 100%; text-align: left; font: inherit; cursor: pointer; color: inherit;
}
.dv-prefill:active { transform: scale(0.98); background: var(--sprout-green-tint); }
.dv-prefill .dv-big { font-family: 'Baloo 2', Nunito, sans-serif; font-size: 1.3rem; font-weight: 800; color: var(--sky); }
.dv-prefill .dv-src { font-size: .66rem; font-weight: 800; color: var(--sky); text-transform: uppercase; letter-spacing: .02em; display: block; margin-bottom: 2px; }
.dv-prefill .dv-txt { font-size: .72rem; font-weight: 700; color: var(--ink-soft); line-height: 1.35; }
.dv-prefill .dv-tap-hint {
  font-size: .68rem; font-weight: 800; color: var(--card); margin-top: 6px;
  display: inline-block; background: var(--sky); padding: 4px 9px; border-radius: 20px;
}

/* ---- result --------------------------------------------------------- */
.dv-result {
  margin-top: 14px; padding: 14px; border-radius: var(--radius-md); text-align: center;
  background: var(--dv-inset);
}
/* v1.37.0: .dv-n is the actual final carbs total for this station
   (#fsOut / #dvOut) — the single most important number on either
   screen — and .dv-l is the caption explaining it (the estimate range,
   "Xg at Yg per 100g", etc. — see food-station.js's carbRangeForFood
   comment for why that range matters). Neither had font-scale support
   before this; both now do. */
.dv-result .dv-n { font-family: 'Baloo 2', Nunito, sans-serif; font-size: calc(2rem * var(--font-scale, 1)); font-weight: 800; color: var(--sky); display: block; }
.dv-result .dv-l { font-size: calc(.76rem * var(--font-scale, 1)); font-weight: 700; color: var(--ink-soft); }

/* ============================================================
   QUICK-LOG NAMING PROMPT (added 25 Aug 2026) — see the matching
   comment block in index.html above #fsNamePrompt for the full
   revert note. This entire block (down to the "END" comment) can
   be deleted as a single unit with nothing else needing to change.
   ============================================================ */
.fs-name-prompt[hidden] { display: none !important; }
.fs-name-prompt {
  margin-top: 12px; padding: 12px; border-radius: 14px;
  background: rgba(63,169,245,0.10); border: 1.5px dashed var(--sky);
  display: flex; flex-direction: column; gap: 8px;
}
@media (prefers-reduced-motion: no-preference) {
  .fs-name-prompt { animation: fsNamePromptIn .2s ease-out; }
  @keyframes fsNamePromptIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
}
.fs-name-prompt-label { font-size: .78rem; font-weight: 800; color: var(--ink); }
.fs-name-prompt-sub { font-size: .72rem; color: var(--ink-soft); font-weight: 600; margin-top: -4px; }
/* Input is now its own full-width row (was inline with Save) — freed up
   by moving Skip out from underneath it, see .fs-name-prompt-row below. */
.fs-name-prompt-input {
  width: 100%; padding: 10px 12px; border-radius: 10px; border: 1.5px solid var(--dv-line);
  /* Was .85rem (13.6px) — under Safari's 16px auto-zoom-on-focus
     threshold. TO REVERT: back to .85rem. */
  background: var(--dv-inset); color: var(--ink); font: inherit; font-size: 1rem;
  box-sizing: border-box;
}
.fs-name-prompt-input::placeholder { color: var(--ink-soft); opacity: .7; }
.fs-name-prompt-input:focus-visible { outline: 2px solid var(--sky); border-color: var(--sky); }
/* Skip and Save as an equal-weight pair (6 Sep 2026 — was Save alone next
   to the input, with Skip a small underlined text link below both; Jon's
   ask was to make Skip more prominent, since not naming a food is a
   completely ordinary choice, not an edge case, and the old link read as
   an afterthought). Same padding/radius/font-size/weight on both — the
   only difference is fill vs outline, which is enough to still read Save
   as the nudged default without making Skip look like a lesser option. */
.fs-name-prompt-row { display: flex; gap: 8px; }
.fs-name-prompt-skip, .fs-name-prompt-save {
  flex: 1; padding: 10px 16px; border-radius: 10px;
  font: inherit; font-weight: 800; font-size: .82rem; cursor: pointer;
}
.fs-name-prompt-save { border: none; background: var(--sky); color: white; }
.fs-name-prompt-save:active { transform: scale(0.97); }
.fs-name-prompt-save:focus-visible { outline: 3px solid var(--sky); outline-offset: 2px; }
.fs-name-prompt-skip { border: 1.5px solid var(--dv-line); background: transparent; color: var(--ink); }
.fs-name-prompt-skip:active { transform: scale(0.97); }
.fs-name-prompt-skip:focus-visible { outline: 2px solid var(--sky); outline-offset: 2px; }
/* END quick-log naming prompt */

.drinks-v2 .drink-go-btn { width: 100%; margin-top: 12px; }

@media (prefers-reduced-motion: no-preference) {
  .dv-step[data-state="open"] .dv-step-body { animation: dvIn .18s ease-out; }
  @keyframes dvIn { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: none; } }
}

/* ====================================================================
   FOOD STATION — food-station-mockup.html (14 Aug 2026)

   The step shell, chips, required-question panels, steppers, prefill
   button and result block are the Drinks Station's .dv-* classes,
   reused rather than duplicated: the mockup's own CSS comment says the
   shell was "lifted wholesale from the Drinks Station", so two copies
   of it would only be two things to drift apart.

   Only what is genuinely food-only lives here. Every colour is a
   per-theme var() — a hardcoded rgba(255,255,255,…) looks right in
   theme-night and vanishes in the light, rainbow, caregiver and
   high-contrast themes.
   ==================================================================== */

.food-station-step .dv-step-opt {
  font-size: .7rem; font-weight: 700; color: var(--ink-soft);
}
/* "(optional)" is guidance for ANSWERING the step. Once it's answered and
   collapsed the word is noise, and it was competing with the answer chip for
   width — "What's on your plate?" wrapped to two lines the moment you picked
   a food. Hiding it once the step closes fixes the wrap and says something
   truer at the same time. */
.food-station-step:not([data-state="open"]) .dv-step-opt { display: none; }

/* Step 1's body is the app's real picker, not the mockup's stub rail, so
   the picker keeps its own layout and only loses the header it used to
   carry (that job belongs to .dv-step-head now). */
.food-station-step .food-emoji-picker {
  background: none; border: none; padding: 0; margin: 0; box-shadow: none;
}
.food-station-step .food-picker-purpose {
  font-size: .72rem; font-weight: 700; color: var(--ink-soft);
  line-height: 1.4; margin: 0 0 10px;
}

/* The portion guide, and the bread panel's escape hatch. */
.fs-guide {
  margin-top: 10px; padding: 9px 11px; border-radius: 11px; background: var(--dv-inset);
  font-size: .72rem; font-weight: 700; color: var(--ink-soft); line-height: 1.6;
}
.fs-guide[hidden] { display: none !important; }
.fs-guide ul { margin: 0; padding-left: 18px; }

.food-station-step .bread-own-numbers-btn {
  display: block; width: 100%; margin-top: 10px; background: none; border: none;
  color: var(--ink-soft); font: inherit; font-size: .72rem; font-weight: 800;
  text-decoration: underline; cursor: pointer; padding: 6px 0 0;
}

/* Three portion chips have longer labels than the drinks station's two, so
   they get a floor width to stop "📦 Packet says" wrapping to three lines
   on a narrow phone. */
.food-station-step #portionCheck .dv-ratio { flex: 1 1 30%; min-width: 96px; }

/* The evidence disclosure — the one addition to the mockup, agreed 14 Aug.
   Deliberately quiet: it sits under the prefill button as a secondary way
   in, never competing with the two required buttons above it. */
.fs-src-evidence { margin-top: 8px; }
.fs-src-evidence[hidden] { display: none !important; }

/* Step 2's gate button. Reuses .go-btn's look (same visual weight as the
   final Add button) but sits mid-step, not at the foot of the card, so it
   needs its own top margin instead of .go-btn's. Always visible and
   rendered normally in its own step's flow — no longer gated behind
   `hidden`/appear-disappear at all (that went through three different
   approaches: a delayed pop-in, then a shared fixed footer bar, both
   dropped — see food-station.js's settleTimer comment and this file's
   git history for why). The button's enabled/disabled STATE is now what
   carbOK/amountDone()/vol>0/carbAccepted drive, via the standard
   `disabled` attribute — `.go-btn:disabled` (main.css) already provides
   the dimmed look, no separate rule needed here. Tapping it is the ONLY
   way Step 3 is revealed (see .food-station-step[data-state="locked"]
   above). */
.fs-continue-btn { margin-top: 14px; margin-bottom: 0; }

/* Drinks Station's own Continue gate buttons — same look, same rules. */
.dv-continue-btn { margin-top: 14px; margin-bottom: 0; }

/* The result block is .dv-result, but food adds the favourite/scanned
   provenance note on its own line under the caption. */
.dv-result .fs-fav-note { display: block; margin-top: 6px; }
.dv-result .fs-fav-note[hidden] { display: none !important; }
.dv-result .fs-fav-note b { color: var(--ink); }

/* The mockup's readonly slice box must not look like a field you can type
   into — it is driven entirely by the − / + either side of it. */
#breadPresetSliceCount { cursor: default; }

/* ============================================================
   RESULT-CARD LOG BUTTON — see the matching comment block in
   index.html above #resultCardLogBtn for the full context/revert note.
   White/light button on the card's own dark green gradient background,
   deliberately NOT reusing .log-meal-btn's green-on-card look — that
   green would blend into #result-card's own green gradient background
   rather than standing out as the thing to tap. Echoes the existing
   white "Nice — added to your plate!" (.sprout-says) pill already
   inside this same card for a consistent "light element on the green
   background" language, rather than introducing a third visual style.
   ============================================================ */
.result-card-log-btn {
  width: 100%; margin-top: 12px; padding: 13px 16px; border: none;
  border-radius: 16px; background: #fff; color: var(--sprout-green-deep);
  font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 1.05rem;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  box-shadow: 0 4px 14px rgba(0,0,0,0.18);
}
.result-card-log-btn[hidden] { display: none !important; }
.result-card-log-btn:active { transform: scale(0.98); }
.result-card-log-btn:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }

/* ============================================================
   OUR STORY SO FAR — see the matching comment block in index.html
   above #storyAccordionHeader for the full context/revert note.
   Warm, celebratory tone rather than a plain stats table — the
   numbers matter less here than the sentences around them.
   ============================================================ */
.story-empty {
  text-align: center; padding: 20px 12px; color: var(--ink-soft);
  font-weight: 700; font-size: 0.9rem;
}
.story-line {
  font-size: 0.85rem; font-weight: 700; color: var(--ink);
  line-height: 1.45; margin: 0 0 12px;
}
.story-since {
  font-family: 'Baloo 2', sans-serif; font-size: 0.95rem; font-weight: 800;
  color: var(--sky); text-align: center; margin-bottom: 16px;
}
.story-stats-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
  margin-bottom: 14px;
}
/* v1.50.0: carbs is the last of six chips in a 2-column grid — the
   number here (all-time lifetime carbs) can genuinely grow longer than
   any other stat's figure the more a device is used, so it gets its
   own full-width row (grid-column: 1 / -1, spanning both columns)
   rather than sharing a row with Sprout Score. This gives it real room
   to grow before wrapping becomes a concern at all, per Jon's "if it
   wraps oddly, just move the chip to the next line" — done here by
   giving it a dedicated row from the start rather than reacting to a
   wrap after the fact.

   Sprout Score (item 5, the one before carbs) is ALSO given
   grid-column: 1/-1 — checked the actual CSS Grid auto-placement
   behaviour before shipping rather than assuming it would just work:
   with sparse packing (the default, no grid-auto-flow:dense set),
   Score alone in row 3 column 1 would leave row 3 column 2 as a
   genuine empty gap, since a full-width item can't backfill a
   half-empty row that's already been passed over — it would push to
   row 4 entirely, leaving a visible hole next to Score. Spanning both
   of the last two chips avoids that gap entirely; both now get their
   own full row rather than one full-width item awkwardly orphaning
   half of the row before it. */
.story-stat--score,
.story-stat--carbs { grid-column: 1 / -1; }
.story-stat {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  background: var(--dv-inset); border-radius: 14px; padding: 12px 8px;
  text-align: center;
}
/* v1.50.0: subtle background icon per stat, per Jon's request — a
   large, low-opacity emoji sitting behind the real number/label rather
   than a separate image asset (none exist for these categories, and a
   text character avoids adding new files/loading for a purely
   decorative touch). Positioned absolutely and behind the real content
   (the number/label spans are later in source order and given
   position:relative so they paint on top with no explicit z-index
   needed) with overflow:hidden on the parent so a slightly-oversized
   icon can't spill past this chip's own rounded corners. Uses plain
   per-chip modifier classes (.story-stat--meals etc., added directly
   in index.html's markup) rather than a :has() relationship selector
   keyed off the inner span's id — this codebase has no existing use of
   :has() anywhere, and it's a newer CSS feature than everything else
   this app relies on; an explicit class on each of these six known,
   fixed chips is simpler and safer than depending on it working
   everywhere this app might run. */
.story-stat::before {
  content: ''; position: absolute; font-size: 2.6rem; line-height: 1;
  opacity: 0.12; pointer-events: none; right: -6px; bottom: -10px;
  transform: rotate(-8deg);
}
.story-stat--meals::before { content: '🍽️'; }
.story-stat--garden::before { content: '🌱'; }
.story-stat--badges::before { content: '🏆'; }
.story-stat--streak::before { content: '🔥'; }
.story-stat--score::before { content: '⭐'; }
.story-stat--carbs::before { content: '🍎'; }
.story-stat-num {
  position: relative;
  font-family: 'Baloo 2', sans-serif; font-weight: 800; font-size: 1.5rem;
  color: var(--sun-ink); line-height: 1;
}
.story-stat-label {
  position: relative;
  font-size: 0.7rem; font-weight: 700; color: var(--ink-soft);
  margin-top: 2px;
}
.story-line[hidden] { display: none !important; }

/* ============================================================
   NAME-SAVED CONFIRMATION + EMPTY-NAME NUDGE — see both elements'
   own comments in index.html above #nameSavedConfirm/#nameNudge for
   the full brief. Neither is a modal or a blocking prompt — both sit
   quietly under the name field itself.
   ============================================================ */
.name-saved-confirm {
  font-size: 0.78rem; font-weight: 800; color: var(--mint);
  opacity: 0; transform: translateY(-4px);
  transition: opacity 0.3s ease, transform 0.3s ease;
  margin-top: 4px;
}
.name-saved-confirm.show { opacity: 1; transform: translateY(0); }
.name-nudge {
  font-size: 0.72rem; font-weight: 700; color: var(--sun-ink);
  background: rgba(255,201,60,0.12); border: 1px dashed var(--sun);
  border-radius: 10px; padding: 6px 10px; margin-top: 4px;
}
