/* Site-wide admin tweaks (loaded on every page via UNFOLD["STYLES"]). */

/* Always reserve room for the vertical scrollbar gutter, so the layout width
 * stays constant whether a page is tall enough to scroll or not — switching
 * between long and short views no longer shifts content horizontally. */
html {
  scrollbar-gutter: stable;
}

/* --- Shared modal-editor chrome --------------------------------------------
 * Every hidden-input-backed modal editor (the analyte/flask/distribution grids,
 * the formula/choices editors) builds its overlay/panel/footer-button from a
 * hand-written HTML string in its own JS file. Each keeps its own ⟨prefix⟩-overlay
 * / ⟨prefix⟩-panel / ⟨prefix⟩-btn class (both for its JS querySelectors and for
 * widget-specific overrides — panel width/max-height, overlay padding-top), but
 * they share this base chrome so it can't drift between the five. */
.nb-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  background: rgba(0, 0, 0, 0.45);
}
.nb-modal-overlay[hidden] {
  display: none;
}
.nb-modal-panel {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px 20px 16px;
  border-radius: 12px;
  background: #ffffff;
  color: #18181b;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.35);
  font-family: ui-sans-serif, system-ui, sans-serif;
}
.dark .nb-modal-panel {
  background: #1f2023;
  color: #e5e5e7;
}
.nb-modal-btn {
  font-size: 13px;
  font-weight: 500;
  padding: 7px 16px;
  border-radius: 7px;
  border: 1px solid transparent;
  cursor: pointer;
}

/* --- Shared dimmed-pill tint tokens -----------------------------------------
 * The "dimmed blue" tint used for both the specimen Select2's Material tag
 * (specimen_select2.css) and the sieve Select2's ASTM/US badge (sieve_select2.css)
 * — same colour, independently maintained pill shapes. Keep the one tint in sync
 * here; each file's own `--tag` selector still owns its own shape (width/padding/
 * text-transform/etc). */
:root {
  --nb-tag-blue-bg: rgba(59, 130, 246, 0.14);
  --nb-tag-blue-fg: #2563eb;
}
.dark {
  --nb-tag-blue-bg: rgba(59, 130, 246, 0.2);
  --nb-tag-blue-fg: #93c5fd;
}

/* --- Form field labels ----------------------------------------------------
 * On the change form Unfold's label column (a fixed lg:w-56) sits flush against
 * the field, so a long label like "Required preparation detail" touches the
 * input. Nudge the label column away from the field on the desktop (row) layout
 * — the field column grows, so this shows up as a gap between the two. Scoped to
 * lg so the stacked mobile layout (label above field) is untouched. The label
 * column is the field line's direct child that wraps the <label> — except the
 * checkbox / badge-toggle row (fieldset_row_checkbox.html), whose label column is
 * nested one level deeper; match that grandchild too so its pill lines up with the
 * other fields' input columns. */
@media (min-width: 1024px) {
  .form-row .field-line > div:has(> label),
  .form-row .field-line > div > div:has(> label) {
    margin-right: 1rem;
  }
}

/* --- Message toasts -------------------------------------------------------
 * Positioning + animation are plain CSS on purpose: the show/hide state machine
 * (.toast--show, toggled by toasts.js) and the reduced-motion fallback are
 * CSS-shaped, and these unlayered rules can't be undercut by utility-layer
 * churn. (The project tw.css build could compile arbitrary utilities now —
 * migrate to template classes only with a live re-verify.) Colour/rounded/
 * shadow tokens stay as utility classes in the template because Unfold's own
 * messages partials already ship them.
 * See templates/unfold/helpers/messages.html + messages/_toast.html and
 * notebook/static/notebook/toasts.js. */
.toast-stack {
  position: fixed;
  top: 5rem;          /* clears Unfold's 4rem (h-16) header band */
  right: 1rem;
  z-index: 9999;      /* above the header (z-40) and dropdown chrome (z-50) */
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  width: 20rem;
  max-width: calc(100vw - 2rem);
  pointer-events: none;
}

