/* ============================================================
   Enduro Field Portal — portal.css
   THE SHARED DESIGN SYSTEM. Every current and future portal
   form/tool consumes this ONE stylesheet.

   Canonical brand tokens: 01_BRAND_LIBRARY/BRAND_SYSTEM.md
   Design law:             PORTAL_DESIGN_CONTRACT.md
   Notion-feel ramp:       _BUILD/research/01_notion-aesthetic-dissection.md
   Driving spec:           _BUILD/WEEKLY_REPORT_BUILD_SPEC.md  (§2)

   ------------------------------------------------------------
   HOW TO ADD A FUTURE FORM / TOOL (the whole recipe):
     1. New folder:  SITE/<form-name>/index.html
                     SITE/<form-name>/<form-name>.css   (page LAYOUT only)
                     SITE/<form-name>/<form-name>.js     (page controller)
     2. In the page <head>, link in this order:
          assets/css/base.css      (brand tokens + reset; do NOT duplicate)
          assets/css/portal.css    (THIS — all shared form components)
          <form-name>.css          (page-specific layout, consumes the above)
     3. Reuse the shared JS modules from assets/js/* (store, repeatable,
        photos, validate, report-export). Wire them in <form-name>.js.
     4. Build markup from the components below (.field / .input / .label /
        .form-section / .btn …). NO inline styles in production markup.

   Square corners everywhere (border-radius:0 — inherited from base.css reset,
   and re-asserted on interactive elements here). Brand red appears ONLY on
   genuine error states. Whitespace is the brand.
   ============================================================ */


/* ============================================================
   0. PAGE-TO-PAGE TRANSITION  (cross-document View Transitions)
   The website's view-transitions "flash", copied VERBATIM (assets/nav.css
   lines 14-29) and scoped to the Portal. A quick 0.2s opacity crossfade on the
   page body; the TOP NAV holds STATIC (no flash) via view-transition-name.
   Present in BOTH base.css (home launcher) and portal.css (forms) because
   cross-document VT needs the rule on the page you LEAVE and the one you LAND
   on. The same crossfade also drives every IN-PAGE wizard step change, fired
   from wizard.js via document.startViewTransition (the nav's name keeps it
   static there too). reduced-motion → no animation (the swap still happens).
   ============================================================ */
@view-transition { navigation: auto; }
::view-transition-group(portal-nav),
::view-transition-old(portal-nav),
::view-transition-new(portal-nav) { animation-duration: 0s; animation-delay: 0s; }
::view-transition-old(root) { animation: vt-fade-out 0.2s ease both; }
::view-transition-new(root) { animation: vt-fade-in  0.2s ease both; }
@keyframes vt-fade-out { to   { opacity: 0; } }
@keyframes vt-fade-in  { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root), ::view-transition-new(root) { animation: none !important; }
}


/* ============================================================
   1. TOKENS  —  layered on top of base.css (brand colours +
   --ink family + easing already live there; we add the ghost-grey
   ramp, spacing scale, and type scale the form system needs).
   ============================================================ */
:root {
  /* — Ghost-grey ramp (Radix Gray role mapping, research 01) — */
  --text:          #1A1A1A;   /* primary form text (near-black, not pure)  */
  --text-2:        #646464;   /* secondary text / hints                    */
  --text-3:        #8D8D8D;   /* tertiary text / faint meta                */
  --placeholder:   #9E9E9E;   /* input placeholders                        */
  --border:        #CECECE;   /* hairline (inset-ring) border              */
  --border-hover:  #BBBBBB;   /* hairline on hover                         */
  --divider:       #ECECEC;   /* section / row dividers                    */
  --fill:          #F6F5F4;   /* warm light surface (Notion fill)          */
  --fill-2:        #FAFAFA;   /* near-white surface                        */

  /* — Accent (brand red is canonical; the ring tint is derived) — */
  --accent:        var(--red);             /* #E43338 — focus + key actions */
  --accent-ring:   rgba(228, 51, 56, 0.18);/* soft outer focus halo         */
  --danger:        var(--red);             /* error ring = same brand red    */
  --danger-tint:   rgba(228, 51, 56, 0.08);/* faint error wash               */

  /* — Spacing scale (4px base) — */
  --s1:  4px;
  --s2:  8px;
  --s3:  12px;
  --s4:  16px;
  --s5:  20px;
  --s6:  24px;
  --s7:  28px;
  --s8:  32px;
  --s9:  36px;
  --s10: 40px;
  --s11: 44px;
  --s12: 48px;

  /* — Type scale (Inter) — */
  --t-page:    32px;   /* page title  (Inter Display feel via tracking)  */
  --t-section: 19px;   /* section heading                                */
  --t-label:   13px;   /* field label                                    */
  --t-body:    14px;   /* input / body text                              */
  --t-hint:    12.5px; /* hint / helper / required-optional              */

  /* — Field rhythm (generous, per spec §2) — */
  --rhythm-section: var(--s11);  /* 44px section ↔ section (40–48 band)    */
  --rhythm-label:   7px;         /* label → input (6–8)                    */
  --rhythm-field:   var(--s5);   /* 20px field → field (20–24)             */

  /* — Motion — */
  --dur:      0.22s;
  --dur-slow: 0.28s;
  /* --ease-out / --ease-in-out / --ease-spring inherited from base.css */

  /* — Soft input surface (the §6 borderless field: no grey/blue/black stroke
       at rest; a calm filled surface makes the field discoverable; brand red
       only on focus). — */
  --fill-hover:  #EFEEEC;   /* a touch deeper than --fill on hover           */

  /* — Shared layout rail (nav + wizard share ONE gutter + ONE content
       measure so the wizard's centered container lines up exactly with the
       top nav, per SPEC §1/§2). Future forms inherit these — never hardcode. */
  --portal-gutter:  clamp(28px, 4.6vw, 58px);   /* matches the portal HOME page gutter (Radik) */
  --portal-measure: var(--max);  /* same box as the portal home card (Radik) */
  --nav-h:          94px;        /* top nav height: prior 64px + 30px (§2)   */
}


/* ============================================================
   2. PAGE / SCROLL CONTEXT
   base.css home.css locks the launcher to no-scroll; form pages
   need normal document scrolling. Scope to .portal so we never
   touch the home launcher.
   ============================================================ */
.portal {
  min-height: 100%;
  background: var(--white);
  color: var(--text);
  font-family: 'Inter', Arial, sans-serif;
  font-size: var(--t-body);
  line-height: 1.55;
}

/* Centred reading column for any form/tool. */
.portal-shell {
  width: 100%;
  max-width: 760px;
  margin: 0 auto;
  padding: 0 var(--s6);
}

/* ============================================================
   3. TYPOGRAPHY PRIMITIVES
   ============================================================ */
.page-title {
  font-size: calc(var(--t-page) * 0.8);   /* −20% (Radik 2026-06-29); scales at both breakpoints */
  font-weight: 900;
  letter-spacing: -0.025em;       /* Inter Display feel */
  line-height: 1.06;
  text-transform: uppercase;      /* intro title in ALL CAPS (Radik 2026-06-29) */
  color: var(--text);
  font-feature-settings: "cv05" 1, "ss03" 1; /* graceful no-op if unsupported */
}

.page-lede {
  margin-top: var(--s3);
  font-size: var(--t-body);
  font-weight: 400;
  color: var(--text-2);
  line-height: 1.6;
  max-width: 56ch;
}

/* Small uppercase eyebrow (matches the launcher's micro-labels). */
.eyebrow {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-2);
}


/* ============================================================
   4. FORM SECTION  (heading + body)
   ============================================================ */
.form-section {
  margin-top: var(--rhythm-section);
  padding-top: var(--rhythm-section);
  border-top: 1px solid var(--divider);
}
.form-section:first-of-type {
  margin-top: var(--s8);
  padding-top: 0;
  border-top: 0;
}

.section-h {
  font-size: var(--t-section);
  font-weight: 700;
  letter-spacing: 0;           /* caps read better without negative tracking */
  line-height: 1.25;
  text-transform: uppercase;   /* all section titles ALL CAPS (Radik 2026-06-29) */
  color: var(--text);
}
/* Section subtext REMOVED from every page (Radik 2026-06-29) — the descriptive
   "What got done this week. One item per line." style lines are gone screen-wide
   (they were already print-hidden). Hidden rather than deleted from each step's
   markup so it's one reversible switch across both forms. */
.section-sub {
  display: none;
}

/* Body of a section sits below its heading with calm spacing. */
.section-body { margin-top: var(--s5); }


/* ============================================================
   5. FIELD  (label · hint · control · error)
   ============================================================ */
.field { margin-top: var(--rhythm-field); }
.field:first-child { margin-top: 0; }

.label {
  display: block;
  font-size: var(--t-label);
  font-weight: 500;
  line-height: 1.4;
  color: var(--text);
  margin-bottom: var(--rhythm-label);
}

/* SPEC §4 — required → a single RED ASTERISK after the label (the grey
   "required" word/pill is gone); optional → removed entirely (no replacement).
   These work on the existing `<span class="required">required</span>` markup
   without an HTML edit: the literal word is collapsed to zero size and a red
   "*" glyph is drawn in its place. One shared rule → every form inherits it. */
.required {
  margin-left: var(--s1);
  font-size: 0;                 /* hide the literal "required" text          */
  letter-spacing: 0;
  color: var(--red);
}
.required::after {
  content: "*";
  font-size: calc(var(--t-body) * 1.125);  /* +25% then −10% (Radik 2026-06-29) */
  font-weight: 700;
  color: var(--red);
  vertical-align: top;          /* top-right of the label text (middle made it drop) */
  line-height: 1;
  position: relative;
  top: 0.1em;                   /* tuck just off the very top edge */
}
/* On big section TITLES the asterisk rides the TOP-RIGHT of the heading as a
   small superscript, instead of a body-size glyph sitting at the baseline-middle
   (which read as "at the bottom of the title"). Radik 2026-06-29. Scoped to
   .section-h so the small field-label asterisks are untouched. Applies to every
   title on both forms (shared rule). */
