/* ============================================================
   tacklebox-extras.css
   The few things Tacklebox needs that Tabler 1.6 doesn't have.
   Load after tacklebox-theme.css.

   Every class here starts with "tbx-". Anything without that
   prefix in a template is Tabler's (or Bootstrap's), and its
   markup is documented at docs.tabler.io.

   Before adding to this file, check Tabler again: a component
   it already ships is always preferred over one of ours.
   ============================================================ */

/* ---------- 1. The Ember button ---------------------------------------------
   The one creative action on a page ("Generate license"). At most one per
   page. Built from Tabler's own button variables, so it behaves like any
   .btn: sizes, icons, loading, disabled.                                   */
.tbx-btn-ember {
  --tblr-btn-color: var(--tbx-on-accent);
  --tblr-btn-bg: var(--tbx-heat);
  --tblr-btn-border-color: transparent;
  --tblr-btn-hover-color: var(--tbx-on-accent);
  --tblr-btn-hover-bg: var(--tbx-heat-deep);
  --tblr-btn-hover-border-color: transparent;
  --tblr-btn-active-color: var(--tbx-on-accent);
  --tblr-btn-active-bg: var(--tbx-heat-deep);
  --tblr-btn-active-border-color: transparent;
  --tblr-btn-disabled-color: var(--tbx-on-accent);
  --tblr-btn-disabled-bg: var(--tbx-heat);
  --tblr-btn-disabled-border-color: transparent;
}

/* ---------- 2. Sidebar details ------------------------------------------------ */
/* The area name at the top of a fly-out menu. Hidden while the sidebar is
   open, because the area's own row already says it. */
.tbx-flyout-title { display: none; }
html[data-bs-sidebar^=folded] .navbar-vertical .tbx-flyout-title {
  display: block;
  padding: var(--tbx-3) 10px var(--tbx-2);
  font-size: var(--tbx-fs-micro);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--tbx-muted);
}
/* The theme choice in the user menu: a check marks the current one
   (aria-pressed, kept in step by tacklebox.js). */
[data-tbx-theme-choice][aria-pressed=false] .tbx-theme-check { visibility: hidden; }
/* The collapse button swaps its icon with the state. */
[data-tbx-sidebar-toggle] .tbx-when-open { display: inline-flex; }
[data-tbx-sidebar-toggle] .tbx-when-folded { display: none; }
html[data-bs-sidebar^=folded] [data-tbx-sidebar-toggle] .tbx-when-folded { display: inline-flex; }
html[data-bs-sidebar^=folded] [data-tbx-sidebar-toggle] .tbx-when-open { display: none; }

/* ---------- 2b. Toolbar --------------------------------------------------------
   toolbar() (_table.jinja2.html) is a flex row with gap-2; its items are the
   controls themselves. A search field in it takes this class (agreed
   2026-10-05): it starts at 16rem and grows to 22rem, so it fills its line on
   a phone and is about a third of the bar on a wide screen, with no breakpoints. */
.tbx-toolbar-search { flex: 1 1 16rem; max-width: 22rem; }
/* A select or field placed straight in the bar takes its own width: Bootstrap gives them
   width: 100%, which in a flex row would put each on a line of its own. */
.card-header[data-tbx-toolbar] > * > :is(.form-select, .form-control) { width: auto; }

/* ---------- 3. Bulk action bar ------------------------------------------------
   Replaces the table's toolbar while rows are selected. An always-dark slab,
   so it reads as a mode. tacklebox.js shows and hides it.                  */
.tbx-bulkbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--tbx-4);
  align-items: center;
  min-height: 46px;
  padding: var(--tbx-4) var(--tbx-6);
  color: var(--tbx-on-inverse);
  background: var(--tbx-inverse);
  border-radius: calc(var(--tbx-r) - 1px) calc(var(--tbx-r) - 1px) 0 0;
}
.tbx-bulkbar[hidden] { display: none; }
.tbx-bulkbar-count { margin-inline-end: var(--tbx-4); font-size: var(--tbx-fs-sm); font-weight: 600; }
.tbx-bulkbar-end { margin-inline-start: auto; }
@media (max-width: 767.98px) {
  .tbx-bulkbar {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 1030;
    padding-bottom: calc(var(--tbx-4) + env(safe-area-inset-bottom));
    border-radius: 0;
    box-shadow: var(--tbx-shadow-pop);
  }
}

