/* ============================================================================
   THE FIELD LANGUAGE (wave D)
   One grammar for every text-entry surface on the platform.

   A field is three lines and nothing else:
     1. the label      — what this is, stated as what happens
     2. the control    — where you type, on a raised surface
     3. the help/error — what it wants, or the fix it is naming

   Loaded LAST so it can normalise controls that older per-surface layers
   styled one at a time, without deleting those layers. Where a surface still
   knows better than the grammar (the borderless capture bar, the learning
   scales), it keeps winning on specificity and opts in with a modifier.

   THE SIGNATURE — the writing rim. On focus a quiet neutral underline draws
   from the left in 120ms and the label brightens one step. It is drawn with
   `::after` on the wrapper rather than on the control itself, because an
   `<input>` is a replaced element and never renders its own pseudo-elements.
   The wrapper owns named grid areas so the underline lands on the control's
   bottom edge regardless of what else the field carries.

   This is the ONLY motion in the field language. Nothing else animates.
   ========================================================================== */

:root {
  --field-radius: 10px;
  --field-min-height: 44px;
  --field-text: .9375rem;
  --field-gap: 6px;
  --field-pad-y: .55rem;
  --field-pad-x: .75rem;
  --field-label-color: var(--text-dim);
  /* One step brighter than dim, never all the way to primary: focus is a
     confirmation, not a shout. */
  --field-label-focus: color-mix(in srgb, var(--text-main) 60%, var(--text-dim));
  --field-border: var(--border-subtle);
  --field-ink: var(--lm-focus, #d8d5d0);
  --field-ink-duration: 120ms;
  --field-ink-ease: cubic-bezier(.16, .8, .26, 1);
}

/* ---------------------------------------------------------------- wrapper */
.field {
  display: grid;
  grid-template-areas: "field-label" "field-control" "field-help";
  grid-template-columns: minmax(0, 1fr);
  gap: var(--field-gap);
  position: relative;
  min-width: 0;
  margin: 0;
}

/* The label sits above, always. Plex uppercase at micro-copy size with the
   platform label tracking — the same voice as every other label on screen.

   The class is doubled on purpose. Several earlier layers set a per-surface
   label voice at `.some-surface label > span` (0,2,0); one voice for the whole
   platform is the point of this wave, so `.field.field > …` (0,2,1) reclaims
   the label line without anyone reaching for `!important`. */
.field.field > :is(span, b):first-child,
.field > :is(span, b):first-child {
  grid-area: field-label;
  color: var(--field-label-color);
  font-family: var(--font-body);
  font-size: var(--micro-copy, .8125rem);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: var(--label-tracking, .14em);
  text-transform: uppercase;
  transition: color var(--field-ink-duration) var(--field-ink-ease);
}
.field > :is(span, b):first-child i {
  font-style: normal;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--text-dim);
}

.field > :is(input, textarea, select) {
  grid-area: field-control;
  width: 100%;
  min-width: 0;
  min-height: var(--field-min-height);
  padding: var(--field-pad-y) var(--field-pad-x);
  border: 1px solid var(--field-border);
  border-radius: var(--field-radius);
  background-color: var(--bg-raised);
  color: var(--text-main);
  font-family: var(--font-body);
  font-size: var(--field-text);
  line-height: 1.45;
}
.field > textarea {
  resize: vertical;
  padding-top: .6rem;
}
.field > :is(input, textarea, select):focus {
  outline: none;
  border-color: var(--border-focus);
}
/* The platform's global `:focus-visible` ring (executive.css) is an
   accessibility contract and is deliberately left alone. The ink-well is the
   field's own signal and fires on every focus, including pointer focus where
   no ring is drawn — the two are additive, not a substitute for each other. */

/* Help and error share the third line. Errors name the fix; they never build
   a red wall — one border, one sentence. */
.field > :is(small, .field-help, .field-error),
.field > span:not(:first-child) {
  grid-area: field-help;
  color: var(--text-dim);
  font-family: var(--font-body);
  font-size: var(--micro-copy, .8125rem);
  font-weight: 400;
  line-height: 1.45;
  letter-spacing: normal;
  text-transform: none;
}
/* The work-composition surfaces pinned their label size with `!important` in
   an earlier layer. One `!important` answers it, scoped to fields only. */
.commitment-capture label.field > span,
.clickup-create label.field > span,
.case-compose label.field > span {
  font-size: var(--micro-copy, .8125rem) !important;
}

/* The error line has to out-rank the help line it shares a row with. */
.field.field > .field-error { color: var(--danger); }
.field.is-invalid { --field-border: var(--danger); }
.field.is-invalid.is-invalid > :is(span, b):first-child { color: var(--danger); }

