/* ═══════════════════════════════════════════════════════════════════════════
   APP-WIDE RESPONSIVE LAYER
   ───────────────────────────────────────────────────────────────────────────
   Loaded on every page, after Tailwind. Everything here is a *guard*: it stops
   a class of layout break from happening anywhere in the app, so a page added
   later inherits the fix instead of re-introducing the bug.

   The trigger was Windows display scaling. A 1920×1080 monitor at 200% reports
   a CSS viewport of 960px — below Tailwind's `lg` (1024px) — so the whole app
   silently dropped into its phone layout on a desktop. The shell now switches
   on a dedicated `app` breakpoint (900px, defined in the Tailwind config in
   _Layout) instead of `lg`, and the rules below keep the 900–1024px band that
   this opens up from overflowing.
   ═══════════════════════════════════════════════════════════════════════════ */


/* ── 1. Nothing may push the document sideways ────────────────────────────
   A single overflowing element used to give the whole page a horizontal
   scrollbar, which on a phone reads as "the site is broken" — you scroll down
   and the content drifts sideways. Genuinely wide content (data tables, chart
   strips) scrolls inside its OWN box via the overflow-x-auto wrappers instead.

   Declared on `body` ONLY, never on `html`. With `html` left at `overflow:
   visible` the body's value propagates to the viewport and the body does not
   itself become a scroll container — which is what keeps `position: sticky`
   working (modal headers, and any sticky table head added later). Setting it
   on both elements is the well-known way to silently kill sticky. */
body {
    max-width: 100%;
    overflow-x: hidden;
    /* A long unbroken token — a CNIC, a ticket number, a pasted URL, an email
       address — used to force its container wider than the screen. `break-word`
       only breaks a word that cannot fit on a line of its own, so ordinary
       prose still wraps at spaces exactly as before. */
    overflow-wrap: break-word;
}


/* ── 1b. The signed-in shell's height on a phone ──────────────────────────
   The shell is `h-screen overflow-hidden` with <main> as the only scroller.
   100vh on a mobile browser is measured with the address bar HIDDEN, so while
   the bar is visible the shell is ~60–110px taller than the screen — and since
   nothing scrolls the window, that overflow is unreachable: the footer and the
   last rows of every list were permanently behind the browser chrome.

   100dvh tracks the visible viewport as the bar shows and hides. Guarded by
   @supports so any browser without it keeps the current h-screen behaviour
   rather than losing its height entirely.

   `div.app-shell` rather than `.app-shell`: Tailwind's own `.h-screen` has
   equal specificity, and the Play CDN injects its stylesheet at runtime, so
   which of the two wins would otherwise depend on injection order. The element
   qualifier settles it without reaching for !important. */
@supports (height: 100dvh) {
    div.app-shell {
        height: 100dvh;
    }
}


/* ── 2. Replaced elements never widen their column ───────────────────────── */
img,
svg,
video,
canvas,
iframe,
embed,
object {
    max-width: 100%;
}

/* Chart.js sizes its canvas from the parent's width; without this the canvas
   keeps its last (wider) size when the viewport shrinks and drags the card
   out with it. */
canvas {
    max-width: 100% !important;
}


/* ── 3. Flex/grid children must be allowed to shrink ──────────────────────
   `min-width: auto` is the default on a flex and grid item, and it means
   "never get narrower than my content". That is what makes one long word in a
   card push the entire row past the viewport, and it is why `truncate` on a
   nested element so often does nothing.

   Scoped to .page-content so no third-party widget (Choices.js, Toastify,
   Quill, Chart.js) is touched. Images and icons are excluded: they have no
   text to wrap, so allowing them to shrink would just distort them — anything
   that must hold its size already carries flex-shrink-0.

   `:not([class*="min-w-"])` is the important one. This selector is two classes
   deep, so it outranks Tailwind's single-class `.min-w-[150px]` / `.sm:min-w-
   [190px]` utilities — without the exclusion it silently zeroed EVERY declared
   minimum in the app. The filter bar on /Admin/Complaints was the visible
   casualty: its five dropdowns collapsed to 37px arrow-only slivers at 960px,
   and no overflow check catches that because nothing overflows — the controls
   are simply unusable. An element carrying min-w-* has already stated its
   minimum on purpose; this rule is only here to undo the *implicit*
   min-width:auto default. */