.section-h .required::after {
  font-size: 12.375px;         /* small top-right mark, +25% then −10% (Radik 2026-06-29) */
  vertical-align: top;         /* ride the TOP of the heading line, not middle  */
  line-height: 1;
  position: relative;
  top: 1px;                    /* tuck just off the very top edge               */
}

/* "optional" tokens are removed from the UI entirely (no text, no space). */
.optional { display: none; }

/* Hint sits ABOVE the input (read before you type). */
.hint {
  font-size: var(--t-hint);
  font-weight: 400;
  color: var(--text-2);
  line-height: 1.5;
  margin-top: -2px;          /* tuck just under the label */
  margin-bottom: var(--s2);
}
/* A hint placed AFTER the control (e.g. error text) gets top spacing. */
.field > .hint:last-child { margin-top: var(--s2); margin-bottom: 0; }


/* ============================================================
   6. CONTROLS  —  .input / textarea / select
   SPEC §6/§7 — borderless fields: NO hairline stroke at rest (no grey / blue /
   black border), and NO stroke / ring / shadow / border-change on FOCUS either
   — a focused field keeps its calm grey surface and shows only the native
   blinking caret ("the flashing pointer"). Brand red on a field appears ONLY as
   an ERROR indication (§7 validation flash), never on plain focus.
   ============================================================ */
.input,
.portal textarea,
.portal select {
  display: block;
  width: 100%;
  padding: 10px 12px;
  font-family: inherit;
  font-size: var(--t-body);
  font-weight: 400;
  line-height: 1.5;
  color: var(--text);
  background-color: var(--fill);   /* soft surface REPLACES the old grey hairline */
  border: 0;
  box-shadow: none;            /* §6: no default border stroke                */
  transition:
    box-shadow var(--dur) var(--ease-out),
    background  var(--dur) var(--ease-out);
  -webkit-appearance: none;
  appearance: none;
}
textarea.input,
.portal textarea {
  min-height: 96px;
  resize: vertical;
}

.input::placeholder,
.portal textarea::placeholder { color: var(--placeholder); }

.input:hover,
.portal textarea:hover,
.portal select:hover {
  background-color: var(--fill-hover);   /* a touch deeper — still NO stroke   */
}

/* FOCUS = NO STROKE AT ALL — JUST THE CARET (SPEC §7, supersedes the earlier
   neutral ring). Clicking/focusing ANY field (text input, textarea, AND select
   for consistency) shows NO outline / ring / border-colour change / box-shadow /
   background change of any kind: the field keeps its default grey surface and
   the ONLY focus indication is the native blinking text caret. Brand red on a
   field stays ERROR-ONLY (§7 below). Every page, both forms. */
.input:focus,
.input:focus-visible,
.portal textarea:focus,
.portal textarea:focus-visible,
.portal select:focus,
.portal select:focus-visible {
  outline: none;
}

.input:disabled,
.portal textarea:disabled,
.portal select:disabled {
  background-color: var(--fill);
  color: var(--text-3);
  box-shadow: none;
  cursor: not-allowed;
  opacity: 0.6;
}

/* SPEC §5 — date / time fields open their NATIVE picker on click/focus
   (pickers.js calls showPicker()). Never expect typing → pointer cursor over
   the whole field so the affordance reads as "tap to pick". The native time
   scroll UI + the calendar glyph are left intact. */
.portal input[type="date"],
.portal input[type="time"],
.portal input[type="datetime-local"],
.portal input[type="month"],
.portal input[type="week"] {
  cursor: pointer;
}

/* ------------------------------------------------------------
   FRIENDLY DATE FIELD  (SPEC §2 — every date field, byte-identical via
   pickers.js). A native <input type=date> can't render "June 25, 2026"
   (the browser owns its display), so pickers.js wraps each date input in
   .date-field and overlays a .date-field-display span showing the value as
   "Month D, YYYY". The native input is KEPT as the real control (its
   id/name/value, the calendar glyph, and click-to-open showPicker all
   intact) — we only hide its own text (color:transparent) and float the
   friendly text on top (pointer-events:none, so a click still reaches the
   native and opens the calendar). Square, no extra chrome.
   ------------------------------------------------------------ */
.date-field {
  position: relative;
  display: block;
  width: 100%;
}
/* Hide the native field's own mm/dd/yyyy value text in EVERY state — the
   calendar glyph stays. On focus / open-picker, WebKit re-inks the value and it
   bled THROUGH the friendly "July 3, 2026" overlay (the "07/03/2026" garble
   Radik saw), so we also kill -webkit-text-fill-color and every datetime
   sub-field, incl. the focused state. (Radik 2026-07-03) */
.date-field > .input,
.date-field > .input:focus,
.date-field > .input:focus-visible {
  color: transparent !important;
  -webkit-text-fill-color: transparent !important;
}
.date-field > .input::-webkit-datetime-edit,
.date-field > .input::-webkit-datetime-edit-fields-wrapper,
.date-field > .input::-webkit-datetime-edit-text,
.date-field > .input::-webkit-datetime-edit-month-field,
.date-field > .input::-webkit-datetime-edit-day-field,
.date-field > .input::-webkit-datetime-edit-year-field,
.date-field > .input:focus::-webkit-datetime-edit-month-field,
.date-field > .input:focus::-webkit-datetime-edit-day-field,
.date-field > .input:focus::-webkit-datetime-edit-year-field {
  color: transparent !important;
  -webkit-text-fill-color: transparent !important;
  background-color: transparent !important;   /* kill the grey field highlight */
}
.date-field-display {
  position: absolute;
  left: 12px;            /* matches the .input left padding */
  right: 34px;           /* clears the native calendar glyph */
  top: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  font-family: inherit;
  font-size: var(--t-body);
  line-height: 1.5;
  color: var(--text);
  pointer-events: none;  /* clicks fall through to the native input */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Blank (e.g. an unfilled "due" cell) reads as a faint placeholder. */
.date-field-display.is-empty { color: var(--placeholder); }
/* The meta-grid baseline pin (page CSS pins .field > .input to the cell
   bottom); the wrapper now stands in for the input, so carry the pin over. */
.wr-meta-grid .field > .date-field,
.tt-meta-grid .field > .date-field { margin-top: auto; }

/* ------------------------------------------------------------
   FRIENDLY TIME FIELD + "SELECT TIME" WHEEL  (SPEC §5 — custom picker,
   built in pickers.js). The native <input type=time> is hidden and kept only
   as the value/name holder; a .time-field-display button (styled exactly like
   .input) shows "3:12 PM" and opens the wheel modal. Square corners throughout.
   ------------------------------------------------------------ */
.time-field { position: relative; display: block; width: 100%; }
/* Hide the native time control; it stays in the DOM as the real value/name. */
.time-field > input.input {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: 0; border: 0; opacity: 0; pointer-events: none;
}
.time-field-display {
  display: flex; align-items: center; text-align: left;
  width: 100%; cursor: pointer;
  font-family: inherit;
}
.time-field-display.is-empty { color: var(--placeholder); }
.wr-meta-grid .field > .time-field,
.tt-meta-grid .field > .time-field { margin-top: auto; }

/* The wheel modal ---------------------------------------------------------- */
.tw-backdrop {
  position: fixed; inset: 0; z-index: 1000;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
  background: rgba(16, 24, 32, 0.45);
  animation: tw-fade 0.16s var(--ease-out);
}
.tw-modal {
  width: min(320px, 92vw);
  padding: 22px 20px 16px;
  background: #fff;
  border-radius: 0;                    /* square — brand */
  box-shadow: 0 18px 50px rgba(16, 24, 32, 0.28);
  animation: tw-rise 0.18s var(--ease-out);
}
.tw-title {
  text-align: center; margin-bottom: 16px;
  font-size: 15px; font-weight: 600; letter-spacing: 0.2px;
  color: var(--text);
}
.tw-wheels {
  position: relative;
  display: flex; align-items: stretch; justify-content: center;
  gap: 2px; height: 200px;
}
/* the centered selection box (one continuous square across all columns) */
.tw-highlight {
  position: absolute; left: 6px; right: 6px; top: 80px; height: 40px;
  background: var(--fill);
  z-index: 0; pointer-events: none;
}
.tw-col { position: relative; z-index: 1; flex: 1 1 0; min-width: 0; }
.tw-col[data-unit="ampm"] { flex: 0 0 64px; }
.tw-scroll {
  height: 200px; overflow-y: scroll;
  scroll-snap-type: y mandatory;
  scrollbar-width: none; -ms-overflow-style: none;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 26%, #000 74%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 26%, #000 74%, transparent);
}
.tw-scroll::-webkit-scrollbar { display: none; }
.tw-spacer { height: 80px; flex: none; }   /* (200 − 40) / 2 → first/last row can center */
.tw-item {
  height: 40px; scroll-snap-align: center;
  display: flex; align-items: center; justify-content: center;
  width: 100%; padding: 0; border: 0; background: none; cursor: pointer;
  -webkit-appearance: none; appearance: none;
  font-family: inherit; font-size: 19px; font-weight: 500;
  color: var(--text);
  will-change: transform, opacity;
}
.tw-item.is-center { font-weight: 700; color: #101820; }
.tw-sep {
  z-index: 1; align-self: center; padding: 0 1px;
  font-size: 19px; font-weight: 700; color: var(--text);
}
.tw-actions {
  display: flex; align-items: center; justify-content: flex-end;
  gap: 4px; margin-top: 16px;
}
.tw-btn {
  font-family: inherit; font-size: 14px; font-weight: 600;
  cursor: pointer; border-radius: 0; -webkit-appearance: none; appearance: none;
  transition: background var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.tw-cancel { background: none; border: 0; color: var(--text-2); padding: 9px 14px; }
.tw-cancel:hover { color: var(--text); }
.tw-save {
  background: var(--accent); border: 1px solid var(--accent); color: #fff;
  padding: 9px 20px;
}
.tw-save:hover { filter: brightness(0.95); }
.tw-save:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--accent-ring); }

@keyframes tw-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes tw-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .tw-backdrop, .tw-modal { animation: none; }
}

