/* ==========================================================================
   nv-mobile.css — phone / iOS hardening for the dashboard, card and menu
   ==========================================================================

   Plain CSS, deliberately NOT Tailwind utilities, for the same reason the
   dashboard shell layout lives in plain CSS: whether a Save button is
   reachable on a phone must not depend on somebody remembering to rebuild
   the Tailwind bundle.

   It is also UNLAYERED on purpose. Tailwind v4 and daisyUI 5 put everything
   inside @layer blocks, and an unlayered rule beats any layered rule no
   matter the specificity. That lets these fixes override daisyUI and
   utility classes (`max-h-[90vh]`, `mb-5`, `overflow-hidden`) without a
   single !important, while a business's own theme CSS — also unlayered and
   loaded after this file — can still override them.

   Nothing here renames, removes or restructures markup, so custom card and
   menu themes keep working.

   WHY THE MODALS WERE BROKEN ON iPHONE
   ------------------------------------
   daisyUI caps `.modal-box` at `max-height: 100vh`. On iOS Safari (and every
   iOS browser, they all use WebKit) `100vh` is the height of the LARGEST
   viewport — the one you get with the toolbars collapsed. The dashboard page
   itself never scrolls, so the toolbars never collapse, so the visible area
   is always shorter than 100vh. The box was therefore taller than the
   screen, its bottom 80–100px sat behind Safari's toolbar, and because the
   box itself is the scroll container that strip could never be scrolled
   into view. The Save button lives in exactly that strip.

   The fix, in three parts:
     1. size boxes with `dvh` (the height actually visible right now),
     2. pin the action row to the bottom of the box (sticky) so it is on
        screen no matter how long the form is,
     3. pad for the home indicator (`env(safe-area-inset-bottom)`), which
        `viewport-fit=cover` otherwise lets content slide under.
   ========================================================================== */


/* ──────────────────────────────────────────────────────────────────────────
   1. daisyUI modals — every page
   ────────────────────────────────────────────────────────────────────────── */

