/*
  TRAP-53 (P1) — responsive layer for the shared foundation layouts (EntityListLayout/
  EntityDetailLayout/EntityFormLayout) and building blocks (FilterBar/PageHero/Breadcrumbs) plus
  MudBlazor dialogs. These ship no CSS isolation and no responsive handling of their own
  (Starruk.Platform.Blazor.Components v1.4.6 / .DesignSystem v1.4.4 source read directly — see
  acceptance/TRAP-49/runbook.md Voortgang) — per CLAUDE.md "Foundation via NuGet, niet forken"
  this file is the local override seam instead of a foundation fork. Selectors target the
  foundation's s-* class contract plus MudBlazor's own utility/table/dialog classes (verified
  against the installed MudBlazor.min.css 9.4.0), never foundation-internal implementation detail
  that isn't part of either contract.
*/

@media (max-width: 599.98px) {
    /* EntityListLayout's toolbar row (SearchContent/FilterBar + spacer + PrimaryAction) is a
       <MudStack Row AlignItems="Center" Spacing="3" Class="mt-4 mb-4">. MudStack has no CSS-class
       rule of its own (MudBlazor.min.css defines zero mud-stack* selectors) — Row/AlignItems
       render as the utility classes below, and MudBlazor ships those utilities !important
       (.flex-row{flex-direction:row!important}, .align-center{align-items:center!important}),
       so beating them here also needs !important; this selector's 5-class specificity still wins
       over their single-class rules. AC-1: search + primary action each get their own full-width
       row instead of being squeezed onto one line. */
    .d-flex.flex-row.align-center.gap-3.mt-4.mb-4 {
        flex-direction: column !important;
        align-items: stretch !important;
    }

    /* FilterBar's search field (.s-filter-grow) already wraps onto its own line once the bar runs
       out of room (.s-filter-bar has flex-wrap:wrap), but AC-1 wants it deterministically
       full-width rather than merely "happens to be alone on a line". */
    .s-filter-bar .s-filter-grow {
        flex-basis: 100%;
        min-width: 0;
    }

    /* EntityDetailLayout's title+actions header (<div class="d-flex align-center
       justify-space-between">) and the same hand-authored toolbar idiom on a few detail pages
       (e.g. CustomerDetail's Properties-tab search+add row) — allow wrapping instead of
       overflowing. AC-2 allows "gestapeld OF overflow-menu"; buttons keep their natural width
       and simply wrap onto their own line rather than being forced full-width. */
    .d-flex.align-center.justify-space-between {
        flex-wrap: wrap;
        row-gap: 8px;
    }

    .d-flex.align-center.justify-space-between > h1 {
        min-width: 0;
        overflow-wrap: break-word;
    }

    /* The Actions slot renders as a nested <MudStack Row Spacing="2"> (d-flex flex-row gap-2, no
       Class param) — scope to a direct child of the header row above so this doesn't reach into
       unrelated stacks elsewhere on the page. */
    .d-flex.align-center.justify-space-between > .d-flex.flex-row.gap-2 {
        flex-wrap: wrap;
    }

    /* PageHero: starruk-components.css already ships a compact-padding modifier (.s-page-hero-sm)
       that PageHero.razor never applies (no Class passthrough on that component) — reproduce its
       effect via a plain media query instead of forking the component to add the class. */
    .s-page-hero {
        padding: 18px 20px 20px;
    }

    .s-page-hero-title {
        font-size: 1.3rem;
    }

    /* Breadcrumbs: never hide an ancestor level (everything must stay navigable per the shared
       breadcrumb convention) — scroll the crumb row itself instead of letting a long trail push
       the whole page into horizontal overflow. */
    .mud-breadcrumbs {
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    .mud-breadcrumbs li {
        white-space: nowrap;
    }

    /* MudDialogProvider carries no global DialogOptions and ConfirmDialog sets none of its own
       (ShowAsync callers don't pass a per-instance FullScreen either) — full-screen-below-sm has
       to be a CSS-only override. .mud-dialog is already display:flex;flex-direction:column with
       .mud-dialog-title/-content/-actions at flex 0-0-auto / 1-1-auto / 0-0-auto (a pinned-header,
       scrolling-body, pinned-footer layout already built into MudBlazor's base CSS) — only the
       outer box needs to fill the viewport for AC-3's "acties bereikbaar" to fall out for free. */
    .mud-dialog-container .mud-dialog {
        position: fixed !important;
        inset: 0 !important;
        width: 100% !important;
        height: 100% !important;
        max-width: 100% !important;
        max-height: none !important;
        margin: 0 !important;
        border-radius: 0 !important;
        /* TRAP-52: full-screen now means genuinely edge-to-edge, so the notch/home-indicator
           safe area has to be paid for explicitly instead of falling out of normal box flow. */
        padding-top: env(safe-area-inset-top);
        padding-bottom: env(safe-area-inset-bottom);
    }

    /* Breadcrumbs (continued): truncate an individual long crumb label with an ellipsis rather
       than letting it dominate the scrollable row — AC-2's "ellipsis" clause. Every crumb stays
       present and tappable (nothing is removed from the DOM), satisfying "blijven navigeerbaar
       naar elk voorouder-niveau" — this narrows a label's display width, it doesn't hide a level. */
    .mud-breadcrumbs li a,
    .mud-breadcrumbs li p {
        display: inline-block;
        max-width: 120px;
        overflow: hidden;
        text-overflow: ellipsis;
        vertical-align: bottom;
    }

    /* The fixed 120px also truncated crumbs on /orders/new where there was room to spare: the
       trail read as "Mijn bestellin… › Bestelling pl…", which left the way back unrecognisable.
       The row scrolls anyway (rule above), so let a crumb use half the screen width first and only
       truncate beyond that; the ellipsis safety net from AC-2 stays in place for labels that are
       still too long even then. */
    .mud-breadcrumbs li a,
    .mud-breadcrumbs li p {
        max-width: 50vw;
    }
}

/*
  TRAP-52 (P2) — app-shell mobile-first: drawer default-state + auto-close (MainLayout.razor
  companion, see drawer-prepaint.js + the _isMobileViewport wiring), touch targets, safe-area-
  insets. Unlike the P1 block above, most of this is NOT media-queried — see each rule for why.
*/

/* AC-1: suppresses the flash of the server-rendered default-open drawer on a fresh mobile visit,
   until MainLayout's own OnAfterRenderAsync (drawer-prepaint.js's isMobile() check) sets
   _drawerOpen=false and re-renders with the real .mud-drawer--closed class. Only ever engages when
   drawer-prepaint.js already determined innerWidth < 960 — no separate breakpoint needed here.
   Same off-screen mechanism MudBlazor's own .mud-drawer--closed rule uses (verified in the
   compiled CSS), not a guess. */
html[data-drawer-hint="closed"] .mud-drawer--open {
    left: calc(-1 * var(--mud-drawer-width, var(--mud-drawer-width-left))) !important;
}

html[data-drawer-hint="closed"] .mud-drawer-overlay {
    display: none !important;
}

/* AC-2: appbar icon buttons (nav-toggle, alerts bell) and drawer nav links default to ~36px in
   MudBlazor (padding 4-6px + a 24px icon) — under the 44px touch-target floor. Scoped to the
   appbar/drawer rather than every .mud-icon-button app-wide (e.g. dense table row actions
   elsewhere are deliberately compact). Applies at every width — a taller nav link or a slightly
   more generous appbar button isn't a desktop regression. */
.mud-appbar .mud-icon-button {
    min-width: 44px;
    min-height: 44px;
}

.mud-drawer .mud-nav-link {
    min-height: 44px;
}

/* AC-2 (continued): the user-menu trigger (MudAvatar, Size.Small = 32px) isn't a .mud-icon-button
   so the rule above misses it; its display name is d-none below sm, leaving just the 32px avatar
   as the visible tap target. Grow the clickable wrapper to the 44px floor via padding rather than
   resizing the avatar itself (keeps the visual mark unchanged, per WCAG's "target can be smaller
   than the touch area" allowance) — safe unconditionally, same reasoning as the icon-button rule. */
.mud-appbar .mud-menu .d-flex.flex-row.align-center.gap-0 {
    min-width: 44px;
    min-height: 44px;
    justify-content: center;
}

/* AC-2 (continued): found via staging Playwright verification (390x844) — Dashboard specifically
   (not other pages) showed 19px of real horizontal scroll on the shell. Root cause, isolated by
   bisecting the page section by section: the 5-card KPI MudGrid (Spacing=6, default) carries
   MudBlazor's standard gutter-compensation `margin-left:-24px` on the grid with matching
   `padding-left:24px` on each item — normally an invisible wash, but on this viewport it's enough
   to trip Chromium's mobile-layout-viewport-expansion heuristic (content that would otherwise be
   clipped left-of-origin widens the whole layout viewport instead), which is why the *appbar*
   (window.innerWidth-driven, `position:fixed`) measured wide even though the grid's own box never
   crosses the right edge (confirmed: hiding just the KPI grid restored scrollWidth to 390, nothing
   else on the page mattered). Any current or future MudGrid elsewhere in the app can trigger the
   same mechanism, so the fix belongs on the document's scroll root, not the one grid instance —
   MudBlazor's negative-margin gutter technique is only ever safe when a `overflow-x:hidden`
   ancestor backstops it, which nothing in the foundation or this app provided before now. */
html, body {
    overflow-x: hidden;
}

/* Safe-area-insets (notch / home-indicator): env() resolves to 0 on non-notched devices/desktop
   browsers, so these are harmless no-ops there — no media query needed. Requires
   viewport-fit=cover on the <meta name="viewport"> tag (App.razor) or env() always returns 0. */
.mud-appbar {
    padding-top: env(safe-area-inset-top);
}

.mud-drawer-header {
    padding-top: calc(12px + env(safe-area-inset-top));
}

/*
  TRAP-54 (P4) — full mobile sweep (390px + 320px) across every routed page. Most of the app was
  already clean (P1's shared-layout fixes + P2's shell fixes cover the vast majority of the ~35
  entity pages by construction); this block is the small residue of page-specific touch-target
  gaps the sweep actually found. Dense per-row icon-buttons in data-dense tables/streams (e.g.
  /ttn-events, list-page row actions) are a deliberate, pre-existing exception (documented on the
  AC-2 .mud-appbar .mud-icon-button rule above) and are intentionally NOT touched here — this
  block only covers prominent, non-dense interactive elements.
*/

/* Found via the route sweep: the trial/blocked-tenant banner (MainLayout.razor, MudAlert) renders
   its billing CTA at Size.Small (31px) — appears on every authenticated page while a tenant is on
   trial or blocked, so unlike a table row action this is a persistent, prominent, business-critical
   CTA (the upgrade-to-paid path) a thumb needs to land on reliably. Scoped via a dedicated class on
   the two MainLayout buttons rather than every Size.Small button app-wide, which would reach into
   the already-decided dense-action exception above. */
.tw-banner-cta {
    min-height: 44px;
}

/* TRAP-56/AC-5: suspicious-connection banner (MainLayout.razor) — gold-accent (huisstijl waarschuwing)
   instead of MudBlazor's stock warning amber, reusing the foundation's own tint/border recipe
   (Starruk.Platform.Blazor.DesignSystem's .s-filter-notice) rather than inventing new hex values. */
.tw-risk-banner {
    background: var(--s-tint-warning) !important;
    border: 1px solid color-mix(in srgb, var(--s-tertiary) 30%, transparent);
}

/* Dismiss control is icon-only/square, so — unlike .tw-banner-cta above, which only needed
   min-height for its existing text buttons — both dimensions need the 44px touch-target floor. */
.tw-risk-banner-dismiss {
    min-width: 44px;
    min-height: 44px;
}

/* Found via the route sweep: MudButton's Size="Size.Large" (MudBlazor's own convention for a
   page's primary/hero action, e.g. Dashboard's "Install trap" CTA) renders at 42px — 2px under the
   floor in this MudBlazor version's default theme, regardless of variant (filled/outlined/text all
   compose the size suffix the same way, verified against the installed MudBlazor.min.css 9.4.0:
   .mud-button-{variant}-size-large). A Large button is never used for a dense/compact context by
   MudBlazor's own convention, so this is safe to apply unconditionally app-wide. */
[class*="-size-large"] {
    min-height: 44px;
}

/* Found via the same sweep, across all three onboarding-family wizards (/devices/onboard,
   /devices/onboard/bulk, /devices/commissioning): every step's Back/Cancel + Next/Save action
   pair renders as a plain flex row (a raw `d-flex gap-2` div, or a `MudStack Row Spacing`) with no
   `.flex-row` class, so it falls outside the P1 wrap rule above (written for EntityDetailLayout's
   `d-flex flex-row gap-2` action rows) — the two buttons stay side by side at roughly half width
   each instead of the full-width, thumb-reachable steps AC-2 asks for. Marker classes rather than
   a broader utility-class match, same reasoning as .tw-banner-cta above: a bare `d-flex gap-2` or
   `MudStack Row` is used all over the app for contexts that should stay compact. */
/* Found via staging re-verification of the fix above: these Back/Next buttons carry no explicit
   Size, so they render at MudBlazor's default-size height (37px) — under the floor regardless of
   the width fix. Wizard step-navigation is never a dense/compact context at any viewport (it's the
   primary way through a multi-step form), so — like the Size.Large rule above — this applies
   unconditionally rather than mobile-only; unlike the width stacking below, stretching button
   *height* on desktop isn't a visual regression. */
.tw-wizard-actions-group .mud-button-root {
    min-height: 44px;
}

/* The primary button of those same action bars (the next-step action on /devices/onboard, the
   upload-and-register action on /devices/onboard/bulk) starts out disabled and then got MudBlazor's
   grey text on barely lighter grey — around 2.5:1, and from any distance no longer recognisable as
   a button. The outlined button beside it (cancel, single trap) kept full emphasis, so the heaviest
   visual emphasis sat on walking away rather than on carrying on; in sunlight, on a field
   technician's phone, the next step vanished altogether. Disabled, the button now keeps the brand
   colour at a lower opacity: white letters reach ~3:1 there, enough to read as a button, while the
   difference from the enabled button stays visible. MudBlazor puts the `disabled` attribute on the
   <button> itself, so :disabled is the only hook needed and the rule touches nothing outside the
   wizards. */
.tw-wizard-actions-group .mud-button-filled-primary:disabled {
    background-color: color-mix(in srgb, var(--mud-palette-primary) 55%, var(--mud-palette-surface)) !important;
    color: var(--mud-palette-primary-text) !important;
}

/* Exactly the same case on the form bars (.tw-form-actions, among them the submit-request action
   on /orders/new): that button is disabled on arrival — no quantity has been entered yet — and is
   grey on grey at that point, even though it is the screen's main action. Same treatment as the
   wizard button above, so both bars look the same while disabled. */
.tw-form-actions .mud-button-filled-primary:disabled {
    background-color: color-mix(in srgb, var(--mud-palette-primary) 55%, var(--mud-palette-surface)) !important;
    color: var(--mud-palette-primary-text) !important;
}

@media (max-width: 640px) {
    .tw-wizard-actions {
        flex-wrap: wrap;
        row-gap: 8px;
    }

    .tw-wizard-actions-group {
        flex-wrap: wrap;
        width: 100%;
    }

    .tw-wizard-actions-group .mud-button-root {
        flex: 1 1 auto;
    }

    /* AC-5.2: the AI FAB (Starruk.Modules.Assistant.Blazor's .s-assist-fab, TRAP-74) is
       position:fixed, 56x56px with a 24px right/bottom offset, on every authenticated page —
       verified against the installed package's own scoped CSS. FieldInstall/DeviceCommissioning
       opt into this marker on their outer MudContainer since their bottom-most content (the
       primary action button, now full-width per the rule above) would otherwise scroll to
       directly under it. pa-2/pa-sm-4 on the same element ships padding !important in
       MudBlazor.min.css, hence !important here too. */
    .tw-wizard-page {
        padding-bottom: 88px !important;
    }
}

/* Form action rows (Cancel / secondary / primary) on the EntityFormLayout pages and Signup: side by
   side on desktop, stacked full-width on phones. At 390px a three-button row (cancel /
   save-and-add-another / add-inspection-point) overflowed the card — Cancel pushed off-screen
   to the left, labels wrapping over three lines. DOM order Cancel → secondary → primary, so the
   primary lands at the bottom, closest to the thumb (mobile-first convention). MudStack's
   flex-row/justify utilities ship !important, hence !important here. */
@media (max-width: 599.98px) {
    .tw-form-actions {
        flex-direction: column !important;
        align-items: stretch !important;
    }

    .tw-form-actions > .mud-button-root {
        width: 100%;
        min-height: 44px;
    }
}

/* Quantity stepper on /orders/new (.tw-qty-field, the wrapper around the MudNumericField).
   MudBlazor stacks the two chevron buttons inside the field height and sets them to `padding: 2px 0`
   with min-width/min-height at `unset`: each touch target thereby ends up roughly 24x18px — in a
   warehouse, wearing gloves, that means ordering the wrong quantity or hitting nothing at all. The
   buttons now split the full field height between them and get a real width. The specificity sits
   deliberately one class above MudBlazor's own
   `.mud-input-control.mud-input-number-control .mud-input-numeric-spin button`, otherwise its
   `padding`/`min-width: unset` would still win. */
.tw-qty-field .mud-input-control.mud-input-number-control .mud-input-numeric-spin button {
    flex: 1 1 50%;
    min-width: 36px;
    padding: 0;
}

/* On a phone the field grows along with it, so each half meets the 44px touch-target norm. */
@media (max-width: 599.98px) {
    .tw-qty-field .mud-input {
        min-height: 88px;
    }

    .tw-qty-field .mud-input-control.mud-input-number-control .mud-input-numeric-spin button {
        min-width: 44px;
    }
}

/* The assistant FAB (position:fixed, bottom-right, 56px + 24px offset) sits over whatever ends up
   at the bottom of a page — on phones typically the primary form button. Give every authenticated
   page (MainLayout's content container) room to scroll its last row clear of it; wizard pages
   already do the same via .tw-wizard-page. */
@media (max-width: 640px) {
    .mud-main-content > .mud-container {
        padding-bottom: 96px !important;
    }
}

/* On wide screens the FAB covers content just as readily: a page that runs all the way down (e.g.
   /device-health, where the trap table card reaches the bottom edge) gets the button over the
   card's lower right corner. Hence the same margin above 640px too - the button sits 24px from the
   edge there, so 96px leaves the last row comfortably clear. */
@media (min-width: 640.02px) {
    .mud-main-content > .mud-container {
        padding-bottom: 96px !important;
    }
}

#components-reconnect-modal {
    padding-top: calc(1rem + env(safe-area-inset-top));
    padding-bottom: calc(1rem + env(safe-area-inset-bottom));
}