.page-content .flex > *:not(img):not(svg):not(canvas):not([class*="min-w-"]),
.page-content .grid > *:not(img):not(svg):not(canvas):not([class*="min-w-"]) {
    min-width: 0;
}


/* ── 4. Urdu (Nastaliq) strings ───────────────────────────────────────────
   This used to say `overflow-wrap: anywhere`, and that was a bad call. In a
   narrow box `anywhere` does not merely wrap Urdu — it shreds it, one glyph
   per line, because Nastaliq has no break opportunities to prefer. On a phone
   the wizard's "شکایت جمع کروائیں" came out as a vertical column of single
   characters inside the Submit button. Unreadable, and worse than the overflow
   it was added to prevent.

   Urdu now inherits the `break-word` on <body>: it breaks a run only when that
   run cannot fit on a line of its own, which is the honest last resort. The
   real cure for a squeezed Urdu label is to give it room — see the wizard's
   nav buttons, which stack instead of sharing one line on a phone.

   Deliberately NOT touching line-height: many bilingual labels pair font-urdu
   with an explicit `leading-tight`, and overriding that app-wide would
   re-space screens that are already correct. */
.font-urdu {
    /* Belt-and-braces against a stray `anywhere` from a page-level style. */
    overflow-wrap: break-word;
    word-break: normal;
}

/* Bilingual labels that share a line with English inside a control — buttons,
   chips, badges. Their Urdu half must never be the thing that wraps: there is
   no width at which "وا / پس" reads better than moving the whole label down. */
button .font-urdu,
a.btn .font-urdu,
.badge .font-urdu {
    white-space: nowrap;
}

/* An Urdu span sharing a line of running text with English becomes its own inline
   BOX rather than a run of inline text.

   Why: Nastaliq's line box is ~30px where the Latin one beside it is ~18px. As
   plain inline text the Urdu's lines interleave with the English's, and when the
   Urdu wraps, its second line is laid out over the English line above it — a real
   38×8px collision on the login header at 768px, and the same on the complainant
   dashboard's welcome line. As an inline-block it either fits on the line or moves
   to the next one whole, and the line box grows to contain it.

   Wrapped in :where() so it has ZERO specificity: every Tailwind display utility
   already on these spans (`block sm:inline`, `hidden`, `inline-flex`) still wins,
   and so does `body.hide-urdu .font-urdu { display: none !important }`. Scoped to
   span because block-level Urdu (<p class="font-urdu">) must stay block. */
:where(span.font-urdu) {
    display: inline-block;
}


/* ── 5. Horizontally scrollable data ──────────────────────────────────────
   The app-wide pattern for a table too wide for the screen: the wrapper
   scrolls, the page does not. These rules make sure the wrapper cannot be
   stretched by the very table it is supposed to be containing — without
   `min-width: 0` a scroll wrapper that is a flex/grid item still sizes itself
   to its content and hands the overflow straight back to the page. */
.table-scroll,
.page-content .overflow-x-auto {
    max-width: 100%;
    min-width: 0;
    -webkit-overflow-scrolling: touch;
}

/* A table squeezed below this stops being a table — columns collapse into
   vertical stacks of one or two characters each. Below the threshold the
   wrapper scrolls instead. Opt out with `data-no-minwidth` on the table. */
.table-scroll > table:not([data-no-minwidth]),
.page-content .overflow-x-auto > table:not([data-no-minwidth]) {
    min-width: 640px;
}