.toast {
  pointer-events: auto;
  opacity: 0;
  transform: translateX(1rem);
  transition: opacity 0.3s ease-out, transform 0.3s ease-out;
}

.toast.toast--show {
  opacity: 1;
  transform: translateX(0);
}

@media (prefers-reduced-motion: reduce) {
  .toast {
    transition: none;
  }
}

/* --- MethodField inline: center the Required toggle -----------------------
 * Unfold's inline cell is <td class="field-<name> ... text-left ...">; on
 * desktop it's a `table-cell`, on mobile a `flex` row. The switch is a raw
 * checkbox Unfold styles inline, so centering = overriding the cell alignment.
 * `!important` (and the td prefix for specificity) is needed to beat Unfold's
 * `text-left` utility — a tw.css utility of ours would lose the same fight, so
 * this stays an unlayered site.css counter.
 * MethodField is the only inline with a `required` field, so this is scoped. */
td.field-required {
  justify-content: center;         /* mobile: the cell is a flex row */
  text-align: center !important;   /* desktop: the cell is a table-cell */
}

/* --- Missing-preparation warning glyph -------------------------------------
 * The ⊙! error glyph next to a specimen (see AnalysisAdmin._prep_warning) whose
 * material is missing a preparation the method requires. The hover bubble is the
 * shared .nb-tip layer's warning skin (`nb-tip nb-tip--warn` on the glyph →
 * .nb-tip-bubble--warn below); this block only styles the glyph itself. */
.prep-warn {
  display: inline-flex;
  align-items: center;
  vertical-align: middle;      /* sit the glyph on the text's midline, not its baseline */
  transform: translateY(-2px);  /* nudge up: `middle` aligns to the x-height midline,
                                   but these cap-height numerals centre a bit higher */
  margin-left: 6px;
  color: #c2410e;              /* orange-700 */
}
.dark .prep-warn {
  color: #fb923c;              /* orange-400 */
}

/* --- Info tooltip (.nb-tip) -------------------------------------------------
 * Applied by notebook/tooltips.js to help glyphs that Unfold renders with a
 * native `title` (e.g. inline column-header "?" icons). Unlike .prep-warn, the
 * bubble is NOT an ::after on the glyph — it's a single shared element appended
 * to <body> and positioned with position:fixed from JS (see tooltips.js).
 *
 * Why not an ::after: the inline formset headers live inside Unfold's
 * `.formset-wrapper` (overflow-x-auto + SimpleBar, see
 * edit_inline/tabular_table.html). A bubble positioned relative to a glyph in
 * there is trapped by that scroll box — it gets clipped at every edge AND, even
 * while hidden, its laid-out size enlarges the wrapper's scroll area, so phantom
 * horizontal/vertical scrollbars appear at rest. Rendering the bubble in the
 * body-level fixed layer sidesteps the scroll container entirely: no clipping,
 * no phantom scrollbars, and JS clamps it inside the viewport on all sides.
 * Same frosted "info" blue palette; it does NOT recolour the glyph (help icons
 * stay neutral). Skins: tooltips.js mirrors a trigger's `nb-tip--<mod>` class
 * onto the bubble as `nb-tip-bubble--<mod>` while shown — see the --warn and
 * --swatch variants after the base rules. */