/* TWP-7/AC-6.1: WhatsAppGuidancePanel — <600px shows one compact line + button (no QR, unusable
   on the same device); >=600px keeps the existing title/subtitle/QR view. Both variants render
   in the DOM unconditionally (WhatsAppGuidancePanel.razor) — CSS-only toggle, same 600px
   threshold MudBlazor's own .d-sm-* utilities use, so no extra Blazor state or JS is needed and
   the component's poll-guard (AC-6.2) is unaffected by which one is visible. */
.tw-wa-compact {
    display: flex;
}

.tw-wa-full {
    display: none;
}

@media (min-width: 600px) {
    .tw-wa-compact {
        display: none;
    }

    .tw-wa-full {
        display: block;
    }
}

/*
  KPI cards (Dashboard/DeviceHealth) — the foundation's KpiCard renders
  <MudCard Class="s-kpi-card"><MudCardContent Class="pa-4"><MudStack Row Spacing=3>
     <MudIcon Size=Large/> <MudStack Spacing=0>value+label</MudStack> <MudSpacer/> <MudIcon Size=Small/>
  (Starruk.Platform.Blazor.Components/KpiCard.razor, source read directly). Per CLAUDE.md
  "Foundation via NuGet, niet forken" this file is the override seam, same as the TRAP-53 block
  at the top.

  On a 390px viewport an xs=6 card leaves ~99px of row width after MudContainer padding, the
  MudGrid gutter, .s-kpi-card's own 16/18px padding AND MudCardContent's pa-4 on top of it — while
  the row's fixed furniture (36px icon + 3 x 12px gap + 20px chevron) alone needs ~92px. The label
  column is a flex item at the default min-width:auto, so it cannot shrink below its longest word
  and pushes itself and the chevron straight through the card's right border instead of wrapping.
*/

