/* ==========================================================================
   responsive.css - the small-screen tier.

   Why this file exists
   --------------------
   The whole of style.css's responsive story was a single rule collapsing .grid2
   to one column below 900px. Everything else - the navbar, the tab bar, tables,
   buttons, inputs, the settings grid - had no small-screen treatment at all,
   which is what "the mobile UI is awful and not mobile friendly" is describing.
   Three things were visibly broken because of it:

     - fixed grid tracks. repeat(auto-fit, minmax(320px, 1fr)) cannot go below
       320px, so on a 360px phone the grid overflowed its container and the
       Settings cards were shoved together. Those have been fixed at source with
       min(320px, 100%); this file adds the tier that makes them stack.

     - touch targets and text size. Buttons were sized for a mouse, and any input
       under 16px makes iOS Safari zoom the page on focus, so a form filled in on
       a phone ended up magnified and unusable.

     - fixed-position furniture. The sticky navbar and the toast container were
       sized and placed without regard for notches, home indicators or the safe
       areas they sit in.

   Loaded last, after theme.css, so it wins on equal specificity without needing
   !important anywhere. Loaded by all three pages.

   Honest limitation: this was written from the stylesheets and the markup, not
   from a rendered screenshot. It addresses every mechanism that can be
   identified statically - overflow, fixed tracks, tap targets, safe areas, form
   zoom, table overflow - and check-responsive.js now fails the build if any of
   those mechanisms come back. What it cannot do is tell you it looks good.
   ========================================================================== */

/* --------------------------------------------------------------- guards ----

   Applied at every width, not just small ones. These are the four ways content
   escapes a viewport, and each one produces a page you can scroll sideways,
   which reads as "broken" long before anyone can say why. */

img,
video,
canvas,
svg,
iframe {
  max-width: 100%;
}

/* A long ticket id in a cell is the classic overflow: one unbroken token in a
   narrow column pushes the whole table wider than the screen. */
