/* json-component-colors.css used to be pulled in here with
   @import url('json-component-colors.css'). It is now a <link> in
   App.razor, immediately above this file, because an @import resolves a
   plain relative URL that MapStaticAssets cannot fingerprint — so that
   file alone kept being served from a stale browser cache after a change,
   while every other stylesheet updated.

   Cascade is unchanged: an @import at the top of a file places the imported
   rules immediately before that file's own, which is exactly where a <link>
   directly above it lands. It also declares nothing but --token-* custom
   properties on :root, so its position relative to MudBlazor.min.css is
   immaterial — Mud defines no such variables to conflict with. */

/* ── JSON editor layout (docs/ux-shape.md entry 15) ─────────────────────────────────────────────
   One home for how a JSON editor tree looks. Two chromes, both rendered by one component each:
   JsonSection (`.json-section`: a heading and a wrapping `.json-flow` of children) and JsonField
   (`.json-field`: a label above one control). Depth styling, field widths, the hover affordances
   and density all attach to these classes here, once; no component writes chrome of its own.

   Only depth 0 is a boxed surface. Below it the hierarchy is carried by the heading's size and a
   rule that changes with depth — a hairline, an accent rule, a dashed rule — because a border, a
   fill and a radius each say "separate object", and stamping all three on every node is what made
   the old editor read as boxes inside boxes. Depth 3 and deeper share the last look and the indent
   stops growing, so a deep convention tree is not squeezed into a narrow column. */

.json-section {
    display: flex;
    flex-direction: column;
    gap: var(--cpd-space-sm);
    min-width: 0;
}

.json-section-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--cpd-space-xs) var(--cpd-space-sm);
    min-width: 0;
}

.json-section-label {
    margin: 0;
    color: var(--cpd-text);
    font-family: var(--cpd-font-sans);
    font-size: var(--json-label-size);
    font-weight: var(--json-label-weight);
    line-height: 1.3;
}

/* The value's text form beside its heading: a glance at what the section holds, never a control. */
.json-section-summary {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    color: var(--cpd-text-muted);
    font-family: var(--cpd-font-mono);
    font-size: var(--cpd-text-0);
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The flow: fields pack left to right and wrap. Every field is one label line above one control line
   of --cpd-control-h (below), so top-aligned fields share both lines, and what a field adds beneath
   its control (an error line, the notional's slider) hangs below rather than lifting the control out
   of the row. Anything that is not a field (a structured section, a pillar row, a grid, a list, a
   note, the formula editor) starts its own full row. No component wraps its children in a row of its
   own: they render straight into this flow (a section's, or a JsonList item's), so a section is never
   squeezed beside a field. */
.json-flow {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--cpd-space-sm) var(--cpd-space-md);
    min-width: 0;
}

.json-flow > .json-section {
    flex: 1 1 100%;
}

.json-flow > :not(.json-field):not(.json-separator):not(.json-present) {
    flex: 1 1 100%;
    min-width: 0;
}

/* ── Depth table ── */

.json-section.depth-0 {
    gap: var(--cpd-space-md);
    padding: var(--cpd-space-md) var(--cpd-space-lg) var(--cpd-space-lg);
    background: var(--cpd-surface-1);
    border: 1px solid var(--cpd-border);
    border-radius: var(--cpd-radius-lg);
}

.json-section.depth-0 > .json-flow {
    gap: var(--cpd-space-md);
}

/* Bracketed by a hairline above and below: depth 1 has no left rule to show where it ends, and a
   sibling field rendered after it (a trade's date and rate after its instrument) must not read as
   one of its fields. */
.json-section.depth-1 {
    padding-block: var(--cpd-space-md);
    border-top: 1px solid var(--cpd-border);
    border-bottom: 1px solid var(--cpd-border);
}

/* The rule runs down the section's children, not its heading: the heading sits on top of the rule it
   owns, so the indent reads as "these belong to the label above". */
.json-section.depth-2 > .json-flow {
    padding-left: var(--cpd-space-md);
    border-left: 2px solid rgba(var(--cpd-gold-rgb), 0.5);
}

.json-section.depth-3 > .json-flow {
    padding-left: var(--cpd-space-sm);
    border-left: 1px dashed var(--cpd-border-strong);
}

/* Labels per depth (docs/ux-shape.md entry 15): a field's label and a section's heading at the same
   depth share one size and weight, so siblings read as peers and depth reads as hierarchy. A field's
   data-depth is the depth its section cascades and a section's depth class its own, so a field and a
   section side by side in one flow match. Depth 3 and deeper share the last look. Colour is not part
   of the table: it comes from the token role (below). The root keeps its title. */
.json-field,
.json-section {
    --json-label-size: var(--cpd-text-0);
    --json-label-weight: 500;
}

.json-section.depth-0 {
    --json-label-size: var(--cpd-text-3);
    --json-label-weight: 600;
}

.json-field[data-depth="1"],
.json-section.depth-1 {
    --json-label-size: var(--cpd-text-1);
    --json-label-weight: 600;
}

.json-field[data-depth="2"],
.json-section.depth-2 {
    --json-label-size: var(--cpd-text-0);
    --json-label-weight: 600;
}

/* Below the root a section's heading is set exactly like a field's label at its depth, so a section
   and a composite field beside it read as peers; what tells them apart is the rule and the indent
   above. */
.json-section:not(.depth-0) > .json-section-head > .json-section-label {
    color: var(--cpd-text-muted);
}

.json-section[data-role]:where(:not(.depth-0)) > .json-section-head > .json-section-label {
    color: var(--token-color, var(--cpd-text-muted));
}

/* ── Fields ──
   One label line above one control line (docs/ux-shape.md entry 15). The field's own font size is
   what `1ch` below is measured in, so a width declared in characters means characters of a
   control's value. */
.json-field {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--cpd-space-xs);
    max-width: 100%;
    min-width: 0;
    font-size: var(--cpd-text-2);
}

