/* ============================================================================
   Portmasters Product TextField — text-field.css  (Visual System v2, G1-2)
   ----------------------------------------------------------------------------
   Plain CSS port of the product TextField CVA
   (packages/shared/src/components/text-field/style.ts + TextField.tsx).
   Consumes ONLY var() tokens from ../../tokens/tokens.css + tokens-semantic.css.
   Every non-token literal below is a documented CURRENT value (px kept as-is).

   Anatomy (TextField.tsx:79-176):
     .pm-textfield            wrapper  (flex-col, space-y-1.5, group, relative)
       .pm-textfield__labelrow  optional  (label + topElement)
       .pm-textfield__control   CONTAINER (CVA)  — the styled box
         [leftIcon | leftElement | input | rightElement | rightIcon]
       .pm-textfield__helper    optional  (helperText / error message; absolute)

   Class model (mirrors the CVA axes + status prop):
     wrapper   .pm-textfield
     control   .pm-textfield__control                             (style.ts:3-19)
     status    [data-status="invalid"] | [data-status="valid"]    (style.ts:22-26)
     select    .pm-textfield--select   (cursor-pointer)           (style.ts:32)
     disabled  .pm-textfield.is-disabled / control[data-disabled] (style.ts:28)

   Frozen-state helpers for the matrix / screenshots (duplicate the pseudo
   rules so a static screenshot captures them):  .is-hover  .is-focus

   SCOPE (G1-2, scope-frozen): SAME API / props / layout. rounded-lg 8px kept
   (--radius-control / --radius-button-product tier, G0-2). h-44 / px-2 kept.
   Change ONLY: token-alignment, :focus-visible ring, --motion timing, and
   state completion (hover / focus / persistent error+valid / disabled ring-off).
   Dark mode is OUT OF SCOPE here. Disabled colours are KEPT VERBATIM per the
   Amir G1-1 veto ("older better") — only box-shadow:none is added.

   G2-1(B) R1 component loop 2026-07-07: left/right icon colour shifted one stop
   neutral-400 -> neutral-500 (2.80:1 -> 4.35:1 vs white; WCAG 1.4.11 needs >=3:1
   for meaningful non-text UI) — same one-stop-same-scale rationale as the
   plan #9 placeholder recolor. Also fixed __righticon margin side (was
   margin-inline-end — sat flush against input text with dead space at the edge;
   mirrored __rightel instead; upstream source bug, not ported silently).

   V1.1 Figma SSOT NOTE: V1.1 SSOT anatomy = filled borderless field w/ magenta
   focus — this bordered recipe is the G1-2 interim; filled variant tracked for
   the V2 Figma rebuild.
   ============================================================================ */

@import url("../../tokens/tokens.css");
@import url("../../tokens/tokens-semantic.css");
@import url("../../tokens/tokens-type.css");  /* product type ramp — the 14 named Figma text styles (2026-08-01) */

/* ----------------------------------------------------------------------------
   WRAPPER  (TextField.tsx:79 — flex-col space-y-1.5 group relative)
   ---------------------------------------------------------------------------- */
.pm-textfield {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);               /* 8px. WAS a raw 6px (source space-y-1.5). 6px is 1.5x the base step and has no token behind it; the label-above-control stack is the FAMILY anchor (date-picker, rich-text-editor and settings-section all cite this line), so it sets the family value. Up, not down: a label crowding its own field is the one failure mode this gap exists to prevent. */
  position: relative;
  font-family: inherit;
}

/* ----------------------------------------------------------------------------
   CONTROL / CONTAINER  (style.ts:3-19 + plan #1 focus, #2 motion)
   flex items-center px-2 h-44 rounded-lg text-2(14px) border border-neutral-300
   bg-transparent.  transition swapped to --motion-standard (button.css:51-53).
   ---------------------------------------------------------------------------- */
