/* ============================================================================
   AVFX CRM — FORM KIT
   ----------------------------------------------------------------------------
   The layout and the controls a record page is built from. The small pieces —
   .field, .input, .checkbox, .radio, .note, .caption — already live in
   components/ui/ui.css and are NOT repeated here. This file adds only what a
   whole form needs: the responsive grid, sections, the sticky footer, the
   native select, and the token box.

   A FORM IS A PAGE, NEVER A DIALOGUE (ADR-0018). Nothing here is sized for a
   560px modal, because nothing here will ever be inside one.

   Specification: docs/packets/PACKET-04-form-kit.md
   ========================================================================== */

/* ==========================================================================
   1 · THE GRID
   --------------------------------------------------------------------------
   Twelve columns, and a field declares its span. Twelve rather than three
   because three cannot say "this field is half as wide as that one", and
   field width has to signal expected input length — docs/03-UX-PATTERNS.md
   §6.2. A postcode box as wide as an address box is a lie about what is
   wanted.

   Two breakpoints, the same two the rest of the product uses.
   ========================================================================== */

.form-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--space-5) var(--space-6);
  align-items: start;
}

/* FORM CONTROLS ARE 38px, NOT 44.
   The 44px size is the grid's row height (ADR-0014), which is right for a
   surface you scan hundreds of rows of. A form is a surface you fill in once,
   and six pixels a row across fourteen controls is a quarter of the vertical
   space that decides whether the page scrolls. 38px is the button height, so
   a form and its footer now line up rather than nearly lining up. */
.form-grid .input,
.form-grid .select { block-size: var(--ctl-h-md); }
.form-grid .tk__box { min-block-size: var(--ctl-h-md); }

.f-2  { grid-column: span 2; }
.f-3  { grid-column: span 3; }
.f-4  { grid-column: span 4; }
.f-6  { grid-column: span 6; }
.f-8  { grid-column: span 8; }
.f-12 { grid-column: span 12; }

/* THE TWELVE COLUMNS HOLD DOWN TO 1024px, the supported floor
   (02-DESIGN-SYSTEM.md §9). They used to collapse to six at 1199px, which
   doubled the number of rows on a 1024-wide laptop and pushed a record page
   nearly 500 pixels past the bottom of the screen. A form that has to be
   scrolled to be filled in is slower than a form with narrower boxes, and
   the product owner's instruction on 2026-08-12 was explicit: the form is a
   full page and does not scroll. */
@media (max-width: 1023px) {
  .form-grid { grid-template-columns: repeat(6, 1fr); }
  .f-2, .f-3 { grid-column: span 2; }
  .f-4 { grid-column: span 3; }
  .f-6, .f-8, .f-12 { grid-column: span 6; }
}

/* Below 720px — one column. */
@media (max-width: 719px) {
  .form-grid { grid-template-columns: 1fr; gap: var(--space-6); }
  /* Every span, not only the ones that existed when this rule was written. A
     span wider than the single column creates implicit extra columns, which is
     how a phone layout silently became two columns wide. */
  .f-2, .f-3, .f-4, .f-6, .f-8, .f-12 { grid-column: span 1; }
}

/* ==========================================================================
   2 · SECTIONS
   ========================================================================== */

.form-section { margin-block-start: var(--space-9); }
.form-section:first-child { margin-block-start: 0; }
.form-section--tight { margin-block-start: var(--space-7); }

/* Several checkboxes on ONE line. A checkbox is 16 pixels of control; giving
   each one a full row of a twelve-column grid is how a form that should fit on
   one screen ends up needing two. */
.check-row {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-4) var(--space-8);
  min-block-size: var(--ctl-h-lg);
}

/* Lines the checkbox up with the CONTROL beside it rather than with the label
   above it, so the two read as one row. */
.check-row--aligned { padding-block-start: calc(var(--fs-xs) + var(--space-3)); align-items: flex-start; }

@media (max-width: 719px) {
  .check-row, .check-row--aligned { min-block-size: 0; padding-block-start: 0; }
}

.form-section__title {
  font-size: var(--fs-md);
  font-weight: var(--fw-semi);
  color: var(--color-text-primary);
  padding-block-end: var(--space-4);
  margin-block-end: var(--space-6);
  border-block-end: 1px solid var(--color-border);
}

/* ==========================================================================
   3 · FIELDS — the parts ui.css does not already carry
   ========================================================================== */