/* The label line is exactly one line of label text tall at its depth's label size, whatever else it
   holds (the presence checkbox and the accessories are centred on that one line), so the control line
   starts at the same height in every field of a row: the fields of one flow share a depth. */
.json-field-head {
    display: flex;
    align-items: center;
    gap: var(--cpd-space-xs);
    height: calc(var(--json-label-size) * 1.3);
    min-width: 0;
}

.json-field-label {
    overflow: hidden;
    color: var(--cpd-text-muted);
    font-size: var(--json-label-size);
    font-weight: var(--json-label-weight);
    line-height: 1.3;
    text-overflow: ellipsis;
    white-space: nowrap;
    user-select: none;
}

/* Width classes (FieldWidth). The control is everything in the field below its head. Its inline
   size is contained, so a control's own intrinsic width (a native <input> is ~20 characters wide
   whatever it holds) never sizes the field: a fixed field is exactly as wide as its declared
   characters, or its label if that is longer, and a greedy one grows from its minimum to fill the
   row. A fit field (every composite) is as wide as its control row, so it is not contained. */
.json-field:not(.w-fit) > :not(.json-field-head):not(.json-field-row) {
    contain: inline-size;
}

/* The declared width: --ch characters of value plus the control's own padding and border. */
.json-field {
    --field-w: calc(var(--ch, 16) * 1ch + 2 * var(--cpd-space-sm) + var(--cpd-space-xs));
}

.json-field.w-fixed {
    flex: 0 0 auto;
}

.json-field.w-fixed > :not(.json-field-head) {
    min-width: var(--field-w);
}

.json-field.w-greedy {
    flex: 1 1 var(--field-w);
    min-width: min(100%, var(--field-w));
}

/* Greedy means the control stretches, never the box alone: the control fills the field, and so does
   the text box or select that is the control, or that leads a control row (a number before its
   unit, the notional box). */
.json-field.w-greedy > :not(.json-field-head) {
    width: 100%;
}

.json-field.w-greedy > :is(.form-input, select),
.json-field.w-greedy > .property-detail-row > :first-child,
.json-field.w-greedy > .property-detail-row > :first-child :is(.form-input, select, .mud-input-control) {
    flex: 1 1 auto;
    width: 100%;
    min-width: 0;
}

.json-field.w-fit {
    flex: 0 0 auto;
}

/* A small composite value (ObjectInput AsField): its sub-fields in one row that never wraps, a little
   tighter than the flow so they read as one value, sitting on the composite's control line. */
.json-field-row {
    display: inline-flex;
    flex-wrap: nowrap;
    align-items: flex-start;
    gap: var(--cpd-space-xs) var(--cpd-space-sm);
    min-width: 0;
    min-height: var(--cpd-control-h);
}

/* A composite shows one label, its own. A sub-field (the "JsonInComposite" cascade) keeps its label
   as the group's accessible name and hides it on screen, and its label line takes no room: its
   accessories, if any, sit above its control in the composite's own label line. */
.json-field.in-composite {
    gap: 0;
}

.json-field.in-composite > .json-field-head {
    position: absolute;
    right: 0;
    bottom: 100%;
}

.json-field.in-composite > .json-field-head > .json-field-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* ── Controls in the chrome ──
   One control height, --cpd-control-h (app.css, re-pointed by density), for every control in a
   field, a section heading or a list: the text box, a native select, the number box, the notional
   box, the date field and a checkbox, so neighbours share a baseline. A text box's text is one line
   box filling its height, matched by repr.css's coloured overlay laid over it character for
   character. A text input is `.form-input` (input-component-common.css) wherever it sits; inside a
   field it fills the control box, so its width is the field's and not the browser's default for an
   <input>. */
:is(.json-field, .json-section-head, .json-list) :is(.form-input:not(textarea), select, input[type="number"], input.notional-box),
.json-field .repr-overlay-wrapper > .repr-overlay {
    height: var(--cpd-control-h);
    padding-block: 0;
    line-height: calc(var(--cpd-control-h) - 2px);
}

.json-field .form-input {
    width: 100%;
    min-width: 0;
}

/* A number is typed, not stepped: the browser's spin buttons are hidden. */
:is(.json-field, .json-list) input[type="number"] {
    appearance: textfield;
}

:is(.json-field, .json-list) input[type="number"]::-webkit-inner-spin-button,
:is(.json-field, .json-list) input[type="number"]::-webkit-outer-spin-button {
    margin: 0;
    appearance: none;
}

/* A native <select> gets the text box's look in a field, in a composite's row, and in a section
   heading (a generator's or a curve type's case selector). */
.json-field select,
.json-section-head > select {
    padding-inline: var(--cpd-space-sm);
    background: var(--cpd-surface-2);
    border: 1px solid var(--cpd-border-strong);
    border-radius: var(--cpd-radius-sm);
    color: var(--cpd-text);
    font-family: var(--cpd-font-mono);
    font-size: var(--cpd-text-2);
    cursor: pointer;
}

.json-field select {
    width: 100%;
    min-width: 0;
}

/* In a composite's row each control is as wide as its content, so the row is the sum of its parts
   (above); stretching one to 100% would push its siblings out of the field. */