/* Input + unit affix (e.g. a "%" sign trailing a number field). The unit is a
   non-interactive overlay; the input reserves right padding so typed text never
   collides with it. Square, calm, no extra chrome — the control's own inset
   ring is the only border. */
.input-affix { position: relative; }
.input-affix > .input { padding-right: 34px; }
.input-affix-unit {
  position: absolute;
  top: 0;
  right: 12px;
  height: 100%;
  display: inline-flex;
  align-items: center;
  font-size: var(--t-body);
  font-weight: 500;
  color: var(--text-3);
  pointer-events: none;
}

/* Native select chevron (square wrapper, ghost-grey glyph). */
.portal select,
.portal input.combo-chevron {
  padding-right: 36px;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='7' viewBox='0 0 11 7' fill='none'%3E%3Cpath d='M1 1L5.5 5.5L10 1' stroke='%238D8D8D' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
}
/* Combobox (datalist) inputs — "Completed by" / "Talk given by" — wear the SAME
   chevron as the Week / % selects so the name dropdown reads identically
   (Radik 2026-06-29). Hide the browser's native datalist button so there is one
   clean arrow; the field still opens its suggestions on focus / typing. */
.portal input.combo-chevron::-webkit-calendar-picker-indicator,
.portal input.combo-chevron::-webkit-list-button {
  display: none !important;
  -webkit-appearance: none;
  appearance: none;
  opacity: 0;
  width: 0;
  height: 0;
  pointer-events: none;
}


/* ============================================================
   7. ERROR STATE  —  RED-FLASH-ONLY  (Refinement Pass 2 §B)
   Validation surfaces PURELY as a red outline on the offending control — no
   summary, no inline message, no injected text, no layout shift. The control
   carries .is-error (steady red inset ring) the moment a Continue (the wizard
   per-step gate) or a submit finds it empty/invalid; .is-flash replays a short
   pulse (~3 cycles) to draw the eye. Both clear the instant the control becomes
   valid (validate.js, on input/change). Brand red on a field = error, ONLY error.
   The classes live on the CONTROL itself so repeatable-row inputs (no .field
   wrapper) and meta-grid fields behave identically.
   ============================================================ */
.portal .input.is-error,
.portal textarea.is-error,
.portal select.is-error {
  box-shadow: inset 0 0 0 2px var(--danger);
  background-color: var(--white);
}
/* Errored + focused: §7 removes the plain focus ring, but the RED error ring is
   an error indication (not a focus one), so it stays — add the soft outer halo
   while focused so the errored field is still obvious as you correct it. */
.portal .input.is-error:focus,
.portal textarea.is-error:focus,
.portal select.is-error:focus {
  box-shadow:
    inset 0 0 0 2px var(--danger),
    0 0 0 3px var(--accent-ring);
}
/* The flash: a short pulse of the outer halo (~3 cycles) that settles back to
   the steady inset ring. box-shadow only → zero layout shift. Under
   prefers-reduced-motion the global rule (§10) collapses this to the steady
   outline — no pulse. */
.portal .input.is-flash,
.portal textarea.is-flash,
.portal select.is-flash {
  animation: portal-field-flash 0.42s var(--ease-out) 3;
}
@keyframes portal-field-flash {
  0%   { box-shadow: inset 0 0 0 2px var(--danger), 0 0 0 0 var(--accent-ring); }
  50%  { box-shadow: inset 0 0 0 2px var(--danger), 0 0 0 5px var(--accent-ring); }
  100% { box-shadow: inset 0 0 0 2px var(--danger), 0 0 0 0 var(--accent-ring); }
}


/* ============================================================
   8. BUTTONS  —  square; hover translateX(4px) + arrow nudge
   Mirrors the launcher / BRAND_SYSTEM button law.
   ============================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  height: 46px;
  padding: 0 var(--s6);
  font-family: inherit;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text);
  background: var(--white);
  border: 0;
  box-shadow: inset 0 0 0 1px var(--border);
  cursor: pointer;
  transition:
    transform var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out),
    background var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out);
}
.btn:hover { transform: translateX(4px); }
.btn:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 1px var(--border), 0 0 0 3px var(--accent-ring);
}

/* Arrow glyph nudges further on hover. */
.btn .btn-arrow {
  display: inline-flex;
  transition: transform var(--dur) var(--ease-out);
}
.btn:hover .btn-arrow { transform: translateX(3px); }

/* Primary — brand red. */
.btn-primary {
  color: var(--white);
  background: var(--accent);
  box-shadow: none;
}
.btn-primary:hover { background: var(--red-dark); }
.btn-primary:focus-visible {
  box-shadow: 0 0 0 3px var(--accent-ring);
}

/* Ghost — hairline, white. */
.btn-ghost {
  background: transparent;
  color: var(--text);
  box-shadow: inset 0 0 0 1px var(--border);
}
.btn-ghost:hover {
  box-shadow: inset 0 0 0 1px var(--border-hover);
  color: var(--text);
}

/* Icon-only button (remove / drag handle / small affordances).
   The visible chip stays a compact 32px square so the row layout reads calm,
   but a relatively-positioned ::before overlay grows the actual tap target to
   ≥44px (WCAG 2.2 Target Size) — this is the primary control a gloved field
   user hits on a phone, so the hit area is enforced ALWAYS, not just mobile. */
.btn-icon {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  color: var(--text-3);
  background: transparent;
  border: 0;
  box-shadow: none;
  cursor: pointer;
  transition: color var(--dur) var(--ease-out), background var(--dur) var(--ease-out);
}
.btn-icon::before {
  content: "";
  position: absolute;
  inset: -6px;           /* 32 + 6 + 6 = 44px hit area */
}
.btn-icon:hover { color: var(--accent); background: var(--fill); }
.btn-icon:focus-visible {
  outline: none;
  color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-ring);
}

/* Full-width primary action row (bottom of a form). */
.btn-block { width: 100%; }
.btn:hover.btn-block { transform: none; }  /* no slide when full-width */

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3);
  align-items: center;
  margin-top: var(--rhythm-section);
}
.actions .note {
  font-size: var(--t-hint);
  color: var(--text-2);
  margin-left: auto;
}

/* Text-style link button ("Clear form" · "Start another" · the signature
   sheet's Cancel). SHARED here (not page-local) because the shared Signature
   module (signature.js) emits a .btn-link Cancel button — keeping the rule in
   portal.css means every current AND future form that reuses the signature
   sheet styles it with no page-CSS duplication. Calm, underlined, brand red
   only on hover/focus. */
.btn-link {
  font-family: inherit;
  font-size: var(--t-hint);
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: none;
  color: var(--text-2);
  background: transparent;
  border: 0;
  padding: var(--s2) var(--s2);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--border);
  transition: color var(--dur) var(--ease-out);
}
.btn-link:hover { color: var(--accent); }
.btn-link:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--accent-ring);
}


/* ============================================================
   8B. REPEATABLE LINE-ITEM CONTROL  (consumed by repeatable.js)
   Clone-a-template rows; remove collapses via grid-template-rows
   1fr -> 0fr (animatable, unlike display:none). Square throughout.
   ============================================================ */
.repeat { margin-top: 0; }

.repeat-list { list-style: none; margin: 0; padding: 0; }

/* Each row is a 1-track grid so it can collapse to 0fr on remove. */
.repeat-row {
  display: grid;
  grid-template-rows: 1fr;
  opacity: 1;
  transition:
    grid-template-rows var(--dur-slow) var(--ease-out),
    opacity var(--dur) var(--ease-out);
}
.repeat-row + .repeat-row { margin-top: var(--s3); }

/* Collapsed state (add-in start / remove end). The inner wrapper hides
   overflow so the content clips cleanly as the track closes. */
.repeat-row.is-collapsed {
  grid-template-rows: 0fr;
  opacity: 0;
}
.repeat-row.is-collapsed + .repeat-row { margin-top: 0; }

.repeat-row-inner {
  min-height: 0;        /* required so the 0fr track can actually collapse */
  /* Resting state lets the .btn-icon ::before 44px hit-overlay (and focus
     rings) extend past the inner box. Overflow is only clipped DURING the
     collapse animation (.is-collapsed below), where clean clipping matters. */
  overflow: visible;
  display: flex;
  align-items: center;        /* §G — vertically centre the remove "×" with the row's input */
  gap: var(--s2);
}
.repeat-row.is-collapsed .repeat-row-inner { overflow: hidden; }
.repeat-input { flex: 1 1 auto; }

/* Remove handle sits flush to the input; greys until hovered (red on hover). */
.repeat-remove {
  flex: 0 0 auto;
  margin-top: 0;
}
.repeat-remove:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}
.repeat-remove:disabled:hover { color: var(--text-3); background: transparent; }

/* "Add line" button — a quiet, borderless INLINE TEXT button (Refinement Pass 2
   §F): no box/stroke, no "+"/arrow glyph, reads like a .btn-link. Overrides the
   .btn/.btn-ghost it still carries in markup so every form inherits the look. */
.repeat-add { margin-top: var(--s3); }
.repeat-add.btn {
  width: auto;
  height: auto;
  min-height: 0;
  padding: var(--s2) 0;
  gap: 0;
  background: transparent;
  box-shadow: none;
  color: var(--text-2);
  font-size: var(--t-hint);
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: none;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--border);
}
.repeat-add.btn:hover {
  transform: none;
  background: transparent;
  box-shadow: none;
  color: var(--accent);
}
.repeat-add.btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--accent-ring);
}
/* Drop the leading "+" glyph (it's an aria-hidden span in the markup). */
.repeat-add > [aria-hidden] { display: none; }


