/* bootstrap alert CSS, translated to the django-standard levels
of debug, info, success, warning, error
 */

.alert-debug {
  background-color: #fff;
  border-color: #d6e9c6;
  color: #000;
}

.alert-error {
  background-color: #f2dede;
  border-color: #eed3d7;
  color: #b94a48;
}

.no-decoration {
  text-decoration: none !important;
}

/*center the logo in mobile*/
a.navbar-brand img {
  margin: auto !important;
}

/* header */
.navbar-header {
  margin: 0 auto;
}

/* remove some spacing between the logo and Nav */
a.navbar-brand {
  margin: auto;
}

.navbar-brand img {
  width: 140px;
}

/* toggler to change to 'x' when hamburger menu shown: https://stackoverflow.com/a/59214165/2469390 */
.navbar-toggler.collapsed .close-icon {
  display: none;
}

/* hack needed to keep the logo from shifting as the 'x' is smaller*/
.navbar-toggler {
  width: 2.75em;
}

.navbar-toggler:not(.collapsed) .navbar-toggler-icon {
  display: inline;
}

/* remove border/outline https://stackoverflow.com/a/51563589/2469390 */
.navbar-toggler:focus,
.navbar-toggler:active,
.navbar-toggler-icon:focus {
  outline: none;
  -webkit-box-shadow: none;
  box-shadow: none;
}

/* forms */
/*add some space above the submit button*/
input.btn-primary {
  margin-top: 0.5em;
}

/* sticky submit button */
.sticky-button {
  position: sticky;
  bottom: 0;
  background-color: white;
  padding: 10px;
  z-index: 1000;
}

/* hide the asterisk for reuired fields (django-crispy-forms) */
.asteriskField {
  display: none;
}

.form-group {
  margin-top: 0.5em;
  margin-bottom: 0.5em;
}

input.numberinput {
  max-width: 6em;
}

/* ``html5dateinput`` / ``html5timeinput`` are the crispy-applied classes for the
   native HTML5 widgets that replaced django-bootstrap-datepicker-plus; without
   them the native <input type="date"> escapes this cap and renders full-width
   (PR #1135 review). Kept alongside the legacy ``datepickerinput`` classes so
   both old and new render identically. */
input.textinput, input.urlinput, input.datepickerinput, input.datetimepickerinput, input.html5dateinput, input.html5timeinput, select.select {
  max-width: 20em;
}

small {
  font-size: 90%;
}

.warn-orange {
  color: rgb(187, 136, 39);
}

/* email input is too wide compared to text inputs */
.emailinput {
  max-width: 320px;
}

/* ALERTS LIST */
.sticky-section-header {
  position: sticky;
  top: 0;
  z-index: 10;
  background-color: white;
  border-bottom: 1px solid #dee2e6;
}

.sticky-section-header + .row {
  margin-top: 1rem;
}

/* SHIPMENTS LIST */

/* sticky header */
#shipments-list-headers {
  position: sticky;
  top: 0;
  z-index: 10;
  background-color: #f8f9fa; /* Ensure it's visually distinct when scrolling */
}

/* The Columns panel is position-absolute end-0 inside .column-picker-wrapper,
   so above lg it hangs under the Columns button and right-aligned with it,
   which is what the picker's Playwright geometry assertions measure.

   Below lg the search row wraps, and the button lands wherever the wrap put it
   rather than at the row's right edge. A panel right-aligned to the button then
   runs off the screen: measured 2026-08-28 at a 375px viewport it started at
   left: -162px, and left-aligning it instead overflows the other edge at 768px,
   so neither anchor is safe on its own. Taking the wrapper out of the
   positioned-ancestor chain below lg hands the panel's containing block to
   #shipment-search-row, and the same `top: 100%` and `end-0` then read as
   "under the whole search row, flush with its right edge", which is on screen
   at every width the row itself is. The row carries position-relative for
   exactly this.

   The trade-off, deliberate: between md and lg the button does not actually
   wrap, and the panel still detaches from it to sit against the row's right
   edge. Anchoring on whether the row wrapped is not something CSS can ask, and
   the picker offers only its non-desktop_only rows below lg anyway.

   The alternative considered and not taken: margin-left: auto on the wrapper
   below lg, which pushes the button to the right end of whatever flex line it
   landed on, so end-0 against the BUTTON stays on screen and the panel keeps
   hanging under it. That trades the panel's position for the button's, moving
   the button away from Clear All at exactly the widths where the row is most
   crowded, which is the placement change this whole section exists to make. */