.json-field .json-field-row > select,
.json-field .json-field-row > .form-input {
    width: auto;
    flex: 0 0 auto;
}

/* A typed box in a composite's row holds a short count (a tenor's 3, a lag's 2): `width: auto` would
   leave it at the browser's ~20-character default and widen the whole composite. */
.json-field .json-field-row input.form-input[type="number"] {
    width: calc(6ch + 2 * var(--cpd-space-sm));
}

.json-section-head > select {
    font-size: var(--cpd-text-1);
    /* sandbox-page-base.css gives every `.form-input` the full width of its container; a case
       selector beside its heading is as wide as its longest option instead. */
    width: auto;
    flex: 0 1 auto;
}

.json-field select:focus,
.json-section-head > select:focus,
.json-field .form-input:focus {
    outline: none;
    border-color: var(--token-color, var(--cpd-gold));
    box-shadow: 0 0 0 2px var(--cpd-gold-ring);
}

/* A text field fills its control box. */
.json-field .repr-overlay-wrapper {
    display: flex;
    min-width: 0;
}

/* The notional: an always-editable text box (NotionalInput) with the log slider beneath it. It is a
   `.form-input` like any other and takes the field's width (the notional is greedy); its figure is
   right-aligned in the notional's colour, as a figure is everywhere (docs/ux-shape.md entry 6). */
.json-field input.notional-box {
    width: 100%;
    min-width: 0;
    color: var(--token-color, var(--cpd-text));
    font-family: var(--cpd-font-mono);
    text-align: right;
}

/* The date field (DateField, a MudTextField) set as a text box: the box is --cpd-control-h tall with
   the text box's border and fill, Mud's underline and label reserve go, and its calendar button fits
   inside the box. Its error line stays beneath it. Mud's stylesheet loads after this one, so each
   selector names the Mud classes it overrides. */
.json-field .mud-input-control {
    margin: 0;
    min-width: 0;
}

.json-field .mud-input-control .mud-input.mud-input-text {
    height: var(--cpd-control-h);
    margin-top: 0;
    padding-inline: var(--cpd-space-sm) var(--cpd-space-xs);
    background: var(--cpd-surface-2);
    border: 1px solid var(--cpd-border-strong);
    border-radius: var(--cpd-radius-sm);
}

.json-field .mud-input-control .mud-input.mud-input-text > input.mud-input-root {
    height: 100%;
    padding: 0;
    color: var(--cpd-text);
    font-family: var(--cpd-font-mono);
    font-size: var(--cpd-text-2);
    line-height: 1.4;
}

.json-field .mud-input-control .mud-input.mud-input-underline::before,
.json-field .mud-input-control .mud-input.mud-input-underline::after {
    display: none;
}

.json-field .mud-input-control .mud-input.mud-input-text:focus-within {
    border-color: var(--token-color, var(--cpd-gold));
    box-shadow: 0 0 0 2px var(--cpd-gold-ring);
}

.json-field .mud-input-control .mud-input.mud-input-text.mud-input-error {
    border-color: var(--cpd-error);
}

.json-field .mud-input-control .mud-input-adornment .mud-icon-button {
    padding: var(--cpd-space-xs);
}

.json-field .mud-input-control .mud-input-adornment .mud-icon-root {
    font-size: var(--cpd-text-3);
}

/* A checkbox that is a field's control (BoolInput, Resetting Notional) is centred in the control
   line, so it sits on its neighbours' baseline. The presence checkbox is the label line's (below). */
:is(.json-field, .json-list) input[type="checkbox"]:not(.json-present) {
    width: var(--cpd-space-lg);
    height: var(--cpd-space-lg);
    margin: calc((var(--cpd-control-h) - var(--cpd-space-lg)) / 2) 0;
    accent-color: var(--token-color, var(--cpd-gold));
    cursor: pointer;
}

/* ── Presence: an optional value's checkbox ──
   Part of the field, not an accessory: first in the label line of a field or the heading of a
   section, always visible, because it is the only way to turn an absent value back on. */
.json-present {
    flex: 0 0 auto;
    width: var(--cpd-space-md);
    height: var(--cpd-space-md);
    margin: 0;
    accent-color: var(--token-color, var(--cpd-gold));
    cursor: pointer;
}

/* In a section's heading the row is top-aligned, so the checkbox is centred on the heading's label
   line by its margin rather than on the row. An inline optional object has no chrome of its own, so
   its checkbox stands in the enclosing flow before its fields, centred the same way on their label
   line — whose size is that of the flow's children, one depth below the enclosing section. */
.json-section-head > .json-present,
.json-flow > .json-present {
    align-self: flex-start;
    margin-block: calc((var(--json-label-size) * 1.3 - var(--cpd-space-md)) / 2);
}

.json-section.depth-0 > .json-flow > .json-present {
    --json-label-size: var(--cpd-text-1);
}

.json-section:not(.depth-0) > .json-flow > .json-present {
    --json-label-size: var(--cpd-text-0);
}

.json-present:focus-visible {
    outline: 2px solid var(--cpd-focus);
    outline-offset: 1px;
}

/* ── Accessories: provenance chip, ref capture ──
   Shown on hover or focus of their field or heading, so they do not clutter every field at rest.
   Faded rather than removed, so the keyboard still reaches them and focusing one reveals it. Always
   shown on a device that cannot hover. */
.json-acc {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    gap: var(--cpd-space-xs);
    margin-left: auto;
    opacity: 0;
    transition: opacity var(--cpd-transition-fast);
}