.pm-textfield__control {
  display: flex;
  align-items: center;
  box-sizing: border-box;
  height: var(--control-h);          /* 36px — the single-row control height, pulled from tokens.css:574 instead of a literal so the field cannot drift off the rule. It replaced a raw 44px at equal value in 2026-08-01; putting it on the token is what let it follow the 2026-08-04 founder reversal down to 36. */
  padding-inline: var(--space-2);    /* px-2 = 8px — snapped to --space ramp (M3, px-identical) */
  /* → control/field. WAS 14px. The input VALUE. */
  font-size: var(--type-control-field-size);
  font-weight: var(--type-control-field-weight);
  line-height: var(--type-control-field-leading);
  letter-spacing: var(--type-control-field-tracking);
  border: 1px solid var(--border);   /* M1: resting control hairline → border role (was --neutral-300 #B4BFC7) */
  border-radius: var(--radius-control);   /* --r-sm — SAME tier as button. NB the value moved: --radius-control now resolves to 12px (tokens-semantic.css:163, "WAS 8px"). The comment used to say 8px; the TOKEN is the source of truth, so do not "correct" this back to 8. */
  background-color: transparent;
  /* plan #2 — 150ms (100-250ms band), canonical easing (button.css:51-53 parity).
     Only border-color + box-shadow animate; no other animation. */
  transition-property: border-color, box-shadow;
  transition-duration: var(--motion-duration, 150ms);
  transition-timing-function: var(--motion-standard);
  /* plan #1 — wire approved focus ring on the CONTAINER; invisible while unfocused */
  outline:2px solid transparent;outline-offset:2px; /* forced-colors visible (R2) */
}

/* input strips its own outline; colour parity with TextField.tsx:124-142 */
.pm-textfield__input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  padding: 0;
  background-color: transparent;
  outline: none;                     /* TextField.tsx:129 outline-none (ring lives on container) */
  font: inherit;
  color: var(--text-primary);         /* M1: input value ink → primary text role (was --neutral-800 #2A2F38) */
}
/* PLACEHOLDER (plan #9) — cur placeholder-neutral-400 #909CA8 = 2.80:1 (FAIL 4.5).
   neutral-500 only reaches 4.35:1 (<4.5, still FAIL); neutral-600 #4E5666 = 7.38:1 PASS. */
.pm-textfield__input::placeholder {
  color: var(--text-secondary);         /* M1: placeholder → secondary text role (was --neutral-600; PASS AA) */
  opacity: 1;                        /* Firefox resets placeholder opacity */
}

/* left/right icon + element spacing — logical margins (RTL-safe, TextField.tsx) */
.pm-textfield__lefticon  { margin-inline-end: 12px; color: var(--text-muted); display: inline-flex; }   /* M1: input icon → muted text role (was --neutral-500 #6F7A8A) */
/* → control/field, the same style as the input it sits beside. WAS undeclared, so
   it rendered at 14px from an ancestor instead of the field size. */
.pm-textfield__leftel    { margin-inline-end: var(--space-3); padding-inline-start: var(--space-1); display: inline-flex; font-size: var(--type-control-field-size); font-weight: var(--type-control-field-weight); line-height: var(--type-control-field-leading); letter-spacing: var(--type-control-field-tracking); }
.pm-textfield__rightel   { margin-inline-start: 12px; display: inline-flex; }
.pm-textfield__righticon { margin-inline-start: 12px; color: var(--text-muted); display: inline-flex; } /* M1: input icon → muted text role (was --neutral-500); margin side fixed (mirror of __rightel) */
.pm-textfield__lefticon  > svg,
.pm-textfield__righticon > svg { height: 18px; width: 18px; }   /* [&>svg]:h-[18px] */

.pm-textfield--select .pm-textfield__control { cursor: pointer; }  /* isSelect (style.ts:32) */

/* ----------------------------------------------------------------------------
   HOVER  (plan #3) — DOES NOT EXIST today (0 hover classes in source).
   Non-disabled control hover → border-neutral-400 #909CA8 (own scale, next stop
   up from resting neutral-300). Completes the missing hover state.
   .is-hover = frozen duplicate for static screenshots.
   ---------------------------------------------------------------------------- */
