/* ============================================================================
   Dynamic form layout & spacing — the single home for every rule that puts
   space between form groups, columns and fields.

   Why one file: these rules used to live in three places (site.css plus
   <style> blocks inside Pages/DynamicForm.razor and
   Shared/DynamicForms/DynamicFormGroup.razor). A component <style> block is
   emitted inline in the DOM, so it beats a <head> stylesheet on document order
   at equal specificity — which forced !important on every modifier in site.css
   and still left .dynamic-form-group declared twice with different values.

   Spacing is expressed as inherited custom properties, so a density set on the
   form root cascades into every nested group, column and field, and any group
   or column can re-point the tokens for its own subtree. Geometry rules are
   prefixed with `.dynamic-form ` so they outrank single-class rules in
   bootstrap.min.css and the Radzen theme regardless of load order (the Radzen
   theme link is injected by <RadzenTheme/> at runtime, i.e. after this file).
   ============================================================================ */

/* --- Baseline tokens — identical to the "Comfortable" preset --------------
   Tuned so a stack of borderless rows lands at roughly 22px between one input
   and the next field's label — the density of a standard tight web form. The
   four presets below stay monotonic around it: Compact < Cozy < Comfortable <
   Spacious.
   ------------------------------------------------------------------------- */
.dynamic-form {
    --df-gutter: 0.75rem;           /* horizontal space between columns */
    --df-row-gap: 0.75rem;          /* space below a bordered column */
    --df-row-gap-plain: 0.25rem;    /* space below a borderless column */
    --df-field-gap: 0.5rem;         /* space between fields within a column */
    --df-group-gap: 0.75rem;        /* space below a bordered group */
    --df-group-gap-plain: 0.25rem;  /* space below a borderless group */
    --df-pad: 0.75rem;              /* inner padding of a bordered box */
    --df-label-gap: 0.75rem;        /* label zone <-> control zone */

    /* Height held under a validated field so a message can appear without
       shifting the layout. Zero by default: it cost a full line of dead space
       beneath every validated field, and messages only show after a blur. */
    --df-validator-reserve: 0px;
}

/* --- Density presets -----------------------------------------------------
   Declared after the baseline so a preset sitting on the form root itself wins
   the 0,1,0 specificity tie. These set custom properties only — never real
   geometry — which is what lets the same class work on the root, on a group or
   on a single column. Emitted by Services/Layout/FormSpacingCss.cs.
   ------------------------------------------------------------------------- */
.df-density-compact {
    --df-gutter: 0.25rem;
    --df-row-gap: 0.25rem;
    --df-row-gap-plain: 0px;
    --df-field-gap: 0.125rem;
    --df-group-gap: 0.25rem;
    --df-group-gap-plain: 0px;
    --df-pad: 0.5rem;
    --df-label-gap: 0.5rem;
    --df-validator-reserve: 0px;
}

.df-density-cozy {
    --df-gutter: 0.5rem;
    --df-row-gap: 0.5rem;
    --df-row-gap-plain: 0.125rem;
    --df-field-gap: 0.25rem;
    --df-group-gap: 0.5rem;
    --df-group-gap-plain: 0.125rem;
    --df-pad: 0.625rem;
    --df-label-gap: 0.5rem;
    --df-validator-reserve: 0px;
}

.df-density-comfortable {
    --df-gutter: 0.75rem;
    --df-row-gap: 0.75rem;
    --df-row-gap-plain: 0.25rem;
    --df-field-gap: 0.5rem;
    --df-group-gap: 0.75rem;
    --df-group-gap-plain: 0.25rem;
    --df-pad: 0.75rem;
    --df-label-gap: 0.75rem;
    --df-validator-reserve: 0px;
}

/* The only preset that still reserves a validation row — roominess is its point. */
.df-density-spacious {
    --df-gutter: 1.5rem;
    --df-row-gap: 1.5rem;
    --df-row-gap-plain: 0.75rem;
    --df-field-gap: 1rem;
    --df-group-gap: 1.5rem;
    --df-group-gap-plain: 0.75rem;
    --df-pad: 1.5rem;
    --df-label-gap: 1rem;
    --df-validator-reserve: 1rem;
}

/* --- Form root ----------------------------------------------------------- */
.dynamic-form__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: var(--df-group-gap);
}

.dynamic-form--disabled {
    opacity: 0.6;
    pointer-events: none;
}

.dynamic-form .dynamic-form__validation-summary {
    margin-bottom: 1rem;
    color: var(--rz-danger);
}

.dynamic-form .dynamic-form__buttons {
    margin-top: 1.5rem;
    padding-top: 1rem;
    border-top: 1px solid var(--rz-base-300);
}

.dynamic-form-debug {
    margin-top: 1rem;
}

