/* Responsive layout of the app (spec 2026-10-20 §3), loaded after styles.css. Breakpoints as layout.js:
   phone under 700 px, tablet 700–1279 px, desktop from 1280 px, capped at 1600 px with side panels from there.
   test/tablet/breakpoints-css.test.ts checks every @media of the app. */

/* ---------- shell: safe areas (viewport-fit=cover), bottom navigation ---------- */

#app {
  grid-template-rows: calc(var(--topbar) + env(safe-area-inset-top)) minmax(0, 1fr) auto;
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}

.topbar {
  padding-top: env(safe-area-inset-top);
  background: var(--rail);
}

.bottomnav {
  display: none;
}

@media (max-width: 699.98px) {
  :root {
    --topbar: 56px;
  }

  .bottomnav:not([hidden]) {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    padding: 6px 8px calc(6px + env(safe-area-inset-bottom));
    border-top: 1px solid var(--line);
    background: var(--rail);
  }

  .bottomnav-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    min-height: 48px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--muted);
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    touch-action: manipulation;
  }

  .bottomnav-item[aria-current="page"]:not(:disabled) {
    color: var(--chalk);
  }

  /* Open tabs in the text colour, a closed one muted (4.5:1, never faded), the current one in chalk. */
  .bottomnav-item:not(:disabled) {
    color: var(--text);
  }

  .bottomnav-item:disabled {
    cursor: default;
    font-weight: 500;
  }

  /* The current tab: its icon on a plum pill (Material's indicator), its name in plum. */
  .bottomnav-item svg {
    box-sizing: content-box;
    padding: 4px 18px;
    border-radius: var(--r-pill);
  }

  .bottomnav-item[aria-current="page"]:not(:disabled) svg {
    background: var(--chalk);
    color: var(--on-chalk);
  }

  /* App toasts above the bottom navigation and the home bar, never over a tab or a hint. */
  body:has(.bottomnav:not([hidden])) .app-toast {
    bottom: calc(76px + env(safe-area-inset-bottom));
  }
}

/* ---------- desktop from 1600 px: content capped ---------- */

@media (min-width: 1600px) {
  #app {
    max-width: 1600px;
    margin: 0 auto;
    border-left: 1px solid var(--line);
    border-right: 1px solid var(--line);
  }
}

/* Breakpoint markers used by the tests and later sections:
   @media (min-width: 700px) and (max-width: 1279.98px) { tablet }   @media (min-width: 1280px) { desktop } */
@media (min-width: 700px) and (max-width: 1279.98px) {
  .bottomnav {
    display: none;
  }
}

@media (min-width: 1280px) {
  .bottomnav {
    display: none;
  }
}

/* ---------- live screen (spec 2026-10-20 §3) ----------
   Children of .live: board (article), side-tabs, live-body (rail drawer + order feed), live-side (counters and
   customers), live-foot (tray and Annuler). Under 700 px: one column; 700–1279: feed + side panel
   with tabs; from 1280: article | feed | summary (artboard). */

.live {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas: "board" "body" "foot";
}

.live > .board {
  grid-area: board;
}

.live > .live-body {
  grid-area: body;
}

.live > .live-foot {
  grid-area: foot;
}

.live > .side-tabs,
.live > .live-side,
.phone-kpis {
  display: none;
}

/* "À vérifier · N / Toutes · N" above the feed (feed-filter.js), every size. */
.feed-tabs {
  align-self: flex-start;
  margin: var(--s3) var(--s4) 0;
}

.live-side {
  flex-direction: column;
  gap: var(--s3);
  min-height: 0;
  overflow-y: auto;
  padding: var(--s4);
  background: var(--surface);
}

/* One counter per line: "CHF 1 250.00" does not fit half of a 260 px column. */
.live-side .kpis {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s2);
}

.live-side .kpi-value {
  font-size: 26px;
}

.live-side .kpi {
  padding: var(--s3);
  border-radius: var(--r-small);
  background: var(--deep);
}

.side-customers {
  margin: 0;
  padding: 0;
  list-style: none;
}

.side-customers li {
  display: flex;
  justify-content: space-between;
  gap: var(--s2);
  padding: 9px 0;
  border-bottom: 1px solid var(--line);
  font-size: 14px;
}