/* ------------------------------------------------------- the ink-well */
.field::after {
  content: "";
  grid-area: field-control;
  align-self: end;
  justify-self: stretch;
  height: 2px;
  margin: 0 1px;
  border-radius: 0 0 2px 2px;
  background: var(--field-ink);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--field-ink-duration) var(--field-ink-ease);
  pointer-events: none;
  z-index: 1;
}
.field:focus-within::after { transform: scaleX(1); }
.field:focus-within > :is(span, b):first-child { color: var(--field-label-focus); }

/* Reduced motion keeps the signal and drops the draw. */
@media (prefers-reduced-motion: reduce) {
  .field::after,
  .field > :is(span, b):first-child { transition: none; }
}
.no-motion .field::after,
.no-motion .field > :is(span, b):first-child { transition: none; }

/* ---------------------------------------------------- adoption, no markup */
/* today.html's markup is frozen. It builds its record editors out of
   `.record-editor-form` — a class it shares with the contact and
   executive-state editors — and its classification note out of one more
   named label. Teaching those selectors the grammar gives all of them the
   same field, ink-well included, without editing a single line of today.html.

   Only the parts the grammar owns are set here: the row structure, the
   underline, and the label's focus step. Each surface keeps its own sizing. */
.record-editor-form label,
.today-record-classification-note {
  display: grid;
  grid-template-areas: "field-label" "field-control" "field-help";
  grid-template-columns: minmax(0, 1fr);
  gap: var(--field-gap);
  min-width: 0;
}
.record-editor-form label > :is(span, b):first-child,
.today-record-classification-note > :is(span, b):first-child {
  grid-area: field-label;
  color: var(--field-label-color);
  font-family: var(--font-body);
  font-size: var(--micro-copy, .8125rem);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: var(--label-tracking, .14em);
  text-transform: uppercase;
  transition: color var(--field-ink-duration) var(--field-ink-ease);
}
.record-editor-form label > :is(input, textarea, select),
.today-record-classification-note > textarea {
  grid-area: field-control;
  border-radius: var(--field-radius);
  min-height: var(--field-min-height);
}
.record-editor-form label > :is(small, .field-help),
.today-record-classification-note > small {
  grid-area: field-help;
}
.record-editor-form label::after,
.today-record-classification-note::after {
  content: "";
  grid-area: field-control;
  align-self: end;
  justify-self: stretch;
  height: 2px;
  margin: 0 1px;
  border-radius: 0 0 2px 2px;
  background: var(--field-ink);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--field-ink-duration) var(--field-ink-ease);
  pointer-events: none;
  z-index: 1;
}
.record-editor-form label:focus-within::after,
.today-record-classification-note:focus-within::after { transform: scaleX(1); }
.record-editor-form label:focus-within > :is(span, b):first-child,
.today-record-classification-note:focus-within > :is(span, b):first-child {
  color: var(--field-label-focus);
}
@media (prefers-reduced-motion: reduce) {
  .record-editor-form label::after,
  .today-record-classification-note::after { transition: none; }
}
.no-motion .record-editor-form label::after,
.no-motion .today-record-classification-note::after { transition: none; }

/* ------------------------------------------------------------- modifiers */
/* A field that lives inside its own bordered bar (the capture bar) draws no
   box of its own — but it still signs itself with the ink-well. */
.field-bare > :is(input, textarea) {
  border: 0;
  border-radius: 0;
  padding-left: 4px;
  padding-right: 4px;
  background-color: transparent;
}
.field-bare::after { margin: 0; }

/* A field whose label is carried by surrounding copy rather than its own
   line — the palette, a search box inside a titled panel. */