.pm-textfield:not(.is-disabled) .pm-textfield__control:hover,
.pm-textfield__control.is-hover {
  border-color: var(--border-strong);       /* M1: hover control border → strong border role (was --neutral-400 #909CA8) */
}

/* ----------------------------------------------------------------------------
   FOCUS RING  (plan #1) — fires when the INPUT is :focus-visible, drawn on the
   CONTAINER via :has().  Copied EXACTLY from button.css:75-82 (two-layer offset
   ring): 2px --surface-1 (white) gap, then 2px --focus-ring.
   .is-focus = frozen duplicate for static screenshots.

   CONTRAST NOTE (WCAG 1.4.11) — carried verbatim from button.css:68-73:
   the ring colour (--primary-500 #d342f0) does NOT reach 3:1 directly against
   the filled button fills (e.g. vs success-600 1.13:1, vs primary-500 1.00:1).
   It relies on the two-layer construction: the 2px --surface-1 (white) offset
   sits BETWEEN ring and fill, and on white/paper surfaces both boundaries the
   eye actually sees clear 3:1 — white-vs-fill and ring-vs-white are each 3.62:1
   for primary. Do NOT claim ring-vs-fill contrast.
   ---------------------------------------------------------------------------- */
.pm-textfield__control:has(.pm-textfield__input:focus-visible),
.pm-textfield__control.is-focus {
  box-shadow:
    0 0 0 2px var(--surface-1),
    0 0 0 4px var(--focus-ring);          /* --focus-ring = --primary-500 #d342f0 */
}

/* invalid + focused → error halo (swaps the magenta ring for --shadow-error).
   Persistent error border (:151) stays underneath. :not(.is-disabled) proactive
   guard so a disabled+invalid field never shows the halo (spec §4c). */
.pm-textfield:not(.is-disabled) .pm-textfield__control[data-status="invalid"]:has(.pm-textfield__input:focus-visible),
.pm-textfield:not(.is-disabled) .pm-textfield__control[data-status="invalid"].is-focus {
  box-shadow: var(--shadow-error);
}

/* ----------------------------------------------------------------------------
   STATUS — PERSISTENT BORDERS  (plans #4 error, #5 valid)
   Today the border only tints inside group-focus-within (style.ts:22-26), so an
   UNFOCUSED invalid/valid field is invisible (indistinguishable from default).
   Fix: colour the resting border by status. CVA variant KEYS unchanged — only
   the class value. Both clear WCAG 1.4.11 3:1 vs white for non-text UI.
   ---------------------------------------------------------------------------- */
.pm-textfield__control[data-status="invalid"] {
  border-color: var(--border-error);      /* was --error-500 #F2407B 3.62:1/white; role dark=error-400 (airtight) */
}
.pm-textfield__control[data-status="valid"] {
  border-color: var(--border-success);    /* → --success-700 #008364, 4.00 plate / 4.74 card (tokens-semantic.css:264); dark → --success-400 #25E2A9. 2026-08-02: the role was repointed off --success-600, which missed the 3:1 non-text bar on the plate; the retired hex is deliberately NOT printed here so this comment can never be read as the live value. */
}
/* status hover — the invalid edge steps one stop up so the signal never regresses.
   The VALID edge does not: --border-success-strong was collapsed onto
   --border-success on 2026-08-02 (tokens-semantic.css:265, DEPRECATED alias), so a
   valid field shows no hover change in either theme. Stated, not hidden — the cure
   is a new green stop between -600 and -700, which is founder decision FD-4, not a
   repoint this file may make. */
.pm-textfield:not(.is-disabled) .pm-textfield__control[data-status="invalid"]:hover,
.pm-textfield__control[data-status="invalid"].is-hover { border-color: var(--border-error-strong); }   /* → --error-600 #E22056, 3.88 plate / 4.59 card */
.pm-textfield:not(.is-disabled) .pm-textfield__control[data-status="valid"]:hover,
.pm-textfield__control[data-status="valid"].is-hover   { border-color: var(--border-success-strong); } /* → #008364 — identical to the resting edge above (see the note). */