/* Equal-height cards within a MudGrid row: .mud-grid-item already stretches to the tallest card in
   its flex line; the card itself didn't follow, so a two-line label left its neighbours short.
   Not media-queried — a wrapped label produces the same ragged row on desktop. */
.s-kpi-card {
    height: 100%;
    box-sizing: border-box;
}

/* The row and the value/label column must be allowed to shrink past min-content so a long label
   wraps inside the card instead of overflowing it (root cause above). */
.s-kpi-card > .mud-card-content > .d-flex,
.s-kpi-card > .mud-card-content > .d-flex > .d-flex {
    min-width: 0;
}

.s-kpi-card .mud-typography {
    overflow-wrap: break-word;
}

/* ...while the icon and chevron keep their intrinsic size rather than being squashed by that
   same shrink. */
.s-kpi-card .mud-icon-root {
    flex-shrink: 0;
}

/* Double padding: .s-kpi-card ships 16px 18px in starruk-components.css and KpiCard puts pa-4
   (16px !important) on the MudCardContent inside it — ~34px per side eaten twice over. Keep the
   card's own padding as the single source and drop the inner one (matching !important needed to
   beat the pa-4 utility). Not media-queried: on a 1366px desktop with four cards per row the label
   column was down to ~90px, which made overflow-wrap split words ("Geactiveerd / e vallen"). */