/* ============================================================
   8C. PHOTO SECTION  (consumed by photos.js + SortableJS)
   Capture input is visually hidden; the .photo-drop label is the
   target. Tiles are square thumbnails with caption + remove + handle.
   ============================================================ */
/* Drag-and-drop box (Radik 2026-06-29): a dashed, square-cornered drop area with
   a square icon tile (NO green), a bold title with a red "browse", and a subtext. */
.photo-drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s3);
  padding: var(--s10) var(--s6);
  text-align: center;
  color: var(--text-2);
  background: var(--white);
  border: 1.5px dashed var(--border);   /* dashed drop box, square corners */
  cursor: pointer;
  transition: border-color var(--dur) var(--ease-out), background var(--dur) var(--ease-out);
}
.photo-drop[hidden] { display: none; }   /* hidden once photos exist (header button takes over) */
.photo-drop:hover { border-color: var(--border-hover); background: var(--fill); }
.photo-drop:focus-within { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-ring); }
.photo-drop.is-dragover { border-color: var(--accent); background: var(--fill); }   /* file dragged over */
/* Square icon tile (no green) + image-stack glyph. */
.photo-drop-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin-bottom: var(--s1);
  color: var(--text);
  background: var(--fill);
}
.photo-drop-icon svg { width: 28px; height: 28px; }
.photo-drop .photo-drop-title { font-size: var(--t-section); font-weight: 700; color: var(--text); }
.photo-drop .photo-drop-browse { color: var(--accent); }
.photo-drop .photo-drop-sub { font-size: var(--t-hint); color: var(--text-2); }

/* Responsive square-tile grid. */
.photo-grid {
  list-style: none;
  margin: var(--s5) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--s4);
}
.photo-tile {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}
.photo-figure {
  position: relative;
  margin: 0;
  background: var(--fill);
  box-shadow: inset 0 0 0 1px var(--border);
  overflow: hidden;
}
/* Whole photo, at its real shape — same in the editor, here, and in the export. */
.photo-thumb {
  width: 100%;
  height: auto;
  display: block;
  user-select: none;
}

/* Drag handle — top-left chip; the grab affordance for SortableJS.
   Hidden by default; shown only when photos.js confirms SortableJS loaded
   and adds .has-reorder to the grid (graceful degradation — no dead handle).
   The visible chip stays a compact 24px glyph, but an inset:-10px pseudo
   overlay expands the actual grab/tap area to ≥44px (WCAG 2.2 Target Size). */
.photo-handle {
  position: absolute;
  top: var(--s2);
  left: var(--s2);
  display: none;
  align-items: center;
  justify-content: center;
  width: 24px; height: 24px;
  color: var(--white);
  background: rgba(16, 24, 32, 0.55);
  cursor: grab;
}
.photo-handle::before {
  content: "";
  position: absolute;
  inset: -10px;          /* 24 + 10 + 10 = 44px hit area */
}
.photo-grid.has-reorder .photo-handle { display: inline-flex; }
.photo-handle:active { cursor: grabbing; }

/* Remove — top-right chip; brand red only on hover (calm by default).
   Same pattern: visible glyph stays 24px, the ::before overlay grows the
   clickable target to ≥44px so a gloved field user can hit it on a phone. */
.photo-remove {
  position: absolute;
  top: var(--s2);
  right: var(--s2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px; height: 24px;
  padding: 0;
  color: var(--white);
  background: rgba(16, 24, 32, 0.55);
  border: 0;
  cursor: pointer;
  transition: background var(--dur) var(--ease-out);
}
.photo-remove::before {
  content: "";
  position: absolute;
  inset: -10px;          /* 24 + 10 + 10 = 44px hit area */
}
.photo-remove:hover { background: var(--red); }
.photo-remove:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--accent-ring); }

.photo-caption { font-size: var(--t-hint); }

/* SortableJS drag states. */
.photo-tile--ghost { opacity: 0.4; }
.photo-tile--chosen .photo-figure { box-shadow: inset 0 0 0 2px var(--accent); }


/* ============================================================
   8D. PHOTO MARKUP EDITOR  (photo-annotate.js — shared by both forms)
   Full-screen overlay matching the portal: dark scrim, white panel, square
   corners, brand accents. Toolbar (tools · colour · zoom) over a canvas stage.
   (Radik 2026-06-29: zoom / crop / draw / text on a tapped photo.)
   ============================================================ */
html.pa-open, html.pa-open body { overflow: hidden; }
.pa-modal { position: fixed; inset: 0; z-index: 1000; display: flex; }
.pa-modal[hidden] { display: none; }
.pa-scrim { position: absolute; inset: 0; background: rgba(16, 24, 32, 0.72); }
.pa-panel {
  position: relative;
  margin: auto;
  width: min(960px, 96vw);
  height: min(92vh, 860px);
  display: flex;
  flex-direction: column;
  background: var(--white);
  box-shadow: 0 24px 60px rgba(16, 24, 32, 0.40);
}
.pa-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s4);
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--divider);
}
.pa-tools, .pa-zoom { display: inline-flex; align-items: center; gap: var(--s1); }
.pa-zoom { margin-left: auto; }
.pa-tool {
  display: inline-flex; align-items: center; gap: var(--s1);
  min-height: 40px; padding: 0 var(--s3);
  font-family: inherit; font-size: var(--t-hint); font-weight: 600;
  color: var(--text-2); background: var(--white); border: 0;
  box-shadow: inset 0 0 0 1px var(--border); cursor: pointer;
  transition: color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.pa-tool span { white-space: nowrap; }
.pa-tool:hover { color: var(--text); }
.pa-tool.is-on { color: var(--text); box-shadow: inset 0 0 0 2px var(--accent); }
.pa-mini {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 40px; height: 40px; padding: 0 var(--s2);
  font-family: inherit; font-size: var(--t-hint); font-weight: 700;
  color: var(--text); background: var(--white); border: 0;
  box-shadow: inset 0 0 0 1px var(--border); cursor: pointer;
  transition: background var(--dur) var(--ease-out);
}
.pa-mini:hover { background: var(--fill); }
.pa-swatches { display: inline-flex; align-items: center; gap: var(--s2); }
.pa-swatch {
  width: 24px; height: 24px; padding: 0; border: 0; cursor: pointer;
  box-shadow: inset 0 0 0 1px rgba(16, 24, 32, 0.25);
}
.pa-swatch.is-on { box-shadow: inset 0 0 0 1px rgba(16, 24, 32, 0.25), 0 0 0 2px var(--accent); }
.pa-stage { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; }
.pa-canvas-wrap { position: relative; flex: 1; min-height: 0; background: #101820; overflow: hidden; }
.pa-canvas { display: block; touch-action: none; }
.pa-text-input {
  position: absolute; z-index: 2; min-width: 120px;
  font: 700 16px Inter, system-ui, sans-serif;
  padding: 2px 4px; background: rgba(255, 255, 255, 0.92);
  border: 0; box-shadow: 0 0 0 1px var(--accent); outline: none;
}
/* Crop apply/reset live INLINE in the toolbar (shown only in crop mode) — no
   separate bar/popup. (Radik 2026-06-29) */
.pa-crop-actions { display: inline-flex; align-items: center; gap: var(--s1); }
.pa-crop-actions[hidden] { display: none; }
.pa-crop-go { box-shadow: inset 0 0 0 2px var(--accent); }
.pa-actions {
  display: flex; align-items: center; justify-content: flex-end; gap: var(--s3);
  padding: var(--s3) var(--s4); border-top: 1px solid var(--divider);
}


/* ============================================================
   8E. PHOTO EDITOR — fabric.js  (photo-editor.js — shared, PRIMARY editor)
   Object-based: every mark is a selectable / movable / deletable object (fabric
   draws the selection handles on the canvas itself). Square portal toolbar.
   ============================================================ */
html.pe-open, html.pe-open body { overflow: hidden; }
.pe-modal { position: fixed; inset: 0; z-index: 1000; display: flex; }
.pe-modal[hidden] { display: none; }
.pe-scrim { position: absolute; inset: 0; background: rgba(16, 24, 32, 0.72); }
.pe-panel {
  position: relative; margin: auto;
  width: min(1040px, 96vw); height: min(92vh, 880px);
  display: flex; flex-direction: column;
  background: var(--white); box-shadow: 0 24px 60px rgba(16, 24, 32, 0.40);
}
.pe-toolbar {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--s4);
  padding: var(--s3) var(--s4); border-bottom: 1px solid var(--divider);
}
.pe-tools, .pe-utils { display: inline-flex; align-items: center; gap: var(--s1); }
.pe-utils { margin-left: auto; }
.pe-tool {
  display: inline-flex; align-items: center; gap: var(--s1);
  min-height: 40px; padding: 0 var(--s3);
  font-family: inherit; font-size: var(--t-hint); font-weight: 600;
  color: var(--text-2); background: var(--white); border: 0;
  box-shadow: inset 0 0 0 1px var(--border); cursor: pointer;
  transition: color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.pe-tool span { white-space: nowrap; }
.pe-tool:hover { color: var(--text); }
.pe-tool.is-on { color: var(--text); box-shadow: inset 0 0 0 2px var(--accent); }
.pe-mini {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 40px; height: 40px; padding: 0 var(--s2);
  font-family: inherit; font-size: var(--t-hint); font-weight: 700;
  color: var(--text); background: var(--white); border: 0;
  box-shadow: inset 0 0 0 1px var(--border); cursor: pointer;
  transition: background var(--dur) var(--ease-out);
}
.pe-mini:hover { background: var(--fill); }
.pe-crop-go { box-shadow: inset 0 0 0 2px var(--accent); }
.pe-crop-actions { display: inline-flex; align-items: center; gap: var(--s1); }
.pe-crop-actions[hidden] { display: none; }
/* Colour swatch button → opens the HSV picker popover (color-picker.js). */
.pe-colorwrap { position: relative; display: inline-flex; }
.pe-color { width: 44px; height: 40px; padding: 0; border: 0; box-shadow: inset 0 0 0 1px var(--border); background: var(--white); cursor: pointer; }
.pe-picker { position: absolute; top: calc(100% + 6px); left: 0; z-index: 30; }
.pe-picker[hidden] { display: none; }

/* The HSV picker itself — SV box + hue slider + preview + Hex/R/G/B. Square. */
.cp {
  display: flex; flex-direction: column; gap: var(--s2);
  width: 248px; padding: var(--s3);
  background: var(--white);
  box-shadow: 0 12px 32px rgba(16, 24, 32, 0.22), inset 0 0 0 1px var(--border);
}
.cp-sv { position: relative; width: 100%; height: 150px; cursor: crosshair; touch-action: none; }
.cp-sv-thumb {
  position: absolute; width: 14px; height: 14px; transform: translate(-50%, -50%);
  box-shadow: 0 0 0 2px #fff, 0 0 0 3px rgba(16, 24, 32, 0.5); pointer-events: none;
}
.cp-hue {
  position: relative; width: 100%; height: 14px; cursor: pointer; touch-action: none;
  background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
}
.cp-hue-thumb {
  position: absolute; top: 50%; width: 10px; height: 18px; transform: translate(-50%, -50%);
  background: #fff; box-shadow: inset 0 0 0 1px rgba(16, 24, 32, 0.5); pointer-events: none;
}
.cp-preview { width: 100%; height: 26px; box-shadow: inset 0 0 0 1px rgba(16, 24, 32, 0.15); }
.cp-fields { display: flex; gap: var(--s2); }
.cp-f {
  display: flex; flex-direction: column; gap: 2px; flex: 1;
  font-size: 10px; font-weight: 800; letter-spacing: 0.5px; text-transform: uppercase; color: var(--text-2);
}
.cp-f-hex { flex: 1.7; }
.cp-f input {
  width: 100%; font-family: inherit; font-size: 12px; font-weight: 500; text-transform: none;
  color: var(--text); padding: 6px; border: 0; box-shadow: inset 0 0 0 1px var(--border); background: var(--white);
}
.cp-f input:focus { outline: none; box-shadow: inset 0 0 0 1px var(--accent); }
.cp-f input[type=number]::-webkit-inner-spin-button,
.cp-f input[type=number]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.cp-f input[type=number] { -moz-appearance: textfield; }
.pe-stage {
  flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center;
  background: #101820; overflow: hidden; padding: var(--s3);
}
.pe-stage .canvas-container { border-radius: 0; }
.pe-actions {
  display: flex; align-items: center; justify-content: flex-end; gap: var(--s3);
  padding: var(--s3) var(--s4); border-top: 1px solid var(--divider);
}


/* ============================================================
   8D. SIGNATURE SHEET  (consumed by signature.js — SHARED)
   A full-screen square sheet over the page: dark-on-white <canvas>,
   Clear / Done (+ an OPT-IN "Can't sign — tap to acknowledge" button,
   hidden unless a form passes allowAcknowledge:true — the Toolbox Talk
   does not, drawn signatures are mandatory there). Built once by
   signature.js and reused by every form (Toolbox Talk attendees +
   sign-off today; future forms tomorrow). Square corners; brand red
   only on the focus ring + the primary Done button. Reveal = opacity
   + single-axis translateY (honors prefers-reduced-motion). NO inline
   styles. .tt-sig-ack stays styled for forms that opt in.
   ============================================================ */

/* Lock background scroll while the sheet is open (set on <html>). */
.tt-sig-open { overflow: hidden; }

.tt-sig-sheet {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: stretch;
  justify-content: center;
  padding: var(--s5);
  background: rgba(16, 24, 32, 0.55);   /* dim brand-black backdrop */
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-out);
}
.tt-sig-sheet[hidden] { display: none; }
.tt-sig-sheet.is-open { opacity: 1; }

/* The white working panel. Slides up under the fading backdrop. */
.tt-sig-panel {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 640px;
  margin: auto;
  max-height: 100%;
  /* On a short viewport (phone landscape) the head + surface + Clear/Done
     actions can exceed the panel height; scroll INSIDE the panel so the
     mandatory Clear/Done actions stay reachable (field crews sign on phones). */
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--white);
  box-shadow: inset 0 0 0 1px var(--border);
  transform: translateY(16px);
  transition: transform var(--dur-slow) var(--ease-out);
}
.tt-sig-sheet.is-open .tt-sig-panel { transform: translateY(0); }

