/* Color theme: a warm clay/terracotta palette (oklch), overriding every
   neutral+primary token Basecoat's "rhea" bundle sets — unlayered, so it
   wins over rhea's own @layer-wrapped :root/.dark regardless of selector
   order. --chart-1..5 carried over for a future dashboard.

   --success/--warning aren't part of the swap — Basecoat has no such
   tokens, and this app needs both for status badges.

   --radius-2xl/3xl/4xl are pinned to --radius (0, sharp corners) since
   Tailwind's own defaults for those don't derive from --radius otherwise,
   which would leave cards/dialogs rounded while everything else went square.

   --sidebar keeps a distinct value from --background on desktop (a
   permanent panel); pinned equal to --background on mobile below. */
:root {
  --background: oklch(1 0 0);
  --foreground: oklch(0.147 0.004 49.3);
  --card: oklch(1 0 0);
  --card-foreground: oklch(0.147 0.004 49.3);
  --popover: oklch(1 0 0);
  --popover-foreground: oklch(0.147 0.004 49.3);
  --primary: oklch(0.555 0.163 48.998);
  --primary-foreground: oklch(0.987 0.022 95.277);
  --secondary: oklch(0.967 0.001 286.375);
  --secondary-foreground: oklch(0.21 0.006 285.885);
  --muted: oklch(0.96 0.002 17.2);
  --muted-foreground: oklch(0.547 0.021 43.1);
  --accent: oklch(0.96 0.002 17.2);
  --accent-foreground: oklch(0.214 0.009 43.1);
  --destructive: oklch(0.577 0.245 27.325);
  --border: oklch(0.922 0.005 34.3);
  --input: oklch(0.922 0.005 34.3);
  --ring: oklch(0.714 0.014 41.2);
  --chart-1: oklch(0.868 0.007 39.5);
  --chart-2: oklch(0.547 0.021 43.1);
  --chart-3: oklch(0.438 0.017 39.3);
  --chart-4: oklch(0.367 0.016 35.7);
  --chart-5: oklch(0.268 0.011 36.5);
  --sidebar: oklch(0.986 0.002 67.8);
  --sidebar-foreground: oklch(0.147 0.004 49.3);
  --sidebar-primary: oklch(0.666 0.179 58.318);
  --sidebar-primary-foreground: oklch(0.987 0.022 95.277);
  --sidebar-accent: oklch(0.96 0.002 17.2);
  --sidebar-accent-foreground: oklch(0.214 0.009 43.1);
  --sidebar-border: oklch(0.922 0.005 34.3);
  --sidebar-ring: oklch(0.714 0.014 41.2);

  --radius: 0.45rem;
  --radius-2xl: var(--radius);
  --radius-3xl: var(--radius);
  --radius-4xl: var(--radius);
  --success: oklch(52% .14 150);
  --warning: oklch(52% .15 70);
}

/* Neither <html> nor <body> has its own background (bg-background lives
   on <main>, which doesn't cover the whole document). <main>'s md:mt-2
   top margin exposes a strip of that bare background at the top edge,
   visibly seamed against the sidebar — and on iOS, viewport-fit=cover
   extends the document under the notch/overscroll region too, hitting
   the same bare strip. var(--sidebar) matches what visually continues
   into that gap on desktop; already equals --background on mobile. */
html {
  background-color: var(--sidebar);
}

/* viewport-fit=cover paints under the iOS notch/safe areas (needed for
   the html{background-color} rule above to reach there), but would also
   push real content under the notch — padding by the safe-area inset
   pulls content back while keeping the background full-bleed. env()
   resolves to 0 with no notch, so this is a no-op elsewhere. */
body {
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
}