.s-kpi-card > .mud-card-content {
    padding: 0 !important;
}

@media (max-width: 599.98px) {
    .s-kpi-card {
        padding: 12px 14px;
    }

    /* MudStack's gap-3 utility is !important in MudBlazor.min.css, hence !important here. */
    .s-kpi-card > .mud-card-content > .d-flex {
        gap: 10px !important;
    }

    .s-kpi-card .mud-icon-size-large {
        font-size: 1.75rem;
    }

    .s-kpi-card .mud-typography-h4 {
        font-size: 1.5rem;
        line-height: 1.15;
    }

    .s-kpi-card .mud-typography-body2 {
        font-size: 0.78rem;
        line-height: 1.25;
    }

    /* The chevron is the only Size.Small icon KpiCard renders. Together with the spacer it costs
       ~32px of a ~100px row — and it carries no information the card doesn't: the whole card is the
       tap target (KpiCard sets cursor:pointer + @onclick on the MudCard when Href is set), so the
       affordance survives its removal on the one viewport where the width actually matters. */
    .s-kpi-card .mud-spacer,
    .s-kpi-card .mud-icon-size-small {
        display: none;
    }
}

/* overflow-x:hidden above makes the document a scroll container, which kills position:sticky for
   everything inside it (the public top bar stopped sticking). overflow-x:clip does the same
   clipping without creating that container; the hidden rule stays as the fallback. */
@supports (overflow-x: clip) {
    html, body {
        overflow-x: clip;
    }
}

/* The appbar grows by the notch inset (rule above), so the content below it has to move down by
   the same amount — otherwise the first row of every page sits under the bar on an iPhone in
   standalone/PWA mode. A margin, not a padding: MudBlazor's own padding-top differs per breakpoint
   (7/8, 3/4 or the full appbar height) and overriding it would break those. env() is 0 elsewhere,
   so this is a no-op on desktop. */
.mud-main-content {
    margin-top: env(safe-area-inset-top);
}

/* MudBlazor gives every tab a 160px minimum, so Settings showed 2 of its 4 tabs on a phone with
   the rest behind scroll arrows. The labels are short; let them size themselves. */
@media (max-width: 599.98px) {
    .mud-tabs .mud-tab {
        min-width: 0;
        padding-left: 12px;
        padding-right: 12px;
    }
}
