/* POS Admin — overrides layered on top of Tabler 1.4.0.
 *
 * Tabler IS a Bootstrap 5.3 distribution, so Bootstrap's grid, utilities and
 * components are all still available — they are simply restyled by Tabler. That
 * is why the separate Bootstrap CDN link was removed rather than the Bootstrap
 * class names: loading both meant two copies of Bootstrap fighting each other.
 *
 * Everything below is deliberately small. If a rule here can be replaced by a
 * Tabler class, delete it and use the class instead. Colours come from Tabler's
 * --tblr-* custom properties so light and dark themes are handled for free.
 */

/* ---------------------------------------------------------------------------
   Global guards
--------------------------------------------------------------------------- */

/* No sideways scroll, ever. `clip` rather than `hidden` because `hidden` turns
   the element into a scroll container, which breaks the sticky navbar; the
   `hidden` line is only a fallback for engines without `clip`. */
html{ overflow-x:hidden; }
html,body{ overflow-x:clip; max-width:100%; }

/* Stop iOS re-flowing text at its own scale in landscape. */
body{ -webkit-text-size-adjust:100%; text-size-adjust:100%; }

/* The usual causes of a broken phone layout. */
img,svg,video,canvas{ max-width:100%; height:auto; }

/* Long unbroken values — emails, tax numbers, device ids — wrap instead of
   widening their container. */
.break-anywhere{ overflow-wrap:anywhere; }

/* ---------------------------------------------------------------------------
   Brand

   Tabler compiles Bootstrap with its own $primary (#066FD1) baked into the
   distributed CSS as literal values, so overriding --tblr-primary alone does
   NOT restyle a .btn-primary — Bootstrap 5.3 emits each button variant with
   its own --tblr-btn-* properties already resolved. Both layers are set below:
   the theme variable, for everything that reads it at runtime (utilities,
   form controls, active nav states), and the button's own properties.

   Two blues, not one. #389DCB is the logo's flat colour and measures 3.06:1 on
   white — fine as a shape, not as a label or a link. Anything carrying text
   uses a darkened partner, and dark mode inverts which one is which because
   the ground does.
--------------------------------------------------------------------------- */

:root{
  --tblr-primary:#2a7ca1;          /* 4.67:1 on white */
  --tblr-primary-rgb:42,124,161;
  --tblr-primary-fg:#ffffff;       /* white on it: 4.67:1 */
  --tblr-link-color:#266f91;       /* 5.58:1 — small text needs the darker one */
  --tblr-link-color-rgb:38,111,145;
  --tblr-link-hover-color:#1d5670;
  --tblr-link-hover-color-rgb:29,86,112;
}

/* On the dark theme the SHAPE colour and the text colour swap roles: the logo
   blue reads at 5.18:1 against Tabler's dark surface, while the darkened
   partner drops to 3.40:1 and is too quiet for a link. */
[data-bs-theme="dark"]{
  --tblr-primary:#389dcb;          /* 5.18:1 on the dark surface */
  --tblr-primary-rgb:56,157,203;
  --tblr-link-color:#5db4de;       /* 6.84:1 */
  --tblr-link-color-rgb:93,180,222;
  --tblr-link-hover-color:#8ccce9;
  --tblr-link-hover-color-rgb:140,204,233;
}

/* Filled buttons keep the DARK blue in both themes. A button's contrast that
   matters is the one against its own label, and white on #2A7CA1 clears AA at
   4.67:1 either way; the light logo blue would put white text at 3.06:1. */
.btn-primary,
[data-bs-theme="dark"] .btn-primary{
  --tblr-btn-color:#ffffff;
  --tblr-btn-bg:#2a7ca1;
  --tblr-btn-border-color:#2a7ca1;
  --tblr-btn-hover-color:#ffffff;
  --tblr-btn-hover-bg:#226381;
  --tblr-btn-hover-border-color:#226381;
  --tblr-btn-active-color:#ffffff;
  --tblr-btn-active-bg:#1d5670;
  --tblr-btn-active-border-color:#1d5670;
  --tblr-btn-disabled-color:#ffffff;
  --tblr-btn-disabled-bg:#2a7ca1;
  --tblr-btn-disabled-border-color:#2a7ca1;
}