.json-field:hover > .json-field-head > .json-acc,
.json-field:focus-within > .json-field-head > .json-acc,
.json-section-head:hover > .json-acc,
.json-section-head:focus-within > .json-acc {
    opacity: 1;
}

@media (hover: none) {
    .json-acc {
        opacity: 1;
    }
}

/* An optional value that is absent: its head stays live (the presence checkbox is there), its
   control does not. */
.json-field.disabled > :not(.json-field-head),
.json-section.disabled > .json-flow {
    opacity: var(--cpd-disabled-opacity);
    pointer-events: none;
    user-select: none;
}

/* ── Token roles ──
   A field's role colours its label, from json-component-colors.css; nothing else of the chrome is
   tinted. The same role variables stay available to what a field or section holds (a focus ring,
   the notional readout), reset on every node so a role-less child never inherits its parent's. */
.json-field,
.json-section {
    --token-color: initial;
    --token-bg: initial;
    --token-border: initial;
}

.json-field[data-role="position"],     .json-section[data-role="position"]     { --token-color: var(--token-position);     --token-bg: var(--token-position-bg);     --token-border: var(--token-position-border);     }
.json-field[data-role="notional"],     .json-section[data-role="notional"]     { --token-color: var(--token-notional);     --token-bg: var(--token-notional-bg);     --token-border: var(--token-notional-border);     }
.json-field[data-role="instrument"],   .json-section[data-role="instrument"]   { --token-color: var(--token-instrument);   --token-bg: var(--token-instrument-bg);   --token-border: var(--token-instrument-border);   }
.json-field[data-role="rate"],         .json-section[data-role="rate"]         { --token-color: var(--token-rate);         --token-bg: var(--token-rate-bg);         --token-border: var(--token-rate-border);         }
.json-field[data-role="date"],         .json-section[data-role="date"]         { --token-color: var(--token-date);         --token-bg: var(--token-date-bg);         --token-border: var(--token-date-border);         }
.json-field[data-role="type"],         .json-section[data-role="type"]         { --token-color: var(--token-type);         --token-bg: var(--token-type-bg);         --token-border: var(--token-type-border);         }
.json-field[data-role="end-of-month"], .json-section[data-role="end-of-month"] { --token-color: var(--token-end-of-month); --token-bg: var(--token-end-of-month-bg); --token-border: var(--token-end-of-month-border); }
.json-field[data-role="convention"],   .json-section[data-role="convention"]   { --token-color: var(--token-instrument);   --token-bg: var(--token-instrument-bg);   --token-border: var(--token-instrument-border);   }
.json-field[data-role="accrual"],      .json-section[data-role="accrual"]      { --token-color: var(--token-rate);         --token-bg: var(--token-rate-bg);         --token-border: var(--token-rate-border);         }
.json-field[data-role="tenor"],        .json-section[data-role="tenor"]        { --token-color: var(--token-rate);         --token-bg: var(--token-rate-bg);         --token-border: var(--token-rate-border);         }
.json-field[data-role="rate-index"],   .json-section[data-role="rate-index"]   { --token-color: var(--token-rate);         --token-bg: var(--token-rate-bg);         --token-border: var(--token-rate-border);         }
.json-field[data-role="calendar"],     .json-section[data-role="calendar"]     { --token-color: var(--token-date);         --token-bg: var(--token-date-bg);         --token-border: var(--token-date-border);         }
.json-field[data-role="trade"],        .json-section[data-role="trade"]        { --token-color: var(--cpd-gold);           --token-bg: var(--cpd-gold-faint);        --token-border: var(--cpd-gold-strong);           }
.json-field[data-role="ref"],          .json-section[data-role="ref"]          { --token-color: var(--cpd-gold);           --token-bg: var(--cpd-gold-faint);        --token-border: var(--cpd-gold-strong);           }
.json-field[data-role="generator"],    .json-section[data-role="generator"]    { --token-color: var(--token-generator);    --token-bg: var(--token-generator-bg);    --token-border: var(--token-generator-border);    }
.json-field[data-role="schedule"],     .json-section[data-role="schedule"]     { --token-color: var(--token-schedule);     --token-bg: var(--token-schedule-bg);     --token-border: var(--token-schedule-border);     }

.json-field[data-role] > .json-field-head > .json-field-label {
    color: var(--token-color, var(--cpd-text-muted));
}

/* A node that is a `{ "$var": … }` hole: its label in the hole colour, matching HoleChip. */
.json-field.is-hole > .json-field-head > .json-field-label,
.json-section.is-hole > .json-section-head > .json-section-label {
    color: var(--token-type);
}

/* ── Text forms ──
   The whole value as one line of text at the root (the trade capture command line) reads as the
   page's main input, so it gets the roomier inset; it keeps the one control height, and elsewhere a
   text field is an ordinary control. */
.json-component-row > .json-field.is-text .form-input,
.json-component-row > .json-field.is-text .repr-overlay-wrapper > .repr-overlay {
    padding-inline: var(--cpd-space-md);
    font-size: var(--cpd-text-2);
    letter-spacing: 0.01em;
}

.json-component-row > .json-field.is-text .form-input:focus {
    border-color: var(--token-color, var(--cpd-gold));
    box-shadow: 0 0 0 2px var(--cpd-gold-ring);
    background: var(--cpd-surface-0);
}

.json-component-row {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--cpd-space-sm);
    align-items: stretch;
}

.json-component-row > .json-field,
.json-component-row > .json-section {
    width: 100%;
    min-width: 0;
}