.column-picker-wrapper {
  position: relative;
}

@media (max-width: 991.98px) {
  .column-picker-wrapper {
    position: static;
  }
}

/* Bootstrap's --bs-link-color (#0d6efd) is 4.27:1 on the footer's .bg-light
   and only 4.50:1 on white, so the stock .btn-link fails WCAG AA 1.4.3 here
   and would pass on white by a rounding error. These are Bootstrap's own
   light-theme values one and two steps darker (--bs-link-hover-color and
   --bs-primary-text-emphasis), which measure 6.11:1 and 12.82:1 on the
   footer. Set as literals rather than var() so the guard in
   test_column_picker_contrast.py can resolve what actually paints: those two
   tokens are each declared twice, once per Bootstrap theme.
   Set on the button variables rather than `color` so .btn keeps reading
   them, which is how Bootstrap 5.3 expects a button to be recolored. */
.column-picker-reset {
  --bs-btn-color: #0a58ca;
  --bs-btn-hover-color: #052c65;
}

/* A Columns picker row is a flex div holding the label and the reorder arrows.
   The hover background belongs to the ROW, every row including the locked one,
   because every row has live arrows now. The pointer cursor belongs to the
   LABEL, which is the part that toggles: it is the form-check and it grows to
   fill everything left of the arrows, so there is no dead zone that looks
   clickable and toggles nothing. The label's ps-5 is what keeps the checkbox
   inside the panel, since .form-check pulls the input left by 1.5em and only
   the padding holds it in; see the comment above the loop in
   shipment_list_hx.html.

   Cross-axis centering is a rule here rather than the align-items-center
   utility on the element: the row's class attribute already contains "row",
   and adding that utility would false-match the legacy selector
   div[class*="row"][class*="align-items-center"] in TestBulkUpdateShipments,
   which counts shipment rows with it. Without centering the default stretch
   makes the arrow buttons full row height on the two-line rows. */
.column-picker-row {
  align-items: center;
}

.column-picker-row:hover {
  background-color: var(--bs-light);
}

.column-picker-label {
  cursor: pointer;
}

/* The mandatory column cannot be toggled, so its label offers no affordance.
   Its arrows still do: the column can move, it just cannot be hidden. */
.column-picker-label-locked {
  cursor: default;
}

/* The color literals are copied from .column-picker-reset, whose ratios the
   contrast guard measures; no guard covers THIS rule, since
   test_column_picker_contrast.py parametrizes over .column-picker-reset alone.
   Never add these arrows to that rule's selector list instead: the guard finds
   it with a regex demanding .column-picker-reset immediately before the brace,
   so a selector list kills three tests with a misleading "rule not found".

   The bar for an icon-only control is WCAG 1.4.11's 3:1 for non-text content,
   which these clear with margin on white and on the --bs-light hover surface.
   The dimmed aria-disabled state is deliberately below it, exempt under 1.4.11
   as an inactive component; aria-disabled rather than the disabled attribute so
   a keyboard user reaching a boundary does not lose focus to <body>.

   The 1.5rem box is WCAG 2.2 SC 2.5.8 target size (24 by 24 CSS px). The
   spacing exception does not apply to two adjacent buttons, so the size is not
   negotiable; inline-flex with centered alignment is what puts the 21px glyph
   line box in the middle of it rather than riding high. */