/* The outline variant is the mirror: the border is a SHAPE, so it may carry the
   logo blue, but the label is text and takes the darker partner. */
.btn-outline-primary{
  --tblr-btn-color:#266f91;
  --tblr-btn-border-color:#389dcb;
  --tblr-btn-hover-color:#ffffff;
  --tblr-btn-hover-bg:#2a7ca1;
  --tblr-btn-hover-border-color:#2a7ca1;
  --tblr-btn-active-color:#ffffff;
  --tblr-btn-active-bg:#2a7ca1;
  --tblr-btn-active-border-color:#2a7ca1;
}
[data-bs-theme="dark"] .btn-outline-primary{
  --tblr-btn-color:#5db4de;
  --tblr-btn-border-color:#389dcb;
}

/* The app icon carries its own LIGHT rounded plate now, white breathing into a
   pale blue. It still reads on both themes — a light chip on the dark navbar is
   the same move as a dark one was on the light navbar — and it still must not
   be inverted. It escapes .navbar-brand-autodark by not carrying the
   .navbar-brand-image class that rule targets; keep it that way. */
.brand-logo{ width:32px; height:32px; border-radius:8px; display:block; flex:none; }

/* ---------------------------------------------------------------------------
   Mobile: table rows become stacked cards

   Tabler has no stacked-table component — .table-responsive (which is applied
   as well) only scrolls sideways. Below 768px that is a poor way to read a
   record, so each row is re-flowed into a card. The column heading for each
   cell comes from its data-label attribute.
--------------------------------------------------------------------------- */

@media (max-width:767.98px){
  .table-mobile-stack thead{
    position:absolute; width:1px; height:1px;
    overflow:hidden; clip-path:inset(50%); white-space:nowrap;
  }
  .table-mobile-stack,
  .table-mobile-stack tbody,
  .table-mobile-stack td{ display:block; width:100%; }

  /* Column flex (not plain block) so cells can be re-ordered — it lets the
     title cell rise to the top of the card without disturbing the desktop
     column order in the markup. */
  .table-mobile-stack tr{
    display:flex;
    flex-direction:column;
    background:var(--tblr-bg-surface);
    border:1px solid var(--tblr-border-color);
    border-radius:var(--tblr-border-radius);
    padding:0.25rem 1rem 0.85rem;
    margin-bottom:0.75rem;
  }
  .table-mobile-stack tbody tr:last-child{ margin-bottom:0; }

  .table-mobile-stack td{
    display:flex; align-items:center; justify-content:space-between;
    gap:1rem;
    padding:0.6rem 0 !important;
    border-bottom:1px solid var(--tblr-border-color) !important;
    text-align:start !important;
    overflow-wrap:anywhere;
    /* Bootstrap paints row striping/hover through an inset shadow on cells,
       which would draw a slab across each stacked row. */
    box-shadow:none !important;
    background:transparent !important;
  }
  .table-mobile-stack tr td:last-child{ border-bottom:0 !important; padding-bottom:0 !important; }

  /* Stand-in for the hidden column header. */
  .table-mobile-stack td::before{
    content:attr(data-label);
    flex:0 0 auto;
    color:var(--tblr-secondary);
    font-size:0.75rem;
    font-weight:var(--tblr-font-weight-medium, 500);
    text-transform:uppercase;
    letter-spacing:0.04em;
  }
  .table-mobile-stack td:not([data-label])::before{ content:none; }

  /* Card heading: pulled to the top, no label, no right-hand value column. */
  .table-mobile-stack td.cell-title{
    order:-1;
    display:block;
    padding:0.85rem 0 0.7rem !important;
    font-size:1rem;
    font-weight:var(--tblr-font-weight-bold, 600);
  }
  .table-mobile-stack td.cell-title::before{ content:none; }

  /* Cells holding buttons or a form: label above, control full width below. */
  .table-mobile-stack td.cell-block{ display:block; padding-top:0.75rem !important; }
  .table-mobile-stack td.cell-block::before{ display:block; margin-bottom:0.5rem; }
  /* !important beats Bootstrap's .flex-nowrap utility, which the markup needs at
     desktop width to keep row actions on one line inside a w-1 column. */
  .table-mobile-stack td.cell-block .btn-list{ display:flex; flex-wrap:wrap !important; gap:0.5rem; }
  .table-mobile-stack td.cell-block .btn-list > *{ flex:1 1 auto; }
  .table-mobile-stack td.cell-block .btn-list .btn{ width:100%; }
  .table-mobile-stack td.cell-block form{ flex:1 1 auto; margin:0; }

  /* Give the seats / days editors the full card width. */
  .table-mobile-stack td.cell-block .input-group{ flex:1 1 auto; }

  /* Tabler pulls card tables flush to the card edge; the stacked cards supply
     their own padding instead. */
  .card > .table-responsive > .table-mobile-stack{ margin-bottom:0; }
}