@media (min-width: 700px) and (max-width: 1279.98px) {
  .live {
    grid-template-columns: minmax(0, 1fr) 300px;
    grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "body tabs"
      "body panel"
      "foot panel";
  }

  .live > .side-tabs {
    display: flex;
    grid-area: tabs;
    margin: var(--s3);
  }

  .live > .side-tabs button {
    flex: 1;
  }

  .live > .board,
  .live > .live-side {
    grid-area: panel;
    border-left: 1px solid var(--line);
  }

  .live[data-side-tab="summary"] > .live-side {
    display: flex;
  }

  .live[data-side-tab="summary"] > .board {
    display: none;
  }

  .live > .board {
    flex-direction: column;
    align-items: stretch;
    min-height: 0;
    padding: var(--s4);
    border-bottom: 0;
    overflow-y: auto;
  }

  .live > .board .board-in {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--s3);
  }

  /* The feed beside the panel is narrow: the order goes under her name (one line each), the button keeps its cell. */
  :is(.live, .fin-feed) .feed-row {
    grid-template-columns: 3.5rem minmax(0, 1fr) auto;
    grid-template-areas:
      "time who action"
      ". what action"
      ". comment action"
      ". status action";
  }

  :is(.live, .fin-feed) .feed-row .feed-what {
    text-align: left;
  }

  /* The top bar holds the stages, the panel toggle, the clock, Terminar and the menu: below 1280 px "Em direto"
     is for screen readers, the dot and the clock stay (as on phones). */
  .pill-live:not(.is-ended) .pill-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}

@media (min-width: 1280px) {
  /* The live's top bar holds the stages, the Page ("Portugal · EUR"), the clock and Terminar: the logo stays, the
     app's name goes (large text included). */
  body[data-screen="live"] .brand-name {
    display: none;
  }

  .live {
    grid-template-columns: 300px minmax(0, 1fr) 260px;
    grid-template-rows: minmax(0, 1fr) auto;
    grid-template-areas:
      "board body side"
      "board foot side";
    gap: var(--s4);
    padding: var(--s4);
  }

  .live > .live-side {
    display: flex;
    grid-area: side;
    border: 1px solid var(--line);
    border-radius: var(--r-card);
  }

  .live > .board {
    flex-direction: column;
    align-items: stretch;
    min-height: 0;
    padding: 22px;
    border: 1px solid var(--line);
    border-radius: var(--r-card);
    background: var(--surface);
    overflow-y: auto;
  }

  .live > .board .board-in {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }

  .live > .board .board-no {
    font-size: 80px;
  }

  .live > .board .board-price {
    font-family: var(--font-num);
    font-size: 40px;
    font-weight: 600;
    color: var(--chalk);
  }

  .live > .board .board-price small {
    font-size: 20px;
  }

  .live > .live-body,
  .live > .live-foot {
    border: 1px solid var(--line);
    border-radius: var(--r-card);
    background: var(--surface);
    overflow: hidden;
  }
}