/* The label row, so a help control can sit beside the text without the label
   losing its click-to-focus behaviour. */
.field__label {
  display: flex; align-items: center; gap: var(--space-3);
  font-size: var(--fs-xs); font-weight: var(--fw-medium);
  color: var(--color-text-secondary);
}

/* An explanation the operator can ask for, rather than one occupying the
   screen for ever. Reaches the same text as the title attribute, so it works
   with a mouse, a keyboard and a screen reader. */
.field__help {
  display: grid; place-items: center;
  inline-size: 15px; block-size: 15px;
  color: var(--color-text-tertiary);
  border-radius: var(--radius-full);
}
.field__help:hover { color: var(--color-primary); }
.field__help svg { inline-size: 13px; block-size: 13px; }

/* The error message floats and never takes space — ui.css, ADR-0070. */

/* One rule marks every kind of failed control, so a new control type inherits
   the treatment instead of needing its own. */
.field.is-invalid .input,
.field.is-invalid .select,
.field.is-invalid .tk__box { border-color: var(--color-danger); }
.field.is-invalid .input:focus-visible,
.field.is-invalid .select:focus-visible { box-shadow: 0 0 0 3px var(--color-danger-subtle); }
.field.is-invalid .tk.is-focus .tk__box { box-shadow: 0 0 0 3px var(--color-danger-subtle); }

/* A field the operator cannot use must LOOK unusable, not merely refuse. */
.input:disabled, .select:disabled { background: var(--color-surface-sunken); color: var(--color-text-disabled); cursor: not-allowed; }

/* ==========================================================================
   4 · NATIVE SELECT
   --------------------------------------------------------------------------
   Native on purpose. The browser's own list is faster than anything we could
   build, works on a phone, and is what the operating system's accessibility
   tools already understand. We restyle the closed control only.
   ========================================================================== */

.select {
  block-size: var(--ctl-h-lg); inline-size: 100%;
  padding-inline: var(--space-5) var(--space-9);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-size: var(--fs-sm); font-family: inherit;
  color: var(--color-text-primary);
  appearance: none;
  transition: border-color var(--dur-instant), box-shadow var(--dur-instant);

  /* The caret is drawn with a gradient rather than an image, so it inherits
     the theme and costs no request. Two 1.5px bars meeting at a point. */
  background-image:
    linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-size: 5px 5px, 5px 5px;
  background-position:
    calc(100% - var(--space-6) - 5px) calc(50% - 1px),
    calc(100% - var(--space-6)) calc(50% - 1px);
  background-repeat: no-repeat;
}
.select:hover { border-color: var(--color-border-strong); }
.select:focus-visible { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 3px var(--color-primary-subtle); }

/* ==========================================================================
   5 · TOKEN SELECT — avfx-token-select
   --------------------------------------------------------------------------
   The box grows with its contents. It is NOT a fixed-height control with a
   scrollbar: an operator must be able to see every group a user belongs to
   without scrolling inside a 44px box.
   ========================================================================== */

.tk { display: block; position: relative; }

.tk__box {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-3);
  /* Belt and braces. Every child is sized, so nothing should ever spill —
     but a token box that silently grows to 164px because one icon lost its
     dimensions is a defect the operator sees and we would not. */
  overflow: hidden;
  min-block-size: var(--ctl-h-lg);
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  cursor: text;
  transition: border-color var(--dur-instant), box-shadow var(--dur-instant);
}
.tk__box:hover { border-color: var(--color-border-strong); }
.tk.is-focus .tk__box { border-color: var(--color-primary); box-shadow: 0 0 0 3px var(--color-primary-subtle); }
.tk.is-disabled .tk__box { background: var(--color-surface-sunken); cursor: not-allowed; }

/* --- a chip -------------------------------------------------------------
   The chips live in a wrapper so the component can repaint them in one
   assignment, but `display: contents` removes that wrapper from the layout,
   so each chip is a real flex child of the box. Without it the whole set is
   one flex item: the box's gap never reaches between chips, and they wrap as
   inline text rather than as boxes. */

.tk__chips { display: contents; }