.dark {
  --background: oklch(0.147 0.004 49.3);
  --foreground: oklch(0.986 0.002 67.8);
  --card: oklch(0.214 0.009 43.1);
  --card-foreground: oklch(0.986 0.002 67.8);
  --popover: oklch(0.214 0.009 43.1);
  --popover-foreground: oklch(0.986 0.002 67.8);
  --primary: oklch(0.473 0.137 46.201);
  --primary-foreground: oklch(0.987 0.022 95.277);
  --secondary: oklch(0.274 0.006 286.033);
  --secondary-foreground: oklch(0.985 0 0);
  --muted: oklch(0.268 0.011 36.5);
  --muted-foreground: oklch(0.714 0.014 41.2);
  --accent: oklch(0.268 0.011 36.5);
  --accent-foreground: oklch(0.986 0.002 67.8);
  --destructive: oklch(0.704 0.191 22.216);
  --border: oklch(1 0 0 / 10%);
  --input: oklch(1 0 0 / 15%);
  --ring: oklch(0.547 0.021 43.1);
  --chart-1: oklch(0.868 0.007 39.5);
  --chart-2: oklch(0.547 0.021 43.1);
  --chart-3: oklch(0.438 0.017 39.3);
  --chart-4: oklch(0.367 0.016 35.7);
  --chart-5: oklch(0.268 0.011 36.5);
  --sidebar: oklch(0.214 0.009 43.1);
  --sidebar-foreground: oklch(0.986 0.002 67.8);
  --sidebar-primary: oklch(0.769 0.188 70.08);
  --sidebar-primary-foreground: oklch(0.279 0.077 45.635);
  --sidebar-accent: oklch(0.268 0.011 36.5);
  --sidebar-accent-foreground: oklch(0.986 0.002 67.8);
  --sidebar-border: oklch(1 0 0 / 10%);
  --sidebar-ring: oklch(0.547 0.021 43.1);

  --success: oklch(68% .15 150);
  --warning: oklch(72% .16 70);
}

/* Below md (768px), --sidebar is pinned to var(--background) instead of
   its own value — confirmed live on iOS Safari that the sidebar (present
   off-canvas via translate, not display:none) otherwise mismatches the
   browser chrome color there. Not visually distinguishable on mobile
   anyway (an overlay, not a permanent panel). */
@media (max-width: 767px) {
  :root {
    --sidebar: var(--background);
  }
}

/* corner-shape only affects elements that already have a border-radius
   (circular arc -> squircle), so applying it globally is inert elsewhere
   and reaches every rounded corner without chasing Basecoat's selectors.
   Chromium 139+; unsupported browsers just keep circular corners. */
* {
  corner-shape: squircle;
}

.badge[data-variant="success"] {
  background-color: color-mix(in oklab, var(--success) 10%, transparent);
  color: var(--success);
}
.badge[data-variant="success"]:hover {
  background-color: color-mix(in oklab, var(--success) 20%, transparent);
}
.badge[data-variant="warning"] {
  background-color: color-mix(in oklab, var(--warning) 10%, transparent);
  color: var(--warning);
}
.badge[data-variant="warning"]:hover {
  background-color: color-mix(in oklab, var(--warning) 20%, transparent);
}

/* Colour swatches (see selectColour in app.js) toggle aria-pressed with
   no visual change otherwise — same active-fill treatment as the
   sidebar's active nav item, applied to a badge instead. */
.badge[data-colour-swatch][aria-pressed="true"] {
  background-color: var(--primary);
  color: var(--primary-foreground);
  border-color: var(--primary);
}

/* Basecoat's `@layer base` sets border-color/outline-color defaults for
   bare border-width utilities (border-t, border-l, ...), but
   @tailwindcss/browser's Preflight resets border-color too via its own
   `border: 0px solid` shorthand in the same layer — a load-order race
   that was rendering borders near-black instead of gray. Restated here,
   unlayered, so it can't lose that race either way. */
/* * {
  border-color: var(--color-border);
  outline-color: var(--color-ring);
} */

/* Basecoat's faint .card ring reads as floating with no boundary in a
   dense grid — add a real, fully-opaque 1px border, on top of (not
   instead of) Basecoat's own ring and shadow. */
/* .card {
  border: 1px solid var(--color-border) !important;
} */

/* Drawer header/body/footer had no visual separation beyond padding,
   unlike every other sectioned surface in the app. Applies to every
   drawer automatically. */
.drawer > * > header {
  border-bottom: 1px solid var(--color-border);
}
.drawer > * > footer {
  border-top: 1px solid var(--color-border);
}

/* htmx adds .htmx-request to the triggering element for the request's
   duration (no hx-indicator configured, so otherwise invisible) — gives
   every action a busy look, and disables buttons (not selects) to guard
   against double-firing. Search inputs excluded: they re-fire every
   ~300ms while typing, and a flicker per keystroke would be worse. */
.btn.htmx-request,
.select.htmx-request {
  opacity: 0.6;
  cursor: progress;
}
.btn.htmx-request {
  pointer-events: none;
}

/* Basecoat's .sidebar nav border-right + <main>'s own border-l doubled
   the seam into two hairlines. Only <main> should mark it. */