@media (max-width: 699.98px) {
  .live > .board {
    min-height: 0;
    margin: var(--s3) var(--s3) 0;
    padding: 14px;
    border: 1px solid var(--line);
    border-radius: var(--r-card);
    background: var(--surface);
  }

  /* Line 1 the number and the price, line 2 the whole name (up to three lines). */
  .live > .board .board-in {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
      "no price"
      "art art";
    align-items: center;
    gap: 2px var(--s3);
  }

  .live > .board .board-no {
    grid-area: no;
    font-size: 44px;
  }

  .live > .board .board-price {
    grid-area: price;
    justify-self: end;
    font-size: 26px;
  }

  .live > .board .board-art {
    grid-area: art;
  }

  .live > .board .board-desc {
    font-size: 18px;
  }

  .live > .board .board-orders {
    display: none;
  }

  .feed-tabs {
    display: flex;
    align-self: stretch;
    margin: var(--s3) var(--s3) 0;
  }

  /* Three tabs share the phone's width on one line each; what does not fit scrolls sideways (scroll-fade.js). */
  .feed-tabs button {
    flex: 1 0 auto;
    min-width: 0;
  }

  /* The counters on one line under the article card (the summary column is hidden on a phone). */
  .phone-kpis {
    display: flex;
    flex-wrap: wrap;
    row-gap: 0;
    gap: 2px var(--s3);
    margin: 0;
    padding: var(--s2) var(--s4) 0;
    color: var(--muted);
    font-size: var(--text-min);
  }

  .phone-kpis strong {
    color: var(--text);
    font-weight: 500;
  }

  .phone-kpis .is-warn strong {
    color: var(--warn);
  }

  /* Every card stacks on a phone: the name and the order, the comment, the state, the buttons below. */
  :is(.live, .fin-feed) .feed-row {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "who what"
      "comment comment"
      "status status"
      "action action";
    padding: 14px;
    border-radius: var(--r-card);
  }

  :is(.live, .fin-feed) .feed-row .feed-what {
    max-width: 50vw;
    overflow-wrap: anywhere;
  }

  :is(.live, .fin-feed) .feed-row.is-review {
    border: 1px solid var(--warn-line);
    background: var(--warn-bg);
  }

  /* The row's own time goes; a conversation card's comments keep theirs (spec 2026-10-23). */
  :is(.live, .fin-feed) .feed-row > .feed-time {
    display: none;
  }

  :is(.live, .fin-feed) .feed-row > .feed-actions {
    justify-content: flex-start;
  }

  /* Oui and Non side by side, full width, 52 px (spec 2026-10-20 §2). */
  :is(.live, .fin-feed) .feed-row.is-review > .feed-actions {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
    gap: var(--s2);
  }

  :is(.live, .fin-feed) .feed-row.is-review > .feed-actions > .btn {
    min-height: 52px;
  }

  /* The sizes take the whole line above Não. */
  :is(.live, .fin-feed) .feed-row.is-review > .feed-actions > .feed-sizes {
    grid-column: 1 / -1;
  }

  :is(.live, .fin-feed) .feed-row.is-review > .feed-actions:has(> .feed-sizes) {
    grid-template-columns: minmax(0, 1fr);
  }

  :is(.live, .fin-feed) .feed-sizes .chip {
    flex: 1 0 52px;
    min-height: 52px;
  }

  .live-foot {
    gap: var(--s2);
    padding: var(--s2) var(--s3);
  }

  /* The printer tray on one line: the icon, the count, the newest label whole, then Desfazer. */
  .tray {
    justify-content: flex-start;
    gap: var(--s2);
  }

  .tray-label .label-mono {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  .tray-strip {
    flex: 1 1 auto;
    padding: 2px;
  }

  .tray-strip.is-overflowing {
    mask-image: none;
  }

  .tray-strip .ticket:not(:first-child) {
    display: none;
  }

  .tray-strip .ticket {
    flex: 1 1 auto;
    min-width: 0;
    max-width: none;
  }

  .btn-feed,
  .auto-chip,
  .topbar-source {
    display: none;
  }

  /* Terminar keeps its word next to the clock (an icon alone is too easy to miss or mistake); the on-air pill keeps
     its dot and its clock, "Em direto" is for screen readers. */
  .btn-finish {
    padding: 0 var(--s3);
  }

  .pill-live:not(.is-ended) .pill-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  /* A conversation card: "· 3 mensagens" on its own line under the name, the order keeps its place. */
  :is(.live, .fin-feed) .feed-row.is-grouped .feed-count {
    display: block;
  }

  :is(.live, .fin-feed) .feed-row.is-grouped .feed-count-sep {
    display: none;
  }

  /* The three tabs fit a 360 px phone on one line. */
  .feed-tabs {
    gap: 2px;
  }

  .feed-tabs button {
    padding: 0 8px;
    font-size: 0.92em;
  }

  .feed-tabs .tab-count {
    margin-left: 4px;
    padding: 0 0.35em;
  }
}

/* The live footer keeps clear of the home bar when no bottom navigation sits under it. */
.live-foot {
  padding-bottom: max(var(--s2), env(safe-area-inset-bottom));
}

body:has(.bottomnav:not([hidden])) .live-foot {
  padding-bottom: var(--s2);
}

/* ---------- short screens (a phone held sideways: a screen under 500 px high, layout.js heightClass) ----------
   Keyed on :root.is-short, set from the screen, not the viewport's height (the soft keyboard shrinks that). The article and the figures fold into one strip above the feed, the side panel goes (its tabs too), the figures
   on one row; the rail stays behind its toggle. */
/* Contrôle: the band scrolls away with the points, the footer stays. */
:root.is-short .fin {
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}

:root.is-short .fin-band {
  flex: none;
  padding: var(--s2) var(--s4);
}

:root.is-short .fin-kpi-value {
  font-size: 28px;
}

:root.is-short .fin-cols {
  flex: 1 0 auto;
}

:root.is-short .fin > .fin-foot {
  position: sticky;
  bottom: 0;
  z-index: 5;
  background: var(--bg);
}

:root.is-short .live {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto;
  grid-template-areas: "board" "body" "foot";
}

:root.is-short .live > .side-tabs,
:root.is-short .live > .live-side,
:root.is-short .live[data-side-tab="summary"] > .live-side {
  display: none;
}

:root.is-short .live > .board,
:root.is-short .live[data-side-tab="summary"] > .board {
  grid-area: board;
  display: flex;
  flex-direction: row;
  min-height: 0;
  padding: var(--s2) var(--s4);
  border-left: 0;
  border-bottom: 1px solid var(--line);
  overflow: hidden;
}

:root.is-short .live > .board .board-in {
  flex-direction: row;
  align-items: center;
  gap: var(--s3);
}

:root.is-short .live > .board .board-no {
  font-size: 34px;
}

:root.is-short .live > .board .board-art {
  flex: 1 1 auto;
}

:root.is-short .live > .board .board-desc {
  font-size: 17px;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}

:root.is-short .live > .board .board-price {
  font-size: 26px;
}

:root.is-short .live > .board .board-sizes .size-tag {
  min-height: 30px;
}

:root.is-short .live > .board :is(.board-since, .board-orders) {
  display: none;
}

:root.is-short .phone-kpis {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--s4);
  margin: 0;
  padding: var(--s2) var(--s4) 0;
  overflow: hidden;
  color: var(--muted);
  font-size: var(--text-min);
  white-space: nowrap;
}