/* ---------------------------------------------------------------------------
   Horizontal scroll cue

   .table-responsive scrolls but gives no sign that it can. These fades sit on a
   wrapper (not the scroller, or they would scroll away with the content) and
   are switched on by JS only on the edge that actually has more content.
--------------------------------------------------------------------------- */

.table-scroll-hint{ position:relative; }
.table-scroll-hint::before,
.table-scroll-hint::after{
  content:'';
  position:absolute; top:0; bottom:0; width:2.5rem;
  pointer-events:none; opacity:0; z-index:2;
  transition:opacity 160ms ease;
}
.table-scroll-hint::before{ left:0; background:linear-gradient(to right, var(--tblr-bg-surface), transparent); }
.table-scroll-hint::after{ right:0; background:linear-gradient(to left, var(--tblr-bg-surface), transparent); }
.table-scroll-hint.can-scroll-left::before{ opacity:1; }
.table-scroll-hint.can-scroll-right::after{ opacity:1; }

@media (max-width:767.98px){
  /* Stacked cards never scroll sideways, so the cue would be a lie. */
  .table-scroll-hint::before,
  .table-scroll-hint::after{ content:none; }
}

/* Above the stacking breakpoint, hold a workable minimum width and let the
   wrapper scroll. Left alone a table squeezes its columns to fit, which at
   tablet width breaks phone numbers across three lines and stacks the action
   buttons. Per-table override: style="--table-min: 60rem". */
@media (min-width:768px){
  .table-responsive > .table-min{ min-width:var(--table-min, 58rem); }
}

/* ---------------------------------------------------------------------------
   Small touches
--------------------------------------------------------------------------- */

/* Number spinners are mouse-only, tiny, and painted by the OS rather than the
   theme. Removed; the seats/days editors provide real -/+ buttons instead. */
input[type="number"]{ appearance:textfield; -moz-appearance:textfield; }
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button{ -webkit-appearance:none; appearance:none; margin:0; }

@media (max-width:767.98px){
  /* 16px is the threshold below which iOS Safari zooms the page on focus. */
  .form-control, .form-select{ font-size:16px; }
}

/* Users who ask for less motion get none of the transitions. */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    animation-duration:0.01ms !important;
    animation-iteration-count:1 !important;
    transition-duration:0.01ms !important;
    scroll-behavior:auto !important;
  }
}

/* ---------------------------------------------------------------------------
   Shell — sidebar, drawer, top bar (both portals)

   ONE model at every width, and it is Bootstrap's: the sidebar is an
   `offcanvas-lg`. Below 992px that is a drawer (Bootstrap's own backdrop,
   focus trap, Escape and scroll lock); from 992px up Bootstrap drops the
   drawer behaviour and this section pins it as a column beside the content.

   🚨 This replaced a sidebar that was `.navbar.navbar-vertical.offcanvas` at
   once, docked and undocked by the old manager.js, with rules in theme.css undoing
   Tabler's `~ .page-wrapper { margin-left: 15rem }` and centred nav items.
   Nothing here overrides Tabler's sidebar any more, because the sidebar no
   longer carries Tabler's sidebar classes. Keep it that way: the nav rows are
   `.app-nav-link`, styled once, below.

   Everything is scoped to `.app-*`. BOTH portals render it — _ManagerLayout
   and the admin _Layout carry the same sidebar markup — which is why it lives
   here in the shared layer and not in manager.css.
--------------------------------------------------------------------------- */