.sidebar[data-side="left"] nav {
  border-right-width: 0 !important;
}

/* <main> sits inset from the sidebar and the top of the viewport (see the
   border-l/border-t/mt-2/rounded-tl-xl on <main> in the layout templates),
   which leaves a few px of bare page background showing above and behind
   that corner. Tinting the page itself with --color-sidebar (the same
   background .sidebar nav's own background already uses) makes that gap
   read as the sidebar continuing behind <main>'s inset rather than a stray
   sliver of unrelated page color. <main> keeps the normal --color-background
   explicitly, so only the sidebar and the gap around it pick this up.
   Scoped to :has(> .sidebar) so the three sidebar-less standalone pages
   (both logins, 404) keep the plain page background instead of picking up
   a sidebar tint for a sidebar they don't have. */
body:has(> .sidebar) {
  background-color: var(--color-sidebar);
}

/* Basecoat's mobile sidebar overlay defaults to 18rem wide, leaving part
   of the dimmed page visible. This app's mobile nav is a full-screen
   takeover instead, so widen it below the same 48rem breakpoint. */
@media (max-width: 47.9375rem) {
  .sidebar {
    --sidebar-mobile-width: 100vw;
  }
}

/* Basecoat gives every nav item the same full-strength text regardless of
   active state, differentiating only via background fill/weight — muting
   inactive items gives the active one real contrast. */
.sidebar nav li > :is(a, button):not([aria-current="page"], [data-active="true"], :hover) {
  color: var(--color-muted-foreground);
}

/* Basecoat's active-item fill barely reads against this near-white
   sidebar. Outline (not border, which would nudge content 1px on
   activation) gives it a defined edge without shifting layout. */
/* .sidebar nav li > :is(a, button):is([aria-current="page"], [data-active="true"]) {
  outline: 1px solid var(--color-sidebar-border);
  outline-offset: -1px;
} */

/* #image-viewer fills the viewport, unlike every other small centered
   .dialog. inset:0 on the <dialog> itself avoids fighting Basecoat's
   centering math; the child fills 100% with position:relative so its
   absolutely-positioned buttons anchor to it directly.

   background-color replaces a hardcoded bg-black with --color-popover, so
   it adapts with the theme instead of staying black in both. */
dialog#image-viewer {
  position: fixed !important;
  inset: 0 !important;
  margin: 0 !important;
  max-width: none !important;
  max-height: none !important;
}
#image-viewer > * {
  position: relative !important;
  top: auto !important;
  left: auto !important;
  translate: none !important;
  width: 100% !important;
  height: 100% !important;
  max-width: 100% !important;
  max-height: 100% !important;
  margin: 0 !important;
  border-radius: 0 !important;
  background-color: var(--color-popover) !important;
}

/* `.table-container flex-1` used to make tables fill remaining height.
   Basecoat's `.table-container` sets overflow-x:auto but not overflow-y,
   which per spec computes overflow-y to auto too — combined with
   flex-1's default flex-shrink:1, the flexbox algorithm compressed the
   table into its own nested scroll region instead of letting it overflow
   into the page's single scroll container like every other page. flex-1
   is now gone from templates, but flex-shrink is flexbox's default, so
   this explicit flex-shrink:0 is still needed to stop the compression.
   overflow-x stays auto (needed by the popover-positioning fix below). */
.table-container {
  flex-shrink: 0;
}

/* Basecoat's .table thead has no fill (tfoot does, color-mix 50%), so a
   header row reads as plain body text. Matches tfoot's mix exactly. */
.table thead tr {
  background-color: color-mix(in oklab, var(--color-muted) 50%, transparent);
}

/* Basecoat's [data-popover] is closed via visibility:hidden, not
   display:none, so its absolutely-positioned box still counts toward a
   scrolling ancestor's overflow — a closed row-actions kebab was forcing
   a permanent horizontal scrollbar in table-container. display:none
   removes it from layout while closed; Basecoat's own JS flips
   aria-hidden on open, when this rule stops applying. */
[data-popover][aria-hidden="true"] {
  display: none !important;
}

/* Belt-and-suspenders for the open state: Basecoat's `[data-align="end"]`
   is supposed to pin the popover's right edge, but it's a bare selector
   with no fallback, and the row-actions kebab sits in the table's
   right-most column where a positioning miss reintroduces the scrollbar.
   14rem viewport-relative (not %) is enough for "Edit"/"Delete" without
   itself widening the page. */