.tk__chip {
  display: inline-flex; align-items: center; gap: var(--space-2);
  max-inline-size: 100%;
  block-size: 24px; padding-inline: var(--space-3) var(--space-2);
  background: var(--color-primary-subtle);
  border: 1px solid var(--color-primary-border);
  border-radius: var(--radius-xs);
  font-size: var(--fs-2xs); color: var(--color-text-primary);
}
.tk__chip-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Armed by Backspace, before a second press removes it. A token that vanished
   on one stray keystroke is an IP address nobody notices is gone. */
.tk__chip.is-armed {
  background: var(--color-danger-subtle);
  border-color: var(--color-danger-border);
  color: var(--color-danger-fg);
}

.tk__x {
  display: grid; place-items: center; flex: none;
  inline-size: 16px; block-size: 16px;
  border-radius: var(--radius-xs);
  color: var(--color-text-tertiary);
}
.tk__x:hover { background: var(--color-surface); color: var(--color-danger); }
.tk__x svg { inline-size: 11px; block-size: 11px; }
.tk.is-disabled .tk__x { display: none; }

/* --- the typing area ---------------------------------------------------- */

.tk__input {
  flex: 1; min-inline-size: 90px;
  block-size: 24px; border: 0; padding: 0;
  background: transparent;
  font-size: var(--fs-sm); font-family: inherit;
  color: var(--color-text-primary);
}
.tk__input:focus { outline: none; }
.tk__input::placeholder { color: var(--color-text-tertiary); }

/* Choice mode is a picker, not a text box: the caret would promise typing
   that commits, which only free mode does. */
.tk--choice .tk__input { caret-color: transparent; cursor: pointer; }

/* The wrapper is sized AND so is the icon inside it. Sizing only the wrapper
   leaves the <svg> at its intrinsic height — 150 pixels — which overflows the
   box and drops the caret below the field. Every other icon in this file is
   sized on the svg for the same reason. */
.tk__caret {
  display: grid; place-items: center; flex: none;
  inline-size: 14px; block-size: 14px;
  color: var(--color-text-tertiary); opacity: .7;
  align-self: center;
}
.tk__caret svg { inline-size: 14px; block-size: 14px; }

/* --- the dropdown, hung on the shared popover --------------------------- */

/* The width is set at open time to the control's own width, so the list reads
   as this control's list rather than a floating panel that happens to be
   nearby. The minimum is here only for a very narrow box on a phone. */
.pop--tk { min-inline-size: 220px; padding: 0; }

.tk-search {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-block-end: 1px solid var(--color-border-subtle);
}
.tk-search svg { inline-size: 13px; block-size: 13px; color: var(--color-text-tertiary); flex: none; }
.tk-search input {
  inline-size: 100%; border: 0; background: transparent;
  font-size: var(--fs-xs); font-family: inherit; color: var(--color-text-primary);
}
.tk-search input:focus { outline: none; }

.tk-list { max-block-size: 300px; overflow-y: auto; padding: var(--space-2); }

.tk-opt {
  display: flex; align-items: center; gap: var(--space-4);
  inline-size: 100%; min-block-size: var(--ctl-h-sm);
  padding-inline: var(--space-4);
  border-radius: var(--radius-xs);
  font-size: var(--fs-xs); color: var(--color-text-primary);
  text-align: start;
}
.tk-opt:hover { background: var(--color-surface-hover); }
.tk-opt__tick {
  display: grid; place-items: center; flex: none;
  inline-size: 15px; block-size: 15px;
  border: 1.5px solid var(--color-border-strong); border-radius: var(--radius-xs);
}
.tk-opt__tick svg { inline-size: 10px; block-size: 10px; color: var(--color-on-accent); opacity: 0; stroke-width: 3; }
.tk-opt[aria-selected="true"] .tk-opt__tick { background: var(--color-primary); border-color: var(--color-primary); }
.tk-opt[aria-selected="true"] .tk-opt__tick svg { opacity: 1; }
.tk-opt__t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.tk-empty {
  padding: var(--space-6) var(--space-4);
  font-size: var(--fs-xs); color: var(--color-text-tertiary); text-align: center;
}

.tk-foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-block-start: 1px solid var(--color-border-subtle);
  background: var(--color-surface-sunken);
  font-size: var(--fs-2xs); color: var(--color-text-secondary);
}
.tk-foot button { font-size: var(--fs-2xs); color: var(--color-primary); font-weight: var(--fw-medium); }

