
  * { box-sizing: border-box; margin: 0; padding: 0; }

  /* Double-tap-zoom fix (real iPhone Safari): WebKit performs its native
     double-tap-to-zoom-to-fit gesture on any tap target that doesn't opt
     out via touch-action -- regardless of the viewport meta's own scale
     settings (templates/base.html no longer tries to lock scale there, for
     the same reason: iOS ignores that for actual zoom gestures). A diner
     tapping a button or card twice in quick succession (an ordinary thing
     to do at a restaurant table, not a deliberate zoom gesture) was being
     read as "zoom here," then bounced back -- exactly the reported bug.
     touch-action: manipulation is the standard, component-level fix: it
     tells the browser a tap on one of these elements means "act on this
     control," not "maybe start a zoom gesture," while explicitly still
     allowing pinch-zoom (a continuous two-finger gesture, a separate
     concern from this property) and normal scrolling/panning everywhere.
     Scoped to actual interactive controls -- every <button>, every <a>,
     and every onclick-driven tap target (dish cards, pairing rows, the
     dish sheet's own tap targets, etc.) -- not the page as a whole, so
     pinch-to-zoom on dish text/images/everything else stays exactly as
     available as it already was. */
  button, a, [onclick] {
    touch-action: manipulation;
  }

  html, body {
    height: 100%;
  }

  body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    background: #1a1a1a;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    /* Mobile Safari's address/toolbar isn't subtracted from 100vh, so a
       100vh-tall .phone can end up taller than what's actually visible,
       clipping content with no way to scroll to it. dvh tracks the real
       visible viewport as Safari's toolbar shows/hides; vh above is the
       fallback for browsers that don't support dvh. */
    min-height: 100dvh;
  }

  .phone {
    width: 390px;
    height: 844px;
    background: #fff;
    border-radius: 40px;
    overflow: hidden;
    position: relative;
    box-shadow: 0 40px 80px rgba(0,0,0,0.5);
  }

  .screen {
    display: flex;
    flex-direction: column;
    height: 100%;
    overflow-y: auto;
    position: relative;
  }

  /* ── FLOATING ORDER BUTTON ── */
  .float-order {
    position: absolute;
    top: 20px;
    right: 20px;
    background: #0f0f0f;
    color: #fff;
    border: none;
    border-radius: 24px;
    padding: 8px 14px;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    display: none;
    align-items: center;
    gap: 6px;
    z-index: 100;
    box-shadow: 0 4px 16px rgba(0,0,0,0.2);
  }
  .float-order.visible { display: flex; }
  .float-order:active { opacity: 0.75; }
  .float-badge {
    background: #fff;
    color: #0f0f0f;
    border-radius: 50%;
    width: 18px; height: 18px;
    font-size: 11px; font-weight: 800;
    display: flex; align-items: center; justify-content: center;
  }

  /* ── RESTAURANT TAG ── */
  .restaurant-tag {
    position: absolute;
    top: 20px;
    left: 20px;
    z-index: 100;
    display: none;
  }
  .restaurant-tag.visible { display: block; }
  .restaurant-tag-name { font-size: 11px; font-weight: 600; color: #666; line-height: 1.2; }
  .restaurant-tag-table { font-size: 10px; color: #888; }

  /* ── SCREEN 1: LANDING ── */
  #s1 { background: #fdf8f2; overflow: hidden; }

  .hero-image {
    width: 100%;
    height: 480px;
    background: linear-gradient(170deg, #fdebd0 0%, #f5cfa0 40%, #e8b87a 100%);
    position: relative;
    overflow: hidden;
    flex-shrink: 0;
    margin-top: -20px;
  }

  .hero-image::before {
    content: '';
    position: absolute; inset: 0;
    background: radial-gradient(ellipse at 50% 50%, rgba(255,255,255,0.3) 0%, transparent 70%);
  }

  .dish-placeholder {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    flex-direction: column; gap: 0;
    padding-top: 50px;
  }
  .dish-icon {
    font-size: 200px;
    line-height: 1;
    filter: drop-shadow(0 20px 40px rgba(120,60,0,0.25));
  }
  .dish-label { display: none; }
  .hero-overlay { position: absolute; bottom: 0; left: 0; right: 0; height: 120px; background: linear-gradient(transparent, #fdf8f2); }

  .brand-name {
    font-size: 13px; font-weight: 800; letter-spacing: 3px; text-transform: uppercase;
    color: rgba(60,30,5,0.9); position: absolute; top: 32px; left: 32px; z-index: 2;
  }

  .landing-content { padding: 0px 32px 44px; display: flex; flex-direction: column; gap: 10px; background: #fdf8f2; }
  .landing-heading { font-size: 30px; font-weight: 800; color: #1a0f05; line-height: 1.2; }
  .landing-sub { font-size: 16px; color: #9a7a5a; line-height: 1.5; margin-bottom: 8px; }

  /* ── BUTTONS ── */
  .btn-primary {
    background: #1a0f05; color: #fff; border: none; border-radius: 16px;
    padding: 18px 32px; font-size: 16px; font-weight: 700; cursor: pointer;
    width: 100%; transition: opacity 0.15s;
  }
  .btn-primary:active { opacity: 0.8; }

  .btn-dark {
    background: #0f0f0f; color: #fff; border: none; border-radius: 16px;
    padding: 18px 32px; font-size: 16px; font-weight: 700; cursor: pointer;
    width: 100%; transition: opacity 0.15s;
  }
  .btn-dark:active { opacity: 0.75; }
  .btn-dark:disabled { opacity: 0.5; cursor: not-allowed; }

  .btn-outline {
    background: #fff; color: #0f0f0f; border: 2px solid #e0e0e0; border-radius: 16px;
    padding: 16px 32px; font-size: 15px; font-weight: 600; cursor: pointer;
    width: 100%; transition: all 0.15s;
  }
  .btn-outline:active { border-color: #0f0f0f; }

  /* ── QUESTIONS ── */
  .question-screen { padding: 52px 28px 40px; display: flex; flex-direction: column; flex: 1; }
  .q-heading { font-size: 24px; font-weight: 700; color: #0f0f0f; line-height: 1.25; margin-bottom: 28px; margin-top: 8px; }
  .option-list { display: flex; flex-direction: column; gap: 10px; flex: 1; }
  .option-btn {
    display: flex; align-items: center; gap: 14px;
    background: #f6f6f6; border: 2px solid transparent; border-radius: 14px;
    padding: 16px 18px; font-size: 16px; font-weight: 500; color: #0f0f0f;
    cursor: pointer; transition: all 0.12s; text-align: left; width: 100%;
  }
  .option-btn:active { background: #0f0f0f; color: #fff; transform: scale(0.98); }
  .option-btn .emoji { font-size: 22px; flex-shrink: 0; }
  .back-link {
    margin-top: 20px; font-size: 13px; color: #888; text-align: center;
    cursor: pointer; background: none; border: none; width: 100%;
  }

  /* ── DIETARY CHIPS (Slice 3B) ──
     Chips reuse .option-btn's base look (same radius/padding/font
     already established for every other question screen) rather than
     introducing a separate component -- only what a toggle needs beyond
     a plain nav button is added here: a persistent .selected state
     (unlike .option-btn:active, which is only a press-flash and reverts
     the instant the finger lifts) and a 2-column grid so all six
     positive categories fit one phone screen without scrolling. */
  .chip-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
  .chip-grid .option-btn { width: auto; justify-content: flex-start; }
  .option-btn.selected {
    background: #0f0f0f; color: #fff; border-color: #0f0f0f;
  }
  .option-btn.selected .chip-check { display: inline; }
  .chip-check { display: none; margin-left: auto; font-weight: 700; }

  /* Exclusion chips: deliberately a different visual language from
     positive chips (never color alone -- the label itself always reads
     "Avoid ___", and a dedicated glyph swaps in on selection) so a
     diner scanning quickly can't mistake "avoid" for "want". */
  .diet-exclusion-toggle {
    margin-top: 18px; background: none; border: none; color: #555;
    font-size: 14px; font-weight: 600; text-align: left; padding: 6px 2px;
    display: flex; align-items: center; gap: 6px; cursor: pointer; width: 100%;
  }
  .diet-exclusion-toggle .chevron { transition: transform 0.15s; }
  .diet-exclusion-toggle[aria-expanded="true"] .chevron { transform: rotate(90deg); }
  .diet-exclusion-panel { margin-top: 10px; }
  .chip-grid.exclusion-grid { margin-top: 8px; }
  .option-btn.chip-exclude {
    background: #fff; border-color: #e6c8c8; color: #7a3b3b;
  }
  .option-btn.chip-exclude.selected {
    background: #c0392b; color: #fff; border-color: #c0392b;
  }

  .diet-save-error {
    margin-top: 14px; padding: 12px 14px; border-radius: 10px;
    background: #fdecea; color: #c0392b; font-size: 13px; text-align: center;
  }
  .diet-save-error .diet-save-retry {
    display: block; margin: 8px auto 0; background: #c0392b; color: #fff;
    border: none; border-radius: 8px; padding: 8px 16px; font-size: 13px;
    font-weight: 600; cursor: pointer;
  }

  /* ── PROGRESS DOTS ── */
  .progress { display: flex; gap: 6px; justify-content: center; padding: 20px 0 0; flex-shrink: 0; }
  .dot { width: 6px; height: 6px; border-radius: 50%; background: #e0e0e0; transition: all 0.2s; }
  .dot.active { background: #0f0f0f; width: 18px; border-radius: 3px; }

  /* ── THINKING ── */
  #s-thinking {
    background: #fff;
    justify-content: center; align-items: center; text-align: center; padding: 48px 32px;
  }
  .thinking-text { font-size: 18px; color: #999; font-weight: 500; }
  .thinking-dots::after {
    content: '';
    animation: dots 1.2s steps(4, end) infinite;
  }
  @keyframes dots {
    0%   { content: ''; }
    25%  { content: '.'; }
    50%  { content: '..'; }
    75%  { content: '...'; }
    100% { content: ''; }
  }

  /* ── RECOMMENDATION ── */
  .rec-image {
    width: 100%; height: 240px;
    background: linear-gradient(135deg, #f4e4c1 0%, #e8c99a 50%, #d4a96a 100%);
    position: relative; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center; overflow: hidden;
  }
  .rec-image::before {
    content: ''; position: absolute; inset: 0;
    background: radial-gradient(ellipse at 50% 60%, rgba(180,100,20,0.15) 0%, transparent 70%);
  }
  .rec-dish-icon { font-size: 88px; filter: drop-shadow(0 8px 24px rgba(0,0,0,0.15)); position: relative; z-index: 1; }
  .rec-image-overlay { position: absolute; bottom: 0; left: 0; right: 0; height: 80px; background: linear-gradient(transparent, #fff); }

  .rec-header { padding: 12px 24px 0; }
  .rec-eyebrow { font-size: 11px; font-weight: 600; letter-spacing: 2px; text-transform: uppercase; color: #888; margin-bottom: 8px; }
  .rec-name { font-size: 26px; font-weight: 800; color: #0f0f0f; line-height: 1.1; margin-bottom: 6px; }
  .rec-tap-hint { font-size: 12px; font-weight: 600; color: #666; margin-bottom: 8px; }
  .rec-meta { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; font-size: 14px; color: #888; font-weight: 500; }
  .rec-meta span { color: #555; }
  .veg-dot {
    width: 14px; height: 14px; border: 2px solid #22a348; border-radius: 2px;
    display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  }
  .veg-dot::after { content: ''; width: 6px; height: 6px; background: #22a348; border-radius: 50%; }
  .nonveg-dot {
    width: 14px; height: 14px; border: 2px solid #c0392b; border-radius: 2px;
    display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  }
  .nonveg-dot::after { content: ''; width: 6px; height: 6px; background: #c0392b; border-radius: 50%; }
  .rec-reason { font-size: 15px; color: #444; line-height: 1.5; margin-bottom: 20px; }
  .spice-badge { font-size: 13px; letter-spacing: -1px; }
  .rec-served-with { font-size: 13px; color: #888; margin-top: -12px; margin-bottom: 16px; }
  .taste-tag-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: -8px; margin-bottom: 16px; }
  .taste-tag {
    font-size: 12px; font-weight: 600; color: #9a7a5a; background: #f4e4c1;
    border-radius: 10px; padding: 3px 10px;
  }

  /* ── PAIRING CONTROLS: RADIO (primary, mutually exclusive) + CHECKBOX (optional extras) ── */
  .pairing-group-label { font-size: 12px; font-weight: 600; color: #999; margin-bottom: 6px; }
  .pairing-row { display: flex; align-items: center; gap: 12px; padding: 8px 0; cursor: pointer; }
  .pairing-name { flex: 1; font-size: 14px; color: #444; }
  .pairing-price { font-size: 14px; color: #444; }
  .pairing-checkbox, .meal-checkbox {
    width: 18px; height: 18px; flex-shrink: 0;
    background: #fff; border: 1px solid #ccc; border-radius: 4px;
    display: flex; align-items: center; justify-content: center;
  }
  .pairing-checkbox.checked, .meal-checkbox.checked { background: #0f0f0f; border-color: #0f0f0f; }
  .pairing-checkbox.checked::after, .meal-checkbox.checked::after { content: '✓'; font-size: 12px; line-height: 1; color: #fff; }
  .pairing-radio {
    width: 18px; height: 18px; flex-shrink: 0;
    background: #fff; border: 1px solid #ccc; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
  }
  .pairing-radio.checked { background: #0f0f0f; border-color: #0f0f0f; }
  .pairing-radio.checked::after { content: ''; width: 6px; height: 6px; border-radius: 50%; background: #fff; }

  .rec-actions { padding: 0 24px 32px; }
  /* Party-size/quantity bug fix: a rejected /recommendation/confirm
     submission (e.g. hero.quantity out of range) surfaces here, right
     above the button it's about, rather than sending the diner to the
     generic error page. Small and text-only, matching this screen's
     existing minimal-chrome hint styling (.rec-hero-added-hint) rather
     than a large new banner. */
  .order-meal-error { color: #b3261e; font-size: 13px; font-weight: 600; text-align: center; margin-bottom: 10px; }
  .secondary-actions { display: flex; justify-content: space-between; margin-top: 12px; }
  .btn-text { background: none; border: none; font-size: 14px; color: #777; cursor: pointer; padding: 4px 0; }
  .btn-text:active { color: #0f0f0f; }
  .view-all-recs { text-align: center; margin-top: 10px; }

  /* ── RECOMMENDATION COMPARISON SCREEN ── */
  .recs-title { font-size: 20px; font-weight: 800; color: #0f0f0f; margin-bottom: 4px; }
  .recs-subtitle { font-size: 13px; color: #888; line-height: 1.4; }
  .rec-card {
    display: block; text-decoration: none; cursor: pointer;
    background: #f6f6f6; border: 2px solid transparent; border-radius: 14px;
    padding: 16px 18px; transition: all 0.12s;
  }
  .rec-card:active { transform: scale(0.98); }
  .rec-card-current { background: #fff; border-color: #0f0f0f; }
  .rec-card-name { font-size: 16px; font-weight: 700; color: #0f0f0f; margin-bottom: 4px; }
  .rec-card-desc { font-size: 13px; color: #777; line-height: 1.4; margin-bottom: 8px; }
  .rec-card-price { font-size: 15px; font-weight: 700; color: #0f0f0f; }
  .rec-card-badge { font-size: 11px; font-weight: 700; color: #22a348; letter-spacing: 0.5px; margin-left: 4px; }

  /* ── EMPTY STATE ── */
  #s6 { background: #fff; padding: 64px 32px 48px; }
  .empty-icon { font-size: 48px; text-align: center; margin-bottom: 24px; opacity: 0.4; }
  .empty-heading { font-size: 20px; font-weight: 700; color: #0f0f0f; margin-bottom: 12px; }
  .empty-body { font-size: 15px; color: #888; line-height: 1.6; margin-bottom: 32px; }
  .empty-actions { display: flex; flex-direction: column; gap: 10px; }

  /* ── FULL MENU ── */
  #s-menu { background: #fff; }
  .menu-header { padding: 52px 24px 16px; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; }
  .menu-header-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
  .menu-title { font-size: 20px; font-weight: 800; color: #0f0f0f; }
  .menu-back { background: none; border: none; font-size: 14px; color: #777; cursor: pointer; }
  .menu-sub { font-size: 13px; color: #777; }
  .menu-search-wrap { padding: 12px 24px; border-bottom: 1px solid #f0f0f0; flex-shrink: 0; }
  .menu-search {
    width: 100%; padding: 10px 14px; border: 1px solid #e8e0d8; border-radius: 10px;
    font-size: 14px; font-family: inherit; color: #0f0f0f; background: #faf9f7;
  }
  .menu-search:focus { outline: none; border-color: #0f0f0f; background: #fff; }
  .menu-no-results { display: none; padding: 48px 28px; text-align: center; }
  .menu-no-results-title { font-size: 15px; font-weight: 600; color: #0f0f0f; margin-bottom: 6px; }
  .menu-no-results-sub { font-size: 13px; color: #999; }
  .menu-items {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 8px 0;
  }
  .menu-section-title { font-size: 11px; font-weight: 700; letter-spacing: 2px; text-transform: uppercase; color: #999; padding: 16px 24px 8px; }
  .menu-item {
    display: flex; align-items: center; gap: 14px;
    padding: 14px 24px; cursor: pointer; transition: background 0.1s;
  }
  .menu-item:active { background: #f6f6f6; }
  .menu-item-icon { font-size: 28px; flex-shrink: 0; }
  /* Phase 3: restaurant-provided dish photo, in place of the emoji icon
     box -- only applied when a dish has a real image_url (see menu.html);
     a dish with none keeps the plain emoji .menu-item-icon above, byte-
     for-byte unchanged. */
  .menu-item-icon-photo {
    width: 44px; height: 44px; border-radius: 10px;
    background-size: cover; background-position: center; background-color: #f2f2f2;
    font-size: 0;
  }
  .menu-item-info { flex: 1; }
  .menu-item-name { font-size: 15px; font-weight: 600; color: #0f0f0f; margin-bottom: 3px; }
  .menu-item-desc { font-size: 13px; color: #777; }
  .menu-item-served-with { font-size: 12px; color: #9a7a5a; margin-top: 2px; }
  .menu-item-right { text-align: right; }
  .menu-item-price { font-size: 14px; font-weight: 700; color: #0f0f0f; }
  .menu-item-dot { display: flex; justify-content: flex-end; margin-bottom: 3px; }
  .menu-item-add {
    margin-top: 6px;
    background: #0f0f0f; color: #fff; border: none; border-radius: 12px;
    padding: 4px 10px; font-size: 12px; font-weight: 700; cursor: pointer;
  }
  .menu-item-add:active { opacity: 0.75; }

  /* ── ORDER SUMMARY ── */
  #s-order { background: #fff; }
  .order-header { background: #0f0f0f; padding: 52px 28px 28px; flex-shrink: 0; position: sticky; top: 0; z-index: 10; }
  .order-header-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
  .order-title { font-size: 13px; font-weight: 600; letter-spacing: 2.5px; text-transform: uppercase; color: rgba(255,255,255,0.7); }
  .order-back { background: none; border: none; font-size: 13px; color: rgba(255,255,255,0.7); cursor: pointer; }
  .order-subtitle { font-size: 16px; font-weight: 500; color: rgba(255,255,255,0.6); }
  .order-items { flex: 1; padding: 8px 0; transition: padding-top 0.2s ease; }
  /* Undo snackbar relocation (top-of-cart discoverability fix): the
     banner itself is an absolute overlay (see .undo-banner below), so
     while it's showing, the item list needs real clearance pushed under
     it or the top row's veg dot / qty stepper would sit underneath the
     banner and be hard to tap -- exactly what the "don't make cart
     quantities difficult to use" requirement rules out. 72px comfortably
     clears the pill's own fairly constant height (~48-50px) plus a gap;
     unlike the old bottom-anchored --undo-banner-bottom value, this
     doesn't need JS measurement since the banner's height doesn't vary
     the way the old footer's did (1 vs 2 buttons) -- it's always the
     same "ITEM removed / UNDO / ✕" pill. Toggled by removeFromOrder()/
     _hideUndoBanner() in order.html alongside the banner's own .visible
     class, and transitions together with it. */
  .order-items.undo-banner-showing { padding-top: 72px; }
  /* Rapid-tap mobile fix, narrow and control-level (not a viewport/global
     zoom change): the existing `button, a, [onclick]` touch-action rule
     doesn't cover a row's own container -- only its buttons. A rapid tap
     that lands on a row (its padding/gap area, not squarely on a
     button) previously had no touch-action override at all, leaving
     WebKit free to treat it as a double-tap-zoom candidate. touch-action
     here only affects gesture handling, never click dispatch, so every
     existing onclick (×, −, +, Undo, dismiss) keeps working exactly as
     before -- this purely closes the coverage gap in the space between/
     around them. Independent of, and still needed alongside,
     templates/order.html's cartMutationQueue: that queue serializes the
     network/session-write side of rapid taps (and, since +/- no longer
     reload the page, rows also never reorder mid-edit -- see that file's
     TestRapidTapDoesNotRaceOrCorruptState-covered redesign), but neither
     of those touches gesture recognition, which is this rule's own,
     separate job. */
  .order-item {
    display: flex; align-items: center; gap: 14px;
    padding: 16px 28px; border-bottom: 1px solid #f0f0f0;
    touch-action: manipulation;
  }
  .order-item-icon { font-size: 32px; flex-shrink: 0; }
  .order-item-info { flex: 1; }
  .order-item-name { font-size: 15px; font-weight: 600; color: #0f0f0f; margin-bottom: 3px; }
  .order-item-meta { font-size: 13px; color: #777; display: flex; align-items: center; gap: 6px; }
  .order-item-price { font-size: 15px; font-weight: 700; color: #0f0f0f; }
  .order-total {
    padding: 20px 28px; border-top: 2px solid #0f0f0f;
    display: flex; justify-content: space-between; align-items: center; flex-shrink: 0;
  }
  .total-label { font-size: 14px; font-weight: 600; color: #888; }
  .total-amount { font-size: 22px; font-weight: 800; color: #0f0f0f; }
  .order-footer { padding: 16px 28px 40px; display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; }
  .order-item-remove { background: none; border: none; font-size: 16px; cursor: pointer; color: #999; padding: 4px; flex-shrink: 0; }
  .order-item-remove:active { color: #e74c3c; }
  .order-item-qty { font-size: 12px; color: #aaa; margin-top: 2px; }

  /* ── UNDO SNACKBAR (Cart delete-with-undo) ──
     position:absolute anchored to .screen (position:relative, itself a
     child of .phone), not position:fixed to the viewport — same reason
     .dish-sheet-backdrop above does this: .phone clips overflow in the
     desktop-mock view but not on a real device, so anchoring here stays
     correct in both.

     TOP-OF-CART placement (discoverability fix): a distracted diner
     looking away when they delete something could easily miss a
     bottom-anchored, short-lived toast — moved here, directly under
     .order-header, instead. .order-header is itself `position: sticky;
     top: 0`, always visible regardless of scroll position, so anchoring
     the banner immediately below it (via --undo-banner-top, measured
     live off the header's actual rendered height by
     _positionUndoBannerBelowHeader() in order.html — the header's
     height is static in practice but this avoids a second hardcoded
     guess going stale if its padding ever changes) means the banner is
     ALWAYS in view the instant it appears, no matter how far down a
     long cart list the diner had scrolled. Compact/pill-shaped and
     centered (auto width, not left:20px;right:20px edge-to-edge) is
     unchanged from before — still a proportional system
     acknowledgment, not a block that competes with page content.

     Shown/hidden via the .visible class, not display:none/flex — opacity
     and transform both transition, and `visibility` is switched with a
     transition-delay (0s going visible, matching the fade-out's own
     duration going hidden) so the element is genuinely non-interactive
     and undetectable at rest (same as display:none would give an
     automated check). The hidden resting transform is now translateY
     *upward* (-16px, tucked toward the header) so becoming visible reads
     as sliding DOWN into place, matching the "slides down from the top"
     requirement — the mirror image of the old bottom placement's
     slide-up. */
  .undo-banner {
    position: absolute; left: 50%; top: calc(var(--undo-banner-top, 124px));
    transform: translateX(-50%) translateY(-16px);
    max-width: calc(100% - 40px);
    background: #1a1a1a; color: #fff; border-radius: 24px;
    padding: 8px 8px 8px 16px; display: inline-flex; align-items: center;
    gap: 6px; box-shadow: 0 6px 20px rgba(0,0,0,0.28); z-index: 150;
    opacity: 0; visibility: hidden; pointer-events: none;
    transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s linear 0.2s;
  }
  .undo-banner.visible {
    opacity: 1; visibility: visible; pointer-events: auto;
    transform: translateX(-50%) translateY(0);
    transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s linear 0s;
  }
  .undo-banner-text {
    font-size: 13px; font-weight: 500; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis; max-width: 190px;
  }
  .undo-banner-action {
    background: rgba(255,255,255,0.1); border: none; color: #ffd479; font-size: 13px;
    font-weight: 800; letter-spacing: 0.4px; cursor: pointer; flex-shrink: 0;
    padding: 7px 14px; border-radius: 18px;
  }
  .undo-banner-action:active { opacity: 0.7; }
  /* Explicit dismiss control: closes the banner without restoring the
     item (deletion stays final) -- required because the banner has no
     auto-hide timer at all, so a diner who's seen it and doesn't want
     Undo needs a clear way to close it themselves. No new wording: a
     bare "✕" matches the existing .order-item-remove button's own
     convention elsewhere on this exact page.

     min-width/min-height (tap-target fix): the glyph itself stays at
     font-size:14px -- visually unchanged -- but the button's own box
     (the actual clickable element, not a visual overlay) is grown to a
     44px square via min-width/min-height + flex centering, replacing
     the old padding:6px box that measured only ~20x26px. 44px matches
     Apple HIG's minimum recommended touch target; this is the only
     control that shrinks the banner's own margin for error now that
     there's no auto-hide timer to fall back on if a distracted diner
     misses it. Growing this one flex child does make the pill itself
     a bit taller/wider than before -- an accepted, minimal side effect
     of the hit area itself, not a redesign of the banner. */
  .undo-banner-dismiss {
    background: none; border: none; color: rgba(255,255,255,0.55); font-size: 14px;
    cursor: pointer; flex-shrink: 0; line-height: 1;
    min-width: 44px; min-height: 44px; padding: 0;
    display: flex; align-items: center; justify-content: center;
  }
  .undo-banner-dismiss:active { color: rgba(255,255,255,0.9); }
  .qty-stepper { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
  .qty-btn {
    width: 26px; height: 26px; border-radius: 8px; border: none;
    background: #0f0f0f; color: #fff; font-size: 15px; font-weight: 700;
    cursor: pointer; line-height: 1; display: flex; align-items: center; justify-content: center;
  }
  .qty-btn:active { opacity: 0.75; }
  /* Audit fix: disabled briefly while this row's removal is in flight
     (templates/order.html's removeFromOrder) -- matches the existing
     .btn-dark:disabled treatment elsewhere, so a diner can visually tell
     the buttons are momentarily inert rather than seeing them look fully
     active while doing nothing. */
  .qty-btn:disabled { opacity: 0.4; cursor: not-allowed; }
  .qty-value { font-size: 14px; font-weight: 700; color: #0f0f0f; min-width: 16px; text-align: center; }
  /* Stepper ceiling fix: shown next to a qty-stepper the instant its "+"
     hits the server-side validation max, so the diner learns the reason
     at the moment they hit it -- not only after "Add meal to order"
     rejects a value they never had any way to see coming. Same red as
     .order-meal-error, sized to sit inline beside a small stepper row
     rather than as a full-width banner. */
  .qty-limit-note { color: #b3261e; font-size: 11px; font-weight: 600; margin-top: 2px; }

  /* ── FINISH SCREEN ── */
  #s-finish {
    background: #fdf8f2;
    justify-content: center;
    align-items: center;
    text-align: center;
    padding: 48px 40px;
  }
  .finish-check { font-size: 56px; margin-bottom: 24px; }
  .finish-heading { font-size: 26px; font-weight: 800; color: #1a0f05; margin-bottom: 12px; line-height: 1.2; }
  .finish-sub { font-size: 16px; color: #9a7a5a; line-height: 1.6; margin-bottom: 40px; }
  .finish-dishes { width: 100%; background: #fff; border-radius: 16px; padding: 16px 20px; margin-bottom: 32px; text-align: left; }
  .finish-dish-row { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; border-bottom: 1px solid #f0e8de; font-size: 14px; color: #5a3a1a; }
  .finish-dish-row:last-child { border-bottom: none; }
  .finish-total-row { display: flex; justify-content: space-between; align-items: center; padding: 12px 0 0; font-size: 15px; font-weight: 700; color: #1a0f05; }

  /* ── ABOUT THIS DISH — bottom sheet (docs/10-about-this-dish-ux.md) ──
     position:absolute anchored to .phone (position:relative), not
     position:fixed to the viewport — .phone clips overflow in the
     desktop-mock view but not on a real mobile screen (see the
     @media rule below), so anchoring to .phone renders correctly in
     both instead of only the real-device case. */
  .dish-sheet-backdrop {
    position: absolute; inset: 0; background: rgba(0,0,0,0.4);
    opacity: 0; pointer-events: none; transition: opacity 0.2s ease;
    z-index: 200;
  }
  .dish-sheet-backdrop.open { opacity: 1; pointer-events: auto; }

  .dish-sheet {
    position: absolute; left: 0; right: 0; bottom: 0;
    max-height: 68%;
    background: #fff; border-radius: 24px 24px 0 0;
    box-shadow: 0 -8px 32px rgba(0,0,0,0.2);
    transform: translateY(100%); transition: transform 0.25s ease;
    z-index: 201;
    display: flex; flex-direction: column;
    padding: 12px 24px calc(20px + env(safe-area-inset-bottom, 0px));
    overflow-y: auto;
  }
  .dish-sheet.open { transform: translateY(0); }

  .dish-sheet-handle { width: 36px; height: 4px; border-radius: 2px; background: #e0e0e0; margin: 0 auto 16px; flex-shrink: 0; }

  .dish-sheet-anchor { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
  .dish-sheet-icon { font-size: 32px; flex-shrink: 0; }
  /* Phase 3: toggled on/off by openDishSheet() (static/js/app.js) per
     dish -- only when dish.image_url is present; a dish with none keeps
     the plain emoji treatment above, unchanged. */
  .dish-sheet-icon.has-image {
    width: 56px; height: 56px; border-radius: 12px;
    background-size: cover; background-position: center; background-color: #f2f2f2;
  }
  .dish-sheet-name { font-size: 16px; font-weight: 700; color: #0f0f0f; }
  .dish-sheet-price { font-size: 13px; color: #888; }

  /* Quiet on purpose — establishes context before the sentence without
     competing with it (docs/10, "Order of information, and why"). */
  .dish-sheet-heading { font-size: 11px; font-weight: 700; letter-spacing: 1.5px; text-transform: uppercase; color: #aaa; margin-bottom: 8px; }

  /* The most visually dominant element in the panel, deliberately —
     larger and bolder than the dish name above it. */
  .dish-sheet-sentence {
    font-size: 17px; font-weight: 700; color: #0f0f0f; line-height: 1.4;
    background: #faf5ee; border-radius: 14px; padding: 16px 18px; margin-bottom: 16px;
  }

  /* Explicitly secondary to the sentence: smaller, lighter, no box. */
  .dish-sheet-facts { display: flex; flex-direction: column; gap: 8px; margin-bottom: 20px; }
  .dish-sheet-fact { display: flex; align-items: center; gap: 8px; font-size: 13px; color: #888; }
  .dish-sheet-fact-icon { flex-shrink: 0; }
  .dish-sheet-fact-label { flex: 1; }
  .dish-sheet-fact-value { color: #444; font-weight: 600; }

  .dish-sheet-action-bar { padding-top: 12px; border-top: 1px solid #f0f0f0; flex-shrink: 0; }

@media (max-width: 430px) {
    body { background: #fff; align-items: flex-start; }
    /* No rounded corners left to clip on a full-viewport mobile screen, so
       overflow:hidden here is vestigial from the desktop phone-mockup frame
       — and left in place, it sits as an ancestor between any
       position:sticky element inside .screen and the viewport, which
       silently disables sticky in Safari/WebKit. .screen's own
       overflow-y:auto already handles real content clipping/scrolling. */
    .phone { width: 100vw; height: 100vh; height: 100dvh; border-radius: 0; box-shadow: none; overflow: visible; }

    /* Mobile menu readability fix: below 420px, .phone above already IS
       the real device viewport (not the desktop phone-mockup frame), so
       these sizes are exactly what a diner reads on their own screen. The
       base .menu-item-name/-desc/-price/.menu-section-title rules further
       up this file are unchanged and still govern the desktop mockup —
       scoping the increase to this existing mobile media query (rather
       than raising those base rules directly) is what keeps the small
       desktop frame from growing to match, per the explicit requirement
       not to enlarge it. Hierarchy is preserved, just each step up in
       size: section title (scan target, stays the smallest/uppercase
       label) < description (supporting text) < price (needs to be
       unambiguous at a glance) < dish name (the single strongest element
       in the row, unchanged in intent — Nasi & Mee's ₹-heavy names and
       Meiko's shorter ones both benefit identically, since this is a
       shared class, not per-restaurant styling). No color, weight
       hierarchy, or font-family changed -- size, line comfort, and row
       spacing only. */
    .menu-section-title { font-size: 13px; }
    .menu-item { padding: 18px 24px; }
    .menu-item-name { font-size: 17px; }
    .menu-item-desc { font-size: 14px; }
    .menu-item-price { font-size: 16px; }
    /* Tap-target fix, same technique as .undo-banner-dismiss elsewhere in
       this file: the button's own clickable box grows via min-height +
       flex centering, not by inflating the pill into something visually
       oversized in this already-dense row. Comfortably tappable without
       stretching the row height dramatically -- proportionate to a small
       secondary action sitting alongside price and the veg dot, not an
       isolated primary control (which is why this targets ~38px rather
       than the full 44px used for that standalone banner button). */
    .menu-item-add {
      min-height: 38px;
      padding: 0 16px;
      font-size: 13px;
      display: flex;
      align-items: center;
      justify-content: center;
    }
  }
