/*
 * Treatment Profile Editor UI - visual styling.
 *
 * Adapted from the Angular logic-engine-v2 module this library ports:
 *   - src/app/Modules/logic-engine-v2/styles/logic-engine-v2.styles.css
 *     (rule-wrapper/-testFactor/-waterCondition/-alwaysRun/-calculateFactor tint colors,
 *     error-wrapper/invalid dashed-border validation styling, delete-rule-icon look)
 *   - src/app/Modules/logic-engine-v2/components/treatment-profile-editor/
 *     treatment-profile-editor-v2.component.html/.ts (rule-container/profile-container border +
 *     min-height, rule-palette layout + per-kind bgColor swatches, group border/header look)
 *   - src/styles.css (parent app's global .drag-handle cursor rules)
 *
 * Translated onto this library's own "tpe-*" class names (see TreatmentProfileEditor.razor and
 * the rule row components for where each class is applied) rather than reusing the Angular names
 * verbatim, to avoid colliding with a host app's own global styles.
 *
 * This is a Razor Class Library static web asset - a consuming host must reference it explicitly:
 *   <link rel="stylesheet" href="_content/LaMotte.WaterLink.TreatmentProfileUI/treatment-profile-editor.css" />
 * (see LaMotte.WaterLink.TreatmentProfileUI.SmokeTest/Components/App.razor for a working example).
 */

/* Canvas / profile-container equivalent. min-height/max-height are set inline from the
   MinHeight/MaxHeight component parameters (see TreatmentProfileEditor.razor) rather than fixed
   here, so a host can override them; overflow-y: auto means once the rule/group list grows past
   max-height, the canvas clips at that height and scrolls internally instead of growing the page
   indefinitely - the flex children below (.k-listbox etc.) all already have min-height: 0, which
   is what lets them shrink smaller than their content and hand the overflow up to this element
   rather than forcing the canvas taller than max-height. */
.tpe-canvas {
    border: 1px solid #000;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    padding: 4px;
    overflow-y: auto;
    box-sizing: border-box;
}

/* Telerik's TelerikListBox defaults to a narrow shrink-to-fit width (~200px) and a fixed 200px
   height, which is fine for its own out-of-the-box list scenarios but wrong here - the Angular
   rule-container/rule-wrapper rows always span the full available width, and a canvas/group
   that's taller than 200px (via .tpe-canvas's min-height/.tpe-group's min-height below) would
   otherwise leave a large dead zone below the listbox that isn't part of the component's actual
   drop-target area at all (confirmed via DOM measurement: .k-listbox stayed a fixed 200px tall
   even inside a 637px-tall .tpe-canvas). Force the root canvas listbox (and every nested
   per-group listbox), its internal scroll/list containers, and its items to stretch to fill
   their flex parent, so the entire visible box - not just Telerik's default 200px sliver - is
   real, droppable list content. */