.column-picker-arrow {
  --bs-btn-color: #0a58ca;
  --bs-btn-hover-color: #052c65;
  min-width: 1.5rem;
  min-height: 1.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.column-picker-arrow[aria-disabled="true"] {
  opacity: 0.35;
  cursor: default;
}

/* Order Date is the one content-sized (col-lg-auto) column in the list, so its
   header cell and its row cells size themselves independently and drift apart
   by a few pixels, which pushes every column to their right out of alignment.
   Pinning one width fixes both, which is why the selector deliberately matches
   header and row cells alike: both carry data-column. Desktop only, because
   below lg the column is a plain col-* and already aligns. Applies to the
   default column set too, so it is a deliberate visible change. */
@media (min-width: 992px) {
  #shipment-table [data-column="order_date"] {
    flex: 0 0 4.5rem;
    width: 4.5rem;
  }
}


/* the table cell originally hidden, which expands  - needs important or shows padding*/
.expansion-cell {
  padding: 0 !important;
}

/* shipments list filters */
fieldset legend.form-label {
  font-size: 1em;
}

/* try to hide expansion-cell on landscape phone orientation */
.expansion-cell-content {
  padding: 0.75em;
}

.accordion div.row div.col {
  padding: 8px;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
}

/* remove the bottom border on the chevron upon expansion */
.accordion-button:not(.collapsed) {
  border: none;
  background-color: inherit;
  -webkit-box-shadow: none;
  box-shadow: none;
  color: black !important;
}

.accordion-button:not(.collapsed)::after {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
}

/* Visible checkbox and switch borders. Bootstrap's .form-check-input draws its
   border with --bs-border-color, #dee2e6 in the light theme, about 1.3:1
   against the white interior of an unchecked box (checked boxes fill and
   border in primary blue, set directly by Bootstrap). WCAG 1.4.11 holds a
   control's boundary to 3:1, so the unchecked box was close to invisible
   (noticed alongside #1692, 2026-08-26). gray-600 is 4.69:1 on white;
   test_notes_icon_contrast.py also measures it against every shipment row
   background. Declared once, on the base class, so checkboxes and switches
   everywhere get the same border.
   .form-control and .form-select draw from the same token and still paint
   the stock value; text inputs are a separate visual decision, left as is.
   The :focus state is Bootstrap's stock pair, set directly rather than via
   the token: a #86b7fe border (about 2.06:1 on white) plus a translucent
   primary ring (about 1.41:1 composited). Both sit under the 3:1 this rule
   buys at rest and both are left alone here; raising them is a separate
   focus-style decision. */
.form-check-input {
  --bs-border-color: #6c757d;
}

/* custom checkbox styling: https://moderncss.dev/pure-css-custom-checkbox-style/
   Slightly larger than Bootstrap's 1em: these are row-selection targets. */
input[type=checkbox].shipments-list {
  margin: auto !important;
  width: 1.15em;
  height: 1.15em;
}

/* shipment filter form */
/* radio legends are too large */
fieldset legend {
  font-size: 1rem;
}

/* toggle switches */
.visually-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* to center accordion button chevron char */
.accordion-button::after {
  margin-left: 40%;
  color: green !important;
}

/* needed for mobile - too much padding by default */
.accordion-button {
  padding: 0 !important;
}

/* password input width (changed with bootstrap 5) */
.passwordinput {
  max-width: 20em;
}


/* rows */
.striped-row:nth-child(odd) {
  background-color: #ffffff; /* White background for odd rows */
}

.striped-row:nth-child(even) {
  background-color: #f2f2f2; /* Light gray background for even rows */
}

/* tooltips */
.dashed-underline {
  border-bottom: 1px dashed #000;
  cursor: help;
}

/* progress/spinnners */
.spinner-border {
  margin-right: 5px;
}

.btn:disabled {
  cursor: not-allowed;
}

#toasts {
  /* Increase z-index to be higher than other elements */
  z-index: 12000;
  /* Position it above everything */
  position: fixed;
}


/* top of page loader
 https://old.reddit.com/r/htmx/comments/1blwnc4/tip_of_the_day_unobtrusive_global_loading/
 */