:root.is-short .phone-kpis strong {
  color: var(--text);
  font-weight: 500;
}

:root.is-short .feed-tabs {
  margin-top: var(--s2);
}

:root.is-short .live-foot {
  padding-top: var(--s1);
  padding-bottom: max(var(--s1), env(safe-area-inset-bottom));
}

/* Avant le live: Começar stays in view at the foot, the tiles scroll behind it. */
:root.is-short .avant-start {
  position: sticky;
  bottom: 0;
  z-index: 3;
  flex-direction: row;
  justify-content: center;
  gap: var(--s3);
  margin-top: 0;
  padding: var(--s2) 0;
  border-top: 1px solid var(--line);
  background: var(--bg);
}

:root.is-short .avant-start .btn-start {
  min-height: 56px;
  width: auto;
  font-size: 18px;
}

:root.is-short .avant-start p {
  margin: 0;
  font-size: var(--text-min);
}

:root.is-short .avant {
  padding-top: var(--s4);
  gap: var(--s4);
}


/* ---------- sheets and line details ----------
   Under 700 px the customer sheets (modal-wide) and the line editor fill the screen; from 1600 px the line editor is
   a side panel on the right (openDialog panel: true). Confirmations stay small and centred everywhere. In Contrôle the
   customer's sheet replaces the "À vérifier" column on a phone. */

@media (max-width: 699.98px) {
  .modal-backdrop:has(> .modal-wide),
  .modal-backdrop:has(> .modal.editor) {
    padding: 0;
    place-items: stretch;
  }

  .modal-wide,
  .modal.editor {
    width: 100%;
    height: 100%;
    max-height: none;
    border: 0;
    border-radius: 0;
    overflow-y: auto;
    padding: calc(var(--s4) + env(safe-area-inset-top)) var(--s4) calc(var(--s4) + env(safe-area-inset-bottom));
  }

  .fin-cols:has(.fin-sheet) > .fin-checks {
    display: none;
  }
}

/* ---------- small heights (a 360×640 phone, a phone held sideways: :root.is-phone-short / .is-short) ----------
   The article is one row (number, name on one line, price) with the "Este live" button; the figures leave the
   screen for a sheet over the feed; every dialog fills the screen and scrolls. */
:root.is-phone-short .live > .board {
  margin: var(--s2) var(--s3) 0;
  padding: 4px 4px 4px 12px;
}

:root.is-phone-short .live > .board .board-in {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-height: 48px;
}

:root.is-phone-short .live > .board .board-no {
  font-size: 28px;
}

:root.is-phone-short .live > .board .board-art {
  flex: 1 1 auto;
}

:root.is-phone-short .live > .board .board-price {
  font-size: 20px;
}

:root.is-phone-short .live > .board .board-price small {
  font-size: 13px;
}

:root.is-phone-short .live > .board :is(.board-sizes, .board-since) {
  display: none;
}

:root.is-phone-short .feed-tabs {
  margin-top: var(--s2);
}