.tt-sig-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  padding: var(--s5) var(--s6);
  border-bottom: 1px solid var(--divider);
}
.tt-sig-title {
  font-size: var(--t-section);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}
.tt-sig-cancel { flex: 0 0 auto; }

/* Canvas surface — fills available height; the "Sign above" guide sits faint
   along the bottom of the drawing area. */
.tt-sig-surface {
  position: relative;
  flex: 1 1 auto;
  min-height: 240px;
  margin: var(--s6);
  background: var(--white);
  box-shadow: inset 0 0 0 1px var(--border);
}
.tt-sig-canvas {
  display: block;
  width: 100%;
  height: 100%;
  /* The line is the signing baseline; touch-action none so a drawing drag
     never scrolls/zooms the page underneath. */
  touch-action: none;
  cursor: crosshair;
}
.tt-sig-guide {
  position: absolute;
  left: var(--s4);
  right: var(--s4);
  bottom: var(--s4);
  padding-top: var(--s2);
  border-top: 1px solid var(--divider);
  font-size: var(--t-hint);
  color: var(--text-3);
  text-align: center;
  pointer-events: none;
}

.tt-sig-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s3);
  padding: var(--s5) var(--s6);
  border-top: 1px solid var(--divider);
}
/* Opt-in Acknowledge fallback takes the middle, pushing Done to the right edge.
   Hidden by default (signature.js only reveals it when allowAcknowledge:true);
   when hidden, Clear stays left and Done sits at the right via its own margin. */
.tt-sig-ack { margin-left: auto; }
.tt-sig-ack[hidden] { display: none; }
.tt-sig-done { flex: 0 0 auto; margin-left: auto; }
/* When the ack button IS shown it owns the auto-margin, so neutralize Done's. */
.tt-sig-ack:not([hidden]) ~ .tt-sig-done { margin-left: 0; }
.btn.tt-sig-done:hover { transform: none; }   /* it's a committing action, no slide */
.tt-sig-done:disabled { opacity: 0.4; cursor: not-allowed; }
.tt-sig-done:disabled:hover { background: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .tt-sig-sheet,
  .tt-sig-panel { transition: none; transform: none; }
}

/* Short viewport (phone landscape ~360–520px tall): the signing area shrinks
   and its margin tightens so the head + canvas + Clear/Done all fit; combined
   with .tt-sig-panel's overflow-y:auto the Clear/Done actions stay reachable
   and the required signature can always be committed. */
@media (max-height: 520px) {
  .tt-sig-surface {
    min-height: 140px;
    margin: var(--s4);
  }
  .tt-sig-head { padding: var(--s3) var(--s5); }
  .tt-sig-actions { padding: var(--s3) var(--s5); }
}

@media (max-width: 640px) {
  .tt-sig-sheet { padding: 0; }
  .tt-sig-panel { max-width: none; }
  .tt-sig-actions { flex-direction: column; align-items: stretch; }
  .tt-sig-ack { margin-left: 0; order: 3; }   /* Done above the fallback on phone */
  .tt-sig-done { order: 1; }
}


/* ============================================================
   8E. SELECTABLE CHIPS + TOGGLE ROWS + COUNT  (SHARED widgets)
   Reusable tap targets for any form: topic/option chips (.chip),
   tap-to-confirm toggle rows (.toggle-row), tappable list rows
   (.tap-row, e.g. an attendee), and a live count pill (.count-note).
   The Toolbox Talk's page CSS composes these into its grids/lists;
   keeping the widget LOOK here means future forms reuse it. Square,
   calm; the one brand red is the selected inset ring + focus ring.
   Checked fill is a faint grey wash — NEVER red, NEVER yellow.
   ============================================================ */

/* — Selectable chip (topic pick, multi-select option) — */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: 56px;                 /* big tired-thumb target (spec §3B) */
  width: 100%;
  padding: var(--s3) var(--s4);
  font-family: inherit;
  font-size: var(--t-body);
  font-weight: 500;
  line-height: 1.35;
  text-align: left;
  color: var(--text);
  background: var(--white);
  border: 0;
  box-shadow: inset 0 0 0 1px var(--border);
  cursor: pointer;
  transition:
    box-shadow var(--dur) var(--ease-out),
    background var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out);
}
.chip:hover { box-shadow: inset 0 0 0 1px var(--border-hover); background: var(--fill); }
.chip:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 1px var(--border), 0 0 0 3px var(--accent-ring);
}
/* Selected = the one decorative brand red (2px inset ring), per spec §2. */
.chip.is-selected {
  box-shadow: inset 0 0 0 2px var(--accent);
  background: var(--white);
  font-weight: 600;
}
.chip.is-selected:focus-visible {
  box-shadow: inset 0 0 0 2px var(--accent), 0 0 0 3px var(--accent-ring);
}

/* — Square ICON TILE (topic/category picker) — a white-card tap target with
     a recognizable line-icon on top and a label below. Same calm white-card
     feel as the home launcher buttons: square, faint shadow, no heavy border.
     A touch more playful than a plain .chip thanks to the icon + a light brand
     accent on hover/selection. Reused by any picker that wants tiles. The
     tiles STAY VISIBLE after a pick (the grid does not collapse); the selected
     one carries .is-selected (brand-accent ring + faint wash) and switching is
     a single tap on another tile. — */