.loading-bar {
  opacity: 0;
  position: fixed;
  /* required to overlay the sticky headers */
  z-index: 20;
  top: 0;
  left: 0;
  width: 100%;
  height: 4px;
  background: linear-gradient(90deg, transparent,
    var(--bs-primary),
    transparent
  );
  /* transition for smoother opacity changes */
  transition: opacity 0.2s ease-out;
}

/* Show and animate when HTMX request is in progress */
.htmx-request.loading-bar {
  opacity: 1;
  animation: slide 0.8s ease-in-out infinite; /* Controls sliding animation */
  animation-delay: 0.5s; /* Delay before animation starts */
  /* Use transition-delay for the opacity change */
  transition-delay: 0.5s;
}

@keyframes slide {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(100%);
  }
}

/* alpinejs */
[x-cloak] { display: none !important; }

/*carrier dropdown selection*/

/* Rotate chevron when expanded */
[aria-expanded="true"] .fa-chevron-down {
  transform: rotate(180deg);
}

.fa-chevron-down {
  transition: transform 0.2s ease-in-out;
}

/* Store Filter Dropdown */
:root {
  --store-dropdown-width: 280px;
  --store-dropdown-z-index: 1050;
  --border-color-light: #dee2e6;
}

.store-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  width: var(--store-dropdown-width);
  z-index: var(--store-dropdown-z-index);
  overflow-y: auto;
  border: 1px solid var(--border-color-light);
  border-top: none;
  background-color: white;
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
  border-radius: 0 0 0.375rem 0.375rem;
}

.store-dropdown-button {
  width: var(--store-dropdown-width);
}

/* Notes feature */
.notes-editor {
  animation: fadeIn 0.15s ease-in-out;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.notes-editor textarea {
  resize: vertical;
  min-height: 80px;
}

.notes-editor textarea:focus {
  border-color: var(--bs-primary);
  box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.25);
}

/* The glyph's own color is set on the <i> (the state rules below carry an
   !important there), so the transition has to reach it: on the button alone
   it would animate a color nothing paints with, and hover would snap. */
.notes-icon-button,
.notes-icon-button .fa-pen-to-square {
  transition: color 0.2s ease;
}

.notes-icon-button {
  line-height: 1;
}

/* Empty ("Add note") state. gray-600 is the same resting value
   .report-status-button uses for a secondary glyph in this row, and it clears
   WCAG 1.4.11's 3:1 minimum on every background a shipment row paints: white,
   the .bg-light zebra stripe, .shipment-highlight (and its hover), and
   .report-status-row-active. test_notes_icon_contrast.py enforces that list;
   add a row background there before introducing one here.

   Both selectors carry the opacity, and the icon is a descendant of the
   button, so the two multiply: the previous 0.55 painted at 0.55 * 0.55 and
   the glyph landed around #e6e9eb, roughly 1.2:1 on a white row. Keep this at
   1 and express any de-emphasis as color, which the ratio check can see. */
.notes-icon-empty,
.notes-icon-empty .fa-pen-to-square {
  color: #6c757d !important;
  opacity: 1;
}

/* Filled state. Kept as the theme variable rather than a literal, and still
   covered: test_notes_icon_contrast.py resolves --bs-primary out of
   bootstrap.min.css and measures the resolved value. This is the thinnest
   margin of any measured state (4.50:1 on white, about 3.42:1 on a
   .report-status-row-active row), and it is the one an upgrade moves, so a
   Bootstrap bump that lightens the token fails that test rather than
   quietly crossing under 3:1. #1332 is the same failure from the other
   side: a color change to this glyph that nothing measured. */
.notes-icon-filled,
.notes-icon-filled .fa-pen-to-square {
  color: var(--bs-primary) !important;
  opacity: 1;
}