:root:is(.is-phone-short, .is-short) .live > .board .board-desc {
  display: block;
  font-size: 15px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

:root:is(.is-phone-short, .is-short) .live > .board .board-sizes {
  display: none;
}

:root:is(.is-phone-short, .is-short) .live .phone-kpis {
  display: none;
}

:root:is(.is-phone-short, .is-short) .modal.editor {
  gap: var(--s3);
}

/* Contrôle: the figures on one row that scrolls sideways, the points right under them. */
:root:is(.is-phone-short, .is-short) .fin-band {
  flex-wrap: nowrap;
  gap: var(--s4);
  padding: var(--s2) var(--s4);
  overflow-x: auto;
  scrollbar-width: none;
  /* The row's end fades: more figures sideways; the padding lets the last one scroll out of the fade. */
  padding-right: 40px;
  mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent);
}

:root:is(.is-phone-short, .is-short) .fin-kpi {
  flex: none;
}

:root:is(.is-phone-short, .is-short) .fin-kpi-value {
  font-size: 22px;
}

:root:is(.is-phone-short, .is-short) .fin-kpi-label {
  font-size: 11px;
  white-space: nowrap;
}

/* Étiquette: the screen scrolls, the label keeps its room and the actions stay at the foot. */
:root:is(.is-phone-short, .is-short) .etiquette {
  grid-template-rows: max-content auto minmax(10rem, 1fr) auto;
  height: auto;
  min-height: 100%;
}

:root:is(.is-phone-short, .is-short) .label-band {
  padding: var(--s2) var(--s3);
}

:root:is(.is-phone-short, .is-short) .label-stage {
  padding: var(--s3);
}

:root:is(.is-phone-short, .is-short) .etiquette > .label-bar {
  position: sticky;
  bottom: 0;
  z-index: 3;
  flex-wrap: wrap;
  gap: var(--s2);
  padding: var(--s2) var(--s3);
}

:root:is(.is-phone-short, .is-short) .editor-title {
  font-size: 20px;
}

:root:is(.is-phone-short, .is-short) .late-articles {
  max-height: none;
  overflow: visible;
}

:root:is(.is-phone-short, .is-short) .live > .board .btn-summary {
  display: inline-flex;
  flex: none;
  margin-left: var(--s2);
}

/* "Este live" opens over the feed from the bottom; the scrim under it closes nothing by itself (Escape, ×). */
:root:is(.is-phone-short, .is-short) .live.show-summary::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 40;
  background: var(--scrim);
}