:root { --app-sidebar-width: 16rem; }

/* The drawer's width below lg. Bootstrap sizes the panel from this variable. */
.app-sidebar.offcanvas-lg { --tblr-offcanvas-width: min(20rem, 86vw); }

/* The panel owns the background and the padding at every width. (From lg up
   Bootstrap forces the offcanvas root itself transparent with !important, so
   painting the root would be a fight; painting a child is not.) */
.app-sidebar-panel {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  min-height: 0;
  overflow-y: auto;
  /* Reaching the end of the list must not start scrolling the page behind. */
  overscroll-behavior: contain;
  padding: 0.75rem;
  padding-bottom: calc(0.75rem + env(safe-area-inset-bottom, 0px));
  background: var(--tblr-bg-surface);
}

/* The sidebar stays put: its head (the brand on /admin) and the account block
   on the floor never move, and only the list of destinations between them
   scrolls when it is longer than the screen (the manager's is). Not on a short
   screen — a phone on its side — where the two fixed blocks would leave the list no room;
   there the whole panel scrolls as one. */
@media (min-height: 40rem) {
  .app-sidebar-panel { overflow-y: hidden; }
  .app-sidebar-panel > .app-nav:not(.app-nav-foot) {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    margin-bottom: 0;
    padding-bottom: 0.75rem;
  }
  .app-sidebar-head, .app-nav-foot { flex: none; }
}

.app-sidebar-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.25rem 0.25rem 0.75rem;
  margin-bottom: 0.75rem;
  border-bottom: 1px solid var(--tblr-border-color);
}

/* Logo + product name + company, in the sidebar and the top bar on /admin. The
   company name wraps rather than truncating: it is the one line that says
   whose takings are on screen. (/manager shows the company's logo alone, at
   the end of its bar — manager.css, `.app-bar-brand`.) */
.app-brand {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  min-width: 0;
  color: inherit;
  text-decoration: none;
}
.app-brand:hover { color: inherit; text-decoration: none; }

.app-brand-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.25;
  text-align: start;
}
.app-brand-text small { font-size: 0.75rem; overflow-wrap: anywhere; }

.app-nav { display: flex; flex-direction: column; gap: 2px; }

/* The account block, on the floor of the panel. */
.app-nav-foot {
  margin-top: auto;
  padding-top: 0.75rem;
  border-top: 1px solid var(--tblr-border-color);
}
.app-sidebar-panel > .app-nav:not(.app-nav-foot) { margin-bottom: 0.75rem; }

/* One row shape for links, buttons and the sign-out submit. The highlight is
   the row's own background, so it always fills exactly the row it belongs
   to — the old navbar's hover sat wider than its item. */
.app-nav-link {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  min-height: 2.75rem;
  padding: 0.5rem 0.75rem;
  border: 0;
  border-radius: var(--tblr-border-radius-lg, 0.5rem);
  background: transparent;
  color: var(--tblr-body-color);
  font: inherit;
  font-weight: var(--tblr-font-weight-medium, 500);
  text-align: start;
  text-decoration: none;
  cursor: pointer;
}
.app-nav-link:hover {
  background: var(--tblr-bg-surface-secondary);
  color: var(--tblr-body-color);
  text-decoration: none;
}
.app-nav-link:focus-visible { outline: 2px solid var(--tblr-primary); outline-offset: -2px; }

/* Text takes the LINK blue, not the primary: --tblr-primary on its own 12%
   tint is 4.0:1 in light mode, the link partner clears 4.5:1. */
.app-nav-link.active {
  background: color-mix(in srgb, var(--tblr-primary) 12%, transparent);
  color: var(--tblr-link-color);
  font-weight: var(--tblr-font-weight-bold, 600);
}

.app-nav-icon {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.25rem;
  height: 1.25rem;
  color: var(--tblr-secondary);
}
.app-nav-link.active .app-nav-icon { color: inherit; }

/* Labels wrap rather than clip: "Groupes de produits" and its Arabic are
   longer than "Groups", and an ellipsis on a destination is a guess. */
