/* Strong8K admin — responsive & cross-browser enhancement layer.
   Loads AFTER admin-theme.css. Pure CSS; remove the <link> to revert.
   Goal: one dashboard that works on phones, tablets, desktops and TVs,
   across Chrome/Edge/Firefox/Safari/Samsung, incl. touch + notched screens. */

/* ---- Foundation / cross-browser normalisation ---------------------------- */
html {
  -webkit-text-size-adjust: 100%;   /* iOS: stop rotate-to-landscape text inflation */
  text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
  scrollbar-width: thin;                                   /* Firefox */
  scrollbar-color: rgba(167,139,250,.4) rgba(255,255,255,.04);
}
body {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;                 /* never allow a sideways body scroll */
  overscroll-behavior-y: contain;     /* kill pull-to-refresh bounce inside the app */
  min-height: 100vh;
  min-height: 100dvh;                 /* dynamic viewport: correct with mobile URL bars */
}
*, *::before, *::after { box-sizing: border-box; }

/* Respect notches / rounded corners on phones & foldables. */
.RaLayout-appFrame, .MuiAppBar-root > .MuiToolbar-root {
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}

/* Fluid base type so everything scales from small phones to 4K TVs. */
:root { font-size: clamp(15px, 0.9rem + 0.15vw, 17px); }

/* Comfortable touch targets on touch/coarse pointers only (keeps desktop tight). */
@media (pointer: coarse) {
  .MuiButtonBase-root, .MuiIconButton-root, .RaMenuItemLink-root, .MuiMenuItem-root,
  .MuiTab-root, .MuiChip-root, .MuiCheckbox-root, .MuiSwitch-root {
    min-height: 44px;
  }
  .MuiIconButton-root { min-width: 44px; }
}

/* ---- Tables: the #1 mobile pain point in react-admin ---------------------
   Let every datagrid scroll horizontally inside its own card instead of
   blowing out the page width. Momentum-scroll on iOS. */
