/*
 * Utilities Bootstrap does not ship, but that our markup already assumes.
 *
 * `min-w-0` is the whole file's reason to exist. Bootstrap 5.3.3 ships
 * .min-vh-100 and .min-vw-100 and nothing else in that family — grep the dist
 * for `min-w-0` and you get no hit. Templates across the Go tier were
 * nonetheless written with `class="flex-grow-1 min-w-0"`, because that class
 * DOES exist in Tailwind and reads like it should exist here. Nine such
 * elements silently did nothing.
 *
 * What they were reaching for is load-bearing. A flex item's automatic minimum
 * is `auto`, meaning it refuses to shrink below its content's intrinsic width,
 * so `text-truncate` on a child has nothing to truncate against and the item
 * shoves its container sideways instead. Measured on
 * /people/30652/profile with cards constrained to 360px: the Files card
 * overflowed its own card by 188px and its title did not truncate. The
 * neighbouring cards escaped only because their content happened to be short —
 * they were relying on the same no-op.
 *
 * !important because every Bootstrap utility carries it (see .min-vw-100
 * above), and because these classes sit on elements the legacy Chronos
 * stylesheets also target — .main-content is styled in modern-sidebar.css and
 * chronos-ui-overrides.css, the latter at a higher specificity than a bare
 * class. A utility that loses to the sheets it is meant to correct is the same
 * lie in a new place.
 *
 * Loaded via StaticStyles() in static.go, which the shell emits LAST.
 */

.min-w-0 { min-width: 0 !important; }

/*
 * A Go page rendered as a dialog fragment.
 *
 * The trail is emitted by the PAGE, inside the layout's children, so a fragment
 * render cannot skip it the way it skips the sidebar and the navbar — by the
 * time Layout knows it is a fragment, the crumbs are already in the child
 * component. Hidden here instead.
 *
 * Worth hiding rather than tolerating: a modal is not a place the operator
 * navigated to, so "Home / People / G4S-000044 / Assign Personal Device" above
 * the form describes a journey that did not happen, and its Home and People
 * links would take them out of the page the dialog is sitting on.
 */
.chronos-dialog-page > .modern-breadcrumb { display: none; }

/*
 * A fragment's own card is redundant inside a modal — the modal IS the card.
 * Flattened rather than restyled so the form inside keeps its spacing.
 *
 * EVERY DECLARATION HERE NEEDS !important, and the border is why this comment
 * exists: chronos-ui-overrides.css (PHP's, shared) carries
 * `.card { border: 1px solid var(--chronos-glass-border) !important }`, so a
 * plain `border: 0` loses to it no matter how specific this selector is. The
 * box-shadow line already had !important and worked, which is exactly why the
 * half that did not was never noticed — every Go dialog has been drawing a
 * bordered, rounded panel inside a modal that is already a panel.
 */
.chronos-dialog-page > .card {
    border: 0 !important;
    border-radius: 0 !important;
    background: transparent !important;
    box-shadow: none !important;
}

.chronos-dialog-page > .card > .card-body { padding: 0; }

/*
 * The composer's thread scroller.
 *
 * NOT IN person-communication.css, which is PHP's file and which this tier
 * otherwise reuses wholesale. PHP does not put these two properties there
 * either — PersonCommunicationWidget writes them as an INLINE style on the
 * thread div (`max-height: 480px; overflow-y: auto`), so the shared stylesheet
 * has never carried them and adding them to it would change the PHP composer
 * as a side effect of a Go change.
 *
 * The same 480px, because the two composers sit on the same page during the
 * migration and a thread that scrolled at a different height would look like a
 * bug in whichever one the operator noticed second.
 */
.chronos-comm-thread {
    max-height: 480px;
    overflow-y: auto;
}