.dropdown-menu > [data-popover] {
  right: 0 !important;
  left: auto !important;
  max-width: min(14rem, 90vw) !important;
}

/* Every dialog form's scrolling `<section>` clips a `.select` popover
   (plain position:absolute, no viewport-aware repositioning) since
   overflow-y:auto computes overflow-x to auto too per spec — the Brand
   terms dialog's last-row select had its dropdown cut off entirely.
   Toggled only while a popover is actually open, or the section could
   never scroll long content with everything closed. */
.dialog section:has(.select > [data-popover][aria-hidden="false"]) {
  overflow: visible;
}

/* The b2b Settings page puts its three sections in one shared tabs panel;
   each section's partial is still its own `.card` (kept as the
   independent hx-target/outerHTML swap boundary for its own mutations),
   which would otherwise nest box-within-box. This flattens only the
   outer box — `.card`'s padding/typography still apply. */
#organization-settings-tabs [role="tabpanel"] .card {
  border: none;
  box-shadow: none;
  background: transparent;
  border-radius: 0;
}

/* Basecoat's vertical tablist sizes to fit-content (3 nav items), leaving
   a gap below on taller panels — height:auto lets the `.tabs` row's
   default stretch take over instead.
   Also: swap the default gray track for --color-sidebar so this nav
   column reads as app chrome, and zero its rounded-2xl corners (sized for
   a pill track, not a full-height column) since the card's own
   overflow:hidden + radius already handles the real corners. */
#organization-settings-tabs > [role="tablist"] {
  height: auto;
  border-radius: 0;
  background-color: var(--color-sidebar);
  justify-content: flex-start;
}

/* Basecoat's tab-item padding is sized for a horizontal strip, cramped
   here. Cancels flex:1 too, so the 3 rows keep their natural size instead
   of stretching to fill the tablist's height equally. */
#organization-settings-tabs > [role="tablist"] > [role="tab"] {
  flex: none;
  padding-block: 0.625rem;
  padding-inline: 0.75rem;
}

/* The desktop nav-column-plus-content layout has no room side by side on
   a narrow screen (confirmed at 390px: w-56 nav column leaves an
   unreadable sliver for content). Below the same 640px breakpoint
   dialogs switch at, stack nav-above-content instead. */
@media (max-width: 39.9975rem) {
  #organization-settings-tabs {
    flex-direction: column;
  }
  #organization-settings-tabs > [role="tablist"] {
    width: 100%;
    border-inline-end: none;
    border-bottom: 1px solid var(--color-border);
  }
}

/* The settings dialog reuses the full-page's card inside the dialog's
   own already-padded (24px) white box — two knock-on fixes: the card's
   border is redundant against the near-identical dialog surface, and the
   dialog's own padding plus the card's edge left a visible gap around it.
   Zeroed out so the card is the dialog's visible surface. */
#settings-dialog-body {
  padding: 0;
}

/* The dialog holds a fixed size (not just a max-height cap, which would
   still shrink to fit short content) regardless of which section is
   open, with only that section's own content scrolling. min-height:0 on
   both flex levels is the classic nested-flexbox-scroll gotcha fix — a
   flex child otherwise refuses to shrink below its content's natural
   height, so overflow-y-auto never engages. `.tabs` needs flex:1 too
   (not just the card's own fixed height) to actually claim the space, which then lets
   the tablist's own align-items:stretch fill it. */
#settings-dialog-body > .card {
  border: none;
  box-shadow: none;
  height: 70vh;
}
#settings-dialog-body #organization-settings-tabs {
  flex: 1;
  min-height: 0;
}
#settings-dialog-body [role="tabpanel"] {
  min-height: 0;
  overflow-y: auto;
}

/* #user-menu-trigger stacks two lines of text plus an avatar, but
   Basecoat's .btn[data-size=default] fixes height at 2rem (sized for one
   line) — a plain py-* class can't win that specificity fight. height:auto
   lets the real padding expand the box instead of clipping content. */
#user-menu-trigger {
  height: auto;
  padding-block: 0.5rem;
}

/* The preferences panel's Theme/Language pills sit on a bg-muted
   backdrop where both the ghost hover (--color-muted) and the selected
   fill (--color-secondary) are barely visible against it. --color-popover
   draws both states the same way instead, so exactly one pill is lit at
   a time — the selected one, or a hovered sibling borrowing it. */