/* ---------- 4. Tables ---------------------------------------------------------- */
/* Group header rows in a grouped table. Sticky under the column headers. */
.tbx-group-row > th,
.tbx-group-row > td {
  position: sticky;
  top: 0;
  z-index: 1;
  height: 28px;
  font-size: var(--tbx-fs-tiny);
  font-weight: 600;
  color: var(--tbx-ink-2);
  background: var(--tbx-sunken);
}
.tbx-group-count { margin-inline-start: var(--tbx-3); font-weight: 400; color: var(--tbx-muted); }

/* Markdown in a table cell (a note on a reference list's entry): the cell's own
   size and line height, paragraphs close together. Tabler's .markdown is reading
   prose: h3-sized, loose lines. */
.markdown.tbx-markdown-cell { font-size: inherit; line-height: inherit; }
.markdown.tbx-markdown-cell > * { margin-bottom: var(--tbx-2); }
.markdown.tbx-markdown-cell > :last-child { margin-bottom: 0; }

/* A row a link points at (…#unit-4) shows as a selected row does, and stops a
   little below the top edge rather than flush against it. */
.table tr:target > * { --tblr-table-bg-state: var(--tblr-table-active-bg); }
.table tr:target { scroll-margin-top: var(--tbx-9); }

/* Keep the identifying column in view while a wide table scrolls sideways. */
.tbx-table-pin > :not(caption) > tr > :first-child {
  position: sticky;
  inset-inline-start: 0;
  z-index: 1;
  background: var(--tbx-raise);
  box-shadow: inset -1px 0 0 var(--tbx-line);
}
.tbx-table-pin > thead > tr > :first-child { background: var(--tbx-sunken); }

/* Column resize handle, added by tacklebox.js to tables with
   data-tbx-resizable. */
.tbx-th-resize {
  position: absolute;
  inset-block: 0;
  inset-inline-end: -3px;
  width: 7px;
  cursor: col-resize;
}
[data-tbx-resizable] th { position: relative; }
.tbx-th-resize:hover,
.is-resizing .tbx-th-resize { background: color-mix(in oklab, var(--tbx-link) 35%, transparent); }
.is-resizing { cursor: col-resize; user-select: none; }

/* ---------- 5. Reordering -------------------------------------------------------
   A list the user puts in order by dragging the handle, or with the
   keyboard. Works on a Tabler .list-group or on table rows.                */
.tbx-drag-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  color: var(--tbx-faint);
  cursor: grab;
  touch-action: none;
  background: none;
  border: 0;
  border-radius: var(--tbx-r-sm);
}
.tbx-drag-handle:hover { color: var(--tbx-ink-2); background: var(--tbx-hover); }
.tbx-drag-handle:focus-visible { outline: 2px solid var(--tbx-link); outline-offset: 1px; }
.tbx-reorder-pos { min-width: 1.5em; color: var(--tbx-muted); text-align: end; }
[data-tbx-reorder-item].is-dragging,
[data-tbx-reorder-item].is-grabbed {
  position: relative;
  z-index: 2;
  background: var(--tbx-selected);
  outline: 2px solid var(--tbx-link);
  outline-offset: -2px;
}
[data-tbx-reorder-item].is-dragging { box-shadow: var(--tbx-shadow-2); }
[data-tbx-reorder-item].is-dragging .tbx-drag-handle { cursor: grabbing; }
[data-tbx-reorder-item].is-settling { transition: transform var(--tbx-speed) var(--tbx-ease); }
.is-reordering { user-select: none; }

/* ---------- 5b. Filter rail ------------------------------------------------------
   The filters of a list page, in a column left of the table's card
   (components/tbx/_filters.jinja2.html). As wide as the sidebar from 768px up,
   full width above the table on phones. The table's column may shrink past
   its content, so a wide table scrolls inside its card instead of pushing out. */