/* ----------------------------------------------------------------------------
   LABEL  (style.ts:45-63, TextField.tsx:84-100)
   text-2(14px) font-bold; neutral-900 rest, status colours already AA-pass.
   ---------------------------------------------------------------------------- */
.pm-textfield__labelrow {
  display: flex;
  align-items: center;
  justify-content: space-between;    /* label + topElement */
  gap: 12px;
}
.pm-textfield__label {
  /* → body/strong. WAS 14px. The field label. */
  font-size: var(--type-body-strong-size);
  font-weight: var(--type-body-strong-weight);
  line-height: var(--type-body-strong-leading);
  letter-spacing: var(--type-body-strong-tracking);
  color: var(--text-primary);         /* M1: label ink → primary text role (was --neutral-900 #1E2229) */
}
.pm-textfield[data-status="invalid"] .pm-textfield__label { color: var(--text-error-strong); }   /* was --error-700 #C4123F 5.99:1; role dark=error-400 */
/* AA FIX 2026-08-02 — the ONE contrast hit `npm test` logs on this component.
   --text-success is the WHITE-tuned success ink (--success-700 #008364, and its
   own token comment says "4.74:1/white"). This label does not land on white: the
   product plane under a field is paper/tray #faf8fc, and #008364 measures
   4.49:1 there — under AA 4.5. It fails on THREE of the four light planes
   (page #f4f2f6 4.26 · tray #faf8fc 4.49 · plate #efeaf3 4.00) and only clears
   on the pure-white card (4.74).
   Repointed to --text-positive, the PAPER-tuned positive ink role
   (--green-text #007a5c; its token comment states "WCAG 5.05:1 on paper", which
   this measurement reproduces exactly). It clears AA on all four light planes:
   page 4.79 · tray 5.05 · plate 4.50 · card 5.33.
   The PLANE WAS NOT TOUCHED — only the ink role moved.
   Dark mode is unchanged: --text-success and --text-positive BOTH resolve to
   --green-bright #00c993 under [data-theme="dark"] (8.42 tray / 6.64 card). */
.pm-textfield[data-status="valid"]   .pm-textfield__label { color: var(--text-positive); }       /* was --text-success #008364 → 4.49:1 on paper FAIL; now #007a5c → 5.05:1 PASS */

/* ----------------------------------------------------------------------------
   HELPER / ERROR TEXT  (style.ts:65-73, TextField.tsx:162-171)
   text-3(12px), absolute -bottom-6 (layout FROZEN per scope).  Helper is small
   text so 4.5:1 applies. Every current token FAILS; step one stop within the
   SAME scale to the first passing value:
     helper   neutral-500 #6F7A8A 4.35:1 FAIL -> neutral-600 #4E5666 7.38:1 PASS  (plan #6)
     invalid  error-500  #F2407B 3.62:1 FAIL -> error-600  #E22056 4.59:1 PASS   (plan #7)
     valid    success-500 #00C993 2.15 / success-600 3.19 FAIL -> success-700 #008364 4.74:1 PASS (plan #8)

   WIRING RULE (a11y, R1 component loop 2026-07-07): whenever a helper/error is
   rendered, give it id="{field-id}-help" and set aria-describedby="{field-id}-help"
   on the input (Polaris/Carbon pattern) so screen readers announce WHY a field
   is invalid, not just that it is. Canonical markup: stories/TextField.stories.js.
   ---------------------------------------------------------------------------- */