.tpe-canvas .k-listbox,
.tpe-group .k-listbox {
    width: 100%;
    height: 100%;
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.tpe-canvas .k-list-scroller,
.tpe-group .k-list-scroller,
.tpe-canvas .k-list,
.tpe-group .k-list {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* The actual drop-target hit area for Telerik's ListBox drag-and-drop is `.k-list-content`/
   `ul.k-list-ul` itself, not just its `.k-listbox`/`.k-list` ancestors - confirmed by DOM
   measurement (a real, CDP-driven Playwright mouse-drag test, not synthetic DOM events) that
   `ul.k-list-ul` stays sized to its natural content height (e.g. ~64px for a single placeholder
   row) even inside a much taller (~468px, from .tpe-canvas's min-height) stretched `.k-listbox`.
   The earlier fix below only stretched `.k-list-scroller`/`.k-list`, leaving `.k-list-content`/
   `ul.k-list-ul` still content-sized - so most of the visually-large canvas/group box was *never*
   part of the actual `<ul>` element at all, and a drop landing in that dead zone (which is most of
   the visible box on a lightly-populated list) silently failed. This reproduced with *any* lightly
   populated list, not just an empty one - the "works after Generate" reports were coincidental:
   enough randomly-generated rows happened to naturally exceed the container's min-height, leaving
   no dead zone to miss. Stretch `.k-list-content`/`ul.k-list-ul` to fill their parent too, so the
   whole visible box is always real, droppable `<ul>` area regardless of item count. */
.tpe-canvas .k-list-content,
.tpe-group .k-list-content,
.tpe-canvas ul.k-list-ul,
.tpe-group ul.k-list-ul {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* When the sole item in a list is the empty-state placeholder (see UiPlaceholderRule), stretch
   its <li> too so the dashed "drop here" box visually fills the whole now-droppable area above,
   rather than leaving a content-sized sliver at the top with dead (but technically droppable)
   space below it. Scoped with :has(> ...) - a *direct child* combinator, not a plain :has()
   descendant match - so a single *real* rule row (a legitimately short list) is not stretched to
   fill the entire canvas - only the synthetic placeholder row is. This distinction matters for a
   *group* row specifically: a group whose own nested list is empty renders its placeholder
   several levels deep (li > .tpe-error-wrapper > .tpe-group > ... > li > .tpe-canvas-empty), so a
   plain `:has(.tpe-canvas-empty)` (matching any descendant) incorrectly matched the *outer*
   group's own <li> too - stretching the whole group item to fill the remaining canvas height and
   leaving a large blank gap above/below its actual (much shorter) border. A real placeholder row,
   by contrast, always renders `.tpe-canvas-empty` as its own <li>'s *direct* child (see
   RenderRule), so restricting the match to `> .tpe-canvas-empty` fixes the group case without
   losing the intended top-level empty-canvas/empty-group stretch behavior. */
.tpe-canvas li:has(> .tpe-canvas-empty),
.tpe-group li:has(> .tpe-canvas-empty) {
    flex: 1 1 auto;
    display: flex;
    min-height: 0;
	align-content: center;
	justify-content: space-around;
}

/* Telerik's `.k-list-item` (the <li>) ships a default `padding: 4px 8px`, which stacks with this
   library's own `.tpe-error-wrapper` margin-bottom (below) to produce a much larger gap between
   rows than either alone - confirmed via DOM measurement: 4px (li's own bottom padding) + 4px
   (next li's own top padding) + a small inherent list-item gap + 4px (wrapper's margin-bottom)
   added up to 14px between two stacked rows, reading as a large empty band. Zero the <li>'s own
   padding entirely and let `.tpe-error-wrapper`'s margin-bottom be the *only* source of vertical
   spacing between rows/groups.

   flex-shrink: 0 is required for the canvas's max-height/overflow-y (see .tpe-canvas) to actually
   scroll rather than silently compress every row: each <li> is itself a flex item of the
   flex-column `ul.k-list-ul`, and flex items default to flex-shrink: 1, so once the canvas hit
   its bounded max-height the browser was shrinking every row equally to force the whole list to
   fit - confirmed via DOM measurement (25 rows all compressed to ~27px each, clientHeight ==
   scrollHeight, no scrollbar) rather than the rows keeping their natural height and the list
   genuinely overflowing. */
.tpe-canvas .k-list-item,
.tpe-group .k-list-item {
    width: 100%;
    padding: 0;
    flex-shrink: 0;
    box-sizing: border-box;
}

/* Confirmed (via a real CDP mouse-driven Playwright drag, not synthetic DOM events) that dropping
   onto the empty space below the last real row - anywhere the list is shorter than its container,
   e.g. a lightly-populated canvas/group well under .tpe-canvas's min-height - silently fails: the
   dragged row simply stays put, no reorder happens at all. Root cause is the same "dead zone"
   problem the placeholder-stretch rule above already solves for an *empty* list: Telerik's
   drop-target detection (like this library's own drop-hint.js, see its comments) only recognizes
   a drop that lands on/within an actual `<li class="k-list-item">` - the leftover canvas space
   below the last li, while visually part of the stretched `ul.k-list-ul` (see rules above), is not
   itself part of any li's box, so a drop landing there hits nothing.

   First attempt: grow the last *real* row's own <li> box to absorb the leftover space. That fixed
   the silent-drop-failure, but introduced a worse bug (confirmed by hands-on testing): Telerik's
   OnDrop only ever reports a single DestinationIndex, computed from where the pointer lands
   within whichever <li> it's hovering - there's no separate before/after/inside hint to
   disambiguate (confirmed via reflection against the installed Telerik.UI.for.Blazor package's
   ListBoxDropEventArgs<TItem>, which exposes only DestinationIndex/DestinationListBoxId/Items).
   Once the last *real* row's box was grown to fill most of the empty canvas, dropping anywhere in
   that dead space - including right where a user would naturally release the mouse, just below
   the visible row - mostly landed above that huge box's own vertical midpoint, which Telerik
   resolved to "insert immediately before the last real row" rather than "append after it".

   Fix: per UiPlaceholderRule.IsSpacer's remarks, TreatmentProfileEditor now always keeps a
   trailing, invisible "spacer" placeholder item as the true last entry of any non-empty
   canvas/group list, so it - never a real rule - is the one whose <li> gets grown here. Because
   the spacer carries no meaning of its own, *either* side of its own grown box's before/after
   boundary resolves to the same outcome from the editor's perspective: "insert as the new last
   real rule" (immediately before this spacer). This sidesteps the ambiguity above entirely,
   without needing to know where exactly within the box the pointer landed.

   align-items: flex-start AND align-content: flex-start are both required here - only setting
   align-items was tried first and did not work (confirmed via a real screenshot and DOM
   measurement after growing the li with only that override - the spacer's own box, which has no
   visible content of its own, doesn't visually show this, but it matters for the *previous*
   sibling real row still using its natural top-aligned flow - kept for consistency/safety with
   the rest of this file's rows). Root cause: Telerik's own `.k-list-item` base styling sets BOTH
   `flex-wrap: wrap` and `align-content: center` (not just `align-items: center`) - with
   flex-wrap enabled, even a single-line row is still subject to `align-content`, which positions
   the whole (one-item) flex *line* within the container's cross-axis space, independently of
   `align-items` (which only positions an item *within* its own line). Overriding both properties
   to flex-start keeps the (invisible, empty) spacer's own box anchored to the top, consistent
   with every other row in the list.

   min-height is required too, and is what makes this rule keep working once the canvas/group has
   no leftover space at all - i.e. the list already fills or overflows .tpe-canvas's max-height
   and is scrolling (confirmed still-broken by hands-on testing with only flex-grow above:
   "still really hard to drop a row at the very end when the canvas is full"). flex-grow: 1 only
   expands an item into space the flex container actually has free; once nothing is free, the
   spacer's <li> collapses toward 0 height and becomes an impossible-to-hit sliver, reproducing
   the original "hard to drop at the very end" complaint even with the spacer in place. A fixed
   min-height guarantees a real, comfortably clickable drop target at the end of every list
   regardless of whether the canvas has slack - reachable by scrolling the canvas down like any
   other row once the list overflows. Sized to roughly one normal row's height (see
   .tpe-rule-row's .35em/.5em padding below), not .tpe-canvas-empty's larger 4em - that one needs
   to visually read as "the whole empty canvas"; this one only needs to be a normal-sized target. */
.tpe-canvas li:has(> .tpe-canvas-spacer),
.tpe-group li:has(> .tpe-canvas-spacer) {
    flex-grow: 1;
    align-items: flex-start;
    align-content: flex-start;
    min-height: 2em;
}

/* Telerik's `.k-list-item` (the <li>) is a flex *row* container, so its only child - the
   .tpe-error-wrapper this library wraps every rendered rule/group in (see
   TreatmentProfileEditor.RenderRule) - is a flex item on the main (horizontal) axis and, without
   an explicit grow factor, shrinks to its own content width rather than filling the <li> (confirmed
   via DOM measurement: a 1090px-wide <li> held only a 908px-wide .tpe-error-wrapper). Force it (and
   the .tpe-rule-row it contains) to stretch to the <li>'s full width so every rule/group row spans
   the whole canvas/group width, matching production, instead of only as wide as its own content. */
.tpe-canvas .k-list-item > .tpe-error-wrapper,
.tpe-group .k-list-item > .tpe-error-wrapper {
    flex: 1 1 auto;
    width: 100%;
    min-width: 0;
    box-sizing: border-box;
}

/* Placeholder shown as an ordinary list row (UiPlaceholderRule/RenderRule) when a canvas/group
   has no real rules - kept as a real <li> (not Telerik's NoDataTemplate/.k-nodata path) because
   Telerik/Kendo's ListBox drag-and-drop has a confirmed defect refusing drops into a genuinely
   empty (Data.Count == 0) list; see docs/known-limitations.md. Stretches to fill its <li> (see
   the :has() rule above) so the whole visible empty canvas/group area reads as one big drop
   target, not a small sliver near the top.
.tpe-canvas-empty {
    display: flex;
    flex: 1 1 auto;
    align-items: center;
    justify-content: center;
    min-height: 4em;
    width: 100%;
    color: #888;
    border: 2px dashed #ccc;
    box-sizing: border-box;
}

/* Individual rule row - rule-wrapper equivalent. Production is a solid-color, borderless,
   single-line (non-wrapping) bar - no box border, no row-to-row wrapping of its inner controls. */
.tpe-rule-row {
    padding: .35em .5em;
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: .5em;
    width: 100%;
    box-sizing: border-box;
    overflow-x: auto;
}

/* error-wrapper/invalid equivalent - wraps each rendered rule (see
   TreatmentProfileEditor.RenderRule), independent of the row's own -kind tint below. Positioned
   so the shared delete icon (below) can be pinned to its top-right corner regardless of the
   row's own flex layout. Inter-row spacing lives here (margin-bottom), not on .tpe-rule-row/
   .tpe-group themselves: those previously carried their own margin-bottom, but since they're
   nested *inside* this wrapper's border, that margin ate into the wrapper's own border box -
   producing a visibly bigger gap between the colored/bordered content and the wrapper's border
   on the bottom edge only (confirmed via DOM measurement: 6px bottom vs. 2px on the other three
   sides). Moving the spacing to the wrapper itself keeps it outside the border on all four
   sides, so the border hugs the content evenly while still spacing sibling rows/groups apart. */
.tpe-error-wrapper {
    position: relative;
    border: solid 4px transparent;
    margin-bottom: 4px;
}

.tpe-error-wrapper.invalid {
    border: dashed 2px red;
}

/* Rule-kind background tints - same colors as the Angular source, applied to both canvas rows
   and palette swatches. */
.-testFactor {
    background-color: rgba(60, 179, 113, 0.4);
}

.-waterCondition {
    background-color: rgb(176, 190, 197);
}

.-alwaysRun {
    background-color: rgba(255, 165, 0, 0.35);
}

.-calculateFactor {
    background-color: rgba(0, 0, 150, 0.35);
}

/* Drag handle - matches the parent app's global .drag-handle cursor behavior. */
.tpe-drag-handle {
    padding-right: 12px;
    cursor: grabbing;
}

.tpe-drag-handle:hover {
    cursor: grab;
}

/* delete-rule-icon equivalent - pinned to the top-right corner of the error-wrapper (see above),
   overlaying the row rather than participating in its flex layout, matching production's floating
   trash-can (no button chrome, just cursor:pointer + a muted icon color). */
.tpe-delete-icon {
    position: absolute;
    top: .15em;
    right: .35em;
    cursor: pointer;
    color: #8D9BB7;
    z-index: 2;
}

.tpe-delete-icon:hover {
    color: #d9534f;
}

/* Group container - group-root-container/child-rule-container equivalent, plus the "Group"
   *ngSwitchCase's 1px solid blue border from rule-selector.component.html. Inter-item spacing
   comes from the enclosing .tpe-error-wrapper's own margin-bottom (see its comment above), not
   from this element - it's nested inside that wrapper's border, so a margin here would eat into
   the wrapper's border box instead of spacing it apart from the next sibling. */
.tpe-group {
    border: 1px solid blue;
    padding: 4px;
    min-height: 10vh;
    width: 100%;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
}

.tpe-group-header {
    font-weight: bold;
    margin-bottom: 4px;
}

/* Rule palette - rule-palette equivalent: production lays these out as a horizontal,
   single-row strip of compact pills (one per rule/group kind) that shrink to fit the available
   width rather than wrapping onto a second row - not a vertical stack of full-width bars, and
   not a multi-row wrap either. Telerik's ListBox defaults every internal container to a vertical
   flex column (fine for the canvas/groups above, which *should* stack full-width), so every
   layer between the outer .k-listbox and the actual <li> items needs to be overridden to a
   single non-wrapping row here. */
.tpe-palette {
    margin-bottom: .5em;
}

.tpe-palette .k-listbox {
    width: 100%;
    height: auto;
}

.tpe-palette .k-list-scroller,
.tpe-palette .k-list,
.tpe-palette .k-list-content,
.tpe-palette ul.k-list-ul {
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: .35em;
    width: 100%;
    height: auto;
    overflow: visible;
}

/* Each palette pill shares the row equally and shrinks (rather than wrapping to a second row)
   once the combined natural widths exceed the available space - min-width: 0 is required for a
   flex item to shrink below its content's own intrinsic width. */
.tpe-palette .k-list-item {
    width: auto;
    flex: 1 1 0;
    min-width: 0;
    box-sizing: border-box;
}

.tpe-palette-item {
    display: flex;
    align-items: center;
    gap: .35em;
    box-sizing: border-box;
    padding: .35em .5em;
    white-space: nowrap;
    overflow: hidden;
    min-width: 0;
}

.tpe-palette-label {
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

/* Groups have no bgColor in the Angular source (sourcePalette entries for both group kinds use
   bgColor: '') - intentionally no tint class for them here either. */

/* Drop hint - highlights the rule/group row currently under the cursor while a drag is in
   progress (see wwwroot/treatment-profile-drop-hint.js). TelerikListBox has no public "dragging
   over this item" event to hook a hint into, so this class is applied/removed purely from that
   JS module tracking the mouse position - it never touches Telerik's own drag/drop state.
   Applies to any list row (`li.k-list-item`), including the empty-state placeholder row, so an
   empty canvas/group still gets a clear "you're about to drop here" indication. */
.tpe-drop-hover {
    outline: 2px dashed #3c78d8;
    outline-offset: -2px;
    background-color: rgba(60, 120, 216, 0.08);
}

/* Sizes and centers the Edit Treatment Action dialog's Close button. Uses plain flex layout here
   (not Bootstrap utility classes like d-flex/justify-content-center/w-100) since this app does not
   load Bootstrap's CSS - those classes are inert no-ops in this dialog. */
.tpe-close-btn-row {
    display: flex;
    justify-content: center;
    margin-top: 0.75em;
}

.tpe-close-btn-row .tpe-close-btn {
    width: 65%;
}