.app-nav-label { min-width: 0; overflow-wrap: anywhere; }

/* Headed groups (both portals): a drawer of many rows reads as short lists. */
.app-nav-group { display: flex; flex-direction: column; gap: 2px; }
.app-nav-group + .app-nav-group { margin-top: 0.75rem; }
.app-nav-heading {
  padding: 0.25rem 0.75rem;
  color: var(--tblr-secondary);
  font-size: 0.75rem;
  font-weight: var(--tblr-font-weight-bold, 600);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* A count on a nav row (/admin Requests): never squeezed by a long label,
   always at the row's end, readable in a phone drawer. */
.app-nav-badge { flex: none; margin-inline-start: auto; font-size: 0.75rem; }

/* The phone top bar's "something is waiting in the menu" dot. */
.app-topbar-dot {
  position: absolute;
  top: 6px;
  inset-inline-end: 6px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--tblr-red);
  box-shadow: 0 0 0 2px var(--tblr-bg-surface);
}

/* The account row's name: wraps like any label, but stops at two lines with
   an ellipsis — a person's name has no length limit, and the full text is in
   the element's title. */
.app-nav-label-clamp {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

.app-nav-link-danger,
.app-nav-link-danger .app-nav-icon,
.app-nav-link-danger:hover { color: var(--tblr-danger); }
.app-nav-link-danger:hover { background: color-mix(in srgb, var(--tblr-danger) 6%, transparent); }

/* The drawer is for thumbs: 48px rows, Material's minimum touch target. */
@media (max-width: 991.98px) {
  .app-nav-link { min-height: 3rem; }
}

/* The bar above the page. On /admin it is only the drawer's handle, below lg
   (d-lg-none in the markup); on /manager it is there at every width, above the
   side menu, with the app's name (the link home) and the logo, and only its
   hamburger is d-lg-none.
   Sticky so the navigation is reachable from half-way down a long list. */
.app-topbar {
  position: sticky;
  top: 0;
  z-index: 1020;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 3.5rem;
  padding: 0.375rem 0.75rem;
  padding-top: max(0.375rem, env(safe-area-inset-top, 0px));
  background: var(--tblr-bg-surface);
  border-bottom: 1px solid var(--tblr-border-color);
}
.app-topbar-toggle { flex: none; width: 2.75rem; height: 2.75rem; }

/* Skip link: hidden until a keyboard user tabs to it, then a chip over the
   top-left corner. The <main> it targets takes focus without a ring. */
.app-skip-link:focus {
  position: fixed;
  top: 0.5rem;
  inset-inline-start: 0.5rem;
  z-index: 1100;
  padding: 0.5rem 1rem;
  border-radius: var(--tblr-border-radius);
  background: var(--tblr-bg-surface);
  box-shadow: 0 0 0 2px var(--tblr-primary);
}
#main:focus { outline: none; }
/* <main> sits between .page-wrapper and its children; let it keep the column
   flow Tabler gives the wrapper so the page body still fills the height. */
.app-main > main { display: flex; flex-direction: column; flex: 1 0 auto; min-width: 0; }

/* Error banners take focus (theme script) — no ring on a non-control. */
.app-notices .alert:focus { outline: none; }

/* Pinned, from lg up: a fixed-width column that stays put while the content
   scrolls. `min-width: 0` on the content is what stops a wide child (a table,
   a long unbroken value) from pushing the page sideways.

   FIXED to the window, not sticky. A sticky column still belongs to the page:
   it rode up with it whenever the page moved past its own end — the bounce at
   the bottom of a trackpad scroll, or a document taller than the shell. Fixed
   is out of the page entirely; the content makes room with a margin. */
@media (min-width: 992px) {
  .app-shell { flex-direction: row; }

  .app-sidebar.offcanvas-lg {
    position: fixed;
    top: 0;
    bottom: 0;
    inset-inline-start: 0;
    z-index: 1030;
    width: var(--app-sidebar-width);
    border-inline-end: 1px solid var(--tblr-border-color);
  }
  .app-shell > .app-main { margin-inline-start: var(--app-sidebar-width); }

  /* And no bounce to begin with: the page stops at its ends. */
  html:has(.app-shell) { overscroll-behavior-y: none; }
}
/* The sidebar is not printed, so its margin is not either. */
@media print {
  .app-shell > .app-main { margin-inline-start: 0; }
}
@media (min-width: 992px) {

  .app-main { flex: 1 1 auto; min-width: 0; }
}

/* ---------------------------------------------------------------------------
   View-only accounts (/admin)

   A staff-role operator can look at everything and change nothing. _Layout puts
   `view-only` on <body> for them, and every control that writes carries
   `admin-only`. Hiding is the courtesy; AdminPageModel refuses the POST and the
   API's StaffReadOnlyFilter refuses the call, so a control missed here fails
   with a message rather than doing anything.
--------------------------------------------------------------------------- */
.view-only .admin-only { display: none !important; }

/* ---------------------------------------------------------------------------
   User card grid (/admin company details)

   Columns come from the width, not a breakpoint: as many 17rem cards as fit.
   min(17rem, 100%) keeps a single card from overflowing a phone narrower than
   17rem plus the gutters.
--------------------------------------------------------------------------- */
.user-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(17rem, 100%), 1fr));
  gap: 0.75rem;
}
/* The ⋮ menu must be able to hang out of its card. */
.user-card { overflow: visible; }