.pm-textfield__helper {
  position: absolute;
  inset-inline-start: 0;
  bottom: -24px;                     /* -bottom-6 */
  /* → body/default. WAS 13px. Helper copy. */
  font-size: var(--type-body-default-size);
  font-weight: var(--type-body-default-weight);
  line-height: var(--type-body-default-leading);
  letter-spacing: var(--type-body-default-tracking);
  color: var(--text-secondary);         /* M1: helper text → secondary text role (was --neutral-600) */
}
.pm-textfield[data-status="invalid"] .pm-textfield__helper { color: var(--text-error); }   /* was --error-600 #E22056 4.59:1; role dark=error-400 */
/* Same AA repoint as the label above — identical ink role on the identical plane,
   so it carried the identical defect (4.49:1 on paper). --text-positive → 5.05:1. */
.pm-textfield[data-status="valid"]   .pm-textfield__helper { color: var(--text-positive); } /* was --text-success #008364 4.49:1 on paper FAIL; now #007a5c 5.05:1 PASS */

/* ----------------------------------------------------------------------------
   DISABLED  (style.ts:28,55,73 + TextField.tsx:136,139 + plan #11)
   Colours KEPT VERBATIM per Amir G1-1 veto ("older better") — WCAG exempts
   disabled controls. The only ADDITIONS are suppression-only, both serving the
   veto: box-shadow:none so the new ring/hover can never show on a disabled field
   (button.css:145 parity), and a placeholder override so the enabled-state AA
   recolor (:86 neutral-600) never reaches a disabled field's placeholder. Disabled
   also beats the status-border rules via source order + the guard on hover selectors.
   ---------------------------------------------------------------------------- */
.pm-textfield.is-disabled .pm-textfield__control,
.pm-textfield__control[data-disabled] {
  background-color: var(--surface-disabled);   /* disabled fill via alias (light=#ECF0F3 verbatim; dark override) */
  border-color: var(--field-border);       /* was --neutral-300; role: light=neutral-300 verbatim, dark=white-alpha (airtight) */
  cursor: not-allowed;
  box-shadow: none;                        /* suppression-only: never ring while disabled */
}
.pm-textfield.is-disabled .pm-textfield__input {
  color: var(--neutral-400);              /* disabled:text-neutral-400 (TextField.tsx:136) — verbatim */
  cursor: not-allowed;
}
.pm-textfield.is-disabled .pm-textfield__input::placeholder {
  color: var(--neutral-400);              /* veto: disabled placeholder kept verbatim (neutral-400) — the AA recolor above is enabled-only */
}
.pm-textfield.is-disabled .pm-textfield__label,
.pm-textfield.is-disabled .pm-textfield__helper {
  color: var(--neutral-300);              /* label+helper text-neutral-300 (style.ts:55,73) — verbatim */
}

/* ----------------------------------------------------------------------------
   REDUCED MOTION — honour prefers-reduced-motion (house pattern, checkbox.css
   parity; R1 component loop 2026-07-07).
   ---------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .pm-textfield__control { transition: none; }
}

/* ============================================================================
   PRODUCT FIELD ANATOMY — the four states/slots the port had not reached yet
   (2026-08-02, forms lane). Every one of these is IN THE SHIPPING PRODUCT and
   was simply missing from this sheet; none is invented. Sources are quoted
   file:line against /Portmasters/portmasters-frontend.
   ============================================================================ */

/* ----------------------------------------------------------------------------
   REQUIRED MARKER  —  .pm-textfield__required
   PRODUCT: an asterisk span appended to the label text, four live sites:
     views/booking-contact/BookingContact.tsx:543,557,571
       {t("firstName")}<span className="text-error-500 ms-1">*</span>
     shared/cancel-order-modal/CancelOrderModal.tsx:61
       {t("reasonLabel")} <span className="text-error-500">*</span>
   …and the same glyph one ramp over, on Tailwind's own red rather than the
   product error ramp — a second small defect this consolidates:
     packages/shared/../rich-text-editor/RichTextEditor.tsx:149,164
       {required && <span className="text-red-500 ms-1">*</span>}
   Geometry kept verbatim (ms-1 = 4px = --space-1, trailing the label word).
   V2 changes the INK ONLY: error-500 #F2407B measures 3.62:1 on white and
   fails AA for a text glyph; --text-error (#E22056, 4.59:1) is the first
   passing stop on the same ramp and is the role every other error string in
   this sheet already uses (:237). No new hue, no new geometry.

   WIRING RULE (a11y): the glyph is decoration — mark it aria-hidden="true" and
   put the machine-readable half on the control (required + aria-required="true").
   A red "*" alone is colour-alone signalling and is not announced.
   ---------------------------------------------------------------------------- */