.icon-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s3);
  width: 100%;
  /* Genuinely square (brand law), with a sane min-height floor so a short
     label never crushes the tile on very narrow columns. aspect-ratio drives
     the height off the column width; min-height guards the tiny end. */
  aspect-ratio: 1 / 1;
  min-height: 116px;
  max-height: 168px;            /* keep wide laptop columns from over-tall tiles */
  padding: var(--s4) var(--s3);
  font-family: inherit;
  text-align: center;
  color: var(--text);
  background: var(--white);
  border: 0;
  box-shadow:
    inset 0 0 0 1px var(--border),
    0 1px 2px rgba(16, 24, 32, 0.05);   /* faint card lift */
  cursor: pointer;
  transition:
    box-shadow var(--dur) var(--ease-out),
    background var(--dur) var(--ease-out),
    transform  var(--dur) var(--ease-out),
    color      var(--dur) var(--ease-out);
}
.icon-tile:hover {
  box-shadow:
    inset 0 0 0 1px var(--border-hover),
    0 2px 8px rgba(16, 24, 32, 0.08);
  transform: translateY(-1px);          /* single-axis lift, calm */
}
.icon-tile:focus-visible {
  outline: none;
  box-shadow:
    inset 0 0 0 1px var(--border),
    0 0 0 3px var(--accent-ring);
}
/* The icon glyph — stroke-based line-SVG, inherits currentColor. */
.icon-tile-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  color: var(--text-2);
  transition: color var(--dur) var(--ease-out);
}
.icon-tile-icon svg {
  width: 100%;
  height: 100%;
  display: block;
}
.icon-tile:hover .icon-tile-icon { color: var(--accent); }
.icon-tile-label {
  font-size: var(--t-hint);
  font-weight: 600;
  line-height: 1.3;
  color: var(--text);
  letter-spacing: -0.005em;
}
/* Selected = the one decorative brand red (2px inset ring) + a faint wash +
   the icon tints accent. Clear, on-brand, square. */
.icon-tile.is-selected {
  box-shadow:
    inset 0 0 0 2px var(--accent),
    0 2px 8px rgba(16, 24, 32, 0.08);
  background: var(--danger-tint);       /* faint brand-red wash (0.08 alpha) */
}
.icon-tile.is-selected .icon-tile-icon { color: var(--accent); }
.icon-tile.is-selected .icon-tile-label { font-weight: 700; }
.icon-tile.is-selected:focus-visible {
  box-shadow:
    inset 0 0 0 2px var(--accent),
    0 0 0 3px var(--accent-ring);
}
@media (prefers-reduced-motion: reduce) {
  .icon-tile:hover { transform: none; }
}

/* — Tap-to-confirm toggle row (checklist). Built on a real checkbox so
     store.js serializes it natively; the visual is this styled label. — */
.toggle-row {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: 52px;
  padding: var(--s3) var(--s4);
  background: var(--white);
  box-shadow: inset 0 0 0 1px var(--border);
  cursor: pointer;
  transition: box-shadow var(--dur) var(--ease-out), background var(--dur) var(--ease-out);
}
.toggle-row:hover { box-shadow: inset 0 0 0 1px var(--border-hover); }
.toggle-row > input[type="checkbox"] {
  position: absolute;
  width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0;
}
.toggle-row .toggle-label {
  flex: 1 1 auto;
  font-size: var(--t-body);
  font-weight: 500;
  color: var(--text);
  line-height: 1.35;
}
/* The box glyph sits on the right (label-left / state-right, spec §3C). */
.toggle-row .toggle-box {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px; height: 24px;
  color: transparent;
  background: var(--white);
  box-shadow: inset 0 0 0 1px var(--border);
  transition: background var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.toggle-row .toggle-box svg { width: 14px; height: 14px; }
/* Checked = faint grey fill wash (NEVER red/yellow) + dark check glyph. */
.toggle-row > input:checked ~ .toggle-label { color: var(--text); }
.toggle-row:has(> input:checked) { background: var(--fill); box-shadow: inset 0 0 0 1px var(--border-hover); }
.toggle-row > input:checked ~ .toggle-box { background: var(--fill); color: var(--text); box-shadow: inset 0 0 0 1px var(--border-hover); }
/* Keyboard focus ring on the styled row. */
.toggle-row:has(> input:focus-visible) {
  box-shadow: inset 0 0 0 1px var(--border), 0 0 0 3px var(--accent-ring);
}

/* — Tappable list row (e.g. an attendee waiting to sign). Whole row is the
     target; a trailing slot shows state ("Tap to sign" → thumbnail + check). — */
.tap-row {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: 56px;
  padding: var(--s3) var(--s4);
  background: var(--white);
  box-shadow: inset 0 0 0 1px var(--border);
  transition: box-shadow var(--dur) var(--ease-out), background var(--dur) var(--ease-out);
}
.tap-row + .tap-row { margin-top: var(--s2); }
.tap-row:hover { box-shadow: inset 0 0 0 1px var(--border-hover); }
/* Auto-advanced "next to sign" highlight (the phone-walks-the-line cue). */
.tap-row.is-active { box-shadow: inset 0 0 0 2px var(--accent); }
.tap-row .tap-row-main { flex: 1 1 auto; min-width: 0; }
.tap-row .tap-row-name { font-weight: 600; color: var(--text); }
.tap-row .tap-row-sub  { font-size: var(--t-hint); color: var(--text-2); }
.tap-row.is-signed .tap-row-sub,
.tap-row.is-done .tap-row-sub { color: var(--text-2); }
/* Trailing signature slot — square, calm; the sign affordance / thumbnail. */
.tap-row .tap-row-slot {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: 44px;
  padding: 0 var(--s3);
  font-size: var(--t-hint);
  font-weight: 600;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  color: var(--text-2);
  background: transparent;
  border: 0;
  box-shadow: inset 0 0 0 1px var(--border);
  cursor: pointer;
  transition: color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.tap-row .tap-row-slot:hover { color: var(--accent); box-shadow: inset 0 0 0 1px var(--border-hover); }
.tap-row .tap-row-slot:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--border), 0 0 0 3px var(--accent-ring); }
.tap-row .tap-row-thumb {
  height: 28px;
  width: auto;
  background: var(--white);
  box-shadow: inset 0 0 0 1px var(--divider);
}

/* — Live count pill (e.g. "5 of 8 signed"); pair with aria-live="polite". — */
.count-note {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s3);
  font-size: var(--t-hint);
  font-weight: 600;
  letter-spacing: 0.3px;
  color: var(--text-2);
  background: var(--fill);
  box-shadow: inset 0 0 0 1px var(--divider);
}


/* ============================================================
   9. SMALL SHARED UTILITIES  (used across forms)
   ============================================================ */
.divider { height: 1px; background: var(--divider); border: 0; margin: var(--s8) 0; }

/* Visually hidden but available to assistive tech. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Calm "draft restored" / status note. */
.status-note {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  font-size: var(--t-hint);
  font-weight: 500;
  color: var(--text-2);
}


/* ============================================================
   9B. SHARED TOP NAV  (SPEC §2 — consumed by every form)
   ONE nav rule for the whole portal: red Enduro logo (left) + "Back to
   portal" (right). Taller than before (prior 64px + 30px = --nav-h 94px),
   ~10px padding above + below the red logo, "Back to portal" vertically
   centered. Aligns to the SAME gutter + measure the wizard uses (so the
   wizard's centered container lines up under the nav). Square, calm; brand
   red only on the back-link hover + focus ring. No inline styles.
   ============================================================ */
.portal-nav {
  border-bottom: 1px solid var(--divider);
  background: var(--white);
  /* HELD STATIC across every transition (home↔form, step↔step, back/forward):
     its own VT group + the instant-duration guard (§0) keep it from fading —
     only the content below the nav crossfades. */
  view-transition-name: portal-nav;
}
.portal-nav-inner {
  width: 90%;                          /* same 90% box as the portal home card */
  max-width: var(--portal-measure);
  margin: 0 auto;
  padding: 0 var(--portal-gutter);
  min-height: var(--nav-h);            /* +30px taller than the old 64px (§2) */
  display: flex;
  align-items: center;                 /* Back-to-portal stays vertically centered */
  justify-content: space-between;
  gap: var(--s4);
}
/* The red logo carries ~10px breathing room above + below itself (§2). */
.portal-nav-logo {
  display: inline-flex;
  align-items: center;
  line-height: 0;
  padding: 10px 0;
}
/* Match the portal home logo size (104px) so both pages read identically. */
.portal-nav-logo svg { width: 104px; height: auto; }
.portal-nav-back {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  /* Tap target (finding): the link's text is only ~17px tall — below the WCAG
     2.2 (2.5.8) 24px minimum and far under a comfortable 44px glove target. A
     real hit area (44px tall) is added WITHOUT changing the visual text size. */
  min-height: 44px;
  padding: 6px 0;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-2);
  transition:
    color var(--dur) var(--ease-out),
    transform var(--dur) var(--ease-out);
}
.portal-nav-back:hover { color: var(--accent); }
.portal-nav-back:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-ring);
}
.portal-nav-back-arrow {
  display: inline-flex;
  line-height: 0;
  transition: transform var(--dur) var(--ease-out);
}
.portal-nav-back:hover .portal-nav-back-arrow { transform: translateX(-3px); }


/* ============================================================
   9C. WIZARD  (SPEC §1 — the core engine layout)
   Each form becomes a multi-step wizard. One step at a time fills the screen
   below the nav; advancing = the view-transitions CROSSFADE (§0), fired from
   wizard.js via document.startViewTransition (Continue AND Back). All of a
   step's content lives in ONE container (.wizard-stage) that is centered both
   axes and respects the SAME gutter/measure as the nav. The page itself never
   scrolls (the body is locked by wizard.js; see html.wizard-lock). The wizard.js
   controller toggles a single .is-active class per step (CSS hides the rest).
   Reused by BOTH forms (and every future one).
   ============================================================ */

/* While a wizard is mounted, the document is held still — the crossfade IS the
   navigation, there is no page scroll. (wizard.js sets/clears this class.) */
html.wizard-lock,
html.wizard-lock body {
  height: 100%;
  overflow: hidden;
}