/* The "on" / "@" words between a trade's top-level fields. */
.json-separator {
    display: flex;
    align-items: flex-end;
    align-self: flex-end;
    padding: 0 var(--cpd-space-xs) var(--cpd-space-xs);
    color: var(--token-separator);
    font-family: var(--cpd-font-mono);
    font-size: var(--cpd-text-2);
    font-weight: 600;
    opacity: 0.7;
}

/* ── A list in a JSON editor (JsonList, docs/ux-shape.md entry 16) ──
   The formula editor's list, made the one shape: the items stacked, a thin insert line before the
   first item and after every item, and a small ✕ beside each. The insert lines are quiet until the
   list is hovered or focused, because there is one between every pair of items and at both ends; at
   that density a real button would read as the content. The ✕ stays faintly visible so an item's
   remove is always discoverable, and comes up fully on hover or focus. An empty list shows its one
   insert line at rest, since it is then the only thing the list has to offer. */
:root {
    --json-list-remove-w: var(--cpd-space-xl);
}

.json-list {
    display: flex;
    flex: 1 1 100%;
    flex-direction: column;
    min-width: 0;
}

.json-list-insert {
    position: relative;
    display: block;
    width: 100%;
    height: var(--cpd-space-md);
    margin: 0;
    padding: 0;
    background: none;
    border: none;
    border-radius: var(--cpd-radius-sm);
    cursor: pointer;
}

/* The line, starting after the "+" so the two never overlap. */
.json-list-insert::before {
    content: "";
    position: absolute;
    top: 50%;
    right: 0;
    left: var(--cpd-space-lg);
    border-top: 1px dashed var(--token-border, var(--cpd-border-strong));
    opacity: 0;
    transition: opacity var(--cpd-transition-fast);
}

.json-list-insert::after {
    content: "+";
    position: absolute;
    top: 50%;
    left: 0;
    transform: translateY(-50%);
    color: var(--token-color, var(--cpd-text-muted));
    font-family: var(--cpd-font-mono);
    font-size: var(--cpd-text-1);
    line-height: 1;
    opacity: 0;
    transition: opacity var(--cpd-transition-fast);
}

.json-list:hover > .json-list-insert::before,
.json-list:hover > .json-list-insert::after,
.json-list:focus-within > .json-list-insert::before,
.json-list:focus-within > .json-list-insert::after,
.json-list:not(:has(> .json-list-row)) > .json-list-insert::before,
.json-list:not(:has(> .json-list-row)) > .json-list-insert::after {
    opacity: 0.4;
}

.json-list-insert:hover::before,
.json-list-insert:hover::after,
.json-list-insert:focus-visible::before,
.json-list-insert:focus-visible::after {
    opacity: 1;
}

.json-list-insert:focus-visible {
    outline: 2px solid var(--cpd-focus);
    outline-offset: 1px;
}

/* One item and its ✕, the ✕ on the item's first line. */
.json-list-row {
    display: flex;
    align-items: flex-start;
    gap: var(--cpd-space-sm);
    min-width: 0;
}

.json-list-item {
    flex: 1 1 auto;
    min-width: 0;
}

.json-list-remove {
    flex: 0 0 var(--json-list-remove-w);
    height: var(--cpd-control-h);
    padding: 0;
    background: none;
    border: none;
    border-radius: var(--cpd-radius-sm);
    color: var(--cpd-text-muted);
    font-family: var(--cpd-font-mono);
    font-size: var(--cpd-text-1);
    line-height: 1;
    cursor: pointer;
    opacity: 0.4;
    transition: opacity var(--cpd-transition-fast);
}

.json-list-row:hover > .json-list-remove,
.json-list-row:focus-within > .json-list-remove {
    opacity: 0.8;
}

.json-list-remove:hover:not(:disabled),
.json-list-remove:focus-visible {
    opacity: 1;
    color: var(--cpd-error);
    background: rgba(var(--cpd-contrast-rgb), 0.08);
}

.json-list-remove:focus-visible {
    outline: 2px solid var(--cpd-focus);
    outline-offset: 1px;
}

/* At the list's minimum the ✕ cannot succeed: it stays faint, does not react, and its tooltip says
   why (docs/ux-shape.md entry 8). */
.json-list-remove:disabled,
.json-list-row:hover > .json-list-remove:disabled,
.json-list-row:focus-within > .json-list-remove:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

@media (hover: none) {
    .json-list > .json-list-insert::before,
    .json-list > .json-list-insert::after {
        opacity: 0.4;
    }

    .json-list-remove {
        opacity: 0.8;
    }
}

/* ── Curve-spec notes and pillar rows ── */

/* A value the editor derives rather than asks for, or a caution about the spec: read-only prose. */
.derived-value {
    margin: 0;
    color: var(--cpd-text-muted);
    font-size: var(--cpd-text-1);
}

.derived-value[data-role="warning"] {
    color: var(--cpd-warning);
}

/* One pillar: its instrument, then its Remove action at the end of the row. */
.curve-pillar-row {
    display: flex;
    align-items: flex-end;
    gap: var(--cpd-space-sm);
    min-width: 0;
}

.curve-pillar-row > :first-child {
    flex: 1 1 auto;
    min-width: 0;
}