/*
 * .table-responsive — a SECOND class of the same kind, found the same way.
 *
 * Bootstrap's own dist defines it as `overflow-x: auto`, and it is absent from
 * the CSS this tier serves: a bare probe element carrying only that class
 * computes `overflow-x: visible`. So every `<div class="table-responsive">` in
 * the Go tier is a no-op, and a table wider than its container pushes the
 * CONTAINER sideways instead of scrolling inside itself.
 *
 * Measured on the monitoring dialog's Events tab with the dialog constrained to
 * 560px: a seven-column table overflowed its pane by 209px. THE BOARD'S OWN
 * GRID has the same wrapper and the same no-op — this is not specific to the
 * dialog, and the grid escapes notice only because it is usually rendered at
 * full page width.
 *
 * !important for the reason at the top of this file: these sit on elements the
 * legacy Chronos stylesheets also target.
 */
.table-responsive {
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch;
}

/*
 * The alert view dialog's map pin — alerta/_view.php shipped this inline in a
 * <style> block per dialog open; here it ships once with the tier's CSS.
 * A rotated square with the icon counter-rotated upright, the same idiom the
 * PHP partial used.
 */
.alert-detail-pin {
    width: 36px;
    height: 36px;
    border-radius: 6px;
    transform: rotate(45deg);
    background: #dc3545;
    border: 2px solid #fff;
    box-shadow: 0 2px 6px rgba(0, 0, 0, .45);
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font-size: 16px;
}

.alert-detail-pin > i {
    transform: rotate(-45deg);
}

.alert-detail-grid dt {
    font-size: .82rem;
    text-transform: uppercase;
    letter-spacing: .03em;
}

.alert-detail-grid dd {
    word-break: break-word;
}

/*
 * The shared autocomplete dropdown — `data-controller="autocomplete"`.
 *
 * Keyed on the CONTROLLER ATTRIBUTES rather than a class, so the six existing
 * fields and every future one are covered without markup that each site has to
 * remember. They did not remember: all six carried `w-auto`, and form.templ —
 * the shared field renderer — had additionally patched the z-index inline,
 * which is the tell that this belonged in one place.
 *
 * THE WRAPPER MUST BE POSITIONED. Without it the results list is absolutely
 * positioned against whatever ancestor happens to be positioned — measured on
 * the assign dialog, that was the `.card`, two levels up. It looked correct only
 * because the card and the field shared a left edge; in a grid where a field
 * sits in a column it would not, and `width: 100%` would resolve against the
 * card's width rather than the field's.
 *
 * WIDTH MATCHES THE FIELD. `w-auto` shrank the list to its content — 193px of
 * dropdown under a 753px input — so the results neither lined up with the box
 * they belong to nor gave the serials room.
 *
 * IT ESCAPES THE CLIP, and the CONTROLLER pins it — see position(). The dialog's `.modal-body`
 * carries `overflow: auto`, and overflow clipping IGNORES z-index, so an
 * unbounded list ran past the modal's edge — unreachably, because an absolutely
 * positioned list adds nothing to the container's scrollHeight, and scrolling
 * the dialog to its end still left 91px of it outside the box. The controller
 * switches it to `fixed` and sets left/width/top/max-height from the field on
 * open; these declarations are the pre-JS baseline, and `width: 100%` against
 * the positioned wrapper is what keeps the list matched to its field before the
 * controller has measured anything.
 */
[data-controller~="autocomplete"] {
    position: relative;
}

[data-autocomplete-target="results"] {
    /* NOT the .position-absolute utility. Bootstrap's utilities carry
       !important, so the class beat the controller's inline `position: fixed`
       and the list stayed clipped inside the dialog with the pinning silently
       ignored — the same way chronos-ui-overrides.css's `.card { border …
       !important }` beat the dialog card reset above. Declared here instead so
       an inline style can win normally. */
    position: absolute;
    width: 100%;
    z-index: 1080;
    max-height: 15rem;
    overflow-y: auto;
}