.pm-textfield__required {
  margin-inline-start: var(--space-1);   /* ms-1 = 4px, verbatim */
  color: var(--text-error);              /* was error-500 #F2407B 3.62:1 FAIL → #E22056 4.59:1 PASS */
  font-weight: inherit;                  /* rides the label's weight, never bolder */
}
/* disabled swallows the marker with the rest of the label row (style.ts:55 parity) */
.pm-textfield.is-disabled .pm-textfield__required { color: var(--neutral-300); }

/* ----------------------------------------------------------------------------
   BOTTOM ROW  —  .pm-textfield__bottomrow   (TextField.tsx:161-173)
   The product renders helperText AND `BottomElement` as SIBLINGS in one
   flex row with justify-between — the mirror of __labelrow at the top. This
   sheet had only the bare __helper, so a caller with a character counter or a
   "Forgot password?" link at the far end had nowhere to put it.
   The row inherits the out-of-flow placement the helper already had, so
   composition behaviour is unchanged for existing callers (a lone __helper
   still works exactly as before).
   ---------------------------------------------------------------------------- */
.pm-textfield__bottomrow {
  position: absolute;
  inset-inline: 0;
  bottom: -24px;                     /* same -bottom-6 the helper used */
  display: flex;
  align-items: center;
  justify-content: space-between;    /* helper start, BottomElement end */
  gap: var(--space-3);               /* 12px — mirrors __labelrow */
}
/* inside the row the helper is in flow again; it keeps its own type + status ink */
.pm-textfield__bottomrow .pm-textfield__helper {
  position: static;
  bottom: auto;
}
.pm-textfield__bottomel {
  /* → meta/default. The counter / trailing link. Quieter than the helper. */
  font-size: var(--type-meta-default-size);
  font-weight: var(--type-meta-default-weight);
  line-height: var(--type-meta-default-leading);
  letter-spacing: var(--type-meta-default-tracking);
  color: var(--text-muted);
  white-space: nowrap;               /* a counter never wraps */
}
.pm-textfield.is-disabled .pm-textfield__bottomel { color: var(--neutral-300); }

/* ----------------------------------------------------------------------------
   READ-ONLY  —  .pm-textfield--readonly   (the biggest gap in the port)
   PRODUCT: `readOnly={isFieldReadonly(...)}` on 20+ fields of the shipping
   order (shared/active-booking/components/ShippingOrder.tsx:686,706,726,746,
   776,796,816,836,866,886,906,926,956,976,996,1016,1045,1074,1103,1122 —
   predicate at :368), and on the cargo segment of the home search bar
   (shared/search-form/SearchForm.tsx:703 `readOnly`). The ONLY styling the
   product gives it is `read-only:cursor-pointer` (TextField.tsx:138) — so a
   locked field is pixel-identical to an editable one. In a 3-column grid that
   mixes locked and editable fields, that is the defect this state fixes.

   V2 treatment — a read-only field stops looking like an input and starts
   looking like a VALUE, while staying focusable and selectable (which is the
   whole difference from disabled):
     · border goes transparent — the 1px is KEPT so the box geometry, and the
       baseline it shares with its editable neighbours, do not shift;
     · ink stays --text-primary at full strength (a locked value must stay
       readable — disabled is the state that greys it out);
     · fill stays transparent. It deliberately does NOT take --surface-plate:
       that plane is a ratified MATERIAL with at most three painting classes
       (tokens-semantic.css:136 — nameplate, disabled control, selected row)
       and a read-only field is not one of them;
     · hover is suppressed (nothing to afford), focus ring is KEPT (it is
       still in the tab order, and you can still copy out of it);
     · cursor: pointer, verbatim from TextField.tsx:138 — in the product a
       read-only field is often a launcher (the cargo segment opens a sheet).
   ---------------------------------------------------------------------------- */