/* ==========================================================================
   6 · THE STICKY FOOTER
   --------------------------------------------------------------------------
   Sticky, not fixed. A fixed bar covers the last field — the exact control
   the operator is reaching for when they are ready to save.
   ========================================================================== */

/* NOT STICKY. It sits at the end of the form and scrolls away with it —
   product owner, 2026-08-12: "Do not want to keep bottom save button fix. it
   is ok it goes down with page length."

   The earlier sticky version was defended on the grounds that Save should
   always be in reach. In practice a bar pinned across the bottom of a record
   page is a permanent 62-pixel tax on the space the form has to fit in, and it
   competes with the page's own end. A form is filled top to bottom and saved
   once, at the end — which is exactly where this button now is. */
.form-foot {
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--space-4);
  margin-block-start: var(--space-7);
  padding: var(--space-5) var(--space-6);
  background: var(--color-surface-sunken);
  border-block-start: 1px solid var(--color-border);
  border-end-start-radius: var(--radius-md);
  border-end-end-radius: var(--radius-md);
}

/* Pushes any status text to the start edge, leaving the buttons at the end. */
.form-foot__note { margin-inline-end: auto; font-size: var(--fs-xs); color: var(--color-text-secondary); }

/* On a phone the two buttons are equal halves. A thumb reaching for Save
   should not have to aim. */
@media (max-width: 719px) {
  .form-foot { padding-inline: var(--space-5); }
  .form-foot .btn { flex: 1; }
  .form-foot__note { display: none; }
}

/* ==========================================================================
   7 · THE FORM CARD
   ========================================================================== */

.form-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.form-card__body { padding: var(--space-8); }

@media (max-width: 719px) {
  .form-card__body { padding: var(--space-6); }
}

/* ==========================================================================
   8 · THE DISPLAY BOX — a value shown, never edited
   --------------------------------------------------------------------------
   DELIBERATELY NOT A DISABLED INPUT. A greyed-out box reads as a page that
   failed to load, and an operator who meets one starts hunting for the reason
   it is broken. This reads as a decision: no border on the sides an input has,
   a sunken ground, and no caret. Three declarations, and it is the whole
   difference between "you may not" and "something went wrong". ADR-0024.
   ========================================================================== */

.display-box {
  display: flex; align-items: center; gap: var(--space-3);
  min-block-size: var(--ctl-h-md);
  padding: 0 var(--space-4);
  background: var(--color-surface-sunken);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-sm);
  font-size: var(--fs-sm); color: var(--color-text-secondary);
}

/* ==========================================================================
   9 · THE SECRET CONTROL — three states, one of them an input
   ========================================================================== */

.secret__stored {
  display: flex; align-items: center; gap: var(--space-3);
  min-block-size: var(--ctl-h-md);
  padding: 0 var(--space-3) 0 var(--space-4);
  background: var(--color-surface-sunken);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
}
.secret__lock { inline-size: 15px; block-size: 15px; flex: none; color: var(--color-success-fg); }
.secret__note { flex: 1; min-inline-size: 0; font-size: var(--fs-xs); color: var(--color-text-secondary); }

.secret__entry { display: flex; align-items: center; gap: var(--space-2); }
/* The box and its eye are one .input-group (ui.css), which fills the row;
   Cancel, when shown, takes only its own width beside it. */
.secret__entry .input-group { flex: 1; min-inline-size: 0; }

/* ==========================================================================
   10 · A SECTION THAT ACTS, AND THE ONE LINE IT ANSWERS WITH
   ========================================================================== */

.form-section__head { display: flex; align-items: center; gap: var(--space-4); margin-block-end: var(--space-4); }
.form-section__head .form-section__title { margin: 0; }
.form-section__act { margin-inline-start: auto; }

.form-section__result {
  display: flex; align-items: center; gap: var(--space-3);
  margin-block-end: var(--space-4);
  font-size: var(--fs-xs);
}
.form-section__result[hidden] { display: none; }
.form-section__result svg { inline-size: 15px; block-size: 15px; flex: none; }
.form-section__result.is-success { color: var(--color-success-fg); }
.form-section__result.is-danger  { color: var(--color-danger-fg); }
.form-section__result.is-warning { color: var(--color-warning-fg); }

/* --- THE DERIVED SENTENCE ------------------------------------------------
   A number the staff already know, with one line beneath saying what it
   MEANS. Same weight as help text, because it is help text — it just happens
   to be computed rather than written. */