/* PRINT SAFETY (shared). The export path (Report.print → window.print) fires
   while the wizard is still mounted, so html.wizard-lock's height:100% +
   overflow:hidden is still in force. In paged media that clips the printed
   document to a single page — every step after the first would be lost. The
   per-page print stylesheets already flatten the wizard back into flow; this
   one shared rule releases the root lock so the WHOLE report paginates. Must
   live here (with the lock it undoes) so every current + future form inherits
   it without re-declaring. */
@media print {
  html.wizard-lock,
  html.wizard-lock body {
    height: auto !important;
    overflow: visible !important;
  }
}

/* The wizard fills the viewport below the nav. overflow:hidden clips the
   off-screen (parked) steps so a wipe reveals exactly one step. */
.wizard {
  position: relative;
  width: 100%;
  height: calc(100vh - var(--nav-h));
  height: calc(100dvh - var(--nav-h));   /* dynamic vh on mobile browser chrome */
  overflow: hidden;
}

/* Every step is absolutely stacked, filling the wizard, and CENTERS its stage
   both axes. Only the ACTIVE step is displayed; step CHANGES are wrapped in
   document.startViewTransition() (wizard.js, Continue AND Back) so the swap gets
   the IDENTICAL 0.2s crossfade as page-to-page navigation, with the nav held
   static (§0). No transform/wipe remains — the View Transition owns the motion,
   and §0's reduced-motion media query collapses it to an instant swap. */
.wizard-step {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  /* OPTICAL VERTICAL CENTERING (Refinement Pass 2 §H). The wizard region starts
     BELOW the nav, so naive centering inside it pushes content down by ~nav-h/2
     (~47px) — content read "too low". We bias the flex centering UP by adding
     extra bottom padding: a centred item shifts up by (padBottom − padTop)/2.
     padBottom − padTop = nav-h raises content by nav-h/2 (true viewport centre);
     the extra --s4 lifts it a touch ABOVE true centre (optical centre), where the
     eye expects it. One shared rule balances EVERY step of BOTH forms: flex
     re-centres around the same axis whatever each step's content height. The
     stage keeps max-height:100% + internal scroll, so nothing is ever clipped. */
  padding: var(--s6) 0 calc(var(--s6) + var(--nav-h) + var(--s4)) 0;
  /* NOTE: on laptop+ (≥641px) this is replaced by the PINNED-CHROME layout in
     §11b, which reserves a top band for the dots + a bottom band for the nav and
     centres the fields between them (with the ~50px upward bias folded in). */
}
/* Only the on-screen step renders; the rest are fully removed from layout so a
   View Transition snapshots exactly one step (old) → one step (new). */
.wizard-step:not(.is-active) { display: none; }

/* The ONE centered container holding a step's content. Same gutter + measure
   as the nav, so it lines up under it. In the normal ~960px case nothing
   scrolls; if a short viewport (zoom / small laptop) would clip content, the
   STAGE — not the page — scrolls internally, so no field is ever trapped. */
.wizard-stage {
  width: 90%;                          /* same 90% as the portal home card */
  max-width: var(--portal-measure);
  margin: 0 auto;
  padding: 0 var(--portal-gutter);
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ON-SCREEN KEYBOARD ACCOMMODATION (finding).
   While the wizard is mounted, html.wizard-lock pins html+body (overflow:hidden),
   so the .wizard-stage is the ONLY scroller. On iOS/Android the soft keyboard
   occludes the bottom of the screen but 100dvh does NOT shrink for it, so lower
   text fields can stay trapped behind the keyboard with no way to scroll them
   up. wizard.js measures the occluded height via visualViewport and exposes it
   as --kb-inset on the wizard root + flags .is-kb-open. We then (a) shrink the
   active stage's scroll viewport to the VISIBLE area and (b) top-align + add
   bottom breathing room so the focused field can scroll above the keyboard.
   Desktop never sets the var, so this is inert there (no no-scroll regression). */
.wizard.is-kb-open .wizard-step.is-active {
  align-items: flex-start;            /* stop centering while typing — simpler scroll math */
  padding-top: var(--s4);
  padding-bottom: 0;
}
.wizard.is-kb-open .wizard-step.is-active .wizard-stage {
  max-height: calc(100% - var(--kb-inset, 0px));
  padding-bottom: var(--s10);         /* room to lift the last fields above the keyboard */
}

/* Per-step footer nav (Refinement Pass 2 §D/§E). Back sits NEXT TO Continue
   (Back left, Continue right) with ~30px between them — the old far-apart spacer
   is gone. NO arrows on any nav button (the .btn-arrow glyphs are hidden; the
   website has no such element). Back stays ghost/secondary, Continue primary. */
.wizard-nav {
  display: flex;
  align-items: center;
  gap: 30px;                                          /* §E — ~30px Back ↔ Continue */
  margin-top: var(--s8);
}
.wizard-nav .wizard-nav-spacer { display: none; }     /* §E — no longer pushes them apart */
.wizard-nav .btn-arrow { display: none; }             /* §D/§E — remove every nav arrow */

/* ------------------------------------------------------------
   STEP INDICATOR — CIRCLES, TRI-STATE (SPEC §5)
   The sanctioned exception to the "box all circles" rule — Radik explicitly
   wants CIRCLES here. One dot per step, built + updated + injected at the top
   of every step's stage by wizard.js, advancing one state per Continue:
     · DEFAULT  (step not yet reached) → ghost grey  #D1D5DB
     · ACTIVE   (current step)         → darker mid-grey var(--text-2), NOT black
     · COMPLETE (steps already done)   → success green #16A34A (off-palette, for
                                         this finished functional state only)
   Keeps the prior position / size / alignment; replaces the filling squares.
   ------------------------------------------------------------ */
.wizard-progress {
  display: flex;
  align-items: center;
  gap: 6px;                  /* left-aligned (left gutter) — the pill grows in place */
  margin-bottom: var(--s6);
}
.wizard-progress-dot {
  position: relative;
  width: 6.75px;                       /* 25% smaller than the original 9px (Radik 2026-06-29) */
  height: 6.75px;
  padding: 0;
  border: 0;
  border-radius: 50%;                 /* CIRCLE — explicit §5 exception to "box all" */
  background: #D1D5DB;                 /* DEFAULT = ghost grey */
  cursor: pointer;                     /* clickable → jump to that step */
  -webkit-appearance: none;
  appearance: none;
  transition: background var(--dur) var(--ease-out), transform 0.16s var(--ease-out),
              width 0.28s var(--ease-out), border-radius 0.28s var(--ease-out);
}
/* Bigger invisible hit area (the visible dot stays tiny). */
.wizard-progress-dot::before { content: ""; position: absolute; inset: -5px; }
/* ACTIVE = a darker-grey PILL: the current dot stretches to ~24px wide with a
   fluid width transition when you jump pages (common page-indicator look). */
.wizard-progress-dot.is-current  { background: var(--text-2); width: 15px; border-radius: 3.4px; }
.wizard-progress-dot.is-complete { background: #16A34A; }        /* COMPLETE = green */
.wizard-progress-dot.is-invalid  { background: var(--accent); }  /* LEFT INCOMPLETE = red */
/* Subtle Mac-dock hover: the hovered circle rises + grows a touch, its immediate
   neighbours nudge aside ~2px. (Radik 2026-06-29) */
.wizard-progress-dot:hover { transform: translateY(-2px) scale(1.35); }
.wizard-progress-dot:hover + .wizard-progress-dot { transform: translateX(2px); }
.wizard-progress-dot:has(+ .wizard-progress-dot:hover) { transform: translateX(-2px); }
/* No focus ring on the dots in ANY state — matches the portal's no-stroke
   focus convention; the red ring is gone. (Radik 2026-07-03) */
.wizard-progress-dot:focus,
.wizard-progress-dot:focus-visible { outline: none; box-shadow: none; }

/* Hover label: a small square tooltip naming the step each circle jumps to
   (Radik 2026-06-29). Lives in ::after (::before is the hit area). The dot
   scales 1.35 on hover, which would scale this too — so we counter-scale by
   1/1.35 (≈0.741) toward its top-center anchor to keep the text crisp + level. */
.wizard-progress-dot::after {
  content: attr(data-label);
  position: absolute;
  bottom: calc(100% + 8px);            /* ABOVE the dot (Radik 2026-07-03) */
  left: 50%;
  transform: translate(-50%, 4px) scale(0.741);
  transform-origin: bottom center;
  padding: 4px 9px;
  background: #101820;
  color: #fff;
  font: 600 11px/1.2 'Inter', system-ui, sans-serif;
  letter-spacing: .2px;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity .15s var(--ease-out), transform .15s var(--ease-out);
  z-index: 30;
}
/* HOVER ONLY — never in the default or post-click focus state. A clicked dot
   keeps focus (Safari matches :focus-visible after a click), which was leaving
   the label stuck on; hover is the only trigger now. (Radik 2026-07-03) */
.wizard-progress-dot:hover::after {
  opacity: 1;
  transform: translate(-50%, 0) scale(0.741);
}

/* The legacy text counter ("Step N of M") is fully replaced by the circles
   (§5): BOTH forms have dropped their per-step "Step N of 6" paragraphs
   (.wr-step-count / .tt-step-count), so no hide rule is needed here anymore —
   the wizard circle indicator is the single source of step state. */


/* ============================================================
   9D. DRAFT CHIP → IN-PLACE MENU  (SPEC §7 — consumed by every form)
   The restore chip is a SINGLE "Draft: <Month Day>" toggle. Clicking it opens an
   in-place dropdown with two stacked actions: CONTINUE (on top) resumes the saved
   draft into the form; DELETE (below) discards it. Both white by default —
   Continue = ghost green on hover, Delete = ghost red. The old separate × is
   gone. (Radik 2026-06-29.)
   OUT OF FLOW (SPEC §8 — zero layout shift, EVER). The chip is absolutely
   positioned and anchored to a stable top-right corner, so toggling its presence
   / the menu reflows ZERO sibling content. Its nearest positioned ancestor is the
   .wizard-step (position:absolute, inset:0) → the top-right of the wizard area on
   the intro step. (The .wizard-stage's overflow:auto does NOT clip it — the stage
   is not its containing block.) The menu is absolute against .portal-draft. */
.portal-draft {
  position: absolute;
  top: var(--s5);
  right: var(--portal-gutter);
  z-index: 6;
  display: inline-block;      /* toggle in flow; menu is absolute below it */
}
.portal-draft[hidden] { display: none; }

/* The chip toggle — calm, square, vertically-centered "Draft: <date>" + caret. */
.portal-draft-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: 48px;
  padding: 0 var(--s4);
  font-family: inherit;
  text-align: left;
  color: var(--text);
  background: var(--white);
  border: 0;
  box-shadow: inset 0 0 0 1px var(--border);
  cursor: pointer;
  transition:
    box-shadow var(--dur) var(--ease-out),
    background var(--dur) var(--ease-out);
}
.portal-draft-toggle:hover {
  box-shadow: inset 0 0 0 1px var(--border-hover);
  background: var(--fill);
}
.portal-draft-toggle:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--accent), 0 0 0 3px var(--accent-ring);
}
.portal-draft-kicker {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  color: var(--text-2);
}
.portal-draft-date { font-weight: 600; color: var(--text); }
.portal-draft-caret {
  display: inline-flex;
  line-height: 0;
  margin-left: var(--s1);
  color: var(--text-3);
  transition: transform var(--dur) var(--ease-out);
}
.portal-draft-toggle[aria-expanded="true"] .portal-draft-caret { transform: rotate(180deg); }

