/* ═══════════════════════════════════════════════════════════════════════
   The AMX field

   One shell for every data-view form control. A field is a single element:
   the shell owns the border, the radius, the hover and the focus ring, and
   whatever sits inside it - a value, a currency symbol, a browse button - is
   borderless and flush, separated only by hairlines. The old controls were an
   input with a bordered button parked beside it, which left a doubled seam
   down the middle and read as two things.

   Everything here is token-driven, so light and dark come for free. It lives
   in one shared sheet rather than in each component's scoped CSS because the
   controls had drifted: eight copies of nearly the same rules, several with
   hard-coded hex that never themed.

   Anatomy
     .amx-field                 the shell
       .amx-field-affix         a static marker, e.g. a currency symbol
       .amx-field-value         the input / textarea / readout
       .amx-field-btn           a flush action, e.g. browse or clear

   Modifiers
     .is-picker      the value opens something on click (pointer, not caret)
     .is-readonly    shown but not editable, on a form that IS being edited
     .is-view        view mode: a readout rather than a field
     .is-multiline   grows with its textarea instead of sitting at one row
   ═══════════════════════════════════════════════════════════════════════ */

.amx-field {
    display: flex;
    align-items: stretch;
    width: 100%;
    min-height: var(--amx-field-min-height, 44px);
    background-color: var(--bg-input, #ffffff);
    /* --field-border, not --border-interactive: the field's resting edge is a shade stronger than
       the app's general control outline and is tuned on its own token, so darkening a form does
       not drag every panel and rail along with it. */
    border: 1px solid var(--field-border, #5f6673);
    border-radius: 8px;
    /* Clips the children's corners to the shell's, so the parts read as one shape. */
    overflow: hidden;
    transition: border-color 120ms ease, box-shadow 120ms ease;
}

.amx-field:hover:not(.is-readonly):not(.is-view) {
    border-color: var(--accent-primary, #2563eb);
}

/* focus-within, so reaching any part lights the whole control - the ring belongs to
   the field, not to whichever child happens to hold focus. */
.amx-field:focus-within {
    border-color: var(--accent-primary, #2563eb);
    box-shadow: 0 0 0 3px rgba(var(--accent-primary-rgb, 37, 99, 235), 0.15);
}

/* ── The value ───────────────────────────────────────────────────────── */

.amx-field-value {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
    margin: 0;
    padding: var(--amx-field-padding-y, 9px) 12px;
    border: 0;
    background-color: transparent;
    color: var(--field-text, #131826);
    /* inherit, not the browser default - otherwise a textarea renders in monospace
       while the single-row input beside it uses the app font. */
    font-family: inherit;
    /* The app's DataView control size, NOT a fixed 0.875rem: it scales with the user's
       font-size setting ([data-font-size="large"]), and it is what AmxGrid, AmxLocation and
       AmxRichText already read. Hard-coding it made an edit field and a view readout differ. */
    font-size: var(--amx-control-font-size, 0.875rem);
    line-height: 1.4;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* No ring of its own - the shell already shows one via :focus-within. */
.amx-field-value:focus,
.amx-field-value:focus-visible {
    outline: none;
    box-shadow: none;
}

.amx-field-value::placeholder {
    color: var(--text-muted, #616776);
}

textarea.amx-field-value {
    display: block;
    height: 100%;
    white-space: pre-wrap;
    overflow: auto;
    resize: none;
}

/* A readout that wraps instead of truncating - view mode has the room, and a long note
   read as one clipped line is worse than a taller box. */
/* A block, so its content flows as text - the value may now contain markup (a linkified
   address is a text node, an <a> and a hidden span), and as a flex container each of those
   became a separate flex ITEM sitting side by side instead of one flowing paragraph.
   align-self keeps the vertical centring that the flex display was there for: the shell is
   the flex container, so the whole block is centred within it. */
.amx-field-value.amx-field-wrap {
    display: block;
    align-self: center;
    white-space: pre-wrap;
    word-break: break-word;
    overflow: visible;
    text-overflow: clip;
}

/* A readout that replaced a textarea: it fills the field and scrolls rather than growing
   without limit, so it stretches instead of being centred like the single-line variant. */
.amx-field-value.amx-field-scroll {
    align-self: stretch;
    height: 100%;
    overflow: auto;
}

/* A value that opens a picker rather than taking text. */
.amx-field.is-picker .amx-field-value {
    cursor: pointer;
}

/* Two halves of one value (a date and its time). Divided, not detached. */
.amx-field-value + .amx-field-value {
    border-left: 1px solid var(--border-color, #e4e8f1);
}

/* A readout standing in for a value the record does not have - "No location set". Reads as
   a placeholder, because that is what it is: it takes the same muted tone as one, so a field
   with nothing in it is told apart from a field with something in it at a glance. */
.amx-field-value.amx-field-empty {
    color: var(--text-muted, #616776);
}

/* A single-line readout too long for its field. At rest it truncates with an ellipsis as any
   value does; on hover - or keyboard focus, when the readout is a button - the text slides left
   until its end is in view, then back, so the whole value can be read without opening it.
   Markup: the value carries .amx-field-marquee and wraps its text in .amx-field-marquee-text.
   The value is a size container so the slide distance is exact: 100cqw is the value's content
   width and 100% the text's own, so the text travels by precisely what overflows - and by
   nothing at all (min with 0) when it fits, so a short value never moves. User-initiated and
   stops when the pointer leaves, so it is not auto-playing motion (WCAG 2.2.2); under reduced
   motion it stays truncated and the title attribute carries the full value. */
.amx-field-value.amx-field-marquee {
    container-type: inline-size;
}

.amx-field-marquee-text {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@keyframes amx-field-marquee {
    0%, 15% { transform: translateX(0); }
    85%, 100% { transform: translateX(min(0px, calc(100cqw - 100%))); }
}

@media (prefers-reduced-motion: no-preference) {
    .amx-field:hover .amx-field-marquee-text,
    .amx-field-marquee:focus-visible .amx-field-marquee-text {
        /* Its full width, so translate's 100% is the whole text, not the clipped box. */
        flex: 0 0 auto;
        overflow: visible;
        text-overflow: clip;
        animation: amx-field-marquee 8s ease-in-out 0.4s infinite alternate;
    }
}

/* ── Bare readout ────────────────────────────────────────────────────── */
/* A view-mode value with no shell around it - a grid cell in view mode, where a box drawn
   around every cell would be far too heavy. It still takes the field's type size and its
   vertical centring, so it lines up with the editable cells beside it. */

.amx-field-readout {
    display: flex;
    align-items: center;
    min-height: 32px;
    padding: 4px 8px;
    color: var(--text-primary, #131826);
    font-family: inherit;
    font-size: var(--amx-control-font-size, 0.875rem);
    line-height: 1.4;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ── Affix ───────────────────────────────────────────────────────────── */
/* A static marker inside the shell - the currency symbol on a money field. Reads as
   part of the field, which is the point: it is not an input and never focusable. */

.amx-field-affix {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    padding: 0 10px;
    background-color: var(--bg-secondary, #eef1f8);
    color: var(--text-secondary, #5b6577);
    font-size: var(--amx-control-font-size, 0.875rem);
    white-space: nowrap;
}

.amx-field-affix:first-child {
    border-right: 1px solid var(--border-color, #e4e8f1);
}

.amx-field-affix:last-child {
    border-left: 1px solid var(--border-color, #e4e8f1);
}

/* ── Action button ───────────────────────────────────────────────────── */
/* Flush inside the shell: no box, no fill at rest, just a hairline dividing it from
   the value. It reads as part of the field rather than a button parked next to one. */

.amx-field-btn {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    min-width: 42px;
    padding: 0 10px;
    border: 0;
    border-left: 1px solid var(--border-color, #e4e8f1);
    background-color: transparent;
    color: var(--text-muted, #616776);
    font-size: 0.85rem;
    cursor: pointer;
    transition: background-color 120ms ease, color 120ms ease;
}

/* An action placed before the value instead of after it - the location control's
   [buttononleft]. The hairline has to move with it, or the button is divided from the edge of
   the shell rather than from the value it belongs to. */
.amx-field-btn:first-child {
    border-left: 0;
    border-right: 1px solid var(--border-color, #e4e8f1);
}

.amx-field-btn:hover:not(:disabled) {
    background-color: var(--bg-hover, #eef2fb);
    color: var(--accent-primary, #2563eb);
}

.amx-field-btn:active:not(:disabled) {
    background-color: var(--bg-secondary, #eef1f8);
}

/* Inset, so the ring is not clipped by the shell's overflow. */
.amx-field-btn:focus-visible {
    outline: 2px solid var(--accent-primary, #2563eb);
    outline-offset: -2px;
}

.amx-field-btn:disabled {
    opacity: 0.5;
    cursor: default;
}

/* ── Read-only ───────────────────────────────────────────────────────── */
/* On a form that IS being edited, a read-only field sits next to editable ones, so it
   has to read as locked rather than merely empty. Matches the read-only checkbox: the
   shape carries the meaning (WCAG 1.4.1), not the colour alone, and the value stays
   perfectly legible. */

.amx-field.is-readonly {
    background-color: var(--field-bg-readonly, #eef1f8);
    border-style: dashed;
    cursor: default;
}

.amx-field.is-readonly .amx-field-value {
    color: var(--field-text-readonly, #5b6577);
    cursor: default;
}

.amx-field.is-readonly .amx-field-affix {
    background-color: transparent;
}

/* ── View mode ───────────────────────────────────────────────────────── */
/* A readout rather than a field: a quieter edge, because nothing on the page is
   editable and a form full of input-looking boxes would say otherwise. */

.amx-field.is-view {
    background-color: var(--field-bg-view, #ffffff);
    /* --field-border-view, not --border-color: this is the field's own quiet edge, tuned on a
       form-control token. --border-color is the hairline every panel, card and divider in the
       app uses, so darkening a readout through it would darken all of those too. */
    border-color: var(--field-border-view, #aeb3bd);
}

.amx-field.is-view:hover {
    border-color: var(--field-border, #5f6673);
}

/* ── Multi-row ───────────────────────────────────────────────────────── */

.amx-field.is-multiline {
    min-height: 0;
    height: 100%;
}

/* ── Invalid ─────────────────────────────────────────────────────────── */
/* The wrapper draws the alert; the field keeps its own surface so the value does not
   sit on a red wash, and takes a danger edge so the control itself is identified. */

.alert-danger .amx-field {
    background-color: var(--bg-input, #ffffff) !important;
    border-color: var(--accent-danger, #ef4444);
}

.alert-danger .amx-field.is-readonly {
    background-color: var(--field-bg-readonly, #eef1f8) !important;
}

/* A grid that fails its requirement (AmxGrid, AmxInlineGridEditor). No red wash over the whole
   grid: the danger edge on its shell, and the message in a band across its top. The selectors
   out-rank the grids' own scoped border rules (hover/focus included), which are !important. */
.form-control.amx-grid-wrapper.has-validation-errors,
.form-control.amx-grid-wrapper.has-validation-errors:hover:not(.is-view),
.form-control.amx-grid-wrapper.has-validation-errors:focus-within:not(.is-view) {
    border-color: var(--accent-danger, #ef4444) !important;
}

.amx-grid-validation {
    align-self: stretch;
    flex: 0 0 auto;
    margin: 0;
    padding: 6px 12px;
    list-style: none;
    background-color: color-mix(in srgb, var(--accent-danger, #ef4444) 10%, transparent);
    border-bottom: 1px solid var(--accent-danger, #ef4444);
    color: var(--accent-danger-text, #c4201f);
    font-size: 0.875rem;
    font-weight: 500;
}

/* ── Dense variant ───────────────────────────────────────────────────── */
/* Inline grid cells have far less room than a form row. */

.amx-field.is-dense {
    min-height: 32px;
    border-radius: 6px;
}

.amx-field.is-dense .amx-field-value {
    padding: 4px 8px;
}

.amx-field.is-dense .amx-field-btn {
    min-width: 30px;
    padding: 0 6px;
    font-size: 0.75rem;
}

.amx-field.is-dense .amx-field-affix {
    padding: 0 7px;
}