.note-line { display: flex; align-items: flex-start; gap: var(--space-2); margin-block-start: var(--space-2); }
.note-line[hidden] { display: none; }
.note-line svg { inline-size: 13px; block-size: 13px; flex: none; margin-block-start: 2px; }
.note-line.is-muted   { color: var(--color-text-secondary); }
.note-line.is-warning { color: var(--color-warning-fg); }
.note-line.is-info    { color: var(--color-info-fg); }

/* ==========================================================================
   11 · THE BULK FIELD — "Unchanged" is a state, not an empty box
   --------------------------------------------------------------------------
   The reference system pre-fills a bulk markup dialogue with 0.00, which is
   not a neutral placeholder — it is the setting that passes the market rate to
   the client with no spread. Opening the dialogue and pressing Enter applied
   it to everything. So a bulk field is not an input until the operator says
   it is, and "leave this one alone" is something they can SEE. ADR-0031.
   ========================================================================== */

.bulk-field__off, .bulk-field__on { display: flex; align-items: center; gap: var(--space-2); }
.bulk-field__off[hidden], .bulk-field__on[hidden] { display: none; }

.bulk-field__off {
  min-block-size: var(--ctl-h-md);
  padding: 0 var(--space-3) 0 var(--space-4);
  background: var(--color-surface-sunken);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
}
.bulk-field__note   { flex: 1; font-size: var(--fs-xs); color: var(--color-text-secondary); }
.bulk-field__on .input, .bulk-field__on .select { flex: 1; min-inline-size: 0; }
.bulk-field__suffix { font-size: var(--fs-sm); color: var(--color-text-secondary); }

/* The scope sentence and the preview. The scope is stated in the body as well
   as on the button, because the button is read last and sometimes not at all. */
.bulk-scope    { margin-block-end: var(--space-6); }
.bulk-preview  { margin-block-start: var(--space-5); font-size: var(--fs-xs); color: var(--color-text-primary); }
.bulk-preview[hidden] { display: none; }
.bulk-preview.is-muted { color: var(--color-text-secondary); font-style: italic; }

/* --- A UNIT BESIDE THE NUMBER --------------------------------------------
   "Markup: Sell" with a % against the box is one reading. "Markup: Sell (%)"
   makes the operator carry the unit across the gap themselves, and reread it
   on every field. The suffix is not a control and never takes focus. */

.input-row { display: flex; align-items: stretch; }
.input-row .input { flex: 1; min-inline-size: 0; border-start-end-radius: 0; border-end-end-radius: 0; }
.input-row__suffix {
  display: flex; align-items: center;
  padding-inline: var(--space-4);
  background: var(--color-surface-sunken);
  border: 1px solid var(--color-border); border-inline-start: 0;
  border-start-end-radius: var(--radius-sm); border-end-end-radius: var(--radius-sm);
  font-size: var(--fs-sm); color: var(--color-text-secondary);
  white-space: nowrap;
}
.field.is-invalid .input-row__suffix { border-color: var(--color-danger-border); }

/* ==========================================================================
   12 · THE SETTINGS PAGE — loading, failure and the version conflict
   --------------------------------------------------------------------------
   ADR-0051. A settings page has one record and loads it before it can show a
   single field, so it needs the two states a record page never shows: the
   page drawn empty while the record arrives, and the note that replaces the
   form when it cannot. Plus the conflict note, when somebody else saved first.
   ========================================================================== */

/* The page's own sections with empty bars, so nothing jumps when the values
   arrive. The same shimmer as the grid's skeleton, and still under reduced
   motion. */
.form-sk {
  display: block; block-size: var(--ctl-h-md);
  border-radius: var(--radius-sm);
  background: linear-gradient(90deg,
    var(--color-surface-sunken) 25%,
    color-mix(in srgb, var(--color-surface-sunken) 55%, var(--color-border)) 50%,
    var(--color-surface-sunken) 75%);
  background-size: 400% 100%;
  animation: form-shimmer 1.4s ease-in-out infinite;
}
.form-sk--label { block-size: 10px; inline-size: 45%; }
@keyframes form-shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
@media (prefers-reduced-motion: reduce) { .form-sk { animation: none; } }

/* A note that carries its own way out — Retry, or Reload their version. */
.form-state { display: flex; align-items: center; gap: var(--space-5); }
.form-state > span { flex: 1; }
.form-conflict { margin-block-end: var(--space-7); }