.modal > .modal-box {
  max-height: calc(100vh - 2rem);          /* browsers without dvh */
  max-height: calc(100dvh - 2rem);
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

/* Chromium makes a scrollable box focusable and showModal() focuses it,
   drawing a focus ring round the whole sheet. */
.modal > .modal-box:focus { outline: none; }

/* Boxes marked overflow-hidden (story details) simply cut off whatever did
   not fit — on a short phone, the approve / delete buttons. Let them scroll.
   The booking box is the exception: it has its own inner scroller (below). */
.modal > .modal-box.overflow-hidden:not(.appt-embedded) {
  overflow-y: auto;
}

/* Booking calendar on the card and menu: the inner scroller was capped at
   78vh — the same large-viewport trap — on top of a header. Make the box a
   column and let the inner area take whatever height is really left. */
.modal > .modal-box.appt-embedded {
  display: flex;
  flex-direction: column;
}
.modal > .modal-box.appt-embedded > header { flex: none; }
.modal > .modal-box.appt-embedded > .overflow-y-auto {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}


/* ── Phones and tablets: pinned header / footer ─────────────────────────── */

@media (max-width: 1023.98px) {

  /* The action row (daisyUI's .modal-action, or the dashboard's own
     .nv-modal-foot) sticks to the bottom of the scrolling box. The box-shadow
     + clip-path pair paints the background out to the box's side edges
     without widening the element (a negative margin would have to know each
     box's padding). */
  .modal-box .modal-action,
  .modal-box .nv-modal-foot {
    position: sticky;
    bottom: 0;
    z-index: 6;
    margin-bottom: 0;
    padding-top: .75rem;
    padding-bottom: max(.75rem, env(safe-area-inset-bottom));
    background-color: var(--nv-modal-bg, var(--color-base-100, #fff));
    box-shadow: 0 0 0 100vmax var(--nv-modal-bg, var(--color-base-100, #fff));
    clip-path: inset(0 -100vmax);
  }

  /* The footer now carries the bottom spacing, so the box and the form stop
     adding their own below it — otherwise a strip of empty padding shows
     under the pinned buttons at the end of the scroll. */
  .modal > .modal-box:has(.modal-action, .nv-modal-foot) { padding-bottom: 0; }
  .modal-box form:has(> .nv-modal-foot) { padding-bottom: 0; }

  .modal-box.bg-base-200 { --nv-modal-bg: var(--color-base-200); }

  /* Title row with a close button, pinned to the top of the box. The
     negative margin pulls it over the box's top padding so the box scrolls
     underneath it cleanly. */
  .modal-box .nv-modal-head {
    position: sticky;
    top: 0;
    z-index: 6;
    margin-top: -1.5rem;
    padding-top: 1rem;
    padding-bottom: .5rem;
    background-color: var(--nv-modal-bg, var(--color-base-100, #fff));
    box-shadow: 0 0 0 100vmax var(--nv-modal-bg, var(--color-base-100, #fff));
    clip-path: inset(0 -100vmax);
  }
}

@media (min-width: 1024px) {
  /* Desktop keeps its original look — the close button is a phone aid. */
  .nv-modal-head .nv-modal-x { display: none; }
}


/* ── Phones: modals become bottom sheets ────────────────────────────────── */

@media (max-width: 639.98px) {
  .modal {
    align-items: end;
    justify-items: center;
  }

  .modal > .modal-box {
    width: 100%;
    max-width: none;
    /* Leave the status bar / notch and a sliver of backdrop visible above
       the sheet, so it reads as a sheet and can be dismissed by tapping. */
    max-height: calc(100vh - 3rem);
    max-height: calc(100dvh - max(env(safe-area-inset-top), .75rem) - 1.25rem);
    border-radius: 1.25rem 1.25rem 0 0;
    padding-bottom: max(1.25rem, env(safe-area-inset-bottom));
  }

  /* Sheets with their own full-bleed header (orders, customers) or a close
     button in the corner (stories) are p-0; keep them flush. */
  .modal > .modal-box.p-0 { padding-bottom: env(safe-area-inset-bottom); }
  .modal > .modal-box.p-0:has(.modal-action, .nv-modal-foot) { padding-bottom: 0; }

  /* Two buttons side by side instead of stacked: the primary action stays
     one thumb-reach from the bottom instead of below a full-width Cancel. */
  .nv-modal-foot.grid { grid-template-columns: 1fr 1fr; }
}


/* ──────────────────────────────────────────────────────────────────────────
   2. Dashboard
   ────────────────────────────────────────────────────────────────────────── */

/* iOS zooms the whole page in when a field under 16px gets focus, and does
   not zoom back out — the dashboard then looks "broken" until pinched. daisy
   inputs are 14px. Touch screens only, so the desktop look is untouched.
   (The card and menu prevent the zoom with maximum-scale in their viewport
   tag instead, and keep their own type sizes.) */
@media (hover: none) and (pointer: coarse) {
  .nv-shell input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]),
  .nv-shell select,
  .nv-shell textarea {
    font-size: 16px;
  }
}

@media (max-width: 1023.98px) {
  /* The closed drawer is translated off-screen, but its shadow was not: it
     painted a grey strip down the edge of every page. */
  .nv-sidebar:not(.is-open) { box-shadow: none; }

  /* Home indicator and landscape notch. viewport-fit=cover hands those areas
     to the page, so the page has to keep its content out of them. */
  .nv-sidebar__body { padding-bottom: calc(.75rem + env(safe-area-inset-bottom)); }

  .nv-header {
    padding-left: max(1rem, env(safe-area-inset-left));
    padding-right: max(1rem, env(safe-area-inset-right));
  }

  #nv-main {
    padding-left: max(1rem, env(safe-area-inset-left));
    padding-right: max(1rem, env(safe-area-inset-right));
    padding-bottom: calc(1.5rem + env(safe-area-inset-bottom));
  }

  .nv-chat { height: calc(100dvh - 8.5rem - env(safe-area-inset-bottom)); }
}

@media (max-width: 639.98px) {
  #nv-main { padding-top: .875rem; }
  .nv-chat { height: calc(100dvh - 7rem - env(safe-area-inset-bottom)); }

  /* Stat tiles: large sums (four-billion-something toman) are wider than a
     phone and pushed the icon off the card. Let the figure shrink and wrap. */
  .nv-shell .stat { min-width: 0; padding-inline: 1rem; }
  .nv-shell .stat-value {
    white-space: normal;
    overflow-wrap: anywhere;
    font-size: clamp(1.35rem, 6.5vw, 2rem);
    line-height: 1.25;
  }

  /* Tables: daisy's 1rem cell padding is what pushed the last column
     (actions, status) off screen on most of them. */
  .nv-shell .table :where(th, td) {
    padding: .625rem .5rem;
  }
  .nv-shell .table :where(th, td):first-child { padding-inline-start: .875rem; }
  .nv-shell .table :where(th, td):last-child { padding-inline-end: .875rem; }

  /* Tables that still do not fit scroll sideways — show that they can. */
  .nv-shell .overflow-x-auto:has(> .table) {
    -webkit-overflow-scrolling: touch;
    background:
      linear-gradient(to left, var(--color-base-100), var(--color-base-100)) left / 1.25rem 100% no-repeat local,
      radial-gradient(farthest-side at 0 50%, rgba(0, 0, 0, .14), transparent) left / .6rem 100% no-repeat scroll;
  }

  /* "My clubs": the name was squeezed to "کا…" beside two buttons. Give it
     the whole first line and put the buttons under it. */
  .nv-club-row { flex-wrap: wrap; row-gap: .75rem; }
  .nv-club-row > :first-child { flex-basis: 100%; }
  .nv-club-row > :last-child { margin-inline-start: auto; }

  /* Product detail rows (key / value / move / delete): two fields on the
     first line, the small buttons under them. */
  .nv-detail-row { flex-wrap: wrap; }
  .nv-detail-row > input { flex: 1 1 8rem; min-width: 0; }
  .nv-detail-row > div:last-child { margin-inline-start: auto; }
  .nv-detail-row .btn-xs { --size: 2rem; }
}


/* ── Products table → cards on phones ───────────────────────────────────── */
/* Five columns (photo, title, category, price, actions) do not fit a phone:
   price and the edit / delete / visible controls ended up off screen behind
   a horizontal scroll nobody knew was there. Same markup, laid out as one
   card per item:

       [photo]  title ..................
       [photo]  category          price
       [photo]            ✎   🗑   ◯
*/
@media (max-width: 639.98px) {
  .nv-rows-products thead { display: none; }
  .nv-rows-products,
  .nv-rows-products tbody { display: block; width: 100%; }

  .nv-rows-products tr {
    display: grid;
    grid-template-columns: 3.5rem minmax(0, 1fr) auto;
    grid-template-areas:
      "img title title"
      "img cat   price"
      "img act   act";
    column-gap: .75rem;
    row-gap: .2rem;
    align-items: center;
    padding: .875rem 1rem;
    border-bottom: 1px solid color-mix(in srgb, currentColor 10%, transparent);
  }
  .nv-rows-products tr:last-child { border-bottom: 0; }

  /* (.table + nth-child to outrank the generic table padding above) */
  .nv-shell .table.nv-rows-products td:nth-child(n) { padding: 0; border: 0; }

  .nv-rows-products td:nth-child(1) { grid-area: img; align-self: start; }
  .nv-rows-products td:nth-child(1) a > div,
  .nv-rows-products td:nth-child(1) > .skeleton { width: 3.5rem; height: 3.5rem; border-radius: .6rem; }

  .nv-rows-products td:nth-child(2) { grid-area: title; min-width: 0; }
  .nv-rows-products td:nth-child(2) > div { flex-wrap: wrap; row-gap: .25rem; }
  .nv-rows-products td:nth-child(3) { grid-area: cat; font-size: .8rem; opacity: .65; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .nv-rows-products td:nth-child(4) { grid-area: price; font-size: .85rem; font-weight: 700; text-align: end; white-space: nowrap; }
  .nv-rows-products td:nth-child(5) { grid-area: act; }

  .nv-rows-products td:nth-child(5) > div { justify-content: flex-end; gap: .5rem; }
  .nv-rows-products td:nth-child(5) .btn { --size: 2.25rem; }
  .nv-rows-products td:nth-child(5) .toggle { --size: 1.5rem; margin-inline-start: .25rem; }
}


/* ── Wide tables → label / value cards on phones ────────────────────────── */
/* For tables marked `nv-stack` (transactions, invoices, installments,
   shifts, reminders, postcard replies): six or seven columns cannot fit a
   phone, and the ones that fell off the side — status, amount, actions —
   were the ones people came to see. Each row becomes a card and each cell a
   "label ...... value" line; nv-mobile.js copies the column header onto the
   cells as data-label. */
@media (max-width: 639.98px) {
  .nv-stack thead { display: none; }
  .nv-stack,
  .nv-stack tbody { display: block; width: 100%; }

  .nv-stack tr {
    display: block;
    padding: .5rem .875rem;
    border-bottom: 1px solid color-mix(in srgb, currentColor 10%, transparent);
  }
  .nv-stack tr:last-child { border-bottom: 0; }

  .nv-shell .table.nv-stack td:nth-child(n) {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    min-height: 2.25rem;
    padding: .3rem 0;
    border: 0;
    text-align: end;
    white-space: normal;
    max-width: none;
  }
  .nv-stack td::before {
    content: attr(data-label);
    flex: none;
    max-width: 45%;
    font-size: .75rem;
    font-weight: 600;
    opacity: .6;
    text-align: start;
  }
  .nv-stack td:not([data-label])::before,
  .nv-stack td[data-label=""]::before { content: none; }
  .nv-stack td[data-label=""] { justify-content: flex-end; }
  .nv-stack td[colspan] { justify-content: center; text-align: center; }

  /* Controls inside cells (the shift editor) keep a sensible width. */
  .nv-stack td > :is(input:not([type="checkbox"]):not([type="radio"]), select) { width: auto; max-width: 60%; }
}


/* ── Club members table → cards on phones ──────────────────────────────── */
/* The generic `nv-stack` treatment (label ...... value) was wrong for this
   one. Its first cell is an avatar with a name and a phone number, its second
   is a progress bar — neither is a "value" that reads next to a label, and
   capping the label at 45% left both squeezed into the remaining half, which
   is why this list looked crushed on a phone while the desktop table was
   fine. Same markup, laid out as a card instead:

       [avatar]  name ......................
       [avatar]  phone · tier
       [progress bar ................]  3/10   [gift ready]
       🕐 2 days ago   🔔        [ stamp ]  [file]
*/
@media (max-width: 639.98px) {
  .nv-rows-members thead { display: none; }
  .nv-rows-members,
  .nv-rows-members tbody { display: block; width: 100%; }

  .nv-rows-members tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "who   who"
      "card  card"
      "meta  act";
    row-gap: .6rem;
    column-gap: .75rem;
    align-items: center;
    padding: .9rem 1rem;
    border-bottom: 1px solid color-mix(in srgb, currentColor 10%, transparent);
  }
  .nv-rows-members tr:last-child { border-bottom: 0; }

  /* (.table + nth-child to outrank daisyUI's own cell padding) */
  .nv-shell .table.nv-rows-members td:nth-child(n) {
    padding: 0;
    border: 0;
    white-space: normal;
    max-width: none;
    min-width: 0;
  }

  .nv-rows-members td:nth-child(1) { grid-area: who; }
  .nv-rows-members td:nth-child(1) .avatar > div { width: 2.75rem; }

  /* the stamp card progress line, full width on its own row */
  .nv-rows-members td:nth-child(2) { grid-area: card; }
  .nv-rows-members td:nth-child(2) > div { gap: .625rem; }
  .nv-rows-members td:nth-child(2) > div > div:first-child { width: auto; flex: 1 1 auto; height: .5rem; }

  /* Last visit and the notification bell share one quiet meta line: both sit
     in the "meta" cell, the time packed to the start and the bell to the end.
     The time is held clear of the bell so a long relative date ("۱۱ ماه پیش")
     cannot run underneath it.

     Padding goes in the .nv-shell block below, not here: the `td:nth-child(n)`
     reset above is (0,4,1) and would outrank a bare `td:nth-child(4)`. */
  .nv-rows-members td:nth-child(3) { grid-area: meta; font-size: .8rem; opacity: .7; }
  .nv-rows-members td:nth-child(4) {
    grid-area: meta;
    justify-self: end;
    align-self: center;
  }
  .nv-shell .table.nv-rows-members td:nth-child(3) {
    justify-self: start;
    padding-inline-end: 1.75rem;
    overflow: hidden;
  }
  .nv-shell .table.nv-rows-members td:nth-child(4) { padding-inline-end: .25rem; }

  .nv-rows-members td:nth-child(5) { grid-area: act; }
  .nv-rows-members td:nth-child(5) > div { justify-content: flex-end; gap: .5rem; }

  /* Thumb-sized, not btn-xs. These two are the only things on the row anyone
     taps, and the stamp button is the one they tap all day. */
  .nv-rows-members td:nth-child(5) .btn {
    height: 2.5rem;
    min-height: 2.5rem;
    font-size: .875rem;
    padding-inline: .9rem;
  }
  .nv-rows-members td:nth-child(5) .nv-stamp { padding-inline: 1.25rem; }

  /* The skeleton rows use the same grid; keep them from looking like data. */
  .nv-rows-members .skeleton { height: .75rem; }
  .nv-rows-members td:nth-child(1) .skeleton.w-10 { width: 2.75rem; height: 2.75rem; }
}

/* ── Club header: the member-code lookup ───────────────────────────────── */
/* A `join` of [code input][camera][stamp] with a fixed w-56 input. On a
   360px screen the three together are wider than the viewport, so the stamp
   button — the point of the control — sat off the edge. */
@media (max-width: 639.98px) {
  .nv-club-lookup { width: 100%; }
  .nv-club-lookup > .input { width: auto; flex: 1 1 auto; min-width: 0; }
  .nv-club-lookup > .btn { flex: 0 0 auto; }
}

/* ── Icon-only buttons on phones ───────────────────────────────────────── */
/* Where a button's text is a convenience rather than the only clue (its icon
   says the same thing), the label steps aside on a narrow screen so the
   tap target can stay full size. */
@media (max-width: 479.98px) {
  .nv-btn-label { display: none; }
}