@media (min-width: 768px) {
  .tbx-filter-rail { flex: 0 0 auto; width: 240px; }
}
.tbx-filter-rail ~ .col { min-width: 0; }
.tbx-filter-rail[hidden] { display: none; }
/* The From and To labels of a date range share one width, so the two fields
   line up. It is the width of the text alone (content-box): the label's
   padding and border are added to it. In em, so it follows the label's font
   size at any zoom or text size setting. "From" in Inter measures 2.38em to
   2.52em between 10.5px and 24px (glyphs snap to whole pixels); 2.6em is the
   narrowest round value that holds it at all of them. */
.tbx-range-label { box-sizing: content-box; width: 2.6em; }

/* ---------- 5c. A list redrawing in place ---------------------------------------
   While htmx fetches the list's region (a filter, a sort, a page), it fades a
   little; only past 150ms, so a quick answer shows no flicker. */
.tbx-list .card { transition: opacity var(--tbx-speed) var(--tbx-ease); }
.tbx-list.htmx-request .card { opacity: 0.6; transition-delay: var(--tbx-speed); }
@media (prefers-reduced-motion: reduce) { .tbx-list .card { transition: none; } }

/* ---------- 5d. Site map ---------------------------------------------------------
   Maps (static/js/maps.js, MapLibre GL JS): the Sites list's map, a site's map page and its thumbnail. The map fills
   its card; its popups are themed like our menus, not the library's white box. */
.tbx-site-map { width: 100%; aspect-ratio: 16 / 9; min-height: 25rem; border-radius: 0 0 var(--tbx-r) var(--tbx-r); }
.tbx-site-map-tall { aspect-ratio: 4 / 3; min-height: 30rem; }
/* The Sites list's map, above the table: shorter, so the rows stay in view. */
.tbx-site-map-list { aspect-ratio: auto; height: 22rem; min-height: 0; border-radius: 0; }
/* The site page's thumbnail: a still map under the card header, the width of the card. */
.tbx-site-map-thumb { aspect-ratio: 4 / 3; min-height: 0; border-radius: 0; border-bottom: 1px solid var(--tbx-line); cursor: pointer; }
.tbx-site-map-thumb .maplibregl-canvas { cursor: pointer; }
.tbx-site-map .maplibregl-popup-content {
  padding: var(--tbx-4) var(--tbx-5);
  font: inherit;
  color: var(--tbx-ink);
  background: var(--tbx-raise);
  border: 1px solid var(--tbx-faint);         /* ~3:1 on the popup in both themes; --tbx-line is too faint over a map */
  border-radius: var(--tbx-r);
  box-shadow: var(--tbx-shadow-2);
}
/* The popup's pointer: MapLibre draws it as a border triangle. It is drawn here in the outline's
   color, and a triangle in the popup's color sits inside it (::after), 1px in and 1px over the
   box's edge, so the fill and the outline run on unbroken from the box into the pointer. */
.tbx-site-map .maplibregl-popup-tip { position: relative; z-index: 1; border-color: transparent; }
.tbx-site-map .maplibregl-popup-tip::after { content: ""; position: absolute; width: 0; height: 0; border: 9px solid transparent; }
.tbx-site-map [class*="maplibregl-popup-anchor-bottom"] .maplibregl-popup-tip { border-top-color: var(--tbx-faint); }
.tbx-site-map [class*="maplibregl-popup-anchor-bottom"] .maplibregl-popup-tip::after { left: -9px; top: -11px; border-bottom-width: 0; border-top-color: var(--tbx-raise); }
.tbx-site-map [class*="maplibregl-popup-anchor-top"] .maplibregl-popup-tip { border-bottom-color: var(--tbx-faint); }
.tbx-site-map [class*="maplibregl-popup-anchor-top"] .maplibregl-popup-tip::after { left: -9px; top: 2px; border-top-width: 0; border-bottom-color: var(--tbx-raise); }
.tbx-site-map .maplibregl-popup-anchor-left .maplibregl-popup-tip { border-right-color: var(--tbx-faint); }
.tbx-site-map .maplibregl-popup-anchor-left .maplibregl-popup-tip::after { top: -9px; left: 2px; border-left-width: 0; border-right-color: var(--tbx-raise); }
.tbx-site-map .maplibregl-popup-anchor-right .maplibregl-popup-tip { border-left-color: var(--tbx-faint); }
.tbx-site-map .maplibregl-popup-anchor-right .maplibregl-popup-tip::after { top: -9px; left: -11px; border-right-width: 0; border-left-color: var(--tbx-raise); }
/* A site with weather is marked by its temperature, tinted by its band: freezing or cold, now or forecast. */
.tbx-map-temp {
  padding: var(--tbx-1) var(--tbx-3);
  font-size: var(--tbx-fs-sm);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--tbx-ink);
  background: var(--tbx-raise);
  border: 1px solid var(--tbx-line-strong);
  border-radius: var(--tbx-r);
  box-shadow: var(--tbx-shadow-2);
  cursor: pointer;
  white-space: nowrap;
}
.tbx-map-temp.is-freezing { background: var(--tbx-danger-wash); border-color: var(--tbx-danger); }
.tbx-map-temp.is-cold { background: var(--tbx-warn-wash); border-color: var(--tbx-warn); }