.RaList-content, .RaDatagrid-root, .MuiTableContainer-root,
.RaDatagrid-tableWrapper {
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.RaDatagrid-table, .MuiTable-root { min-width: max-content; }

/* ---- Small phones (<= 600px) --------------------------------------------- */
@media (max-width: 600px) {
  .RaLayout-content, .RaList-main, .RaCreate-main, .RaEdit-main, .RaShow-main {
    padding: 8px !important;
    margin: 0 !important;
  }
  .MuiPaper-root { border-radius: 12px !important; }
  .MuiCard-root, .RaList-content { border-radius: 12px !important; }

  /* Denser tables so more columns fit before scrolling. */
  .MuiTableCell-root { padding: 8px 10px !important; font-size: .92rem !important; }
  .MuiTableCell-head { font-size: .8rem !important; }

  /* Toolbars, filters and pagination wrap instead of overflowing. */
  .RaTopToolbar-root, .RaFilterForm-root, .MuiTablePagination-toolbar,
  .RaListToolbar-toolbar, .MuiToolbar-root {
    flex-wrap: wrap !important;
    row-gap: 8px;
  }
  .RaFilterForm-root .MuiFormControl-root { width: 100%; }

  /* Forms go single-column and full width. */
  .RaSimpleForm-root .MuiFormControl-root,
  .RaSimpleFormIterator-form .MuiFormControl-root,
  .MuiCardContent-root > .ra-input { width: 100% !important; }
  .MuiFormControl-root.MuiTextField-root { min-width: 0 !important; }

  /* Dialogs use the full screen on phones. */
  .MuiDialog-paper {
    margin: 8px !important;
    width: calc(100% - 16px) !important;
    max-width: calc(100% - 16px) !important;
    max-height: calc(100dvh - 16px) !important;
  }

  /* Page title never pushes the appbar actions off-screen. */
  .MuiAppBar-root .RaAppBar-title {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
}

/* ---- Tablets (601–1024px) ------------------------------------------------- */
@media (min-width: 601px) and (max-width: 1024px) {
  .RaLayout-content { padding: 12px !important; }
}

/* ---- Full-width layout: every section spans 100% of the content area ------
   No centered max-width cap on any breakpoint — the dashboard and all
   react-admin list/edit/create/show sections use the full available width. */
.RaLayout-content,
.RaList-main, .RaEdit-main, .RaCreate-main, .RaShow-main {
  max-width: none !important;
  width: 100% !important;
  margin-inline: 0 !important;
}
.RaList-content, .RaEdit-main > .MuiCard-root, .RaCreate-main > .MuiCard-root,
.RaShow-main > .MuiCard-root, .RaList-main > .MuiCard-root {
  width: 100% !important;
  max-width: none !important;
}
/* Let datagrids use the whole width instead of shrinking to content. */
.RaDatagrid-table, .MuiTable-root { width: 100% !important; }

/* ---- Inputs: kill the iOS "zoom on focus" (needs >=16px font) ------------- */
.MuiInputBase-input, .MuiInputBase-input.MuiOutlinedInput-input,
.MuiInputBase-input.MuiFilledInput-input, select, textarea {
  font-size: max(16px, 1rem);
}

/* ---- Motion & accessibility ---------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}
:focus-visible { outline: 2px solid var(--s8k-gold, #a78bfa); outline-offset: 2px; }

/* ---- Install chip: keep clear of the react-admin FAB/appbar on mobile ---- */
#s8k-install-chip { bottom: max(20px, env(safe-area-inset-bottom)) !important; }

/* ---- Devices list: fit + trim columns on FIRST paint (no old-design flash) --
   These live in the head stylesheet (loaded before the app bundle) and are
   scoped with :has() to the devices datagrid — device_key (MAC) is a
   devices-only column — so they apply the instant the table renders, before the
   overlay JS (devices-list.js) runs. That JS still adds the row Delete button
   and the trial-only column reduction. */
.RaDatagrid-table:has(th.column-device_key) {
  min-width: 0 !important;
  width: 100% !important;
  table-layout: auto;
}
.RaDatagrid-tableWrapper:has(th.column-device_key),
.MuiTableContainer-root:has(th.column-device_key),
.RaList-content:has(th.column-device_key) {
  overflow-x: hidden !important;
}
.RaDatagrid-table:has(th.column-device_key) th,
.RaDatagrid-table:has(th.column-device_key) td {
  padding: 6px 8px !important;
  font-size: 12px !important;
  white-space: normal !important;
  word-break: break-word;
  overflow-wrap: anywhere;
}
.RaDatagrid-table:has(th.column-device_key) .MuiTableCell-head { font-size: 11px !important; }
/* Low-value columns removed from the devices list (data stays in the edit form). */
.RaDatagrid-table:has(th.column-device_key) th.column-name,
.RaDatagrid-table:has(th.column-device_key) td.column-name,
.RaDatagrid-table:has(th.column-device_key) th.column-username,
.RaDatagrid-table:has(th.column-device_key) td.column-username,
.RaDatagrid-table:has(th.column-device_key) th.column-m3u_url,
.RaDatagrid-table:has(th.column-device_key) td.column-m3u_url,
.RaDatagrid-table:has(th.column-device_key) th.column-ip_address,
.RaDatagrid-table:has(th.column-device_key) td.column-ip_address {
  display: none !important;
}

/* ==== s8k-admin-fit ==== */
/* Shell: let the layout shrink to the viewport; existing table scroll boxes then scroll */
#root > .layout { min-width: 0; }
#root .RaLayout-content { min-width: 0; }
#root .RaLayout-contentWithSidebar:has(> [data-s8k-tc], > [data-s8k-pe]) > .RaLayout-content { min-width: auto; } /* hidden App Settings cards */
#root > .layout:has(.RaLayout-contentWithSidebar > :is([data-s8k-tc], [data-s8k-pe])) { min-width: fit-content; } /* hidden App Settings: keep original layout width */
/* Sidebar: stays in view while the page scrolls (Chrome, Safari, Firefox) */
#root .RaSidebar-docked { position: sticky; top: 48px; }
#root .RaSidebar-docked.RaSidebar-appBarCollapsed { top: 0; }
#root .RaSidebar-docked > .RaSidebar-paper { overflow: hidden; }
#root .RaSidebar-docked .RaSidebar-fixed { position: relative; width: fit-content; }
/* Modals: card capped to the viewport (18px margin), header/footer fixed, body scrolls */
body > #s8k-ap-modal { overflow-y: auto; overscroll-behavior: contain; }
body > #s8k-ap-modal > .mcard { display: flex; flex-direction: column; max-height: calc(100vh - 36px); max-height: calc(100dvh - 36px); }
body > #s8k-ap-modal > .mcard > .mhead, body > #s8k-ap-modal > .mcard > .mfoot { flex: none; }
body > #s8k-ap-modal > .mcard > .mbody { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
body > #s8k-ap-modal > .mcard > .mbody > * { flex-shrink: 0; }
body > #s8k-ap-modal > .mcard > .mbody > .err:not(:empty) { position: sticky; bottom: -18px; z-index: 1; margin: 0 -20px -18px; padding: 10px 20px 12px; background: #16131c; border-top: 1px solid rgba(255,255,255,.08); }
/* Tables: keyboard focus never hides under the sticky header; MAC column pinned on MACs/Online/Trial between 1024px and 2199px */
body #s8k-ap .otbody-scroll { scroll-padding-top: 44px; }
@media (min-width: 1024px) and (max-width: 2199px) {
  body #s8k-ap:is([data-view="macs"], [data-view="online"], [data-view="trials"]) table.ot th { z-index: 2; }
  body #s8k-ap:is([data-view="macs"], [data-view="online"], [data-view="trials"]) table.ot th:first-child { left: 0; z-index: 3; background: #141119; }
  body #s8k-ap:is([data-view="macs"], [data-view="online"], [data-view="trials"]) table.ot td:first-child { position: sticky; left: 0; z-index: 1; background: #17141f; }
  body #s8k-ap:is([data-view="macs"], [data-view="online"], [data-view="trials"]) table.ot tbody tr[data-idx]:hover td:first-child { background: #1c1925; }
}
/* ==== /s8k-admin-fit ==== */

/* ==== s8k-settings-layout-fit ==== */
/* App Settings (native react-admin pages) rendered wider than the viewport.
   Cause: admin-panel.js (s8k-theme-v3 / s8k-platform-env) picks its mount point with
     (host || document.querySelector('main') || document.body).appendChild(card)
   and in react-admin <main> IS .RaLayout-contentWithSidebar — the flex row that holds the
   sidebar and the content column. `host` is only found when input[name="color_accent"] is
   already in the DOM, which is false on the #/app_settings LIST page, so opening the list
   first strands the theme card [data-s8k-tc] and the platform-env card [data-s8k-pe] as
   extra flex items beside the page. The mount is decided once per session (tick() returns
   early while the card exists), so it stays stranded through list -> edit.
   The s8k-admin-fit block above then widens #root > .layout to fit-content to keep those
   cards whole, and body{overflow-x:hidden} makes the excess unreachable: measured
   scrollWidth 1595/1754 vs clientWidth 1366, with the THEME and LOGIN & BEHAVIOUR tabs
   covered by the stranded card and the Platform environment card cut off.
   Fix: take the stranded cards out of the flex row and release the two fit-content
   fallbacks that only existed to accommodate them. A correctly mounted card (inside
   #tabpanel-3 on the THEME tab) is NOT a child of .RaLayout-contentWithSidebar and is
   left completely untouched. */
#root .RaLayout-contentWithSidebar > :is([data-s8k-tc], [data-s8k-pe]) { display: none !important; }
#root .RaLayout-contentWithSidebar:has(> [data-s8k-tc], > [data-s8k-pe]) > .RaLayout-content { min-width: 0; }
#root > .layout:has(.RaLayout-contentWithSidebar > :is([data-s8k-tc], [data-s8k-pe])) { min-width: 0; }
/* While a theme card is stranded (and therefore hidden by the rule above) the THEME tab
   would otherwise have no colour controls at all, because admin-panel.js hides the three
   native colour fields for it with an inline style. Put those back — inline display:none
   loses to !important — so the tab stays usable. Scoped to the stranded case only. */
#root .RaLayout-contentWithSidebar:has(> [data-s8k-tc]) .RaLayout-content .MuiFormControl-root:has(> .MuiInputBase-root > input[name="color_accent"], > .MuiInputBase-root > input[name="color_primary"], > .MuiInputBase-root > input[name="color_background"]) { display: inline-flex !important; }
/* ==== /s8k-settings-layout-fit ==== */

/* ==== s8k-a11y-focus ==== */
/* 1. Visible keyboard focus (WCAG 2.4.7).
   MUI ButtonBase ships `outline: 0` from an emotion class that is injected into <head>
   AFTER this stylesheet, so the generic `:focus-visible` rule earlier in this file loses
   the cascade tie and every button, tab, menu item and icon button focuses invisibly
   (measured: outline "none 0px" on all of them, including tabs that do carry
   .Mui-focusVisible). The sidebar entries admin-panel.js clones ([data-s8k-ap]) are plain
   DOM nodes that React never marks .Mui-focusVisible, so they have no indicator at all.
   Re-assert the ring with !important on the real :focus-visible pseudo-class, which the
   browser only matches for keyboard focus — a mouse click still draws nothing.
   outline-offset is negative so the ring is painted inside the element and is never
   clipped by .RaSidebar-paper{overflow:hidden} or by a scrolling tab strip. */
#root :is(.MuiButtonBase-root, .MuiTab-root, [role="tab"], .RaMenuItemLink-root,
          .MuiMenuItem-root, .MuiIconButton-root, .MuiChip-root):focus-visible,
#s8k-ap :is(button, select, a[href], [tabindex]):focus-visible,
body > #s8k-ap-modal :is(button, select, a[href], [tabindex]):focus-visible {
  outline: 2px solid #c4b5fd !important;   /* >= 5.4:1 on every admin surface */
  outline-offset: -2px !important;
}
/* A light-violet ring disappears on the brand gradient: use the dark ink there (>= 3.6:1). */
#root :is(.MuiButton-contained, .MuiButton-containedPrimary, .MuiFab-root):focus-visible,
#s8k-ap :is(.btn.primary, .btn.newbtn):focus-visible { outline-color: #0b0a1f !important; }