.dynamic-form-debug pre {
    white-space: pre-wrap;
    word-break: break-all;
}

/* --- Grid: rows and columns ----------------------------------------------
   Bootstrap 4 hardcodes .row{margin:0 -15px} and .col-*{padding:0 15px} with no
   variable to tune, so the gutter is re-declared here from --df-gutter. Only
   rows that hold DynamicFormColumn children carry .dynamic-form__row — a plain
   bootstrap .row nested inside a form (e.g. inside a collection control) keeps
   its stock 15px gutter and stays aligned.
   ------------------------------------------------------------------------- */
.dynamic-form .dynamic-form__row {
    margin-left: calc(var(--df-gutter) / -2);
    margin-right: calc(var(--df-gutter) / -2);
}

.dynamic-form .dynamic-form__column {
    position: relative;
    padding-left: calc(var(--df-gutter) / 2);
    padding-right: calc(var(--df-gutter) / 2);
    margin-bottom: var(--df-row-gap);
}

/* A column with no border is chrome-free, so it gets the tight row gap. */
.dynamic-form .dynamic-form__column--plain {
    margin-bottom: var(--df-row-gap-plain);
}

.dynamic-form .dynamic-form__column--disabled {
    opacity: 0.6;
    pointer-events: none;
}

.dynamic-form .dynamic-form__column-body {
    position: relative;
    border-radius: 6px;
}

/* Padding and the white card surface belong to the bordered box only — a
   borderless column has no visible edge to inset content from. The dashed
   design-mode outline deliberately adds no padding, so the canvas renders at
   the same density as runtime. */
.dynamic-form .dynamic-form__column-body--border {
    border: 1px solid var(--rz-base-300);
    background: white;
    padding: var(--df-pad);
}

.dynamic-form .dynamic-form__column-body--border-dashed {
    border: 1px dashed var(--rz-base-300);
}

/* The floating column label overlaps the top edge, so it needs room below it. */
.dynamic-form .dynamic-form__column-body:has(> .dynamic-form__column-label) {
    padding-top: max(var(--df-pad), 1.25rem);
}

.dynamic-form .dynamic-form__column-label {
    position: absolute;
    top: -0.65rem;
    left: 1.25rem;
    padding: 0 0.5rem;
    background: white;
    font-weight: 600;
    color: var(--rz-text-color);
    font-size: 0.95rem;
}

/* --- Group --------------------------------------------------------------- */
.dynamic-form .dynamic-form-group {
    position: relative;
    border-radius: 6px;
    margin-bottom: var(--df-group-gap-plain);
}

.dynamic-form .dynamic-form-group--border {
    border: 1px solid var(--rz-base-300);
    background: white;
    padding: var(--df-pad);
    margin-bottom: var(--df-group-gap);
}

.dynamic-form .dynamic-form-group--borderless {
    border: none;
    background: transparent;
}

.dynamic-form .dynamic-form-group--border-dashed {
    border: 1px dashed var(--rz-base-300);
}

/* The legend-style label sits on the border line, so a bordered group that
   shows one needs extra room above its content. Previously a blanket
   `padding-top: 2rem` on every group leaked this design-mode allowance into
   runtime. */
.dynamic-form .dynamic-form-group--border:has(> .dynamic-form-group__label) {
    padding-top: 1.75rem;
}

.dynamic-form .dynamic-form-group__label {
    position: absolute;
    top: -0.65rem;
    left: 1.25rem;
    padding: 0 0.5rem;
    background: white;
    font-weight: 600;
    color: var(--rz-text-color);
    font-size: 0.95rem;
}

.dynamic-form .dynamic-form-group--borderless > .dynamic-form-group__label {
    position: static;
    padding: 0;
    margin-bottom: 0.5rem;
}

.dynamic-form .dynamic-form-group__description {
    margin: 0 0 0.5rem 0;
    color: var(--rz-text-secondary-color);
    font-size: 0.9rem;
}

.dynamic-form .dynamic-form-group--disabled {
    opacity: 0.6;
    pointer-events: none;
}

/* --- Legacy per-container flags ------------------------------------------
   Kept for templates saved before density existed. Note the asymmetry, which
   predates this file: for a column "no spacing" means the horizontal gutter,
   for a group it means the bottom margin. Declared after the border rules so
   they win the specificity tie; no !important needed now that the competing
   component <style> blocks are gone.
   ------------------------------------------------------------------------- */
.dynamic-form .dynamic-form__column--no-spacing {
    padding-left: 0;
    padding-right: 0;
}

.dynamic-form .dynamic-form__column--no-padding .dynamic-form__column-body,
.dynamic-form .dynamic-form__column-body--no-padding {
    padding: 0;
}

.dynamic-form .dynamic-form-group--no-padding {
    padding: 0;
}