/* ---------- 5e. Comments ---------------------------------------------------------
   A reply indents under what it answers, a step per level up to four, with
   a line down its left. Reply and Delete open in place (<details>).        */
.tbx-reply { border-left: 1px solid var(--tbx-line); padding-left: var(--tbx-5); }
.tbx-reply-1 { margin-left: var(--tbx-6); }
.tbx-reply-2 { margin-left: calc(var(--tbx-6) * 2); }
.tbx-reply-3 { margin-left: calc(var(--tbx-6) * 3); }
.tbx-reply-4 { margin-left: calc(var(--tbx-6) * 4); }
@media (max-width: 575.98px) { .tbx-reply-2, .tbx-reply-3, .tbx-reply-4 { margin-left: var(--tbx-6); } }
.tbx-inline-action > summary { cursor: pointer; list-style: none; }
.tbx-inline-action > summary::-webkit-details-marker { display: none; }
.tbx-inline-action[open] { flex-basis: 100%; }

/* ---------- 6. Forms ------------------------------------------------------------- */
/* On phones the action row stays pinned to the bottom, so Save is always
   reachable. Put it on the .card-footer that holds the form's buttons. */
@media (max-width: 767.98px) {
  .tbx-form-actions {
    position: sticky;
    bottom: 0;
    z-index: 3;
    padding-bottom: calc(var(--tbx-4) + env(safe-area-inset-bottom));
    background: var(--tbx-raise);
    box-shadow: 0 -1px 0 var(--tbx-line);
  }
}

/* ---------- 7. Toasts ------------------------------------------------------------
   Tabler's toast, themed as the dark slab in tacklebox-theme.css. A toast
   carries data-bs-theme="dark", so tokens inside it resolve to their dark
   values. These rules place the pieces Tacklebox.toast() adds.             */
.tbx-toast-body { display: flex; gap: var(--tbx-4); align-items: center; }
.tbx-toast-icon { display: inline-flex; flex: none; }
.tbx-toast-ok .tbx-toast-icon   { color: var(--tbx-ok); }
.tbx-toast-warn .tbx-toast-icon { color: var(--tbx-warn); }
.tbx-toast-message { flex: 1; }
.tbx-toast-action {
  flex: none;
  padding: 0;
  font-weight: 600;
  color: var(--tbx-heat-on-inverse);
  background: none;
  border: 0;
}
.tbx-toast-action:hover { text-decoration: underline; }
.toast .btn-close { --tblr-btn-close-color: var(--tbx-on-inverse); }

/* ---------- 8. Reduced motion ------------------------------------------------------
   Tabler already switches its own transitions off. This covers ours.       */
@media (prefers-reduced-motion: reduce) {
  [data-tbx-reorder-item].is-settling { transition: none; }
}

/* ---------- 9. Avatar picker -------------------------------------------------------
   The photo swatch: the person's own picture, cropped to the swatch.        */
.tbx-swatch-photo { background-size: cover; background-position: center; }

/* ---------- 10. Signed-out pages --------------------------------------------------
   Sign in, request access, password reset: the mark and the name above the
   card, as the sidebar sets them (the name lowercase, in the title cut).    */
.tbx-signed-out-brand { font-size: var(--tbx-fs-lg); font-weight: 600; text-transform: lowercase; font-variation-settings: var(--tbx-font-title); letter-spacing: var(--tbx-track-title); }
.tbx-signed-out-brand svg { height: 1.5rem; width: auto; }