/*
 * A CARD'S TONE ACCENT — the thick coloured left edge the KPI tiles use.
 *
 * NOT Bootstrap's `border-start border-4 border-<tone>`, and the reason is worth
 * having written down, because that trio is what everyone reaches for and it is
 * DEAD ON A CARD in this application.
 *
 * Two things defeat it, independently:
 *
 * 1. chronos-ui-overrides.css (PHP's, shared by both tiers, loaded on every
 *    page) carries `.card { border: 1px solid var(--chronos-glass-border)
 *    !important }`. That is a SHORTHAND, and it beats the longhands the
 *    utilities set — `.border-4` is `border-width: 4px !important` and
 *    `.border-<tone>` is `border-color: … !important`. Every declaration
 *    involved is !important and one class, so source order decides, and the
 *    overrides sheet loads after Bootstrap.
 * 2. Even with the utilities winning, they could not draw a one-sided accent
 *    here. `.border-4` and `.border-<tone>` apply to all four sides; on a bare
 *    element only the left paints because the other three have `border-style:
 *    none`, but `.card`'s shorthand styles all four, so the card would come out
 *    uniformly thick and coloured rather than accented.
 *
 * Measured 2026-09-08 on /suspensions/dashboard and /tasks/dashboard: every
 * tile drew `1px rgb(73,80,87)` on all four sides — the three suspension tiles
 * (danger / warning / info) were pixel-identical to each other.
 *
 * It went unnoticed for as long as it did because these tiles used to ask for
 * `.metric-card`, a class only the person page defines, so there was no card
 * border for the utilities to lose to and the accent painted by default.
 *
 * TWO CLASSES, so this outranks the single-class `.card` rule it is correcting
 * rather than relying on source order. The tone travels as a custom property so
 * the width is declared once. `--bs-<tone>` rather than the `-rgb` triplet:
 * no opacity is wanted here, and both are defined under either theme.
 *
 * A tone with no rule below leaves the property unset, which makes the whole
 * `border-left` invalid at computed-value time — the card keeps its own border
 * and draws no accent, rather than drawing a broken one.
 */
.card-accent-primary   { --card-accent-color: var(--bs-primary); }
.card-accent-success   { --card-accent-color: var(--bs-success); }
.card-accent-danger    { --card-accent-color: var(--bs-danger); }
.card-accent-warning   { --card-accent-color: var(--bs-warning); }
.card-accent-info      { --card-accent-color: var(--bs-info); }
/* The "nothing to report" tone. The card's own border colour rather than a
 * tone, so the edge reads as an edge and not as a signal. */
.card-accent-secondary { --card-accent-color: var(--bs-border-color); }

.card.card-accent { border-left: 4px solid var(--card-accent-color) !important; }

/*
 * BOOTSTRAP'S BORDER UTILITIES, RESTORED ON A `.card`.
 *
 * Reason (1) above is not specific to the accent: it kills EVERY border utility
 * written on a card. `.card { border: … !important }` is a shorthand, so it
 * outranks `.border-<tone>` (a `border-color`), `.border-<n>` (a `border-width`)
 * and `.border-0` (a shorthand of equal specificity that simply loses on order,
 * because the overrides sheet loads after Bootstrap).
 *
 * Swept 2026-09-08: TWENTY-TWO class attributes in ui/*.templ put one of these
 * on an element carrying the bare `card` class, and every one was inert.
 * Eleven were `border-0` — cards that had explicitly disclaimed a border and
 * were drawing one anyway, most of them monitoring dialog fragments. Nine were
 * red/amber/blue TONES standing in for an operational state: a breached route,
 * an officer at risk, a forecast breach, a disabled protocol step, a failed
 * audit entry. None of it painted, so a breached route card looked exactly like
 * a healthy one.
 *
 * It is silent in both directions — the page renders 200, the classes are all
 * present in the DOM, and only a computed style says otherwise — which is how
 * it survived this long. `.chronos-dialog-page > .card` above is somebody
 * hitting this same wall and routing around it for one case.
 *
 * TWO CLASSES throughout, so each rule outranks the single-class `.card` it
 * corrects rather than depending on source order. The values are Bootstrap's
 * own, copied from its `.border-*` rules verbatim — including
 * `var(--bs-border-opacity)`, so the `.border-opacity-*` utilities keep working
 * on a card the way they do everywhere else.
 *
 * THE FULL SET, not only the classes in use today. A rule that exists only for
 * the tones someone happened to need leaves the next `border-success` exactly
 * as broken, and this whole family fails without a symptom.
 *
 * The SIDE utilities (`border-top`, `border-start`, …) are deliberately absent.
 * They draw a side from `var(--bs-border-width)`/`var(--bs-border-color)`, which
 * is the root default rather than anything the neighbouring utilities set, so on
 * a card they cannot express the one thing they get reached for — a thick
 * coloured edge. That is what `.card-accent` above is for, and no bare `.card`
 * in the tree uses them.
 */