/* No opacity dip on hover. Fading was the old affordance, and it put both
   states under WCAG 1.4.11's 3:1 on the hovered .shipment-highlight row in
   use before 2026-08 (translucent blue, #c2dbfe composited): gray-600 fell
   to about 2.88:1 and primary blue to about 2.85:1. Hover is
   a color change now, which the guard can measure. Keep it that way: an
   opacity on any .notes-icon rule fades a state the ratio checks think they
   have measured, so test_notes_icon_contrast.py fails on one anywhere in
   this group, not just here.

   The declaration below is a fallback the per-state rules override on
   specificity rather than on position, so reordering this file cannot change
   which color a state gets. */
.notes-icon-button:hover,
.notes-icon-button:focus,
.notes-icon-button:active {
  text-decoration: none;
  /* Covers the open state, which matches neither state rule below: both are
     bound with `!open &&` in notes_icon.html, so an open button keeps only
     .text-reset, whose `color: inherit !important` also beats
     .btn-link:hover. Without this it has no hover feedback at all, the
     opacity dip having been the only thing it ever had. !important is what
     gets past .text-reset; the per-state rules below outrank this one on
     specificity, so it reaches only the state they do not cover. */
  color: #0a58ca !important;
}

/* Darken on hover, one step per state, so the two stay distinguishable while
   pressed. Both clear 3:1 on every row background test_notes_icon_contrast.py
   enumerates.

   The button-targeting selectors name .notes-icon-button as well as the state
   class, purely for specificity: that makes them (0,3,0) against the fallback
   rule's (0,2,0), so they win from anywhere in the file rather than by sitting
   after it. Both classes are always on the button together, one static and one
   bound, so the extra class costs nothing. The .fa-pen-to-square selectors
   need no such help; they target the icon, which the fallback never matches. */
.notes-icon-button.notes-icon-empty:hover,
.notes-icon-empty:hover .fa-pen-to-square,
.notes-icon-button.notes-icon-empty:focus,
.notes-icon-empty:focus .fa-pen-to-square {
  color: #495057 !important;
}

.notes-icon-button.notes-icon-filled:hover,
.notes-icon-filled:hover .fa-pen-to-square,
.notes-icon-button.notes-icon-filled:focus,
.notes-icon-filled:focus .fa-pen-to-square {
  color: #0a58ca !important;
}

/* Shipment highlight for new/changed shipments (applyHighlights in
   shipment_list_hx.html adds it after a list refresh; a row click clears it).

   !important: the alternating .bg-light stripe declares its background
   !important, so without the flag this rule lost on every striped row and
   only half the changed shipments were marked (#1692).

   Opaque literal, not rgba and not var(--bs-warning-bg-subtle): an opaque
   color yields one background whichever stripe sits beneath it, and a
   literal is what test_notes_icon_contrast.py reads to measure the notes
   glyph against this row. A var() would take the value out of that guard's
   reach.

   Yellow rather than blue: blue is the selection convention on a list that
   has checkboxes and bulk actions, and it lowered the contrast of the link
   text on the row. Yellow raises it (Bootstrap's link blue is about 4.06:1
   at rest and 3.65:1 on hover, against 3.67:1 and 3.18:1 on the old blue)
   without reaching the 4.5:1 that WCAG 1.4.3 asks of normal text, and the
   untouched .bg-light stripe falls short too, near 4.27:1. The rest
   and hover tints differ only by color, which is
   fine here: the highlight is transient decoration, not a state the user
   has to tell apart from another.

   Precedence with .report-status-row-active (inline <style> in the same
   template, also !important): the :not() below keeps the report highlight
   on top in every state. Without it the hover selector has four class-level
   components against that rule's three and would win once both are flagged,
   painting yellow over the reported row during the modal's post-close
   linger. At rest both selectors tie and the report rule wins by source
   order. The report highlight has no hover state of its own, so a row that
   carries both paints flat #cfe2ff hovered or not. */
.shipment-rows-container .row.shipment-highlight {
  background-color: #fff3cd !important;
  transition: background-color 0.3s ease;
}

.shipment-rows-container .row.shipment-highlight:not(.report-status-row-active):hover {
  background-color: #ffe69c !important;
}
