/* Handy (design 3.2; batch F, f5; batch I1, i1): the reference is the iPad mini portrait 744x1133 (6th and 7th gen),
   with the phone 390x844 as the compact tier on the same code, on the tiers of web/platform/tiers.js (compact under
   600 px, medium 600 to 1023 px, wide 1024 px and up; BATCH-I1-PLAN I1-D1 to I1-D7). Density low, 48 px top row,
   56 px bottom bar. Drinks first: the chip row is one scrolling line on every tier (batch M), tiles with
   the category colour as a border 3, 3 and 5 across in the items column (batch I2), Mesas rows and Listos cards 1, 2
   and 3 across, the pinned "Enviar ronda" above the bottom bar on the compact tier, sheets with their primary pinned at
   the bottom. Safe areas through base.css's --safe-bottom (I1-D14). Long press hardening on tiles (HANDY-COMPETITION.md
   rank 2): no callout, no selection, no drag, touch-action manipulation. Wholly inside @layer app; colours only
   through tokens.

   Batch I2 (k2; BATCH-I2-PLAN I2-D5, I2-D13 to I2-D18 and 1.3): the ordering layout. web/handy/ordering.js renders
   the Carta and Ticket tabs as one .hd-order, the items column (.hd-items: chips and tiles) first and the ticket column
   (.hd-ticket) second, each with its own data-paint-scroll key. The compact tier (under 600) shows one page at a time
   by data-tab, the page scrolling as before (I2-D13). From 600 the Carta and Ticket tabs are screen-high (I2-D18, the
   definite height chain): the app is 100dvh (100vh before it as the fallback) with min-height 0, .content is a
   scrolling column flex with min-height 0, .hd-order fills it (flex 1 1 0, a 50vh floor, one minmax(0, 1fr) row) and
   each column scrolls on its own (min-height 0, overflow-y auto; no position, max-height or z-index on .hd-items),
   so whatever renders above the columns shortens them and the bottom bar, the app's last in-flow child, stays below
   them. The widths (I2-D5, 1.3, content W - 32, a 16 px column gap, tiles with the 8 px --touch-gap): equal halves from
   600 to 1023 (276 and 276 at 600, 348 and 348 at 744 with tiles 3 across at 110.67, 386 and 386 at 820), from 1024
   minmax(0, 2fr) minmax(340px, 1fr) (636 and 340 at 1024 with tiles 5 across at 120.8, the ticket held at its 340 px
   minimum; 723.33 and 361.67 at 1133 with tiles at 138.27). From 600 the send bar is hidden and the ticket column's
   primary sits in its .ticket-send foot, sticky at the column's bottom (I2-D14); the Ticket tab is hidden and the
   Carta tab reads pressed on both ordering tabs (I2-D16). The bottom bar paints above the page on every tier (z-index
   6: over the send bar's 5, the ticket foot's 1 and the map cards' 1 and 2, web/platform/floormap.css:32-33; under
   the sheet's 10, web/platform/base.css:142, and the step-up sheet's 11 further down this file). iPadOS behaviour
   (100dvh in Safari and standalone, sticky inside an inner scroller, a finger drag across the repaint) is UNVERIFIED
   until measured on the device (BATCH-I2-PLAN 14.2).

   Batch M (m1; BATCH-M-PLAN M-D3, M-D10): the group (category) row is one scrolling line on every tier. The medium
   tier's wrap (I1-D6) is gone, so the base .carta-row rule (nowrap, overflow-x auto) holds at every width, and after a
   chip tap web/handy/carta.js keepChipInView scrolls the pressed chip fully into view (the row's offset is kept across
   the repaint by its paint key "chips"). A chip may show its group's picture or library icon in a 28 px box
   (--pill-height, M-D10) centred in the 48 px chip before its name; the name always stays as text, and a chip with
   neither is the chip it was. A failed picture loses has-image (web/platform/tiles.js onImageError), so its box hides
   at once and the next paint renders the icon or the colour. From 600 the row is sticky at the top of the items
   column, which scrolls on its own there, so a change of group never needs a scroll back up; the rule names
   .app-handy .carta-row, never a selector naming .hd-items (the items column keeps no position, max-height or z-index),
   and its z-index 2 stays under the bottom bar's 6. On the phone the row stays in the page flow. The scroll into view,
   the sticky row and the row on WebKit are UNVERIFIED until measured on the device (BATCH-M-PLAN 14.2 item 5).

   Batch S (s4; BATCH-S-PLAN 7.4 item f, R-D5): the tab-current span that web/handy/ticket.js bottomBar puts in the
   Carta tab on the ticket tab is display none under 600, where the Ticket tab shows and carries aria-current, and from
   600, where the Ticket tab is hidden, visually hidden but read (the pattern of web/platform/editor/editor.css:65), so
   a screen reader hears the Carta tab as the current screen (UNVERIFIED on a device, BATCH-S-PLAN 14.2 row 1). */