/* In-place dropdown — drops just below the chip, right-aligned to it. */
.portal-draft-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 100%;
  display: flex;
  flex-direction: column;
  background: var(--white);
  box-shadow: 0 6px 20px rgba(16, 24, 32, 0.14), inset 0 0 0 1px var(--border);
  z-index: 7;
}
.portal-draft-menu[hidden] { display: none; }

/* Shared menu-button shape — BOTH white by default, square corners, one per row.
   (Radik 2026-06-29: white default; Delete = ghost red on hover, Continue = ghost
   green on hover.) */
.portal-draft-delete,
.portal-draft-continue {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: 44px;
  padding: 0 var(--s4);
  font-family: inherit;
  font-weight: 600;
  text-align: left;
  white-space: nowrap;
  color: var(--text);
  background: var(--white);
  border: 0;
  box-shadow: inset 0 0 0 1px var(--border);
  cursor: pointer;
  transition:
    background var(--dur) var(--ease-out),
    color var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out);
}
.portal-draft-delete .pd-ico,
.portal-draft-continue .pd-ico { display: inline-flex; line-height: 0; }

/* DELETE — white default, subtle GHOST RED on hover/focus. */
.portal-draft-delete:hover {
  color: var(--red);
  background: var(--danger-tint);              /* faint red wash */
  box-shadow: inset 0 0 0 1px var(--red);
}
.portal-draft-delete:focus-visible {
  outline: none;
  color: var(--red);
  box-shadow: inset 0 0 0 2px var(--red), 0 0 0 3px var(--accent-ring);
}

/* CONTINUE — white default, subtle GHOST GREEN on hover/focus (success green). */
.portal-draft-continue:hover {
  color: #16A34A;
  background: rgba(22, 163, 74, 0.08);         /* faint green wash */
  box-shadow: inset 0 0 0 1px #16A34A;
}
.portal-draft-continue:focus-visible {
  outline: none;
  color: #16A34A;
  box-shadow: inset 0 0 0 2px #16A34A, 0 0 0 3px rgba(22, 163, 74, 0.18);
}


/* ============================================================
   10. MOTION PREFERENCE
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .btn:hover,
  .btn:hover .btn-arrow { transform: none; }
}


/* ============================================================
   11. RESPONSIVE  (laptop primary; mobile pleasant — spec §6)
   ============================================================ */
@media (max-width: 640px) {
  .portal-shell { padding: 0 var(--s4); }
  /* Narrow the shared rail on phones — nav + wizard stage tighten together so
     they stay aligned (both consume --portal-gutter). The nav loses a little
     height too, but stays clearly taller than the pre-spec 64px. */
  :root {
    --t-page: 26px;
    --rhythm-section: var(--s10);
    /* --portal-gutter keeps its clamp (28px min) so the forms match the home
       page gutter on phones too. (Radik) */
    --nav-h: 84px;
  }
  /* NO-SCROLL ON PHONES (finding). The §H optical bias added nav-h (+s3) of
     bottom padding to lift content above true centre — a laptop nicety. On a
     390×844 phone the wizard region is only ~760px, and that ~100px of bias
     shrank the stage enough that even STEP 1's Continue + last meta field fell
     below the fold, breaking the no-scroll promise on the opening screen. Drop
     the nav-h term on phones: recover the height so the gate (and the heavier
     toolbox steps) fit, and let the sticky nav below keep Continue reachable on
     any step that still overflows. */
  .wizard-step { padding: var(--s4) 0 var(--s4) 0; }
  /* PHONE STICKY NAV (finding) — applied to BOTH forms identically (standing
     rule: the two forms stay visually identical). On a step tall enough to
     scroll the stage internally (e.g. the 31-tile topic pick, photos + sign-off
     + export), Back / Continue / Export stay pinned to the bottom of the visible
     stage instead of sinking below the fold. A white backdrop + top hairline
     keep scrolled content from bleeding under the buttons. Inert on steps that
     already fit (nothing to scroll). */
  .wizard-nav {
    position: sticky;
    bottom: 0;
    margin-top: var(--s5);
    padding: var(--s3) 0;
    background: var(--white);
    box-shadow: 0 -1px 0 var(--divider);
  }
  .btn { height: 48px; }              /* ≥44px touch targets */
  /* On phones the icon controls grow to a full 44px VISIBLE square (not just
     the pseudo overlay), so the primary field tap targets are unambiguous. */
  .btn-icon,
  .repeat-remove { width: 44px; height: 44px; }
  .input,
  .portal textarea,
  .portal select { font-size: 16px; } /* prevents iOS zoom-on-focus */
  .date-field-display { font-size: 16px; }   /* match the native input on phones */
  /* Tap targets (SPEC §6) — give the text-style controls a real ≥44px hit box
     on phones without changing their visual weight (hit box only). */
  .btn-link,
  .repeat-add.btn,
  .tt-topic-change {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
  .actions { flex-direction: column; align-items: stretch; }
  .actions .note { margin-left: 0; }
}


/* ============================================================
   11b. DESKTOP / LAPTOP — PINNED WIZARD CHROME  (Radik 2026-06-29)
   On laptop+ the step indicator and the Back/Continue nav are PINNED to fixed
   positions, and the content is TOP-ALIGNED, instead of the whole block being
   vertically centred. This kills the cross-step "bounce": every step's title
   lands on the SAME line, the dots are always 50px below the nav hairline, and
   Continue is always the SAME spot — the user navigates to one place instead of
   chasing a button that jumps left↔right (step 1 has no Back) or up↕down
   (content height varies). Only the form fields move (they flow under the title).
   Mobile (≤640) keeps its in-flow + sticky-nav layout untouched (its soft-keyboard
   handling depends on it). Both forms get this identically.
   ANCHORING: the pinned elements are absolute against .wizard-step
   (position:absolute, inset:0); .wizard-stage is static, so it is NOT their
   containing block and its overflow does not clip them — the same trick the
   draft chip already relies on. */
@media (min-width: 641px) {
  /* TOP-ALIGN content; reserve a top band for the dots + a bottom band for the
     nav. Title rides a fixed top line (≈88px below the nav region top); fields
     flow below it. */
  .wizard-step {
    align-items: flex-start;          /* title at a fixed Y, not vertically centred */
    padding: 88px 0 120px 0;          /* top: clear the dots · bottom: clear the nav */
  }

  /* Step dots — pinned ~50px below the nav hairline, aligned to the column. */
  .wizard-progress {
    position: absolute;
    top: 50px;
    left: 50%;
    transform: translateX(-50%);
    width: 90%;
    max-width: var(--portal-measure);
    padding: 0 var(--portal-gutter);
    margin: 0;
    z-index: 5;
  }

  /* Back / Continue — pinned to a fixed bottom band; Continue ALWAYS at the
     right edge of the column → the SAME spot on every step. */
  .wizard-nav {
    position: absolute;
    bottom: 40px;
    left: 50%;
    transform: translateX(-50%);
    width: 100%;
    max-width: var(--portal-measure);
    padding: 0 var(--portal-gutter);
    margin: 0;
    justify-content: space-between;     /* secondary left · primary right          */
    z-index: 5;
  }
  /* The primary action (Continue, or Export on the last step) is ALWAYS the
     rightmost control → identical right-edge position on every step. The
     secondary (Back, or "Clear form" on step 1) sits left. order does this
     without reordering any markup. */
  .wizard-nav [data-wizard-next],
  .wizard-nav button[type="submit"] { order: 2; }
}


/* ============================================================
   12. PRINT  —  friendly-date overlay (shared)
   The PDF is the permanent compliance record, so it must show the SAME
   unambiguous long-form date as the screen ("June 25, 2026") — never the
   browser's locale-native text (mm/dd vs dd/mm is ambiguous). Keep the friendly
   overlay span in ink and force the native input's own value text transparent.
   The `.portal` prefix out-specifies the per-page ::-webkit-datetime-edit ink
   overrides (weekly/toolbox print CSS). Date prints ONCE (the friendly span).
   ============================================================ */
@media print {
  .portal .date-field-display { color: #101820 !important; }
  .portal .date-field > .input,
  .portal .date-field > .input::-webkit-datetime-edit { color: transparent !important; }
  .portal .date-field > .input::-webkit-calendar-picker-indicator { display: none !important; }
}