/* ReprHelper.ToMarkup's wrapper: a value's coloured text form inline (GrammarExamples). */
.token-repr-summary {
    color: var(--token-color);
    font-family: var(--cpd-font-mono);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

.token-repr-summary::before {
    content: ":\00a0";
    opacity: 0.5;
}

/* ── Text that does not read or print: TextReprInput's error line (docs/ux-shape.md §7) ── */

.repr-error {
    color: var(--cpd-error);
    font-size: var(--cpd-text-0);
    padding-top: var(--cpd-space-xs);
    white-space: normal;
}

/* ── RefWithOverrides incomplete/unresolved state ──────────── */

.ref-incomplete {
    color: rgba(255, 100, 100, 0.75);
    font-size: 0.8rem;
    font-style: italic;
    padding: 0.2rem 0;
}

/* ObjectInput's "Resolving reference…" placeholder (W5a). It stands where the fields would be while
   a `$ref` is in flight, which on the server never happens — the in-process IRefClient answers from
   the warm cache without yielding — so this only ever shows on a WebAssembly page. Small, italic and
   muted so it reads as chrome rather than as a value. */
.token-resolving {
    padding: var(--cpd-space-xs) 0;
    color: var(--cpd-text-muted);
    font-size: var(--cpd-text-0);
    font-style: italic;
}

/* ── C6: provenance chips, holes, ref capture (docs/conventions-editor-plan.md) ───── */

/* ProvenanceChip — one pill per InputComponentBase leaf/container, cascaded by a ConventionEditScope
   (the rule editor). Kind is chosen in ProvenanceChip.razor.cs: .mine (this rule's own draft patch)
   beats a hole (.var) beats the resolve's own provenance (.rule), with a `$ref` suffix when
   the value travelled through one. */
.provenance-chip {
    display: inline-flex;
    align-items: center;
    font-family: var(--cpd-font-mono);
    font-size: 10.5px;
    line-height: 1;
    padding: 2px 7px;
    border-radius: 999px;
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

.provenance-chip.rule {
    border: 1px solid rgba(var(--token-instrument-rgb), 0.5);
    color: rgb(var(--token-instrument-rgb));
}

.provenance-chip.ref {
    border: 1px solid rgba(var(--cpd-gold-rgb), 0.45);
    color: var(--cpd-gold);
}

.provenance-chip.var {
    border: 1px dashed rgba(var(--token-type-rgb), 0.6);
    color: rgb(var(--token-type-rgb));
}

.provenance-chip.mine {
    border: 1px solid transparent;
    background: var(--cpd-gold);
    color: #061221;
    font-weight: 600;
}

/* HoleChip — a `{ "$var": name }` node. Purple-dashed, matching the .var chip, with the sample
   instrument's preview value (when a ConventionEditScope is cascaded) shown faded underneath. */
.json-hole {
    display: flex;
    flex-direction: column;
    gap: var(--cpd-space-xs);
    padding: var(--cpd-space-xs) 0;
}

.json-hole .hole-preview-value {
    font-family: var(--cpd-font-mono);
    font-size: 0.75rem;
    color: rgba(255, 255, 255, 0.45);
}

/* RefCaptureButton — the gold "link this field" glyph, rendered wherever a RefCaptureScope is
   cascaded. */
.ref-capture-button {
    color: var(--cpd-gold);
    opacity: 0.75;
}

.ref-capture-button:hover {
    opacity: 1;
}

.ref-capture-search {
    min-width: 220px;
    padding: 0.25rem;
}

/* ProvenancePopover — the layer stack for one field's pointer. */
.provenance-popover {
    min-width: 260px;
    max-width: 360px;
    padding: 0.6rem 0.75rem;
    font-family: var(--cpd-font-mono);
    font-size: 0.75rem;
}

.provenance-popover-header {
    font-size: 0.7rem;
    opacity: 0.6;
    margin-bottom: 0.4rem;
}

.provenance-layer-stack {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.provenance-layer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
}

.provenance-layer.not-writing {
    opacity: 0.45;
}

.provenance-layer-note {
    font-size: 0.65rem;
    font-style: italic;
    opacity: 0.7;
}

.provenance-layer-open {
    background: none;
    border: none;
    color: var(--cpd-gold);
    cursor: pointer;
    font-family: inherit;
    font-size: inherit;
    padding: 0;
}

/* Grammar examples */

.grammar-examples {
    margin-top: 0.4rem;
    font-family: var(--cpd-font-mono);
    font-size: 0.85rem;
}

.grammar-examples > summary {
    cursor: pointer;
    color: var(--cpd-text-muted);
    font-size: 0.8rem;
}

.grammar-examples-group {
    margin: 0.5rem 0 0.75rem 0.5rem;
    overflow-x: auto;
}

.grammar-examples-group > h4 {
    margin: 0 0 0.25rem;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--cpd-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.grammar-example {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    white-space: nowrap;
    padding: 0.1rem 0;
}

.grammar-example-label {
    flex: 0 0 auto;
    color: var(--cpd-text-muted);
    font-size: 0.75rem;
}

.grammar-example-text {
    font-family: var(--cpd-font-mono);
}

.grammar-example-load {
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    font: inherit;
    cursor: pointer;
}

.grammar-example-load:hover .grammar-example-text {
    text-decoration: underline;
}

/* --- Formula equation editor -------------------------------------------------
   The block spells `settle <s> in <ccy> { <binding>; ...; return <f> }`, and it is laid out to
   read as that text rather than as a stack of form cards: one grid shared by every line, so the
   keyword, the name, the `=` and the value sit in the same columns all the way down. The leaf
   editors keep their controls (date pickers, dropdowns) but lose their section rules and their
   labels -- inside a line of code the keywords ARE the labels, and a second, louder set of them
   is what made the first attempt unreadable. */

/* The section is built for the stacked layout every other section uses: a heading above, then its
   flow, separated by the section's flex `gap`. In this editor the block starts immediately under
   the heading, so the gap is tightened here only, keyed off the wrapper, so no other section is
   affected. */
.json-section:has(> .json-flow > .formula-editor) {
    gap: var(--cpd-space-xs);
}

.formula-editor {
    display: flex;
    flex: 1 1 100%;
    flex-direction: column;
    min-width: 0;
}

.formula-line .property-detail-row,
.formula-header .property-detail-row {
    gap: 0.3rem;
}

.formula-header {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 0.2rem;
}

/* The header is one sentence -- `settle <when> in <ccy>` -- so its fields lay their label beside
   their control rather than above it. Stacked, the two of them wrapped into three ragged rows. */
.formula-header .json-field {
    flex-direction: row;
    align-items: center;
    flex-wrap: nowrap;
    gap: var(--cpd-space-sm);
}

.formula-header .json-acc {
    margin-left: 0;
}

.formula-header .json-field-head {
    flex: 0 0 auto;
}

.formula-block {
    display: flex;
    flex-direction: column;
    font-family: var(--cpd-font-mono);
    border-left: 1px solid var(--token-border);
    padding-left: 0.75rem;
    margin-top: 0.2rem;
}

.formula-line {
    display: grid;
    grid-template-columns: 4.75rem minmax(6rem, 10rem) 0.75rem minmax(10rem, 1fr) 1.1rem;
    align-items: center;
    column-gap: 0.4rem;
    min-height: 1.75rem;
}

/* `return` binds no name, so its expression starts where the names start and runs to the end of
   the value column. It is the longest text in the block -- the whole payoff -- and confining it to
   the value column alone gave the one line that needs the most room the least of it. */
.formula-return-line > .formula-keyword {
    grid-column: 1;
}

.formula-return-line > .formula-slot-value {
    grid-column: 2 / span 3;
}

/* The bindings are a JsonList, whose rows end in a remove ✕ (below); `return` is not an item, so it
   takes the ✕'s width and the row gap as padding, and its status tick stays in the ticks' column. */
.formula-block > .formula-return-line {
    padding-right: calc(var(--json-list-remove-w) + var(--cpd-space-sm));
}

.formula-slot {
    min-width: 0;
}

.formula-slot-value {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.3rem;
}

.formula-slot-value > .json-field,
.formula-slot-value > .json-section {
    flex: 1 1 8rem;
}

.formula-keyword,
.formula-op {
    color: var(--cpd-text-muted);
    font-family: var(--cpd-font-mono);
    font-size: 0.85rem;
    user-select: none;
}

.formula-op {
    text-align: center;
    color: var(--cpd-text-muted);
}

/* Strip the depth rules. The leaves below are ordinary fields and sections -- the structured
   controls are what keep the date pickers and the currency dropdown -- so only a section's indent
   and rule are removed: inside a line of code they would read as a nested block. */
.formula-line .json-section,
.formula-header .json-section {
    padding: 0;
    border: none;
    gap: 0;
}

.formula-line .json-field,
.formula-header .json-field {
    gap: 0;
}

/* Visually hidden rather than removed: the line's keywords carry the meaning on screen, but the
   labels are still the only text naming these fields to a screen reader. */
.formula-line .json-field-label,
.formula-line .json-section-label,
.formula-line .json-section-summary {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.formula-header .json-field-label {
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

/* A date picker is 10 characters of content. Mud sizes its inputs for a form field, which at two
   pickers plus a currency was enough to wrap the header onto a second line. */
.formula-line .mud-picker,
.formula-header .mud-picker {
    min-width: 0;
}

.formula-line .mud-picker .mud-input-control,
.formula-header .mud-picker .mud-input-control {
    width: 9.5rem;
}

.formula-line .json-flow,
.formula-header .json-flow {
    gap: var(--cpd-space-xs);
    align-items: center;
}

/* Fields read as slots in a line of code: no border until they are focused. */
.formula-line .form-input,
.formula-header .form-input,
.formula-line input[type="date"],
.formula-header input[type="date"] {
    width: 100%;
    min-width: 0;
    padding: 0.2rem 0.4rem;
    font-family: var(--cpd-font-mono);
    font-size: 0.85rem;
    line-height: 1.3;
    color: var(--cpd-text);
    background: rgba(var(--cpd-contrast-rgb), 0.05);
    border: 1px solid transparent;
    border-radius: 3px;
}

.formula-line .form-input:focus,
.formula-header .form-input:focus,
.formula-line input[type="date"]:focus,
.formula-header input[type="date"]:focus {
    outline: none;
    border-color: var(--token-color);
    background: var(--cpd-surface-0);
}

.formula-keyword-select {
    width: 100%;
    padding: 0.15rem 0.2rem;
    color: var(--token-separator);
    background: none;
    border: 1px solid transparent;
    border-radius: 3px;
    font-family: var(--cpd-font-mono);
    font-size: 0.85rem;
    cursor: pointer;
    appearance: none;
}

.formula-keyword-select:hover,
.formula-keyword-select:focus {
    outline: none;
    border-color: var(--token-border);
    color: var(--cpd-text);
}

.formula-status {
    font-family: var(--cpd-font-mono);
    font-size: 0.8rem;
    line-height: 1;
    text-align: center;
    cursor: help;
}

.formula-status-ok {
    color: var(--cpd-success, #4caf50);
    opacity: 0.55;
}

.formula-status-error {
    color: var(--cpd-error, #ef5350);
}

.formula-chip-remove,
.formula-chip-add {
    padding: 0.05rem 0.25rem;
    color: var(--cpd-text-muted);
    background: none;
    border: none;
    border-radius: 3px;
    font-family: var(--cpd-font-mono);
    font-size: 0.9rem;
    line-height: 1;
    cursor: pointer;
    opacity: 0.4;
    transition: opacity 0.15s ease;
}

.formula-line:hover .formula-chip-remove,
.formula-line:hover .formula-chip-add {
    opacity: 0.8;
}

.formula-chip-remove:hover {
    opacity: 1;
    color: var(--cpd-error, #ef5350);
    background: rgba(var(--cpd-contrast-rgb), 0.08);
}

.formula-chip-add:hover {
    opacity: 1;
    color: var(--cpd-text);
    background: rgba(var(--cpd-contrast-rgb), 0.08);
}

.formula-date {
    display: inline-flex;
    align-items: center;
    gap: 0.1rem;
}

.formula-date > input[type="date"] {
    width: auto;
}

.formula-settlement-error {
    margin-top: 0.3rem;
    color: var(--cpd-error, #ef5350);
    font-size: 0.75rem;
}

/* A hover-only explanation beside a label whose name does not say what it does. */
.token-hint {
    cursor: help;
    opacity: 0.6;
}

.token-hint:hover {
    opacity: 1;
}

/* MudDatePicker inside a formula line. Mud's own input chrome (floating label, helper-text
   reserve, 16px type) is sized for a form, and a line of code is not a form -- the keyword to its
   left already names the field, so the label slot is dead space here. */
.formula-line .mud-input-control,
.formula-header .mud-input-control {
    margin: 0;
}

.formula-line .mud-input-control .mud-input-control-helper-container,
.formula-header .mud-input-control .mud-input-control-helper-container {
    display: none;
}

.formula-line .mud-input.mud-input-text,
.formula-header .mud-input.mud-input-text {
    margin-top: 0;
}

.formula-line .mud-input > input.mud-input-root,
.formula-header .mud-input > input.mud-input-root {
    padding: 0.2rem 0.4rem;
    font-family: var(--cpd-font-mono);
    font-size: 0.85rem;
    line-height: 1.3;
    color: var(--cpd-text);
}

.formula-line .mud-input.mud-input-underline::before,
.formula-line .mud-input.mud-input-underline::after,
.formula-header .mud-input.mud-input-underline::before,
.formula-header .mud-input.mud-input-underline::after {
    display: none;
}

.formula-line .mud-input,
.formula-header .mud-input {
    background: rgba(var(--cpd-contrast-rgb), 0.05);
    border: 1px solid transparent;
    border-radius: 3px;
}

.formula-line .mud-input:focus-within,
.formula-header .mud-input:focus-within {
    border-color: var(--token-color);
    background: var(--cpd-surface-0);
}

.formula-line .mud-input-adornment .mud-icon-root,
.formula-header .mud-input-adornment .mud-icon-root {
    font-size: 0.95rem;
}

/* The "where do these conventions come from?" link (ConventionLink.razor), shown beside an
   instrument's type selector and under a generator cluster that is on System Convention. It is an
   aside, not an action: it inherits the token colour and stays quiet until hovered, so it never
   competes with the selector it sits next to. */
.convention-link {
    display: inline-block;
    margin-top: var(--cpd-space-xs);
    color: var(--token-color);
    text-decoration: none;
    opacity: 0.75;
    transition: opacity 0.25s ease;
}

.convention-link:hover,
.convention-link:focus-visible {
    opacity: 1;
    text-decoration: underline;
}

/* Payoff editor */

/* The Payoffs folder's components and ExoticTrades/PayoffTradeInput. One card per payoff: a
   header line saying which way the money goes, what kind of payoff it is and what it does, then the
   payoff; a condition wrapping a payoff is a bordered clause around the payoff it wraps. Tokens
   only. */
.payoff-list {
    display: flex;
    flex-direction: column;
    gap: var(--cpd-space-md);
    width: 100%;
}

.payoff-list-label {
    font-size: var(--cpd-text-0);
    color: var(--cpd-text-muted);
}

.payoff-card {
    display: flex;
    flex-direction: column;
    gap: var(--cpd-space-sm);
    padding: var(--cpd-space-md);
    border: 1px solid var(--cpd-border);
    border-radius: var(--cpd-radius-md);
}

.payoff-header {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--cpd-space-sm);
}

.payoff-title {
    font-size: var(--cpd-text-2);
    font-weight: 600;
}

.payoff-direction {
    display: inline-flex;
    gap: var(--cpd-space-xs);
    font-weight: 600;
}

.payoff-kind {
    font-size: var(--cpd-text-1);
}

/* The sentence takes its own line so a long description never squeezes the controls beside it. */
.payoff-sentence {
    flex-basis: 100%;
    font-size: var(--cpd-text-1);
    color: var(--cpd-text-muted);
}

.payoff-conditions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--cpd-space-sm);
}

/* A payoff's own actions (Duplicate), right-aligned at the foot of its card. Inserting and removing
   payoffs are the list's (JsonList), not the card's. */
.payoff-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--cpd-space-sm);
}

.payoff-conditions-label {
    font-size: var(--cpd-text-0);
    color: var(--cpd-text-muted);
}

.payoff-clause {
    display: flex;
    flex-direction: column;
    gap: var(--cpd-space-sm);
    padding: var(--cpd-space-sm) var(--cpd-space-md);
    border: 1px solid var(--cpd-border-strong);
    border-radius: var(--cpd-radius-md);
}

.payoff-clause-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--cpd-space-sm);
}

.payoff-clause-title {
    font-size: var(--cpd-text-1);
    font-weight: 600;
}