:root:is(.is-phone-short, .is-short) .live.show-summary > .live-side {
  display: flex;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 41;
  max-height: min(85dvh, 560px);
  padding-bottom: calc(var(--s4) + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
  border-radius: var(--r-card) var(--r-card) 0 0;
  box-shadow: var(--shadow-raised);
}

:root:is(.is-phone-short, .is-short) .live-side .btn-summary-close {
  display: inline-flex;
}

:root:is(.is-phone-short, .is-short) .live-side .kpis {
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
}

/* Every dialog scrolls inside the screen; the sheets and the line editor fill it (as on phones). */
:root:is(.is-phone-short, .is-short) .modal-backdrop > .modal {
  max-height: 100%;
  overflow-y: auto;
  scroll-padding-bottom: 96px;
}

:root:is(.is-phone-short, .is-short) .modal-backdrop:has(> .modal-wide),
:root:is(.is-phone-short, .is-short) .modal-backdrop:has(> .modal.editor) {
  padding: 0;
  place-items: stretch;
}

:root:is(.is-phone-short, .is-short) .modal-backdrop > :is(.modal-wide, .modal.editor) {
  width: 100%;
  max-width: none;
  height: 100%;
  border: 0;
  border-radius: 0;
  padding: calc(var(--s4) + env(safe-area-inset-top)) var(--s4) calc(var(--s4) + env(safe-area-inset-bottom));
}


@media (min-width: 1600px) {
  .modal-backdrop.is-panel {
    place-items: stretch end;
    padding: 0;
  }

  .modal-backdrop.is-panel > .modal {
    width: 520px;
    height: 100%;
    max-height: none;
    border-width: 0 0 0 1px;
    border-radius: 0;
    overflow-y: auto;
    animation: panel-in var(--motion) ease-out;
  }
}

@keyframes panel-in {
  from {
    transform: translateX(24px);
    opacity: 0;
  }
}

/* ---------- every screen under 700 px ---------- */

@media (max-width: 699.98px) {
  /* Top bar: the mark, the store, the screen's own controls; the stages are in the bottom navigation. */
  .topbar {
    gap: var(--s2);
    padding-left: var(--s3);
    padding-right: var(--s3);
  }

  .stage-pills,
  .topbar-page,
  .brand-tag {
    display: none;
  }

  /* Store switcher (platform, two stores or more): a compact pill; the "during a live" hint stays for screen readers. */
  .store-switch-select {
    max-width: 8.5rem;
    min-height: var(--tap);
    border-radius: var(--r-pill);
  }

  /* Lives home top bar: the language is in Réglages, Réglages in the bottom navigation, the search is an icon (styles.css, under 1280 px). */
  .topbar .lang-switch,
  body:not([data-screen="connexion"]) .topbar .btn-settings {
    display: none;
  }

  .store-switch-hint {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  /* Lives home: "Início" and the time beside the live's name and figures; its state, then each button on its own
     full-width line (UX review E3). */
  .live-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
      "time name"
      "time sub"
      "tag tag"
      "late late"
      "action action";
    align-items: center;
    gap: var(--s1) var(--s3);
  }

  .live-row > .live-row-main {
    display: contents;
  }

  .live-row > .live-row-time {
    grid-area: time;
    align-self: start;
    min-width: 0;
  }

  .live-row-name {
    grid-area: name;
  }

  .live-row-sub {
    grid-area: sub;
    align-self: start;
  }

  .live-row > .tag {
    grid-area: tag;
    justify-self: start;
    margin-top: var(--s2);
  }

  .live-row-late,
  .live-row > .btn {
    width: 100%;
    margin-top: var(--s2);
  }

  .live-row-late {
    grid-area: late;
  }

  .live-row > .btn {
    grid-area: action;
  }

  .lives-main,
  .lives-aside {
    padding: var(--s4);
  }

  /* Connexion and Avant le live. */
  .connexion {
    padding: var(--s5) var(--s4) var(--s6);
  }

  .avant {
    padding-top: var(--s5);
  }

  .avant-start .btn-start {
    width: 100%;
  }

  /* Começar stays in view above the bottom navigation, its hint under it; the tiles scroll behind. */
  .avant-start {
    position: sticky;
    bottom: 0;
    z-index: 3;
    gap: 4px;
    margin: 0 calc(-1 * var(--s4)) calc(-1 * var(--s6));
    padding: var(--s3) var(--s4);
    border-top: 1px solid var(--line);
    background: var(--bg);
  }

  .avant-start .btn-start {
    min-height: 56px;
    font-size: 18px;
  }

  .avant-start p {
    margin: 0;
    font-size: var(--text-min);
  }

  /* One way back per screen: the label screen has Voltar, a customer's message has its own back link. */
  body[data-screen="etiquette"] .btn-lives,
  body:has(.msgs.is-detail) .btn-lives {
    display: none;
  }

  .page {
    padding: var(--s4) var(--s4) var(--s6);
  }

  /* Contrôle (fin du live): the band scrolls away with the points, the footer stays (UX review 2026-10-23). */
  .fin {
    display: flex;
    flex-direction: column;
    overflow-y: auto;
  }

  .fin-band {
    flex: none;
    padding: var(--s3) var(--s4);
  }

  .fin-kpi-value {
    font-size: 26px;
  }

  .fin-cols {
    flex: 1 0 auto;
    overflow: visible;
  }

  /* Défaire as an icon, ⋯ and the one primary action on one line; "print the missing labels" above them. */
  .fin > .fin-foot {
    position: sticky;
    bottom: 0;
    z-index: 5;
    flex-wrap: wrap;
    gap: var(--s2);
    padding: var(--s2) var(--s3);
    background: var(--bg);
  }

  .fin > .fin-foot .btn-undo {
    width: var(--tap);
    padding: 0;
  }

  .fin > .fin-foot .btn-undo .btn-text {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }

  .fin > .fin-foot .btn-print-missing {
    flex-basis: 100%;
    order: -1;
  }

  .fin > .fin-foot .btn-all-good {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0 var(--s3);
  }

  .fin > .fin-foot > .spacer {
    display: none;
  }

  /* Suivi's footer: Défaire full width, the "all done" tag above it. */
  .suivi > .fin-foot {
    gap: var(--s2);
    padding: var(--s2) var(--s3);
  }

  .suivi > .fin-foot .btn {
    flex: 1 1 auto;
  }

  .suivi > .fin-foot .tag {
    flex-basis: 100%;
    order: -1;
    max-width: none;
  }

  .suivi > .fin-foot > .spacer {
    display: none;
  }

  /* Suivi: header rows grow with their content. */
  .suivi {
    grid-template-rows: auto auto max-content minmax(12rem, 1fr) auto;
  }

  /* Étiquette: the band scrolls sideways, the actions wrap two by two. */
  .etiquette {
    /* max-content: the band scrolls sideways, and a scroller's auto row would shrink on a short screen. */
    grid-template-rows: max-content auto minmax(12rem, 1fr) auto;
  }

  .label-band {
    padding: var(--s2) var(--s3);
  }

  .label-stage {
    padding: var(--s3);
  }

  .label-bar {
    flex-wrap: wrap;
    gap: var(--s2);
    padding: var(--s3);
  }

  .label-bar > .btn {
    flex: 1 1 calc(50% - var(--s2));
  }

  .label-bar > .spacer {
    display: none;
  }

  /* Messages: the list, or one customer's message with a way back (UX review 2026-10-23). */
  .msgs {
    grid-template-rows: minmax(0, 1fr);
  }

  .msgs > .msg-list {
    max-height: none;
    border-bottom: 0;
  }

  .msgs.is-detail > .msg-list,
  .msgs:not(.is-detail) > .msg-pane {
    display: none;
  }

  .msg-page-title {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip: auto;
    white-space: normal;
    padding: var(--s4) var(--s4) var(--s1);
  }

  .topbar .btn-send-all {
    display: none;
  }

  .msg-list-foot {
    position: sticky;
    bottom: 0;
    display: block;
    margin-top: auto;
    padding: var(--s3);
    border-top: 1px solid var(--line);
    background: var(--rail);
  }

  .msg-back {
    display: inline-flex;
    flex-basis: 100%;
    justify-content: flex-start;
    margin-left: calc(-1 * var(--s2));
    padding: 0 var(--s2);
  }

  /* Suivi rows: more room for her name. */
  .track-rows,
  .track-empty {
    margin-inline: var(--s3);
  }

  .track-row {
    padding-inline: var(--s2);
  }

  /* Messages and Réglages: the list above, the pane below (styles.css); tighter padding. */
  .msg-pane,
  .settings-body {
    padding: var(--s4);
  }

  .settings-nav {
    flex-wrap: nowrap;
    overflow-x: auto;
    padding: var(--s2);
  }

  /* One line per category in the scrolling row ("Durante o live" never on two lines). */
  .settings-cat {
    flex: none;
    white-space: nowrap;
  }
}

/* Réglages › Mensagens on a phone: the tag table becomes one card per tag (tag above, value below). */
@media (max-width: 699.98px) {
  .tpl-table thead {
    display: none;
  }

  .tpl-table,
  .tpl-table tbody,
  .tpl-table tr,
  .tpl-table td {
    display: block;
    width: 100%;
  }

  .tpl-table tr {
    padding: var(--s3) 0;
    border-top: 1px solid var(--line);
  }

  .tpl-table td {
    padding: 0;
    border-top: 0;
  }

  .tpl-table td + td {
    margin-top: var(--s2);
  }
}

/* Parcels (spec 2026-10-24 §7): "Digitalizar etiquetas" in the page on a phone, in the top bar from 700 px; her parcel
   in a side panel beside Seguimento's rows from 700 px (a screen of its own on a phone). */
@media (max-width: 699.98px) {
  .topbar-scan {
    display: none;
  }

  .track-scan {
    margin: var(--s3) var(--s3) 0;
    width: calc(100% - 2 * var(--s3));
  }

  .colis {
    padding: var(--s4) var(--s3);
  }

  .colis-foot {
    padding: var(--s3);
  }

  /* Réglages › Impressora: the four label sizes wrap ("50×30 (padrão)" is long), none hidden behind a scroll. */
  .segmented[data-setting="addressFormat"] {
    flex-wrap: wrap;
    overflow: visible;
    border-radius: var(--r-card);
  }

  /* A late order on a phone: who it is for first, then the (long) list of articles. */
  .late-order .editor-grid > .late-customer-field {
    order: -1;
  }

  /* Seguimento on a phone (mockup 04): three figures; the scope and the filters wrap rather than hide behind a scroll. */
  .suivi .fin-kpi.is-phone-hidden {
    display: none;
  }

  .track-filters,
  .track-bar .segmented {
    flex-wrap: wrap;
    overflow: visible;
    border-radius: var(--r-card);
  }

  /* A compact row: her name on one line, her counts, one status and ⋯ (the reference, payment tag, stepper and the
     row's action are in her parcel and in ⋯, whose first item is the row's action). */
  .track-row.has-status {
    grid-template-columns: minmax(0, 1fr) auto auto;
    column-gap: var(--s2);
    min-height: 72px;
  }

  .track-row.has-status > :is(.track-ref, .track-pay, .track-delivery) {
    display: none;
  }

  .track-row.has-status > .track-status {
    display: block;
  }

  .track-row.has-status .track-acts {
    grid-column: auto;
    justify-content: flex-end;
  }

  .track-row.has-status .track-primary {
    display: none;
  }

  .track-row.has-status .menu-anchor.is-phone-only {
    display: block;
  }

  .track-row.has-status .menu-item.is-phone-only {
    display: flex;
  }

  .track-row.has-status .track-open {
    max-width: 100%;
  }

  .track-row.has-status .track-who strong {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .track-row.has-status .track-money {
    display: none;
  }
}

@media (min-width: 700px) and (max-width: 1279.98px) {
  .track-scan {
    display: none;
  }

  /* The top bar is full here: the icon alone (its name stays in aria-label). */
  .topbar-scan {
    width: var(--tap);
    padding: 0;
    justify-content: center;
  }

  .topbar-scan .scan-open-label {
    display: none;
  }

  .track-split {
    grid-template-columns: minmax(0, 1fr) minmax(300px, 40%);
  }

  /* The panel's rows keep one column beside it. */
  .track-split .track-rows {
    grid-template-columns: minmax(0, 1fr) auto;
  }
}

@media (min-width: 1280px) {
  .track-scan {
    display: none;
  }

  .track-split {
    grid-template-columns: minmax(0, 1fr) minmax(360px, 32%);
  }

  /* Her parcel open beside the rows: the columns may shrink, and the reference (in the panel's head) goes. */
  .track-split .track-rows {
    grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) minmax(0, 1.2fr) auto;
  }

  .track-split .track-rows.no-pay {
    grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) auto;
  }

  .track-split .track-rows.no-delivery {
    grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr) auto;
  }

  .track-split .track-ref {
    display: none;
  }
}