#user-menu-panel [data-theme-group] > .btn[data-variant="ghost"]:hover,
#user-menu-panel [data-language-group] > .btn[data-variant="ghost"]:hover,
#user-menu-panel [data-theme-group]:not(:has(> .btn[data-variant="ghost"]:hover)) > .btn[data-variant="secondary"],
#user-menu-panel [data-language-group]:not(:has(> .btn[data-variant="ghost"]:hover)) > .btn[data-variant="secondary"] {
  background-color: var(--color-popover);
}
/* ...cleared while a sibling IS hovered, so only one pill is ever lit. */
#user-menu-panel [data-theme-group]:has(> .btn[data-variant="ghost"]:hover) > .btn[data-variant="secondary"],
#user-menu-panel [data-language-group]:has(> .btn[data-variant="ghost"]:hover) > .btn[data-variant="secondary"] {
  background-color: transparent;
}

/* #lang-switch (the login pages' own EN/SI pair) has the same low-contrast
   problem as the preferences panel above, but sits directly on the page
   background instead of a bg-muted panel — --color-popover would be the
   *same* color as that background here, not a fix. Outline (not border,
   which would nudge the button's content by 1px) does what fill alone
   can't, same technique as the sidebar's own active-nav-item. */
#lang-switch > .btn[data-variant="secondary"],
#lang-switch > .btn[data-variant="ghost"]:hover {
  outline: 1px solid var(--color-border);
  outline-offset: -1px;
}

/* #user-menu-panel's separator used to inherit its look from
   `.dropdown-menu [role=separator]` — reproduced directly now that the
   panel isn't a real .dropdown-menu. margin-inline explicitly zeroed
   since a broader `.sidebar nav [role=separator]` rule also matches
   here and would otherwise inset the line past the panel's padding. */
#user-menu-panel [role="separator"] {
  margin-inline: 0;
  margin-block: 0.25rem;
  height: 1px;
  background-color: color-mix(in oklab, var(--color-border) 50%, transparent);
}

/* data-align="start" unreliability — filter dropdowns rendered
   right-aligned regardless, pushing the popover's left edge behind the
   sidebar for triggers near the page's left edge (all of these are).
   #order-status-filter-popover is shared by two pages, fine since only
   one is ever in the DOM at a time. */
#brand-filter-popover,
#availability-filter-popover,
#order-status-filter-popover,
#organization-status-filter-popover,
#audit-action-filter-popover {
  left: 0 !important;
  right: auto !important;
}

/* #toaster gets popover="manual" from app.js so it can render above an
   open <dialog>'s top layer, but the [popover] UA stylesheet fills gaps
   Basecoat's .toaster rule never had to set (top/left/margin/border/
   background), defaulting to inset:0 + opaque Canvas — the toaster
   rendered as a tall, opaque, top-left-pinned box instead of a
   transparent bottom-right strip. Both top and left need clearing (UA
   inset:0 sets both), letting Basecoat's own right:0 + max-width take over. */
#toaster[popover] {
  top: auto;
  left: auto;
  margin: 0;
  border: none;
  background: transparent;
}

/* Native number-input spinners (up/down arrows) are hidden app-wide —
   not just on the catalogue matrix's cramped `w-14` cells — per explicit
   product decision, so every `input[type=number]` stays consistent. */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}

/* Historical note: this file used to carry hardcoded shadow/ring/
   translate overrides working around the legacy Tailwind Play CDN's
   unlayered Preflight reset, which always beat Basecoat's own
   `@layer components` for those properties. Now that templates load the
   real `@tailwindcss/browser@4` compiler (properly layered, its reset
   touches only box-sizing/margin/padding/border), Basecoat's own rules
   simply win the cascade — no workaround needed, and it fixed two values
   that had drifted (blur, ring alpha) along the way. */

/* Basecoat's .tabs flex gap is meant for a horizontal strip; stacked with
   the vertical Company Settings tablist's own border-e, the two read as
   one oddly wide gutter with a stray line floating inside it. Zeroed
   here (not the border, which is the only separator left once gap is gone). */
#organization-settings-tabs.tabs {
  gap: 0;
}

/* #line-entry-drawer-title is focused programmatically on open (see
   openLineEntryDrawer, app.js) to avoid showModal() auto-focusing the
   first qty input and popping the mobile keyboard — the focus ring on
   this non-interactive heading is just visual noise, suppressed here
   rather than by removing the focus() call that fixes the real bug. */
#line-entry-drawer-title:focus {
  outline: none;
}