.pm-textfield--readonly .pm-textfield__control {
  border-color: transparent;         /* 1px width kept → zero layout shift when a field unlocks */
  background-color: transparent;
  cursor: pointer;                   /* read-only:cursor-pointer (TextField.tsx:138) */
}
.pm-textfield--readonly .pm-textfield__input {
  cursor: pointer;
  color: var(--text-primary);        /* full-strength ink — NOT the disabled grey */
}
/* hover must not resurrect an edge on a field you cannot edit */
.pm-textfield--readonly:not(.is-disabled) .pm-textfield__control:hover,
.pm-textfield--readonly .pm-textfield__control.is-hover {
  border-color: transparent;
}
/* status still speaks: a locked field can carry a server-side error, and the
   edge is the only carrier it has left, so status beats the transparent rule.
   The :not(.is-disabled) guard keeps disabled above status, the same order the
   base rules already use — without it these would out-rank the disabled block
   on cascade position and a disabled+invalid read-only field would ring red. */
.pm-textfield--readonly:not(.is-disabled) .pm-textfield__control[data-status="invalid"] { border-color: var(--border-error); }
.pm-textfield--readonly:not(.is-disabled) .pm-textfield__control[data-status="valid"]   { border-color: var(--border-success); }

/* ----------------------------------------------------------------------------
   MULTILINE  —  .pm-textfield--multiline / .pm-textfield__textarea
   PRODUCT: the long-answer control is never TextField — it is a raw <textarea>
   repeated with the same recipe across the website app:
     views/support/Support.tsx:337-340
       "w-full min-h-[100px] rounded-lg border border-neutral-300 px-3 py-2
        text-sm outline-none focus:border-primary-500 focus:ring-1 resize-y"
     shared/cancel-order-modal/CancelOrderModal.tsx:63-72  (rows={4}, resize-none,
        border-error-500 when the field is in error)
     also ContactUs.tsx:137 · BookDemo.tsx:309 · PlansPricingContact.tsx:147 ·
     SubmitReviewModal.tsx:200 · BookingSupportTab.tsx:319,634 · ViewTicket.tsx:324 ·
     UploadDocumentModal.tsx:296 · RejectCommentModal.tsx:70 · BookingCargo.tsx:269 ·
     Request.tsx:1744 · DeleteQuoteRequestModal.tsx:96
   Bringing it into the family is what makes "Message", "Reason for rejection"
   and "Leave us a comment" share one skin with the fields above them.

   HEIGHT RULE — read this before flagging it: a textarea is NOT a single-row
   control (named in the founder’s own carve-out list), so the 36px rule does not
   reach it. Its floor is the product's own
   min-h-[100px]; --control-h must not be applied here.
   ---------------------------------------------------------------------------- */
.pm-textfield--multiline .pm-textfield__control {
  height: auto;                      /* releases --control-h — see HEIGHT RULE above */
  min-height: 100px;                 /* min-h-[100px], verbatim (Support.tsx:339) */
  align-items: stretch;              /* the textarea fills the box, not a centred row */
  padding: var(--space-2) var(--space-3);   /* py-2 px-3 = 8px / 12px, verbatim */
}
.pm-textfield__textarea {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  padding: 0;
  margin: 0;
  background-color: transparent;
  outline: none;                     /* ring lives on the container, as with __input */
  font: inherit;                     /* picks up --type-control-field-* from __control */
  color: var(--text-primary);
  resize: vertical;                  /* resize-y (Support.tsx:339). --multiline-fixed pins it */
}
.pm-textfield__textarea::placeholder {
  color: var(--text-secondary);
  opacity: 1;
}
.pm-textfield--multiline-fixed .pm-textfield__textarea { resize: none; }  /* resize-none (CancelOrderModal.tsx:68) */
/* the focus ring is keyed off __input; give the textarea the identical hook */
.pm-textfield__control:has(.pm-textfield__textarea:focus-visible) {
  box-shadow:
    0 0 0 2px var(--surface-1),
    0 0 0 4px var(--focus-ring);
}
.pm-textfield:not(.is-disabled) .pm-textfield__control[data-status="invalid"]:has(.pm-textfield__textarea:focus-visible) {
  box-shadow: var(--shadow-error);
}
.pm-textfield.is-disabled .pm-textfield__textarea {
  color: var(--neutral-400);
  cursor: not-allowed;
  resize: none;
}
.pm-textfield.is-disabled .pm-textfield__textarea::placeholder { color: var(--neutral-400); }