@media (max-width: 719px) {
  .form-state { flex-direction: column; align-items: stretch; }
}

/* ==========================================================================
   PANELS — THE STANDARD FOR EVERY FORM (ADR-0060)
   --------------------------------------------------------------------------
   Scoped under `.form--panels`. The screen engine sets it on every form it
   renders, and each hand-written record page sets it on its form body. The
   scope is KEPT as the marker, so a page that has not been converted yet
   keeps its old layout instead of half-breaking.

   Each section is a bordered panel. Its heading is a tinted strip — the strip
   is the separator, so the title carries no underline. That short underline,
   drawn under the title's own width, is what looked broken.
   ========================================================================== */

.form--panels .form-section {
  margin-block-start: var(--space-6);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
}
.form--panels .form-section:first-child { margin-block-start: 0; }

.form--panels .form-section__head {
  flex-wrap: wrap;
  column-gap: var(--space-5);
  row-gap: var(--space-2);
  min-block-size: 44px;
  margin: 0;
  padding: var(--space-3) var(--space-5);
  background: var(--color-surface-hover);
  border-block-end: 1px solid var(--color-border);
  border-start-start-radius: var(--radius-lg);
  border-start-end-radius: var(--radius-lg);
}
.form--panels .form-section__title { padding: 0; margin: 0; border: 0; }
.form--panels .form-section__head .form-section__note {
  flex: 0 1 auto; min-inline-size: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: var(--fs-xs);
  color: var(--color-text-tertiary);
}
.form--panels .form-section__act { flex: none; }
.form--panels .form-section__body { padding: var(--space-6) var(--space-5); }

/* The help trigger: the standard circled "i", with NO box around it — no
   focus outline, no background (product owner, 2026-09-23). The glyph is
   small; the target is 24 × 24 (WCAG 2.5.8), with negative margins so the
   label row does not grow. Focus is shown by the icon turning primary AND by
   the bubble opening, so removing the outline does not hide where focus is. */
.form--panels .field__help--tip {
  inline-size: 24px; block-size: 24px;
  margin-block: -5px;
  margin-inline: calc(var(--space-2) * -1);
  background: none; border: 0; box-shadow: none; outline: none;
}
.form--panels .field__help--tip svg { inline-size: 14px; block-size: 14px; }
.form--panels .field__help--tip:hover,
.form--panels .field__help--tip:focus-visible,
.form--panels .field__help--tip[aria-describedby] { color: var(--color-primary); }

/* Phone: the note drops to its own line under the title and the action, and
   the body tightens. The action button never shrinks below its text. */
@media (max-width: 719px) {
  /* A panel is already a frame, so the card around the panels gives up most
     of its padding — two frames of padding is a quarter of a phone's width. */
  /* The panels ARE the frame on a phone: the card around them loses its
     border, background and padding, so the panels sit on the page's own
     16 px gutter instead of three nested margins deep. */
  .form-card:has(> .form--panels) { background: none; border: 0; }
  .form-card__body.form--panels { padding: 0; }
  .form--panels .form-section__head { padding: var(--space-3) var(--space-4); }
  .form--panels .form-section__head .form-section__note { order: 3; flex-basis: 100%; white-space: normal; }
  /* The action cannot share a line with a long title at this width, so it
     takes a full line of its own under the note instead of hanging right. */
  .form--panels .form-section__act { order: 4; flex-basis: 100%; margin-inline-start: 0; margin-block: var(--space-2); }
  .form--panels .form-section__body { padding: var(--space-5) var(--space-4); }
}

/* A rendered template, shown in a sandbox where no script runs, with the
   subject it will be sent under above it. Packet 103. */
.tpl-subject { margin-block: 0 var(--space-4); font-size: var(--fs-sm); color: var(--color-text-primary); }
.tpl-subject b { color: var(--color-text-secondary); font-weight: 500; margin-inline-end: var(--space-2); }
.tpl-frame {
  display: block;
  inline-size: 100%;
  block-size: calc(var(--space-11) * 9);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}

/* What an event handler posts, shown as it will be written. Packet 109. */
.payload {
  margin: var(--space-4) 0 0;
  padding: var(--space-5);
  font-size: var(--fs-xs);
  color: var(--color-text-primary);
  background: var(--color-surface-sunken);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