.nb-tip {
  cursor: help;
}
/* Interactive triggers keep their action cursor — `help` is for info-only glyphs. */
button.nb-tip {
  cursor: pointer;
}
.nb-tip-bubble {
  position: fixed;
  top: 0;
  left: 0;                                /* JS overwrites both each show */
  z-index: 10000;                         /* above header (z-40), dropdowns (z-50) AND
                                             the modal overlays (z-9999, appended to body
                                             after this bubble) so in-modal tips show */
  max-width: 20rem;
  width: max-content;
  padding: 0.5rem 0.75rem;
  border-radius: 0.375rem;
  box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  font-size: 12px;
  font-weight: 500;
  line-height: 1.35;
  white-space: normal;
  text-align: left;
  /* The help glyph is an ICON font (span.material-symbols-outlined); force the
   * real UI font so tip text never renders in the icon font — ligature words like
   * "repeat" would otherwise turn into glyphs and the rest look like odd caps. */
  font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif);
  font-feature-settings: normal;
  font-variant: normal;
  text-transform: none;
  letter-spacing: normal;
  background: rgb(219 234 254 / 0.6);    /* blue-100 @60% */
  color: #1e40af;                        /* blue-800 */
  border: 1px solid rgb(59 130 246 / 0.35);
  backdrop-filter: blur(7px) saturate(1.3);
  -webkit-backdrop-filter: blur(7px) saturate(1.3);
  opacity: 0;
  pointer-events: none;                  /* never intercepts the pointer */
  transition: opacity 0.1s ease-out;
}
.nb-tip-bubble--show {
  opacity: 1;
}
.dark .nb-tip-bubble {
  background: rgb(30 58 138 / 0.35);   /* blue-900 @35% */
  color: #dbeafe;                      /* blue-100 */
  border-color: rgb(96 165 250 / 0.35);
}
/* Warning skin (`nb-tip--warn` triggers: missing-preparation ⚠, stale ƒx formula).
 * Colours mirror the "warning" toast — the old .prep-warn bubble palette. */
.nb-tip-bubble--warn {
  background: rgb(255 237 213 / 0.6);    /* orange-100 @60% */
  color: #9a3412;                        /* orange-800 — stronger contrast */
  border-color: rgb(249 115 22 / 0.35);
}
.dark .nb-tip-bubble--warn {
  background: rgb(124 45 18 / 0.35);   /* orange-900 @35% — clearly see-through */
  color: #ffedd5;                      /* orange-100 — high contrast on the dark tint */
  border-color: rgb(251 146 60 / 0.35);
}
/* Accent-swatch skin (`nb-tip--swatch`): the bubble takes the hovered swatch's
 * own colour (tooltips.js copies the trigger's `--swatch` into `--nb-swatch`)
 * in the same frosted-translucent treatment, with the white ✓-mark text styling
 * from the swatch grid so the colour name stays legible on any hue. */