.dynamic-form .dynamic-form-group--no-spacing {
    margin-bottom: 0;
}

/* --- Field shell --------------------------------------------------------- */
.dynamic-form .dynamic-form__field-shell {
    position: relative;
    border-radius: 4px;
    margin-bottom: var(--df-field-gap);
    transition: border-color 0.2s ease, background-color 0.2s ease;
}

.dynamic-form .dynamic-form__field-shell.simplified {
    display: inline-block;
    vertical-align: top;
    margin-right: 0.5rem;
}

.dynamic-form .dynamic-form__field-shell.inline {
    display: inline-block;
    vertical-align: top;
    margin-right: 0.75rem;
}

/* A nested group is hosted inside a field shell, so without this it would carry a bottom gap twice:
   the shell's --df-field-gap and the group's own --df-group-gap. The group owns its spacing. */
.dynamic-form .dynamic-form__field-shell.group {
    margin-bottom: 0;
}

.dynamic-form__field-simplified {
    display: inline-block;
}

/* --- Field row ----------------------------------------------------------- */
/* RadzenRow only exposes RowGap, leaving --rz-gap at the theme's 1rem for the
   horizontal label/control split; point it at the density token instead. */
.dynamic-form .dynamic-form__field-row {
    --rz-gap: var(--df-label-gap);
    margin-bottom: 0;
}

.dynamic-form .dynamic-form__field-label-zone {
    text-align: left;
}

@media (min-width: 768px) {
    .dynamic-form .dynamic-form__field-label-zone {
        text-align: left;
        padding-right: 0.75rem;
    }
}

.dynamic-form .dynamic-form__field-label {
    font-weight: 600;
    display: inline-block;
    margin-bottom: 0.15rem;
}

.dynamic-form .dynamic-form__field-small-label {
    display: block;
    line-height: 1;
    margin-bottom: 0.125rem;
}

.dynamic-form .dynamic-form__field-label--small {
    font-size: 0.75rem;
    line-height: 1;
    margin-bottom: 0;
}

/* Radzen's ".rz-form .row .rz-label" rule sets min-height to match the input
   control's height, which just adds dead space below the label text here.
   !important is required: that selector is more specific than this one. */
.dynamic-form .dynamic-form__field-label.rz-label {
    min-height: unset !important;
}

.dynamic-form .dynamic-form__required-mark {
    color: var(--rz-danger);
    font-weight: 600;
    margin-left: 0.15rem;
}

.dynamic-form .dynamic-form__field-hint {
    color: var(--rz-text-secondary-color);
    display: block;
    margin-top: 0.1rem;
}

.dynamic-form .dynamic-form__field-control-zone {
    position: relative;
}

/* Radzen only floats a form-field label on focus for textbox/textarea/numeric/autocomplete;
   dropdowns get no focus rule, and an open dropdown panel is moved to <body> with focus on
   the filter input there — so an open dropdown kept the label resting inside the input.
   Float the label when anything inside the content has focus, or when the popup is open
   (the "rz-popup-open" class is kept in sync by js/formfield-popup-state.js).
   The property values mirror Radzen's own "floated" rule in default-base.css. */
.dynamic-form__field-formfield.rz-floating-label .rz-form-field-content:focus-within > .rz-form-field-label,
.dynamic-form__field-formfield.rz-floating-label.rz-popup-open .rz-form-field-content > .rz-form-field-label {
    inset-inline-end: auto;
    inset-block-start: var(--rz-form-field-label-floating-top);
    padding-block-start: 0;
    padding-block-end: 0;
    transform: translate(0, 0);
    color: var(--rz-input-placeholder-color);
    background-color: var(--rz-form-field-label-floating-background-color);
    font-size: 0.75rem;
    line-height: 1rem;
    max-width: calc(100% - 1.5rem);
}

.dynamic-form .dynamic-form__field-validators {
    position: relative;
    min-height: var(--df-validator-reserve);
}

/* Radzen validators hardcode `margin-top: 0.25rem` inline, hence !important — the container above
   already owns the spacing under a control. */
.dynamic-form .dynamic-form__field-validators .rz-message {
    margin-top: 0 !important;
    line-height: 1.2;
}

.dynamic-form .dynamic-form__field-validator-preview {
    font-size: 0.75rem;
    color: var(--rz-text-secondary-color);
    margin-top: 0.25rem;
}

/* --- Design-mode affordances --------------------------------------------
   Declared last so the room reserved for absolutely positioned toolbars wins
   over the legacy --no-padding flags; without it a toolbar would sit on top of
   the first field. Everything in this section is editing chrome only and never
   renders at runtime.
   ------------------------------------------------------------------------- */
.dynamic-form--design .dynamic-form-group {
    padding-top: 2rem;
}