td,
th,
.hint,
.field-hint,
.small,
.muted {
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* Long codes and URLs get their own scroll rather than wrapping mid-character,
   because a wrapped id is hard to read aloud over the gate. */
.code-box,
.code,
[contenteditable] {
  overflow-wrap: anywhere;
}

/* Tables scroll inside their own wrapper (.table-wrap exists for this) rather
   than widening the page. */
.table-wrap {
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* Anything focusable gets a comfortable target and no double-tap delay.
   touch-action: manipulation removes the 300ms wait without disabling pinch
   zoom, which user-scalable=no would have done at the cost of accessibility. */
button,
a,
input,
select,
textarea,
[role="button"] {
  touch-action: manipulation;
}

/* ------------------------------------------------------- <= 900px: tablets -- */

@media (max-width: 900px) {
  /* Every multi-column layout in the project, collapsed. Named explicitly rather
     than by guessing, because a grid that is missed here is exactly the layout
     that ends up side-by-side and squeezed on a phone. */
  .grid2,
  .settings-grid,
  .field-row,
  .qr-layout-left,
  .qr-layout-right,
  .admin-sections,
  .stat-grid,
  .feed-stats,
  .store-grid,
  .order-grid {
    grid-template-columns: 1fr !important;
    flex-direction: column !important;
  }

  /* Page headers stack so a long title does not push the actions off-screen. */
  .page-header {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
  }

  .page-header .row {
    width: 100%;
    flex-wrap: wrap;
  }

  /* Tabs scroll horizontally instead of wrapping onto several ragged rows. */
  .tabs,
  .tab-bar {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    gap: var(--space-1);
  }
  .tabs::-webkit-scrollbar,
  .tab-bar::-webkit-scrollbar {
    display: none;
  }

  .tab-btn {
    flex: 0 0 auto;
    white-space: nowrap;
  }
}

/* ------------------------------------------------ <= 600px: real phones ---- */

/*
   Mobile-first layout for the storefront, not a squeezed desktop one.

   The storefront is the page buyers pay on, so almost every visitor arrives
   here on a phone. Collapsing the desktop columns - which is all the tiers above
   do - leaves a page where the only call to action is somewhere in the middle of
   a scrolling column, reached only by scrolling to find it. That is the actual
   meaning of "not mobile friendly", and it is not fixed by media queries.

   Three things below are what a purpose-built mobile shop does, applied to the
   markup that already exists rather than rebuilt from scratch:

     1. The primary action is pinned to the bottom of the viewport, inside thumb
        reach, instead of living mid-page.
     2. Sign-in becomes a bottom sheet rather than a dropdown wedged under a
        sticky bar, where it had roughly 60px of usable height.
     3. The bought ticket code - the one thing a buyer must read, copy and hand
        over - is given the whole width, at a size that can be read at arm's
        length, with a 44px tap target for copying.
*/
@media (max-width: 600px) {
  /* Reduce the page gutter so content gets the width it needs. */
  body {
    padding-left: var(--space-3);
    padding-right: var(--space-3);
  }

  /* --- 1. the primary action, in thumb reach -------------------------- */

  /*
    Fixed to the bottom, spanning the width.

    The stacking level comes from the scale rather than a literal. It has to sit
    above page content (--z-content, 10) and below both the scrim (50) and the
    header (60), so the sign-in sheet and the account panel still open over this
    bar. A hardcoded number here is how the account panel once ended up underneath
    the scrim and read as "a dark overlay with nothing on it".
  */
  .store-actions {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: var(--z-action);
    margin: 0;
    padding: var(--space-3) var(--space-4);
    /* Clear the home indicator on a notched phone. */
    padding-bottom: calc(var(--space-3) + var(--safe-bottom, 0px));
    gap: var(--space-2);
    background: color-mix(in srgb, var(--color-bg) 94%, transparent);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-top: var(--border-w) solid var(--color-border);
    box-shadow: 0 -6px 20px rgb(0 0 0 / 0.28);
  }

  .store-actions .btn {
    flex: 1 1 0;
    margin: 0;
  }

  /* Room for the pinned bar, or it covers the end of the page. */
  .storefront {
    padding-bottom: calc(96px + var(--safe-bottom, 0px));
  }

  /* --- the hero -------------------------------------------------------- */

  /*
    The accent moves from a left border to a top border. A 3px left border plus
    24px of padding costs 27px of a 390px screen, which is 7% of the width, to
    decorate an edge nobody is reading.
  */
  .store-hero {
    max-width: 100%;
    border-left: 0;
    border-top: 3px solid var(--accent);
    padding-left: 0;
    padding-top: var(--space-4);
  }

  .store-hero h1 {
    font-size: 1.5rem;
    line-height: 1.25;
  }

  /* Longer measure on a narrow screen is the main readability win here. */
  .store-hero p,
  .store-foot,
  .store-note {
    line-height: 1.6;
  }

  .storefront {
    padding-top: var(--space-5);
  }

  /* --- 2. sign-in as a bottom sheet ------------------------------------- */

  /*
    Anchored absolute under the topbar, the panel had whatever height the bar
    left it - which on a phone is very little. Anchored to the bottom instead, it
    gets most of the screen, the way a phone sheet is expected to behave, and the
    close button stays within reach rather than at the top of a cramped strip.
  */
  .account-panel {
    position: fixed;
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    max-height: 88vh;
    overflow-y: auto;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    border-left: 0;
    border-right: 0;
    border-bottom: 0;
    padding: var(--space-5) var(--space-4);
    padding-bottom: calc(var(--space-5) + var(--safe-bottom, 0px));
    box-shadow: 0 -12px 40px rgb(0 0 0 / 0.5);
  }

  /* A sheet is a sheet, and the scrim above it still has to work. */
  .account-panel::before {
    content: '';
    display: block;
    width: 44px;
    height: 4px;
    margin: calc(var(--space-2) * -1) auto var(--space-4);
    border-radius: 2px;
    background: var(--color-border);
  }

  /* --- 3. the ticket code the buyer needs ------------------------------- */

  /*
    This is the payload of the whole page: a code to read, copy and show at the
    door. Given the full width and a size legible at arm's length, and made
    tappable to copy rather than requiring a precise drag to select.
  */
  .order-list {
    display: grid;
    gap: var(--space-3);
  }

  .order-item {
    padding: var(--space-4);
  }

  .order-code {
    display: block;
    width: 100%;
    /* 17px reads at arm's length without pinching. */
    font-size: 17px;
    line-height: 1.5;
    letter-spacing: 0.04em;
    padding: var(--space-3);
    margin: var(--space-2) 0;
    min-height: 44px;
    text-align: center;
    /* The code is one unbroken 26-character token. It must break rather than
       push the page sideways. */
    overflow-wrap: anywhere;
    word-break: break-all;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-bg-2);
    cursor: pointer;
  }

  .order-head {
    flex-wrap: wrap;
    gap: var(--space-2);
  }

  .order-ref,
  .order-note {
    line-height: 1.5;
  }

  /* --- device picker --------------------------------------------------- */

  /* Two cards side by side on a phone means two ~45px-wide tap targets. */
  .devices {
    grid-template-columns: minmax(0, 1fr);
  }

  /* --- tap targets ----------------------------------------------------- */

  /*
    44px, from WCAG 2.2 Success Criterion 2.5.5 (Target Size, Enhanced). The
    AA minimum in 2.5.8 is 24px; 44 is the level that actually works with a
    thumb in motion, which is the case here - a gate operator walking up to a
    phone.
  */
  .btn,
  .tab-btn,
  .chip,
  .theme-switch,
  input[type="button"],
  input[type="submit"] {
    min-height: 44px;
  }

  .btn-block {
    width: 100%;
  }

  /*
    16px on every input, and this is the single most important rule in the file.
    iOS Safari zooms the whole page when a field with a font-size under 16px takes
    focus, and it does not zoom back out when focus leaves. Filling in a form
    therefore ends with the page magnified, scrolled to the wrong place, and the
    user has to pinch out to continue. Setting 16px prevents the zoom entirely.
  */
  input,
  select,
  textarea {
    font-size: 16px;
    min-height: 44px;
  }

  /* An icon inside an input must not push the text out of view. */
  .input-wrap input,
  .input-wrap select {
    width: 100%;
    min-width: 0;
  }

  .input-wrap {
    min-width: 0;
  }

  /* Cards and fieldsets stop trying to be side by side. */
  .card,
  .settings-card,
  .stat-card,
  .auth-card {
    padding: var(--space-4);
    max-width: 100%;
  }

  /* The settings legends were the worst-looking part on a phone. A legend that
     overflows its own fieldset border reads as overlapping text. */
  .settings-card legend {
    padding: 0 var(--space-1);
    font-size: var(--text-xs);
    max-width: 100%;
    overflow-wrap: anywhere;
  }

  /* Dense tables: drop the padding and let the wrapper scroll. Trying to fit ten
     columns into 360px is what produces the crushed, overlapping look. */
  .table-wrap table {
    font-size: var(--text-sm);
  }
  .table-wrap th,
  .table-wrap td {
    padding: var(--space-2) var(--space-2);
    white-space: nowrap;
  }

  /* Long-form text should not be measured in a single wide column on a phone. */
  .auth-card,
  .store-copy {
    max-width: 100%;
  }

  /* Code boxes must not force a scrollbar on the page itself. */
  .code-box {
    max-width: 100%;
  }
}

/* ------------------------------------------------ <= 400px: small phones --- */

@media (max-width: 400px) {
  body {
    padding-left: var(--space-2);
    padding-right: var(--space-2);
  }

  .card,
  .settings-card,
  .stat-card,
  .auth-card {
    padding: var(--space-3);
  }

  /* Row groups stack rather than sit shoulder to shoulder. */
  .row {
    flex-wrap: wrap;
  }

  .row > .btn {
    flex: 1 1 auto;
  }
}

/* ------------------------------------------------------------ safe areas ---

   Notches, rounded corners and home indicators. Fixed-position furniture sits
   under them without this, which on a modern iPhone means the first tab is
   partly off the top of the screen and the last button is under the swipe bar.
   viewport-fit=cover on the meta tag is what makes these insets non-zero. */

@supports (padding: max(0px)) {
  :root {
    --safe-top: env(safe-area-inset-top, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-left: env(safe-area-inset-left, 0px);
    --safe-right: env(safe-area-inset-right, 0px);
  }
}

.navbar {
  padding-top: calc(var(--safe-top, 0px) + var(--space-2, 8px));
  padding-left: calc(var(--safe-left, 0px) + var(--space-4, 16px));
  padding-right: calc(var(--safe-right, 0px) + var(--space-4, 16px));
}

.toast-container {
  bottom: calc(var(--safe-bottom, 0px) + var(--space-3, 12px));
  left: calc(var(--safe-left, 0px) + var(--space-2, 8px));
  right: calc(var(--safe-right, 0px) + var(--space-2, 8px));
}

/* The toast is fixed to a min-width, so it is also clamped rather than allowed to
   push the page sideways. */
.toast {
  min-width: 0;
  width: 100%;
  max-width: 520px;
  margin-left: auto;
  margin-right: auto;
}

/* ------------------------------------------------------------ print / zoom - */

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}