/* The parcel sheet on a phone's screen (screens/parcel-sheet.js): each line as "224 · Vestido" over "M · 1 × 19,90 €"
   instead of a five-column table. Screen only: the printed sheet keeps its table whatever the paper's width. */
@media screen and (max-width: 699.98px) {
  .parcel-sheet-dialog .parcel-lines thead {
    display: none;
  }

  .parcel-sheet-dialog .parcel-lines,
  .parcel-sheet-dialog .parcel-lines tbody,
  .parcel-sheet-dialog .parcel-lines tr {
    display: block;
  }

  .parcel-sheet-dialog .parcel-lines tr {
    padding: var(--s2) 0;
    border-top: 1px solid var(--line);
    color: var(--muted);
  }

  .parcel-sheet-dialog .parcel-lines td {
    display: inline;
    padding: 0;
    text-align: left;
  }

  .parcel-sheet-dialog .parcel-lines td.parcel-art {
    display: block;
    width: auto;
    color: var(--text);
    font-weight: 600;
  }

  .parcel-sheet-dialog .parcel-lines td.parcel-color.is-none {
    display: none;
  }

  .parcel-sheet-dialog .parcel-lines td:is(.parcel-color, .parcel-qty)::before {
    content: " · ";
  }

  .parcel-sheet-dialog .parcel-lines td.parcel-qty::after {
    content: " × ";
  }

  /* The QR code under the head, smaller, so the name keeps the width. */
  .parcel-sheet-dialog .parcel {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .parcel-sheet-dialog .parcel-qr {
    width: 64px;
    height: 64px;
  }
}

/* The Colis screen from 700 px: the shipping label's preview beside the address, smaller, so it shows above the
   actions' foot instead of under it. */
@media (min-width: 700px) {
  .colis:not(.is-panel):has(> .colis-preview) {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .colis:not(.is-panel):has(> .colis-preview) > * {
    grid-column: 1 / -1;
  }

  .colis:not(.is-panel) > .colis-ship:has(~ .colis-preview) {
    grid-column: 1;
    align-self: start;
  }

  .colis:not(.is-panel) > .colis-ship ~ .colis-preview {
    grid-column: 2;
    align-self: start;
  }

  .colis:not(.is-panel) > .colis-preview .colis-preview-canvas {
    width: 220px;
  }
}