.dynamic-form--design .dynamic-form-group__toolbar {
    position: absolute;
    top: 0.5rem;
    left: 0.5rem;
    display: flex;
    gap: 0.25rem;
    z-index: 5;
    opacity: 0;
    transition: opacity 0.15s ease;
}

.dynamic-form--design .dynamic-form-group:hover:not(:has(.dynamic-form-group:hover)) > .dynamic-form-group__toolbar {
    opacity: 1;
}

.dynamic-form--design .dynamic-form-group__variant-toolbar {
    display: flex;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

.dynamic-form--design .conditional-group-tabs-wrapper {
    padding-top: 2.5rem;
}

.dynamic-form--design .conditional-group-tabs ul[role=tablist] {
    flex-wrap: wrap;
}

/* The design canvas is much narrower than the viewport, so viewport-based
   col-md-* splits can produce unusably narrow columns. In design mode each
   row queries its own width and stacks columns when there is no room to split. */
.dynamic-form--design .dynamic-form__row {
    container-type: inline-size;
}

@container (max-width: 560px) {
    .dynamic-form--design .dynamic-form__column {
        flex: 0 0 100%;
        width: 100%;
        max-width: 100%;
    }
}

.dynamic-form--design .dynamic-form__column-toolbar {
    position: absolute;
    top: -32px;
    right: 0;
    display: flex;
    gap: 0.25rem;
    z-index: 5;
    opacity: 0;
    transition: opacity 0.15s ease;
}

.dynamic-form--design .dynamic-form__column:hover:not(:has(.dynamic-form__column:hover)) > .dynamic-form__column-toolbar {
    opacity: 1;
}

/* Padding here gives the dashed hover outline somewhere to sit. */
.dynamic-form--design .dynamic-form__field-shell {
    padding: 0.25rem;
    border: 1px dashed transparent;
    cursor: grab;
}

.dynamic-form--design .dynamic-form__field-shell.group {
    padding-left: 0;
    padding-right: 0;
}

.dynamic-form--design .dynamic-form__field-shell:active {
    cursor: grabbing;
}

.dynamic-form--design .dynamic-form__field-shell:hover {
    border-color: var(--rz-primary);
}

.dynamic-form--design .dynamic-form__field-shell.drag-over {
    border-color: var(--rz-success);
    background-color: rgba(var(--rz-success-rgb), 0.1);
    border-style: solid;
}

.dynamic-form--design .dynamic-form__field-toolbar {
    position: absolute;
    top: -20px;
    right: 4px;
    display: flex;
    gap: 0.25rem;
    opacity: 0;
    transition: opacity 0.2s ease;
    z-index: 10;
}

.dynamic-form--design .dynamic-form__field-toolbar .drag-handle {
    cursor: grab;
}

.dynamic-form--design .dynamic-form__field-toolbar .drag-handle:active {
    cursor: grabbing;
}

.dynamic-form--design .dynamic-form__field-shell:hover .dynamic-form__field-toolbar {
    opacity: 1;
}

.dynamic-form__empty {
    border: 2px dashed var(--rz-base-400);
    border-radius: 8px;
    padding: 2rem;
    text-align: center;
    color: var(--rz-text-secondary-color);
}

.dynamic-form__empty-icon {
    font-size: 40px;
    display: block;
    margin-bottom: 0.5rem;
}

/* Empty columns and drop targets carry their own height — that is why the
   column body itself no longer needs a min-height, which used to force every
   36px input to occupy 100px at runtime. */
.dynamic-form__empty-field {
    border: 1px dashed var(--rz-base-400);
    padding: 1rem;
    text-align: center;
    cursor: pointer;
    border-radius: 4px;
    color: var(--rz-text-secondary-color);
}

.dynamic-form__drop-zone {
    min-height: 40px;
    border: 2px dashed transparent;
    border-radius: 4px;
    transition: border-color 0.2s ease, background-color 0.2s ease;
    padding: 0.5rem;
}

.dynamic-form__drop-zone.drag-over {
    border-color: var(--rz-success);
    background-color: rgba(var(--rz-success-rgb), 0.1);
}

.dynamic-form__add-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.5rem;
}

.dynamic-form__add-action {
    flex: 1 1 160px;
}

/* --- Narrow viewports ---------------------------------------------------
   Last in the file so it overrides an explicit density on the form root: there
   is no room for generous gutters on a phone. (The previous attempt at this
   set --bs-gutter-x, a Bootstrap 5 variable the vendored Bootstrap 4 ignores.)
   ------------------------------------------------------------------------- */
@media (max-width: 768px) {
    .dynamic-form {
        --df-gutter: 0.25rem;
        --df-row-gap: 0.5rem;
        --df-field-gap: 0.375rem;
        --df-pad: 0.5rem;
        --df-label-gap: 0.5rem;
    }
}