.field-quiet > :is(span, b):first-child {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

/* Spans the full width of whatever grid the field sits in. */
.field-wide { grid-column: 1 / -1; }

/* The palette's one input now has a real label; the wrapper takes the flex
   slot the bare input used to hold. */
.palette-top > .palette-field { flex: 1 1 auto; min-width: 0; }
.palette-top > .palette-field > input { width: 100%; font-size: 1rem; }

/* ------------------------------------------------- auto-growing textareas */
/* CSS-first where the browser can do it alone; fields.js does the same job
   everywhere else. Either way a textarea is as tall as its content, up to
   40vh, and then scrolls inside itself instead of pushing the page. */
textarea[data-autogrow] {
  resize: none;
  overflow-y: auto;
  max-height: 40vh;
  field-sizing: content;
}
.field > textarea[data-autogrow] { resize: none; }

/* -------------------------------------------------------- state honesty */
/* idle → dirty → saving → saved, in one chip and one status line. */
.field-chip {
  display: inline-flex;
  align-items: center;
  gap: .3rem;
  padding: .12rem .5rem;
  border: 1px solid var(--warning);
  border-radius: 999px;
  color: var(--warning);
  font-family: var(--font-body);
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  white-space: nowrap;
}
.field-chip[hidden] { display: none; }
.field-chip.is-saved { border-color: var(--success); color: var(--success); }

.field-status {
  grid-column: 1 / -1;
  margin: 0;
  color: var(--text-dim);
  font-family: var(--font-body);
  font-size: var(--micro-copy, .8125rem);
  line-height: 1.45;
}
.field-status:empty { display: none; }
/* A status line whose only content is a hidden chip is not a blank line. */
.field-status:has(> .field-chip[hidden]:only-child) { display: none; }
.field-status.is-error { color: var(--danger); }

/* A form the shell has marked in-flight cannot be submitted twice. The shell
   already relabels the button; this removes the target under the thumb. */
form.is-submitting :is(button[type="submit"], button:not([type]), .adjudication-submit) {
  pointer-events: none;
  opacity: .58;
}

/* ------------------------------------------------------------ field group */
/* A note and its reason chips are one thought, not two controls. The group
   draws one container so they read as a single field with an options row.
   Visual only — every name, endpoint, and data attribute inside is untouched. */
.field-group {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 9px;
  min-width: 0;
  padding: 11px 12px 12px;
  border: 1px solid var(--field-border);
  border-radius: 12px;
  background: var(--bg-surface);
}
.field-group:focus-within { border-color: var(--border-focus); }
.field-group > .field { gap: var(--field-gap); }

/* The options row sits under the note, inside the same container. These two
   surfaces previously took their column grid from `> div`; the wrapper now
   holds that slot, so the row keeps its own layout explicitly. */
.commitment-primary-action > .field-group,
.stack-correction-form > .field-group,
.commitment-destructive-tool form > .field-group {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 9px;
}
.field-group > .adjudication-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}
.field-group > .adjudication-actions > button {
  flex: 1 1 auto;
  min-height: 44px;
}
@media (max-width: 640px) {
  .field-group > .adjudication-actions > button { flex-basis: 100%; }
}

/* ------------------------------------------- approvals: the artifact card */
/* The artifact is now a real textarea. It keeps reading as the card it was —
   the thing that will be sent — while being editable by every input method,
   announced correctly, and undoable. */
textarea.appr-artifact {
  display: block;
  width: 100%;
  min-height: 96px;
  padding: .9rem 1rem;
  border: 1px solid var(--border-subtle);
  border-radius: 10px;
  background-color: var(--bg-app);
  color: var(--text-main);
  font-family: var(--font-body);
  font-size: .95rem;
  font-feature-settings: "tnum" 1;
  line-height: 1.6;
  white-space: pre-wrap;
}
textarea.appr-artifact:focus {
  outline: none;
  border-color: var(--border-focus);
}
.appr-artifact-field { gap: 8px; margin: .6rem 0 0; }
.appr-artifact-label { margin-top: 0; gap: .6rem; }
.appr-artifact-meta {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  flex: 0 0 auto;
}
.appr-note-field { margin-top: .8rem; }
.appr-note-field > input { width: 100%; }

/* A field inside a one-line inline form takes the flex slot its bare control
   used to hold, so labelling a control never collapses the row. */
.inline-form > .field { flex: 1 1 220px; min-width: 0; }
@media (max-width: 640px) {
  .inline-form > .field { flex: 1 1 100%; }
}

/* The lane cadence cell used to carry its width in a style attribute. It is a
   field now, and the width belongs to the field language. */
.lane-cadence-field { flex: 0 0 auto; }
.lane-cadence-field > input { width: 148px; font-size: .82rem; }

/* ---------------------------------------------------------- forge columns */
/* The agent and skill forges were placeholder-only inputs stacked in a
   column. They are fields now, so the column has to breathe like one. */
.stack-form .field { margin: 0; }
.stack-form { gap: .7rem; }

/* -------------------------------------------------------- the search bars */
/* A quiet field hides its label line and usually carries no help line either,
   but the two empty rows still bill their gap — 12px that would drop the
   control below the button standing beside it. Charge only for rows that
   render something. */
.field-quiet:not(:has(> :is(small, .field-help, .field-error))) { row-gap: 0; }

/* The relationship, conference, and buyer bars lay out their own columns and
   used to place a bare control in the first one. The wrapper holds that slot
   now, so each control keeps the width its bar always gave it. */
.intel-search > .field,
.relationship-query > .field,
.contact-history-search > .field { min-width: 0; }

/* The per-account label on Connections carried its width in a style attribute.
   It is a field now, and the width belongs to the field language. */
.connection-label-field { flex: 0 0 auto; }
.connection-label-field > input { width: 150px; }

/* --------------------------------------------------------------- mobile */
@media (max-width: 640px) {
  .field { gap: 5px; }
  .field-group { padding: 10px; }
}