/* Tone. */
.card.border-primary   { border-color: rgba(var(--bs-primary-rgb),   var(--bs-border-opacity)) !important; }
.card.border-secondary { border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important; }
.card.border-success   { border-color: rgba(var(--bs-success-rgb),   var(--bs-border-opacity)) !important; }
.card.border-danger    { border-color: rgba(var(--bs-danger-rgb),    var(--bs-border-opacity)) !important; }
.card.border-warning   { border-color: rgba(var(--bs-warning-rgb),   var(--bs-border-opacity)) !important; }
.card.border-info      { border-color: rgba(var(--bs-info-rgb),      var(--bs-border-opacity)) !important; }
.card.border-light     { border-color: rgba(var(--bs-light-rgb),     var(--bs-border-opacity)) !important; }
.card.border-dark      { border-color: rgba(var(--bs-dark-rgb),      var(--bs-border-opacity)) !important; }
.card.border-black     { border-color: rgba(var(--bs-black-rgb),     var(--bs-border-opacity)) !important; }
.card.border-white     { border-color: rgba(var(--bs-white-rgb),     var(--bs-border-opacity)) !important; }

/* Tone, subtle. These carry no opacity variable in Bootstrap either. */
.card.border-primary-subtle   { border-color: var(--bs-primary-border-subtle) !important; }
.card.border-secondary-subtle { border-color: var(--bs-secondary-border-subtle) !important; }
.card.border-success-subtle   { border-color: var(--bs-success-border-subtle) !important; }
.card.border-danger-subtle    { border-color: var(--bs-danger-border-subtle) !important; }
.card.border-warning-subtle   { border-color: var(--bs-warning-border-subtle) !important; }
.card.border-info-subtle      { border-color: var(--bs-info-border-subtle) !important; }
.card.border-light-subtle     { border-color: var(--bs-light-border-subtle) !important; }
.card.border-dark-subtle      { border-color: var(--bs-dark-border-subtle) !important; }

/* Width. */
.card.border-1 { border-width: 1px !important; }
.card.border-2 { border-width: 2px !important; }
.card.border-3 { border-width: 3px !important; }
.card.border-4 { border-width: 4px !important; }
.card.border-5 { border-width: 5px !important; }

/* Removal. LAST of the three groups: "no border" is the least ambiguous thing
 * a call site can say, so it wins over a tone or a width left on the same
 * element. Bootstrap orders these the other way round, but only an element
 * carrying both would ever notice and none does. */
.card.border-0        { border: 0 !important; }
.card.border-top-0    { border-top: 0 !important; }
.card.border-end-0    { border-right: 0 !important; }
.card.border-bottom-0 { border-bottom: 0 !important; }
.card.border-start-0  { border-left: 0 !important; }