@layer app {
  .app-handy .content { padding-bottom: calc(var(--touch-keypad) + var(--space-4) + var(--safe-bottom)); }
  .app-handy.has-send-bar .content { padding-bottom: calc(var(--touch-keypad) + var(--touch-primary) + var(--space-5) + var(--safe-bottom)); }

  /* Top row: the table name is the door to the table sheet (La cuenta, Cobrar). */
  .app-handy .topbar-table { margin-right: auto; font-size: var(--font-size-400); font-weight: 700; border-color: transparent;
    background: transparent; padding: 0 var(--space-2); }

  /* Mesas by zone, one truthful state per row, minutes that never wrap. */
  .app-handy .zone-head { font-size: var(--font-size-200); text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted);
    margin: var(--space-4) 0 var(--space-2); }
  .app-handy .row-state { font-size: var(--font-size-200); color: var(--text-muted); }
  .app-handy .row-min { white-space: nowrap; }

  /* Carta: one scrolling chip row on every tier (batch M, M-D3), then the overflow for Fotos or Lista. */
  .app-handy .carta-row { display: flex; flex-wrap: nowrap; gap: var(--touch-gap); overflow-x: auto; overscroll-behavior-x: contain;
    padding-bottom: var(--space-2); margin-bottom: var(--space-2); scrollbar-width: none; }
  .app-handy .carta-row .chip { flex: none; white-space: nowrap; }
  .app-handy .carta-row .chip-more { margin-left: auto; }
  .app-handy .carta-overflow { display: flex; justify-content: flex-end; gap: var(--touch-gap); margin-bottom: var(--space-2); }
  /* Batch M (M-D10): the group's picture or library icon in a 28 px box before the chip's name (tiles.js groupVisual);
     the last rule hides the empty box of a picture whose load failed until the next paint renders the icon or the colour. */
  .app-handy .carta-row .chip.has-media { display: inline-flex; align-items: center; gap: var(--space-2); }
  .app-handy .chip-media { flex: none; display: block; width: var(--pill-height); height: var(--pill-height); border-radius: var(--radius-s);
    overflow: hidden; }
  .app-handy .chip-img { display: block; width: 100%; height: 100%; object-fit: cover; }
  .app-handy .chip-icon svg { display: block; width: 100%; height: 100%; }
  .app-handy .chip:not(.has-image) .chip-img { display: none; }
  .app-handy .chip.has-media:not(.has-image):not(.has-icon) .chip-media { display: none; }

  /* Compact tiles: three across on the compact tier (the phone), a short media box, the category colour as the border. */
  .app-handy .tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .app-handy .tile-wrap { display: flex; min-width: 0; }
  .app-handy .tile-wrap .tile { flex: 1; min-width: 0; border-width: 2px; border-color: var(--cat-fill, var(--border)); padding: var(--space-1); }
  .app-handy .tile-wrap .tile-media { aspect-ratio: auto; min-height: var(--touch-keypad); }
  .app-handy .tile-wrap .tile-name { font-size: var(--font-size-200); overflow-wrap: anywhere; }
  .app-handy .tile-wrap .tile-price { font-size: var(--font-size-200); }
  .app-handy .tile-wrap .tile-cat { display: none; }
  .app-handy .tile { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; touch-action: manipulation; }
  .app-handy .tile img { -webkit-user-drag: none; }
  /* Batch I2 (I2-D13, I2-D18): the compact tier shows one ordering page at a time, the items column on the Carta tab and
     the ticket column on the Ticket tab; the bottom bar paints above the page on every tier (base.css:158 gives the
     sticky bar no z-index, so the map cards' 1 and 2 would paint over its tabs where the plan scrolls under it). */
  .app-handy[data-tab="menu"] .hd-ticket { display: none; }
  .app-handy[data-tab="ticket"] .hd-items { display: none; }
  .app-handy .bottombar { z-index: 6; }
  .app-handy .tab-current { display: none; }

  /* The medium tier (the iPad mini portrait, I1-D3, I1-D4, I1-D6, I1-D7): Mesas rows 2 across (352 px), the chip row
     one scrolling line as on every tier (batch M, M-D3), Listos cards 2 across (350 px) on main's last panel only,
     because the queue panel (web/handy/ticket.js:90)
     comes first as attention and stays a list; the empty line spans the row. The tiles stay 3 across in the items
     column (110.67 px at 744, batch I2). The wide tier: 5 tiles in the items column, 3 rows and 3 cards.
     Batch I2 (I2-D18, the definite height chain): on the Carta and Ticket tabs the app is screen-high and each column
     scrolls on its own; the .content rule sits after the has-send-bar rule above (same specificity, later wins). */
  @media (min-width: 600px) {
    .app-handy .rows { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .app-handy .carta-row { position: sticky; top: 0; z-index: 2; background: var(--bg); }
    .app-handy[data-tab="ready"] .content > .panel:last-child { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); align-items: start; }
    .app-handy[data-tab="ready"] .ready-card { margin-bottom: 0; }
    .app-handy[data-tab="ready"] .content > .panel:last-child > .empty { grid-column: 1 / -1; }
    .app-handy[data-tab="menu"], .app-handy[data-tab="ticket"] { height: 100vh; height: 100dvh; min-height: 0; }
    .app-handy[data-tab="menu"] .content, .app-handy[data-tab="ticket"] .content { min-height: 0; overflow-y: auto; display: flex;
      flex-direction: column; padding-bottom: var(--space-3); }
    .app-handy .hd-order { flex: 1 1 0; min-height: 50vh; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
      grid-template-rows: minmax(0, 1fr); column-gap: var(--space-4); }
    .app-handy[data-tab="menu"] .hd-ticket, .app-handy[data-tab="ticket"] .hd-items { display: block; }
    .app-handy .hd-items, .app-handy .hd-ticket { min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
    .app-handy .ticket-send { position: sticky; bottom: 0; z-index: 1; background: var(--bg); padding: var(--space-2) 0 0; }
    .app-handy .bottombar .tab[data-value="ticket"] { display: none; }
    .app-handy .bottombar .tab[data-order="on"] { border-top-color: var(--accent); font-weight: 700; }
    .app-handy .bottombar .tab-current { display: inline; position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  }
  @media (min-width: 1024px) {
    .app-handy .tiles { grid-template-columns: repeat(5, minmax(0, 1fr)); }
    .app-handy .rows { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .app-handy[data-tab="ready"] .content > .panel:last-child { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .app-handy .hd-order { grid-template-columns: minmax(0, 2fr) minmax(340px, 1fr); }
  }

  /* The pinned send bar sits above the bottom bar and its safe-area inset (I1-D14), full width; since batch I2 it shows on
     the compact tier only (hidden from 600 by the block below). */
  .app-handy .send-bar { position: sticky; bottom: calc(var(--touch-keypad) + var(--safe-bottom)); z-index: 5; display: flex; flex-wrap: wrap; align-items: center;
    gap: var(--space-2); padding: var(--space-2) var(--space-4); background: var(--surface); border-top: 1px solid var(--border); }
  .app-handy .send-bar .send-last { flex: 1 1 40%; min-width: 0; justify-content: flex-start; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .app-handy .send-bar .send-now { flex: 1 1 50%; }
  .app-handy .send-bar .reason { flex-basis: 100%; }
  /* Batch I2 (I2-D14): from 600 the send bar gives way to the ticket column's own primary (.ticket-send). This block sits
     after the send bar's rules above because its selector is the same one, so only the later rule wins. */
  @media (min-width: 600px) {
    .app-handy .send-bar { display: none; }
  }

  /* Ticket lines are tap targets. */
  .app-handy .line { padding: 0; }
  .app-handy .line-btn { flex: 1; justify-content: flex-start; width: 100%; border-color: transparent; background: transparent;
    padding: 0 var(--space-3); text-align: left; }
  .app-handy .line-mods { color: var(--text-muted); font-size: var(--font-size-200); }
  .app-handy .line.is-void .line-name { text-decoration: line-through; }
  .app-handy .line-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1); font-size: var(--font-size-200); }

  /* Sheets: groups, and the primary pinned at the bottom of the scrolling sheet. The foot's 16 px negative inline
     margins span the sheet's content column plus 16 px a side: the whole sheet below 600, a centred 600 px foot from
     600 and 720 from 1024 (base.css's inline padding, I1-D5). Its bottom offset, bottom margin and bottom padding carry
     --safe-bottom as the sheet's own bottom padding does, so the foot stays flush with the sheet's bottom edge (I1-D14). */
  .app-handy .sheet-group { margin: var(--space-2) 0; }
  .app-handy .sheet-group .h { font-size: var(--font-size-200); margin: var(--space-2) 0 var(--space-1); }
  .app-handy .sheet-price { font-size: var(--font-size-300); color: var(--text-muted); font-weight: 400; }
  .app-handy .sheet-pin { position: sticky; bottom: calc(-1 * (var(--space-4) + var(--safe-bottom)));
    margin: var(--space-3) calc(-1 * var(--space-4)); margin-bottom: calc(-1 * (var(--space-4) + var(--safe-bottom)));
    padding: var(--space-3) var(--space-4); padding-bottom: calc(var(--space-3) + var(--safe-bottom));
    background: var(--surface); border-top: 1px solid var(--border); }
  .app-handy .sheet-pin .btn-primary { flex: 1; }

  /* Batch G (g5). Rank 9: the read-back under a line's name (pase, comensal, añadidos with prices, nota); the
     supplement marker; each held pase under its header with Marchar; the sent rounds folded below. */
  .app-handy .line-name { min-width: 0; }
  .app-handy .line-read { display: block; color: var(--text-muted); font-size: var(--font-size-200); font-weight: 400; overflow-wrap: anywhere; }
  .app-handy .line-price { margin-left: auto; white-space: nowrap; }
  .app-handy .supl { color: var(--text-muted); font-size: var(--font-size-200); }
  .app-handy .pase { margin: var(--space-5) 0 var(--space-2); }
  .app-handy .pase-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
  .app-handy .pase-head .h { margin: 0; font-size: var(--font-size-300); }
  .app-handy .sent-rounds { margin-top: var(--space-5); border-top: 1px solid var(--border); }
  /* Rank 11: offline, one line on the first view; the details sheet one tap away. */
  .app-handy .offline-line { justify-content: flex-start; gap: var(--space-2); margin: var(--space-2) var(--space-4); width: auto; }

  /* The checkout (web/platform/checkout.js markup). */
  .app-handy .checkout-due { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-2); margin: var(--space-2) 0; }
  .app-handy .checkout-amount { font-size: var(--font-size-600); }
  .app-handy .checkout-modes { display: grid; grid-template-columns: 1fr 1fr; gap: var(--touch-gap); }
  .app-handy .btn-pay { min-height: var(--touch-primary); font-weight: 700; font-size: var(--font-size-400); }
  .app-handy .checkout-hint { font-size: var(--font-size-400); font-weight: 700; margin: var(--space-3) 0 var(--space-1); }
  .app-handy .checkout-ref { font-size: var(--font-size-800); font-weight: 700; letter-spacing: 0.08em; text-align: center; margin: var(--space-2) 0; }
  .app-handy .checkout-change { font-size: var(--font-size-500); font-weight: 700; }

  /* Batch H (h5). The staff menu door in the top row; the step-up sheet above the sent-line sheet it confirms; the
     ready banner on Mesas; Listos per line with its age and Servido; the Mover líneas check boxes. */
  .app-handy .topbar-menu { border-color: transparent; background: transparent; padding: 0 var(--space-2); }
  .app-handy .sheet-stepup { z-index: 11; }
  .app-handy .ready-banner { justify-content: flex-start; gap: var(--space-2); font-weight: 700; }
  .app-handy .ready-line { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-1) 0; }
  .app-handy .ready-line .btn-small { margin-left: auto; }
  .app-handy .ready-age { display: block; }
  .app-handy .move-lines { list-style: none; margin: 0; padding: 0; }
  .app-handy .move-line { width: 100%; justify-content: flex-start; border: 0; background: transparent; color: var(--text); text-align: left; }
  .app-handy .move-line .box { flex: none; width: var(--space-5); height: var(--space-5); border: 2px solid var(--border); border-radius: var(--space-1); }
  .app-handy .move-line[aria-checked="true"] .box { background: var(--accent); border-color: var(--accent); }
  .app-handy .move-line:disabled { color: var(--text-muted); }
  .app-handy .move-line .line-state { margin-left: auto; font-size: var(--font-size-200); }

  /* Batch UNIFY (unH; UNIFY-D5, UNIFY-D11, fold round 1 m5): a Juntar or Mover líneas target that cannot be chosen
     shows its reason as a line under the table name (a phone never shows a title). */
  .app-handy .grid-tables .btn-why { flex-direction: column; gap: 0; }
  .app-handy .grid-tables .btn-why .reason { margin: 0; text-align: center; }
}