/* ---------------------------------------------------------------------------
   Admin portal only (body.admin-portal — the manager portal is not affected)
--------------------------------------------------------------------------- */

/* Tabler's light-tint badges and .status-* dots paint the raw palette colour
   on a 10% tint of itself: yellow measures 2.1:1, green 2.7:1, azure and
   orange 3.0:1. Darker text partners, light theme only — dark already passes.
   All measure 4.9-5.9:1 on their tints. */
[data-bs-theme="light"] .admin-portal .badge.bg-green-lt,
[data-bs-theme="light"] .admin-portal .status-green { color: #1e7a2f !important; }
[data-bs-theme="light"] .admin-portal .text-success { color: #1e7a2f !important; }
[data-bs-theme="light"] .admin-portal .badge.bg-yellow-lt,
[data-bs-theme="light"] .admin-portal .status-yellow,
[data-bs-theme="light"] .admin-portal .text-warning { color: #8a5a00 !important; }
[data-bs-theme="light"] .admin-portal .badge.bg-azure-lt,
[data-bs-theme="light"] .admin-portal .status-azure { color: #1a6aa3 !important; }
[data-bs-theme="light"] .admin-portal .badge.bg-orange-lt,
[data-bs-theme="light"] .admin-portal .status-orange { color: #b04a00 !important; }
[data-bs-theme="light"] .admin-portal .badge.bg-red-lt,
[data-bs-theme="light"] .admin-portal .badge.bg-danger-lt,
[data-bs-theme="light"] .admin-portal .status-red { color: #b02a2a !important; }
[data-bs-theme="light"] .admin-portal .badge.bg-secondary-lt { color: #4b5563 !important; }

/* Stat tiles: as many 10rem tiles as fit, no breakpoints. */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: 0.75rem;
}

/* Fingers, not cursors, below md: every button in a stacked table and card icon buttons grow to
   44px; the drawer's close button gets a bigger hit area. */
@media (max-width: 767.98px) {
  .admin-portal .table-mobile-stack .btn { min-height: 2.75rem; }
  .admin-portal .user-card .btn-icon { width: 2.75rem; height: 2.75rem; }
  .admin-portal .app-sidebar .btn-close { padding: 0.875rem; }
}

/* Update notice: the email preview. Full letter height beside the form on a
   desktop; below lg it is stacked under it, so it takes most of a screen only. */
.admin-portal .notice-preview {
  display: block; width: 100%; height: 980px; border: 0;
  background: var(--tblr-bg-surface-secondary);
}
@media (max-width: 991.98px) {
  .admin-portal .notice-preview { height: min(980px, 70vh); }
}

/* Tabler's green (#2fb344) under white text measures 2.7:1. The darker green
   the badges already use for text reads at 5.4:1, on both themes. */
.btn-success {
  --tblr-btn-bg: #1e7a2f;
  --tblr-btn-border-color: transparent;
  --tblr-btn-hover-bg: #17621f;
  --tblr-btn-hover-border-color: transparent;
  --tblr-btn-active-bg: #17621f;
  --tblr-btn-disabled-bg: #1e7a2f;
}

/* A dialog's tick list is worked with a thumb: each row is a full touch target. */
@media (max-width: 991.98px) {
  .modal .modal-body > .form-check { min-height: 2.75rem; display: flex; align-items: center; margin-bottom: 0; }
  .modal .modal-body > .form-check .form-check-label { padding-inline-start: 0.5rem; }
}

/* A scrollable dialog whose header, body and footer sit inside a <form>.
   Bootstrap makes .modal-content the flex column and lets .modal-body scroll;
   a form in between is an ordinary block, so the body never scrolled and the
   footer — Save, Cancel — was pushed out of reach on a long form. The form
   becomes the same flex column, bounded by the dialog. */
.modal-dialog-scrollable .modal-content > form {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}

/* A row that opens its record on click (<tr data-href>, wired in _Layout). */
.admin-portal .row-link { cursor: pointer; }
/* Tabbing to the row's link shows which row it belongs to. */
.admin-portal .row-link:focus-within { background: var(--tblr-bg-surface-secondary); }

/* Floating action button: the page's one "add" action, pinned to the bottom
   corner above the content and clear of the phone's home indicator. Logical
   inset, so it would follow the reading direction. Under the drawer and modals. */
.admin-portal .app-fab {
  position: fixed;
  inset-inline-end: calc(1.25rem + env(safe-area-inset-right, 0px));
  bottom: calc(1.25rem + env(safe-area-inset-bottom, 0px));
  z-index: 1020;
  min-height: 3.5rem;
  padding-inline: 1.5rem;
  font-size: 1rem;
  box-shadow: var(--tblr-box-shadow-lg, 0 0.5rem 1.5rem rgba(0, 0, 0, 0.2));
}
/* On a phone the pill would hide a third of a card: a round icon button there,
   its label kept for screen readers. */
@media (max-width: 767.98px) {
  .admin-portal .app-fab { width: 3.5rem; padding-inline: 0; }
  .admin-portal .app-fab .icon { margin: 0; }
  .admin-portal .app-fab-label {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
}
/* Room for the last row to scroll clear of it. */
.admin-portal .page-body.has-fab { padding-bottom: calc(5.5rem + env(safe-area-inset-bottom, 0px)); }

/* Tabler thins .icon-lg to a 1px stroke; the empty state keeps the weight
   every other icon has. */
.empty-icon .icon { stroke-width: 1.5; }

/* Sync inbox: its two halves as a tray of pills, the active one lifted onto
   the surface — Tabler's thin tab underline was easy to miss. Tokens only, so
   dark mode needs no rule of its own. */
.admin-portal .sync-tabs {
  gap: 0.5rem;
  padding: 0.25rem;
  background: var(--tblr-bg-surface-secondary);
  border: 1px solid var(--tblr-border-color);
  border-radius: var(--tblr-border-radius);
}
.admin-portal .sync-tabs .nav-link {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 2.75rem;
  font-weight: 600;
  color: var(--tblr-body-color);
}
.admin-portal .sync-tabs .nav-link.active {
  background: var(--tblr-bg-surface);
  color: var(--tblr-primary);
  box-shadow: var(--tblr-box-shadow-card);
}
.admin-portal .sync-tabs .nav-link:focus-visible {
  outline: 2px solid var(--tblr-primary);
  outline-offset: 2px;
}

/* In-page anchors clear the sticky top bar. */
.admin-portal [id] { scroll-margin-top: 4.5rem; }
/* Small named helpers instead of inline styles. */
.min-w-0 { min-width: 0; }
.mw-14 { max-width: 14rem; }
.access-row { cursor: pointer; }
.access-row .form-check-input { width: 1.5rem; height: 1.5rem; }
/* Manager-access list while "Every manager" is chosen: inactive, still
   readable (a fade took its secondary text below AA). */
.access-list-inactive { border-style: dashed !important; }