/*
 * THE SAME DEFEAT, ON BACKGROUNDS. Audited 2026-09-08, and this is the other
 * half of the border block above.
 *
 * chronos-ui-overrides.css sets `background:` — a SHORTHAND — with !important on
 * `.card` and on `.card-header, .card-footer`. Bootstrap's `.bg-*` utilities set
 * the LONGHAND `background-color`. Same specificity, same !important, and the
 * overrides sheet loads later, so the shorthand wins and every `bg-` utility
 * written on a card part is inert.
 *
 * Measured, both themes: `.card-header.bg-danger-subtle` — the audit log's ERROR
 * panel header — computed the ordinary 3% card-header tint instead of red, and
 * `.card-header.bg-info` computed it instead of cyan. The state a reader is meant
 * to see at a glance was simply not drawn.
 *
 * DELIBERATELY NOT INCLUDED: `.shadow` / `.shadow-sm` on a card. The overrides
 * sheet replaces the card shadow with the glass one on purpose, 130 call sites
 * inherit that, and it carries no state — making the utility win there would
 * restyle every card in the application to chase a class that was copied in from
 * PHP. Same for `.nav-item.position-relative` in the sidenav: modern-sidebar.css
 * sets `position: static !important` with a comment explaining that the submenu
 * must position against an outer ancestor, so the utility is dead ON PURPOSE and
 * making it win would misplace the submenu.
 */
.card.bg-primary, .card-header.bg-primary, .card-footer.bg-primary { background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important; }
.card.bg-secondary, .card-header.bg-secondary, .card-footer.bg-secondary { background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important; }
.card.bg-success, .card-header.bg-success, .card-footer.bg-success { background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important; }
.card.bg-danger, .card-header.bg-danger, .card-footer.bg-danger { background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important; }
.card.bg-warning, .card-header.bg-warning, .card-footer.bg-warning { background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important; }
.card.bg-info, .card-header.bg-info, .card-footer.bg-info { background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important; }
.card.bg-light, .card-header.bg-light, .card-footer.bg-light { background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important; }
.card.bg-dark, .card-header.bg-dark, .card-footer.bg-dark { background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important; }
.card.bg-black, .card-header.bg-black, .card-footer.bg-black { background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important; }
.card.bg-white, .card-header.bg-white, .card-footer.bg-white { background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important; }
.card.bg-body, .card-header.bg-body, .card-footer.bg-body { background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important; }
.card.bg-body-secondary, .card-header.bg-body-secondary, .card-footer.bg-body-secondary { background-color: rgba(var(--bs-secondary-bg-rgb), var(--bs-bg-opacity)) !important; }
.card.bg-body-tertiary, .card-header.bg-body-tertiary, .card-footer.bg-body-tertiary { background-color: rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity)) !important; }

.card.bg-primary-subtle, .card-header.bg-primary-subtle, .card-footer.bg-primary-subtle { background-color: var(--bs-primary-bg-subtle) !important; }
.card.bg-secondary-subtle, .card-header.bg-secondary-subtle, .card-footer.bg-secondary-subtle { background-color: var(--bs-secondary-bg-subtle) !important; }
.card.bg-success-subtle, .card-header.bg-success-subtle, .card-footer.bg-success-subtle { background-color: var(--bs-success-bg-subtle) !important; }
.card.bg-danger-subtle, .card-header.bg-danger-subtle, .card-footer.bg-danger-subtle { background-color: var(--bs-danger-bg-subtle) !important; }
.card.bg-warning-subtle, .card-header.bg-warning-subtle, .card-footer.bg-warning-subtle { background-color: var(--bs-warning-bg-subtle) !important; }
.card.bg-info-subtle, .card-header.bg-info-subtle, .card-footer.bg-info-subtle { background-color: var(--bs-info-bg-subtle) !important; }
.card.bg-light-subtle, .card-header.bg-light-subtle, .card-footer.bg-light-subtle { background-color: var(--bs-light-bg-subtle) !important; }
.card.bg-dark-subtle, .card-header.bg-dark-subtle, .card-footer.bg-dark-subtle { background-color: var(--bs-dark-bg-subtle) !important; }

/* The explicit disclaimer, like border-0 above. */
.card.bg-transparent, .card-header.bg-transparent, .card-footer.bg-transparent { background-color: transparent !important; }