/* ----------------------------------------------------------------------------
   QUIET  —  .pm-textfield--quiet   (the field-inside-a-bar)
   PRODUCT: the home search bar does not use a bordered field. It overrides the
   container to nothing and shrinks the label into a caption:
     shared/search-form/SearchForm.tsx:486-487
       containerClassName="bg-transparent group-focus-within:border-transparent
                           border-transparent h-auto pb-2"
       labelClassName="text-neutral-400 group-focus-within:text-neutral-800
                       px-2 font-light text-3"
     repeated at :570-571 (destination), :646-647 (date), :681-682 (cargo),
     :745-746 (vessel id).
   So "quiet" is a REAL product variant of this component, expressed today as
   five copies of the same override string. Named here once; search-form.css
   composes it and owns nothing about the field itself.

   Two honest V2 changes:
     1. the caption moves onto the type ramp (--type-meta-default) instead of
        text-3 + font-light, which has no rung;
     2. the focus ring is KEPT. The product's override deletes the border on
        focus too, which leaves a focusable control with no visible focus at
        all (WCAG 2.4.7). Not portable. The ring is the house two-layer build,
        unchanged.
   ---------------------------------------------------------------------------- */
.pm-textfield--quiet .pm-textfield__control {
  border-color: transparent;
  background-color: transparent;
}
.pm-textfield--quiet:not(.is-disabled) .pm-textfield__control:hover,
.pm-textfield--quiet .pm-textfield__control.is-hover {
  border-color: transparent;         /* the bar segment never grows an edge */
}
.pm-textfield--quiet .pm-textfield__label {
  /* → meta/default. WAS text-3 + font-light — a size/weight pair with no rung. */
  font-size: var(--type-meta-default-size);
  font-weight: var(--type-meta-default-weight);
  line-height: var(--type-meta-default-leading);
  letter-spacing: var(--type-meta-default-tracking);
  color: var(--text-muted);          /* was neutral-400 #909CA8 (2.80:1) → muted role */
  padding-inline: var(--space-2);    /* px-2, verbatim — aligns the caption over the value */
}
/* label darkens with focus, exactly as group-focus-within does in the product */
.pm-textfield--quiet:focus-within .pm-textfield__label { color: var(--text-primary); }
.pm-textfield--quiet:focus-within .pm-textfield__lefticon,
.pm-textfield--quiet:focus-within .pm-textfield__righticon { color: var(--text-primary); }
/* status keeps its edge even here — otherwise an invalid bar segment is silent.
   Same disabled guard as the read-only rules above, for the same reason. */
.pm-textfield--quiet:not(.is-disabled) .pm-textfield__control[data-status="invalid"] { border-color: var(--border-error); }

/* ----------------------------------------------------------------------------
   RTL — already logical-property safe in the real component (me-, ms-, ps- utilities;
   TextField.tsx:116,123,149,153; style.ts:36). Every margin/padding above uses
   inset-inline / margin-inline, so nothing here is physical — it flips with
   dir="rtl" automatically. --font-arabic token (tokens.css:216) drives ar type.
   ---------------------------------------------------------------------------- */

