/* Shopping (grocery lists) — the /shopping index + /shopping/{id} detail.
 *
 * Ported to the locked Go stack (semantic CSS, no Tailwind) from the
 * legacy grocery components (shy-tomato-legacy src/app/components/grocery
 * + the two shopping route clients). Consumes the production --md-* token
 * palette under @layer components so token-driven theming flows through.
 *
 * Surfaces:
 *   .grocery-index   — the card-grid index + empty state + create modal
 *   .grocery-detail  — one list: header actions, store-section groups,
 *                      sticky add-item bar
 *
 * htmx contract: item mutations re-render #grocery-body; grocery.js
 * layers the optimistic checkbox flip, group collapse, sources expand,
 * modal, and add-bar UX on top.
 */

@layer components {
  /* ===================================================== Shared buttons */
  .grocery-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-block-size: var(--control-h-sm);
    padding-inline: var(--space-4);
    border: 1px solid transparent;
    border-radius: var(--radius);
    font-family: var(--font-body);
    font-size: var(--text-sm);
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    transition:
      background-color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects),
      color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects),
      border-color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects),
      opacity var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects);
  }

  .grocery-btn .icon {
    inline-size: 18px;
    block-size: 18px;
  }

  .grocery-btn--primary {
    background: var(--md-primary);
    color: var(--md-on-primary);
  }

  /* MD3 hover/active via the shared .state-layer overlay (globals.css):
     a darkening ::after on hover/active rather than a flat background
     swap. Decision 4 reserves the --md-primary-hover token for save-heart
     alone, so the primary button uses the canonical state-layer pattern
     (same as .add-button--primary). */
  .grocery-btn--primary.state-layer::after {
    background: var(--md-on-surface);
  }

  .grocery-btn--ghost {
    background: transparent;
    color: var(--md-on-surface-variant);
  }

  .grocery-btn--ghost:hover {
    background: var(--md-surface-container);
    color: var(--md-on-surface);
  }

  .grocery-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  .grocery-btn:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
  }

  /* ====================================================== Index page */
  .grocery-index {
    padding-block: var(--space-6) var(--space-10);
  }

  .grocery-index__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    margin-block-end: var(--space-6);
  }

  .grocery-index__title {
    margin: var(--space-1) 0 0;
    font-family: var(--font-heading);
    font-size: var(--text-2xl);
    font-weight: 500;
    letter-spacing: var(--tracking-tight);
    line-height: 1.1;
    color: var(--md-on-surface);
  }

  .grocery-index__subtitle {
    margin: var(--space-1) 0 0;
    font-size: var(--text-sm);
    color: var(--md-on-surface-variant);
  }

  /* Card grid — 2-up on phones, scaling to 4-up on wide. */
  .grocery-card-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-4);
    margin: 0;
    padding: 0;
    list-style: none;
  }

  @media (min-width: 640px) {
    .grocery-card-grid {
      grid-template-columns: repeat(3, minmax(0, 1fr));
    }
  }

  @media (min-width: 1024px) {
    .grocery-card-grid {
      grid-template-columns: repeat(4, minmax(0, 1fr));
    }
  }

  /* -------------------------------------------------- Grocery list card */
  .grocery-card {
    display: block;
    overflow: hidden;
    border: 1px solid var(--md-border);
    border-radius: var(--radius-lg);
    background: var(--md-surface-container-low);
    text-decoration: none;
    transition:
      border-color var(--motion-spring-default-effects-duration) var(--motion-spring-default-effects),
      box-shadow var(--motion-spring-default-effects-duration) var(--motion-spring-default-effects);
  }

  .grocery-card:hover {
    border-color: var(--md-border-strong);
    box-shadow: 0 4px 12px rgb(0 0 0 / 8%);
  }

  .grocery-card:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
  }

  .grocery-card__ring-band {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    block-size: 112px;
    background: var(--md-tag-green);
  }

  .grocery-card__ring {
    transform: rotate(-90deg);
  }

  .grocery-card__ring-track {
    color: color-mix(in oklch, var(--md-on-surface-variant) 20%, transparent);
  }

  .grocery-card__ring-fill {
    color: var(--md-success);
    transition: stroke-dashoffset var(--motion-spring-slow-spatial-duration) var(--motion-spring-slow-spatial);
  }

  .grocery-card__ring-label {
    position: absolute;
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--md-on-surface);
  }

  .grocery-card__body {
    padding: var(--space-4);
  }

  .grocery-card__name {
    margin: 0;
    font-family: var(--font-heading);
    font-size: var(--text-base);
    font-weight: 500;
    color: var(--md-on-surface);
    transition: color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects);
  }

  .grocery-card:hover .grocery-card__name {
    color: var(--md-primary);
  }

  .grocery-card__count {
    margin: var(--space-1) 0 0;
    font-size: var(--text-sm);
    color: var(--md-on-surface-variant);
  }

  /* ----------------------------------------------------- Empty state */
  .grocery-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-block-size: 60vh;
    padding-inline: var(--space-4);
    text-align: center;
  }

  .grocery-empty__mascot {
    margin-block-end: var(--space-6);
  }

  .grocery-empty__heading {
    margin: 0;
    font-family: var(--font-heading);
    font-size: var(--text-2xl);
    font-weight: 600;
    color: var(--md-on-surface);
  }

  .grocery-empty__body {
    margin: var(--space-3) 0 0;
    max-inline-size: 28rem;
    font-size: var(--text-base);
    color: var(--md-on-surface-variant);
  }

  .grocery-empty__cta {
    margin-block-start: var(--space-6);
  }

  /* ======================================================= Create modal */
  .grocery-modal {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
  }

  .grocery-modal[hidden] {
    display: none;
  }

  .grocery-modal__scrim {
    position: absolute;
    inset: 0;
    background: var(--md-overlay);
    animation: fade-in 160ms var(--motion-spring-fast-effects);
  }

  .grocery-modal__panel {
    position: relative;
    inline-size: min(100%, 26rem);
    padding: var(--space-6);
    border-radius: var(--radius-lg);
    background: var(--md-surface-container-low);
    box-shadow: 0 12px 40px rgb(0 0 0 / 18%);
    animation: scale-in 200ms var(--motion-spring-fast-spatial);
  }

  .grocery-modal__title {
    margin: 0;
    font-family: var(--font-heading);
    font-size: var(--text-xl);
    font-weight: 600;
    color: var(--md-on-surface);
  }

  .grocery-modal__desc {
    margin: var(--space-2) 0 0;
    font-size: var(--text-sm);
    color: var(--md-on-surface-variant);
  }

  .grocery-modal__label {
    display: block;
    margin-block-start: var(--space-5);
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--md-on-surface);
  }

  .grocery-modal__label-hint {
    font-weight: 400;
    color: var(--md-on-surface-variant);
  }

  .grocery-modal__input {
    inline-size: 100%;
    margin-block-start: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--md-border);
    border-radius: var(--radius);
    background: var(--md-surface-container-low);
    font-family: var(--font-body);
    font-size: var(--text-base);
    color: var(--md-on-surface);
  }

  .grocery-modal__input:focus-visible {
    outline: none;
    border-color: var(--md-primary);
    box-shadow: var(--focus-ring);
  }

  .grocery-modal__actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-3);
    margin-block-start: var(--space-6);
  }

  /* ====================================================== Detail page */
  .grocery-detail {
    display: flex;
    flex-direction: column;
    min-block-size: calc(100dvh - var(--tab-bar-height));
  }

  .grocery-detail__scroll {
    flex: 1;
    padding-block: var(--space-6) var(--space-8);
  }

  .grocery-detail__back {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin-block-end: var(--space-4);
    font-size: var(--text-sm);
    color: var(--md-on-surface-variant);
    text-decoration: none;
    transition: color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects);
  }

  .grocery-detail__back:hover {
    color: var(--md-on-surface);
  }

  .grocery-detail__back .icon {
    inline-size: 16px;
    block-size: 16px;
  }

  .grocery-detail__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    margin-block-end: var(--space-5);
    flex-wrap: wrap;
  }

  .grocery-detail__heading {
    flex: 1;
    min-inline-size: 0;
  }

  /* The page's one generous move (bible 0.3.2): a confident Fraunces
     title at editorial scale, like the mockup's big "Groceries" head. */
  .grocery-detail__title {
    margin: 0;
    font-family: var(--font-heading);
    font-size: var(--text-3xl);
    font-weight: 600;
    letter-spacing: var(--tracking-tight);
    line-height: 1.05;
    color: var(--md-on-surface);
  }

  .grocery-detail__subtitle {
    margin: var(--space-2) 0 0;
    font-size: var(--text-sm);
    color: var(--md-on-surface-variant);
  }

  .grocery-detail__subtitle b {
    font-weight: 600;
    color: var(--md-on-surface);
  }

  /* Gather-progress track under the title (mockup parity). Tomato fill on
     a calm zinc track; the only motion is the width easing as items are
     gathered (bible 1.9). */
  .grocery-detail__progress {
    margin-block-start: var(--space-3);
    block-size: 7px;
    border-radius: var(--radius-pill);
    background: var(--md-surface-container-high);
    overflow: hidden;
  }

  .grocery-detail__progress-bar {
    display: block;
    block-size: 100%;
    border-radius: inherit;
    background: var(--md-primary);
    transition: inline-size var(--motion-spring-slow-spatial-duration) var(--motion-spring-slow-spatial);
  }

  .grocery-detail__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
  }

  .grocery-detail__cart-error {
    margin-block-end: var(--space-3);
  }

  .grocery-detail__cart-error:empty {
    display: none;
  }

  .grocery-detail__cart-error .shop-error__copy {
    display: block;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    background: var(--md-error-soft);
    color: var(--md-error);
    font-size: var(--text-sm);
  }

  /* ----------------------------------------------- Pinned "Shop" bar
     SHY-65 reskin: the mockup's signature floating CTA. Lives in
     #grocery-body so its "{n} to buy" count re-renders live on each
     toggle; sticks to the bottom of the scroll column, just above the
     sticky add-item bar. A frosted card (elevation-in-motion shadow is
     OK here — it's a floating, raised affordance, not resting chrome). */
  .grocery-shopbar {
    position: sticky;
    inset-block-end: var(--space-3);
    z-index: 6;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-block-start: var(--space-5);
    padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
    border: 1px solid var(--md-border-muted);
    border-radius: var(--radius-lg);
    background: var(--md-overlay-light);
    backdrop-filter: saturate(180%) blur(12px);
    -webkit-backdrop-filter: saturate(180%) blur(12px);
    box-shadow: var(--shadow-lg);
  }

  .grocery-shopbar__meta {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    line-height: 1.05;
    color: var(--md-on-surface-variant);
  }

  .grocery-shopbar__meta b {
    font-family: var(--font-heading);
    font-size: var(--text-xl);
    font-weight: 600;
    color: var(--md-on-surface);
  }

  .grocery-shopbar__meta span {
    font-size: var(--text-xs);
  }

  .grocery-shopbar__cta {
    flex: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-block-size: var(--control-h);
    padding-inline: var(--space-4);
    border: none;
    border-radius: var(--radius);
    background: var(--md-primary);
    color: var(--md-on-primary);
    font-family: var(--font-body);
    font-size: var(--text-sm);
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    transition: transform var(--motion-spring-fast-spatial-duration) var(--motion-spring-fast-spatial);
  }

  .grocery-shopbar__cta .icon {
    inline-size: 18px;
    block-size: 18px;
  }

  /* MD3 state-layer darken on hover/press (matches .grocery-btn--primary). */
  .grocery-shopbar__cta.state-layer::after {
    background: var(--md-on-surface);
  }

  .grocery-shopbar__cta:active {
    transform: scale(0.98);
  }

  .grocery-shopbar__cta:disabled {
    background: var(--md-surface-container-high);
    color: var(--md-on-surface-variant);
    box-shadow: none;
    cursor: default;
  }

  .grocery-shopbar__cta:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
  }

  /* ------------------------------------------------- Store-section groups
     SHY-65 reskin: each aisle reads as a calm white CARD (the mockup's
     aisle-grouped grocery cards) — hairline border, symmetric radius, no
     shadow at rest (bible 1.6/1.8). The header carries a soft tinted icon
     chip + accent uppercase label + a pill count, with the colour pulled
     per-aisle from the bible-sanctioned --md-tag-* secondary palette
     (bible 2.5), defaulting to tomato-warm. Tomato stays the only PRIMARY
     accent; the aisle tints are the same restrained metadata palette the
     recipe surfaces already use, applied as soft icon chips — never as
     saturated full-width blocks (bible 1.12). */
  .grocery-groups {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
  }

  .grocery-group {
    /* Per-aisle accent pair (bg tint + foreground). Default = tomato-soft
       (--md-primary-container / its on-color — the warm tag family);
       overridden per data-aisle below. */
    --aisle-tint: var(--md-primary-container);
    --aisle-ink: var(--md-on-primary-container);
    overflow: hidden;
    border: 1px solid var(--md-border-muted);
    border-radius: var(--radius-lg);
    background: var(--md-surface-container-low);
  }

  .grocery-group__header {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    inline-size: 100%;
    padding: var(--space-3) var(--space-4);
    border: none;
    background: transparent;
    color: var(--md-on-surface);
    text-align: start;
    cursor: pointer;
    transition: color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects);
  }

  /* Soft tinted icon chip — the aisle's recognizable marker. */
  .grocery-group__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    inline-size: 30px;
    block-size: 30px;
    border-radius: var(--radius);
    background: var(--aisle-tint);
    color: var(--aisle-ink);
  }

  .grocery-group__name {
    flex: 1;
    font-size: var(--text-xs);
    font-weight: 700;
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--aisle-ink);
  }

  .grocery-group__count {
    flex-shrink: 0;
    padding: 2px var(--space-2);
    border-radius: var(--radius-pill);
    background: var(--md-surface-container);
    font-family: var(--font-mono);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: normal;
    text-transform: none;
    color: var(--md-on-surface-variant);
  }

  /* All gathered: the count pill goes green-on-soft-green (success), the
     card calms (dim), and the header ink mutes. */
  .grocery-group[data-all-checked] .grocery-group__count {
    background: var(--md-success-soft);
    color: var(--md-success);
  }

  .grocery-group[data-all-checked] {
    background: var(--md-surface);
  }

  .grocery-group__caret {
    flex-shrink: 0;
    color: var(--md-on-surface-variant);
    transition: transform var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects);
  }

  .grocery-group.is-collapsed .grocery-group__caret {
    transform: rotate(-90deg);
  }

  /* Per-aisle accent pairs — bible-sanctioned --md-tag-* secondary palette
     (2.5). Soft tints only; the saturated value never blocks a surface. */
  .grocery-group[data-aisle="produce"] {
    --aisle-tint: var(--md-tag-green);
    --aisle-ink: var(--md-tag-green-foreground);
  }
  .grocery-group[data-aisle="meat"],
  .grocery-group[data-aisle="seafood"] {
    --aisle-tint: var(--md-primary-container);
    --aisle-ink: var(--md-on-primary-container);
  }
  .grocery-group[data-aisle="dairy"],
  .grocery-group[data-aisle="frozen"],
  .grocery-group[data-aisle="beverages"] {
    --aisle-tint: var(--md-tag-blue);
    --aisle-ink: var(--md-tag-blue-foreground);
  }
  .grocery-group[data-aisle="pantry"],
  .grocery-group[data-aisle="canned"],
  .grocery-group[data-aisle="baking"],
  .grocery-group[data-aisle="spices"],
  .grocery-group[data-aisle="condiments"],
  .grocery-group[data-aisle="bakery"] {
    --aisle-tint: var(--md-primary-container);
    --aisle-ink: var(--md-on-primary-container);
  }
  /* Catch-all "Other" (empty slug) + manual items: neutral chip. */
  .grocery-group[data-aisle=""] {
    --aisle-tint: var(--md-tag-neutral);
    --aisle-ink: var(--md-tag-neutral-foreground);
  }

  .grocery-group__items {
    padding: 0 var(--space-2) var(--space-2);
    transition: opacity var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects);
  }

  .grocery-group[data-all-checked] .grocery-group__items {
    opacity: 0.55;
  }

  .grocery-group.is-collapsed .grocery-group__items {
    display: none;
  }

  /* --------------------------------------------------------- Item rows
     SHY-65 reskin: roomier rows with a ROUND checkbox (the mockup's
     gather circle), a stacked name + "from N recipes" provenance line,
     and a hairline divider between rows. */
  .grocery-item + .grocery-item .grocery-item__row {
    border-block-start: 1px solid var(--md-border-muted);
  }

  .grocery-item__row {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-2);
  }

  .grocery-item__check {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    inline-size: 26px;
    block-size: 26px;
    margin-block-start: 1px;
    padding: 0;
    /* SHY-72 (WCAG 1.4.11): resting border was --md-border-strong (#a1a1aa),
       2.56:1 vs the white card — below the 3:1 non-text minimum for this
       primary tap affordance. --md-on-surface-variant (#71717a) is 4.83:1
       vs white. Checked state below keeps the tomato fill, unchanged. */
    border: 2px solid var(--md-on-surface-variant);
    border-radius: var(--radius-pill);
    background: transparent;
    color: transparent;
    cursor: pointer;
    transition:
      background-color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects),
      border-color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects),
      color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects),
      transform var(--motion-spring-fast-spatial-duration) var(--motion-spring-fast-spatial);
  }

  .grocery-item__check:hover {
    border-color: var(--md-primary);
  }

  .grocery-item.is-checked .grocery-item__check {
    border-color: var(--md-primary);
    background: var(--md-primary);
    color: var(--md-on-primary);
  }

  /* Functional state-spring on the tick (bible 1.9: motion in service of
     state). Honoured only when motion is allowed (block below). */
  .grocery-item.is-checked .grocery-item__check {
    transform: scale(1);
  }

  .grocery-item__check:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
  }

  .grocery-item__check-mark {
    opacity: 0;
    transition: opacity var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects);
  }

  .grocery-item.is-checked .grocery-item__check-mark {
    opacity: 1;
  }

  /* Stacked name + provenance. */
  .grocery-item__main {
    flex: 1;
    min-inline-size: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
  }

  .grocery-item__text {
    font-size: var(--text-base);
    line-height: var(--leading-snug);
    color: var(--md-on-surface);
    transition: color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects);
  }

  .grocery-item.is-checked .grocery-item__text {
    color: var(--md-on-surface-variant);
    text-decoration: line-through;
    text-decoration-color: var(--md-border-strong);
  }

  /* Provenance line — "from N recipes", a quiet expandable caption. */
  .grocery-item__sources-toggle {
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 0;
    border: none;
    background: transparent;
    color: var(--md-on-surface-variant);
    font-size: var(--text-xs);
    font-weight: 500;
    cursor: pointer;
    transition: color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects);
  }

  .grocery-item__sources-toggle:hover {
    color: var(--md-on-surface);
  }

  .grocery-item.is-checked .grocery-item__sources-toggle {
    color: color-mix(in oklch, var(--md-on-surface-variant) 70%, transparent);
  }

  .grocery-item__sources-caret {
    flex-shrink: 0;
    transition: transform var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects);
  }

  .grocery-item__sources-toggle.is-open .grocery-item__sources-caret {
    transform: rotate(180deg);
  }

  /* Remove — a quiet icon button, hover/focus-revealed (bible 1.6: no
     resting chrome). The text label is visually hidden so the row reads
     clean; assistive tech still gets it via the button's aria-label. */
  .grocery-item__remove {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 28px;
    block-size: 28px;
    margin-block-start: -2px;
    padding: 0;
    border: none;
    border-radius: var(--radius);
    background: transparent;
    color: color-mix(in oklch, var(--md-on-surface-variant) 70%, transparent);
    cursor: pointer;
    opacity: 0;
    transition:
      opacity var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects),
      background-color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects),
      color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects);
  }

  .grocery-item__remove .icon {
    inline-size: 16px;
    block-size: 16px;
  }

  .grocery-item__remove-label {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }

  .grocery-item__row:hover .grocery-item__remove,
  .grocery-item__remove:focus-visible {
    opacity: 1;
  }

  .grocery-item__remove:hover {
    background: var(--md-error-soft);
    color: var(--md-error);
  }

  /* On touch (no hover), keep Remove faintly visible so it's reachable. */
  @media (hover: none) {
    .grocery-item__remove {
      opacity: 0.6;
    }
  }

  /* Item-sources expansion. Indents under the name (past the round check). */
  .grocery-item__sources {
    margin-inline-start: 50px;
    padding-block: var(--space-1) var(--space-2);
    padding-inline-start: var(--space-3);
    border-inline-start: 2px solid var(--md-border-muted);
  }

  .grocery-item__sources[hidden] {
    display: none;
  }

  .grocery-item__source {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding-block: 2px;
  }

  .grocery-item__source-link {
    font-size: var(--text-xs);
    color: var(--md-on-surface-variant);
    text-decoration: none;
    transition: color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects);
  }

  .grocery-item__source-link:hover {
    color: var(--md-primary);
  }

  .grocery-item__source-qty {
    font-size: var(--text-xs);
    color: color-mix(in oklch, var(--md-on-surface-variant) 75%, transparent);
  }

  /* ------------------------------------------------- List detail empty
     Warm mascot direction — the static brand mark over a serif heading +
     nudge, mirroring the empty grocery-lists state (.grocery-empty). */
  .grocery-detail__empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-block-size: 40vh;
    text-align: center;
  }

  .grocery-detail__empty-mascot {
    width: 64px;
    height: 64px;
    margin-block-end: var(--space-4);
  }

  .grocery-detail__empty-heading {
    margin: 0;
    font-family: var(--font-heading);
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--md-on-surface);
  }

  .grocery-detail__empty-body {
    margin: var(--space-2) 0 0;
    font-size: var(--text-sm);
    color: var(--md-on-surface-variant);
  }

  /* --------------------------------------------------- Sticky add bar */
  .grocery-add {
    position: sticky;
    inset-block-end: 0;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-page);
    border-block-start: 1px solid var(--md-border);
    background: var(--md-surface);
  }

  .grocery-add__icon {
    flex-shrink: 0;
    color: var(--md-on-surface-variant);
  }

  .grocery-add__input {
    flex: 1;
    border: none;
    background: transparent;
    font-family: var(--font-body);
    font-size: var(--text-sm);
    color: var(--md-on-surface);
  }

  .grocery-add__input::placeholder {
    color: color-mix(in oklch, var(--md-on-surface-variant) 75%, transparent);
  }

  .grocery-add__input:focus-visible {
    outline: none;
  }

  .grocery-add__submit {
    padding: var(--space-1) var(--space-3);
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--md-primary);
    font-size: var(--text-xs);
    font-weight: 600;
    cursor: pointer;
    /* Hidden until the input has text (grocery.js toggles .has-text). */
    display: none;
    transition: background-color var(--motion-spring-fast-effects-duration) var(--motion-spring-fast-effects);
  }

  .grocery-add.has-text .grocery-add__submit {
    display: inline-flex;
  }

  .grocery-add__submit:hover {
    background: var(--md-primary-container);
  }

  /* The sticky add bar sits flush under the pinned shop bar — give it the
     same calm card edge so the two read as one stacked footer (mockup). */
  .grocery-add {
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  }

  /* ---------------------------------------------------- Reduced motion */
  @media (prefers-reduced-motion: reduce) {
    .grocery-detail__progress-bar,
    .grocery-item__check,
    .grocery-shopbar__cta {
      transition: none;
    }
  }
}