.nb-tip-bubble--swatch {
  background: color-mix(in srgb, var(--nb-swatch, #7b62ff) 70%, transparent);
  color: #fff;
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.35);
  border-color: color-mix(in srgb, var(--nb-swatch, #7b62ff) 50%, transparent);
}
.dark .nb-tip-bubble--swatch {
  background: color-mix(in srgb, var(--nb-swatch, #7b62ff) 60%, transparent);
  color: #fff;
  border-color: color-mix(in srgb, var(--nb-swatch, #7b62ff) 50%, transparent);
}
/* Three-part bubble body (see tooltips.js render): a "(type) Name" heading, the
 * description, then a muted monospace list of example values. */
.nb-tip-head {
  font-weight: 600;
  margin-bottom: 0.2rem;
}
.nb-tip-type {
  font-weight: 400;
  font-style: italic;
  opacity: 0.7;                        /* the type reads as a quiet prefix */
}
.nb-tip-desc {
  font-weight: 400;
}
.nb-tip-eg {
  margin-top: 0.35rem;
  /* Example values are code-ish (keys, units, formulas) — monospace + dimmer so
   * they read as samples, set apart from the prose above. */
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9em;
  opacity: 0.8;
}
@media (prefers-reduced-motion: reduce) {
  .nb-tip-bubble {
    transition: none;
  }
}

/* --- Computed value chips (.nb-calc) ---------------------------------------
 * The read-only formula outputs in the results worksheet (e.g. water content,
 * see MeasurementInline._make_calc). A subtle rounded pill, prefixed with a ƒ
 * "formula" mark, sets these *derived* values apart from the editable input
 * cells beside them. Tinted against the --color-secondary-* ramp — a SEPARATE
 * CharmTone accent (Site settings → secondary_color, served by the
 * admin/secondary.css endpoint) so it's independent of the primary accent.
 * Those vars hold full rgb(...) colours (the endpoint mirrors Unfold's
 * convert_color), so they're used bare — `var()` directly, `color-mix` for
 * translucent tints — exactly like flow_events.css / font_picker.css use the
 * primary vars. Plain CSS, not utility classes: one .nb-calc hook keeps the
 * pill's markup (built in Python, _make_calc) free of a long utility string. */
.nb-calc {
  display: inline-flex;
  align-items: baseline;
  padding: 0.125rem 0.5rem;
  border-radius: 9999px;                 /* full pill */
  font-variant-numeric: tabular-nums;    /* digits line up column-to-column */
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
  color: var(--color-secondary-700);
  background: color-mix(in srgb, var(--color-secondary-500) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--color-secondary-500) 30%, transparent);
}
.dark .nb-calc {
  color: var(--color-secondary-200);
  background: color-mix(in srgb, var(--color-secondary-500) 22%, transparent);
  border-color: color-mix(in srgb, var(--color-secondary-500) 38%, transparent);
}
/* The ƒ (U+0192, "latin small letter f with hook") — the conventional formula /
 * function glyph. A pseudo-element so it stays purely presentational; the muted
 * weight keeps it a quiet marker rather than competing with the number. */
.nb-calc::before {
  content: "\0192";
  margin-right: 0.3rem;
  font-weight: 700;
  opacity: 0.6;
}
/* Unit rides lighter/smaller than the number so the value reads first. */
.nb-calc-unit {
  margin-left: 0.25rem;
  font-weight: 400;
  font-size: 0.9em;
  opacity: 0.7;
}
/* Not-yet-computed rows: a muted em dash, no chrome. */
.nb-calc-empty {
  color: #9ca3af;                        /* gray-400 */
}
.dark .nb-calc-empty {
  color: #6b7280;                        /* gray-500 */
}

/* ── Column diagram (server-rendered inline SVG) ─────────────────────────────
   Rendered by notebook/diagrams/column.py into
   notebook/templates/notebook/widgets/column_diagram.html. Strokes and text use
   `currentColor`, so the drawing follows the admin's light/dark text token; only
   the material fills carry fixed hues (they must read on both themes). */
.nb-coldiag {
  display: flex;
  justify-content: center;
  padding: 4px 0;
}
.nb-coldiag-svg {
  width: 100%;
  max-width: 320px;
  height: auto;
  font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif);
}
.nb-coldiag-empty {
  color: #9ca3af;
  font-size: 0.8rem;
}
.nb-coldiag-body-outline {
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
  opacity: 0.7;
}

/* Band textures are SVG <pattern>s defined in the template (fixed legend hues). */

/* Centred name chip: white box, thin outline, dark label — reads over any texture. */
.nb-coldiag-chip {
  fill: #ffffff;
  stroke: #9aa1ac;
  stroke-width: 0.75;
}
.nb-coldiag-chip-label {
  fill: #1f2430;
  font-size: 10px;
}

/* flow ↓ labels, above and below the body; the arrow glyph is enlarged. */
.nb-coldiag-flow { fill: currentColor; font-size: 10px; opacity: 0.7; }
.nb-coldiag-flow-arrow { font-size: 13px; }

/* Per-band thickness, outside on the right. Sized/contrasted for legibility. */
.nb-coldiag-thick { fill: currentColor; font-size: 10px; opacity: 0.85; }

/* Inlet / outlet ports. Fixed accent hue (not currentColor): the band fills are
   fixed light tones, so a theme-following light port vanishes over them in dark
   mode. The accent contrasts every band in both themes; the dot gets a light
   halo ring so it also lifts off the darker page gutter. */
.nb-coldiag-port { stroke: var(--color-primary-600); stroke-width: 2; }
.nb-coldiag-porthead { fill: var(--color-primary-600); }
.nb-coldiag-port-dot {
  fill: var(--color-primary-600);
  stroke: #ffffff;
  stroke-width: 0.9;
}
.dark .nb-coldiag-port { stroke: var(--color-primary-400); }
.dark .nb-coldiag-porthead { fill: var(--color-primary-400); }
.dark .nb-coldiag-port-dot { fill: var(--color-primary-400); }
.nb-coldiag-port-label {
  fill: currentColor;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
}

.nb-coldiag-meta { fill: currentColor; font-size: 9px; opacity: 0.6; }

/* Distribution (Array) result chart — SVG line/bar renderers (admin.ResultAdmin). */
.nb-dist-chart {
  max-width: 100%;
  height: auto;
  background: var(--color-surface, #fff);
  border: 1px solid var(--color-border, #d0d5dd);
  border-radius: 6px;
  padding: 4px;
}
/* Trace thumbnail (DataFileInline) — reuses the line chart for a spectrum/pattern.
   A trace is hundreds of points where a distribution is a handful, so the per-point
   dots that mark a sieve stack's readings become noise: hide them and thin the line
   so the peaks stay legible at thumbnail size. */
.nb-trace {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 15rem;
}
.nb-trace .nb-dist-chart circle {
  display: none;
}
.nb-trace .nb-dist-line {
  stroke-width: 1;
}
.nb-trace-meta {
  font-size: 11px;
  color: var(--color-text-muted, #667085);
}

.nb-dist-axis {
  stroke: var(--color-border, #d0d5dd);
  stroke-width: 1;
}
.nb-dist-line {
  fill: none;
  stroke: var(--color-primary, #1570ef);
  stroke-width: 2;
  stroke-linejoin: round;
}
.nb-dist-chart circle {
  fill: var(--color-primary, #1570ef);
}
.nb-dist-axis-label {
  fill: var(--color-text-muted, #667085);
  font-size: 11px;
}
.nb-dist-bar {
  fill: var(--color-primary, #1570ef);
  opacity: 0.85;
}
.nb-dist-bar-label {
  fill: var(--color-text, #101828);
  font-size: 12px;
}
.nb-dist-bar-val {
  fill: var(--color-text-muted, #667085);
  font-size: 11px;
}

/* --- PairedQuantityWidget row (paired_quantity_row.html) -------------------
   Unfold's compiled styles force `.select2.select2-container { width: 100%
   !important }`, so in a one-row flex composite the unit picker's container
   claims all the slack before any "flex" number slot can grow. Scoped to this
   widget's row only: let the picker share the slack as a flex item instead. */
.nb-paired-row .select2.select2-container {
  width: auto !important;
  flex: 1 1 0;
  min-width: 8rem;
}

/* --- Select2 dropdown highlight ---------------------------------------------
   Unfold paints the keyboard/hover-highlighted option plain grey
   (base-100/base-800 !important), which clashes with the accent-coloured
   "selected" text. Tint the highlight from the live accent instead so the
   dropdown reads as one system whatever accent is picked. Unfold's own rule is
   `.select2-container .select2-results__option--highlighted[aria-selected]`
   (0,3,0) !important in a later-loading sheet, so two ancestor classes here
   (0,4,0) are required to outrank it — one is not enough. */
.select2-results .select2-results__options .select2-results__option--highlighted[aria-selected] {
  background-color: color-mix(in oklab, var(--color-primary-500) 12%, transparent) !important;
}
.select2-results .select2-results__options .select2-results__option--highlighted[aria-selected]:where(.dark, .dark *) {
  background-color: color-mix(in oklab, var(--color-primary-500) 24%, transparent) !important;
}