/* Monospace table cells hold IDENTIFIERS — ticket numbers, CNICs, IP addresses,
   file sizes. They are single tokens: breaking one across lines makes it
   unreadable and impossible to match by eye against a reference. On the RP's
   complaint list the ticket column came out 76px wide and "CMS-2026-00047"
   rendered as "CMS-" / "2026-" / "00047" on three lines.

   The cell now refuses to wrap, so the COLUMN claims the width it needs; the
   table is already inside an overflow-x-auto wrapper, so the row scrolls
   sideways instead — which is the behaviour a data grid is supposed to have.
   Only td/th, so a monospace <pre> of JSON in an expanded audit row still
   wraps as before. */
.page-content td.font-mono,
.page-content th.font-mono {
    white-space: nowrap;
}


/* ── 6. Modals and dialogs ────────────────────────────────────────────────
   Every dialog in this app is one panel inside a `fixed inset-0 … flex`
   backdrop. The backdrop's own p-4 already caps the panel's WIDTH; what was
   uncapped was its HEIGHT, so a long form (Add User, Add Complainant) ran off
   the bottom of a phone or a laptop in landscape and its Save button could not
   be reached at all. Capping the panel and letting it scroll internally fixes
   every one of them at once.

   Only max-height and overflow are set — deliberately NOT max-width. Each
   panel picks its own `max-w-md` / `max-w-2xl`, and a width declaration here
   would silently flatten all of them to the same size.

   The splash screen is excluded via an ATTRIBUTE selector, not `:not(#id)`:
   an id inside :not() contributes id-level specificity (1,0,0), which would
   have let this rule outrank every Tailwind utility on the panel. */
div.fixed.inset-0.flex:not([id="pageLoader"]) > div {
    max-height: calc(100dvh - 2rem);
    overflow-y: auto;
}

@supports not (height: 100dvh) {
    div.fixed.inset-0.flex:not([id="pageLoader"]) > div {
        max-height: calc(100vh - 2rem);
    }
}


/* ── 7. Form controls ─────────────────────────────────────────────────────
   iOS Safari zooms the whole page in when a focused input's font is under
   16px, and never zooms back out — after which every subsequent screen looks
   broken. Phones only, so the denser desktop form styling is untouched. */
@media (max-width: 767.98px) {

    /* !important is doing real work here: every field in this app carries
       Tailwind's `text-sm`, a class selector, which outranks a plain element
       rule. Without it the declaration is written and never applied, and iOS
       goes on zooming. Confined to phones and to font-size alone. */
    input:not([type="checkbox"]):not([type="radio"]),
    select,
    textarea {
        font-size: 16px !important;
    }

    /* A thumb-sized tap target on the controls a complainant actually uses. */
    input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
    select,
    textarea {
        min-height: 44px;
    }
}

/* A number input's spinner steals ~20px of an already narrow field. */
input[type="number"] {
    -moz-appearance: textfield;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* Choices.js renders its own box outside Tailwind's reach. */
.choices,
.choices__inner {
    max-width: 100%;
}
.choices__list--dropdown .choices__item {
    white-space: normal;
}


/* ── 8. Very small phones (≤360px) ────────────────────────────────────────
   The 320–360px class is still in real use in Pakistan. Trimming horizontal
   padding buys back 16px of content width, which is the difference between a
   two-column stat row fitting and collapsing to one column of half-empty
   cards. Only padding is touched — no font, colour or spacing rhythm. */
@media (max-width: 360px) {

    .page-content .px-6 { padding-left: 1rem; padding-right: 1rem; }
    .page-content .px-5 { padding-left: 0.875rem; padding-right: 0.875rem; }
    .page-content .p-6  { padding: 1rem; }
    .page-content .p-5  { padding: 0.875rem; }
}


/* ── 9. Print ─────────────────────────────────────────────────────────────
   Staff print complaint records off these screens. Without this the sidebar,
   the top bar and the notification panel print on page 1 and the record the
   user actually wanted starts on page 2. */
@media print {
    #sidebar,
    #sidebarOverlay,
    header,
    footer,
    #notifPanel,
    #notifOverlay,
    #pageLoader,
    #navLoader,
    .no-print {
        display: none !important;
    }

    main {
        overflow: visible !important;
        height: auto !important;
    }

    .page-content {
        max-width: none !important;
    }
}