/* 2. Tab-focus must not scroll a field under the sticky save bar of the App Settings
   overlay (#s8k-ap .set-foot: sticky, bottom 14px, 79px tall -> it covers the bottom 93px
   of the viewport, and seven controls were measured landing behind it). scroll-padding
   has no paint effect at all; it only tells scroll-into-view where the usable area ends.
   It must sit on the scrolling element, which for this page is the root element. */
html:has(#s8k-ap .set-foot) { scroll-padding-top: 58px; scroll-padding-bottom: 104px; }
@media (max-width: 600px) { html:has(#s8k-ap .set-foot) { scroll-padding-bottom: 152px; } }

/* 3. Contrast (WCAG 1.4.3 text, 1.4.11 non-text). Targeted overrides only — no brand
   token is changed. Measured before -> after at 1366x768:
     placeholder #6b6780 3.32-3.46:1 and UA #757575 3.43:1  ->  #9691ad 5.2-6.2:1
     #s8k-ap .txt border rgba(255,255,255,.13) 1.47:1        ->  .38 3.56:1
     #s8k-ap .apis .txt   rgba(255,255,255,.16) 1.64:1       ->  .38 3.5:1
     #s8k-ap-modal .txt   rgba(255,255,255,.14) 1.50:1       ->  .38 3.57:1
     MUI notched outline  rgba(255,255,255,.16) 1.65:1       ->  .38 3.52:1
   The border really is load-bearing here: the input fill is only 1.06-1.10:1 against the
   surface it sits on, so the 1px border is the only thing that identifies the control. */
#s8k-ap :is(input, textarea)::placeholder,
body > #s8k-ap-modal :is(input, textarea)::placeholder,
#root .MuiInputBase-input::placeholder { color: #9691ad !important; opacity: 1 !important; }
#s8k-ap :is(.txt, .apis .txt),
body > #s8k-ap-modal :is(input.txt, select.txt, textarea.ta) { border-color: rgba(255,255,255,.38) !important; }
/* hover stays visibly brighter than the new resting value (it was .13 -> .22; now .38 -> .52),
   and focus keeps exactly the colour admin-panel.js:366/411/413/499 already gives it. */
#s8k-ap :is(.txt, .apis .txt):hover,
body > #s8k-ap-modal :is(input.txt, select.txt, textarea.ta):hover { border-color: rgba(255,255,255,.52) !important; }
#s8k-ap :is(.txt, .apis .txt):focus,
body > #s8k-ap-modal :is(input.txt, select.txt, textarea.ta):focus { border-color: var(--acc, #a78bfa) !important; }
/* :not(.Mui-focused):not(.Mui-error) so the violet focus outline (admin-theme.css:208) and
   MUI's red error outline keep winning — only the resting hairline is raised. */
#root .MuiOutlinedInput-root:not(.Mui-focused):not(.Mui-error) .MuiOutlinedInput-notchedOutline {
  border-color: rgba(255,255,255,.38) !important;
}
/* ==== /s8k-a11y-focus ==== */

/* ==== s8k-native-save-sticky ==== */
/* The native react-admin Save toolbar is the last child of the form, position:relative and
   not sticky, so on a 1366x768 screen it sits at y=927 on #/app_settings/1 (document height
   979) and at y=714 on #/hosts/create (document height 779): off-screen until you scroll to
   the very bottom. Pin it to the bottom of the viewport while the form is longer than the
   viewport; once the form bottom is reached it settles back into its normal place.
   .RaToolbar-mobileToolbar (<600px) is already position:fixed and is not touched. */
#root form .RaToolbar-desktopToolbar {
  position: sticky;
  bottom: 0;
  z-index: 3;
  border-top: 1px solid rgba(255, 255, 255, .10);
  box-shadow: 0 -14px 26px -18px rgba(0, 0, 0, .9);
}
/* The form card is a MuiPaper with overflow:hidden, which makes it the sticky element's
   scrollport — a scrollport that never scrolls, so position:sticky alone is a no-op
   (verified: toolbar stays at y=927). overflow:clip clips exactly the same way but does
   NOT create a scroll container, so the toolbar sticks to the viewport again. */
#root .MuiCard-root:has(form .RaToolbar-desktopToolbar) { overflow: clip; }
/* Keyboard focus must not scroll a field under the bar this block just pinned.
   scroll-padding has no paint effect; it only moves where scroll-into-view stops. */
html:has(#root form .RaToolbar-desktopToolbar) { scroll-padding-top: 58px; scroll-padding-bottom: 88px; }
/* ==== /s8k-native-save-sticky ==== */