/*
 * AND THE SHADOWS. The last of the three families, added at Marcel's direction
 * 2026-09-08 after the audit had deliberately left it out.
 *
 * Same mechanism: chronos-ui-overrides.css sets `box-shadow: var(--chronos-glass-
 * shadow), var(--chronos-glass-inset-shadow) !important` on `.card` (and on
 * `.chronos-glassmorphism, .glass-effect`), Bootstrap's `.shadow*` set the same
 * property at the same specificity, and the overrides sheet loads later. So
 * every `shadow-sm` written on a card has been inert.
 *
 * READ THIS BEFORE CHANGING IT — it is not the same call as the borders and
 * backgrounds, and the reasoning was recorded rather than assumed:
 *
 * A tone or a `bg-transparent` carries STATE or an explicit disclaimer, and
 * losing it loses information. A shadow carries neither: it is chrome, and the
 * glass shadow was a deliberate house style applied to every card at once. The
 * audit therefore left these alone, and the count is why — 130 `card shadow-sm`,
 * 5 `card shadow`, 1 on `.chronos-glassmorphism`.
 *
 * The consequence to watch, which the counts make concrete: 135 card elements in
 * ui/*.templ carry a shadow utility and 81 do not. Honouring the utility means
 * those two groups no longer match — the 81 keep the soft glass shadow with its
 * inset highlight, the 135 take Bootstrap's flatter one. Before this change all
 * 216 looked alike. If the split ever reads as inconsistent, the fix is to
 * delete this block (restoring one uniform treatment) or to strip the utility
 * from the templates (making the markup honest), NOT to add a third shadow.
 *
 * `--bs-box-shadow*` rather than literal values: Bootstrap resolves these per
 * theme, so the utility keeps meaning the same thing in light and dark.
 */
.card.shadow-sm,   .chronos-glassmorphism.shadow-sm,   .glass-effect.shadow-sm   { box-shadow: var(--bs-box-shadow-sm) !important; }
.card.shadow,      .chronos-glassmorphism.shadow,      .glass-effect.shadow      { box-shadow: var(--bs-box-shadow) !important; }
.card.shadow-lg,   .chronos-glassmorphism.shadow-lg,   .glass-effect.shadow-lg   { box-shadow: var(--bs-box-shadow-lg) !important; }
.card.shadow-none, .chronos-glassmorphism.shadow-none, .glass-effect.shadow-none { box-shadow: none !important; }

/*
 * .modal-board — the third dialog size, beside Bootstrap's lg and xl.
 *
 * The event-handling dialog on the monitoring board opened at modal-xl:
 * 1140px wide and as tall as its Notes tab, 1140x635 in an 1800x1043
 * viewport, with the board showing round three sides of it. An operator
 * handling an event works inside that dialog — remarks, the map, device
 * status, history, the close form — and Marcel asked for it to fill most of
 * the screen (2026-09-16). Near-full-screen rather than Bootstrap's
 * .modal-fullscreen, which drops the margins, the radius and the backdrop
 * edge that say "this is a dialog over the board".
 *
 * WIDTH from the viewport, not a max-width cap: xl is 1140px whatever the
 * screen. HEIGHT: .modal-dialog-scrollable already sizes the frame to the
 * viewport less its margins, but lets .modal-content shrink to its content;
 * pinning the content to 100% is what makes the Map and History panes get
 * the room instead of the board behind them.
 *
 * Below md the frame is Bootstrap's ordinary full-width modal anyway, so the
 * rule applies from md up, like modal-lg and modal-xl do.
 */
@media (min-width: 768px) {
    .modal-dialog.modal-board {
        max-width: calc(100vw - 3rem);
        width: calc(100vw - 3rem);
    }

    .modal-dialog.modal-board .modal-content {
        height: 100%;
    }
}

/* A small split dropdown toggle is 22px wide (Bootstrap's .btn-sm padding on
 * a lone caret) — under the 24px floor for a pointer target (WCAG 2.5.8;
 * Lighthouse target-size, fifty of them on the monitoring board). Widen the
 * toggle, never the button beside it. */
.btn-sm.dropdown-toggle-split {
    min-width: 1.5rem;
}
