/* 2026-07-20 — per-form sticky pinning REMOVED from the step head and page picker.
   It existed because .wizard-stage was the scroller: the head would scroll out of its
   own private scroll box, so it was pinned at top:0 of THAT box. With the document
   scrolling and the portal nav itself sticky, top:0 now resolves to the viewport top —
   i.e. BEHIND the nav — so the pin no longer put anything anywhere useful.
   The rule was always 'every page opens with its title on the same baseline', not
   'the title occupies viewport space forever'. The baseline is set by the step's top
   padding and is unchanged (99.39px); the head now simply scrolls away under the opaque
   nav like normal page content. Padding/background are kept so the box model — and
   therefore the baseline and the header→content buffer — do not move. */
/* ============================================================
   Field Portal — site-orientation.css
   PAGE LAYOUT ONLY. All components (.field/.input/.btn/.toggle-row/
   .chip/.page-title/.section-h/.tt-sig-*/.date-field/
   .portal-nav/.wizard…) come from assets/css/portal.css, which sits on
   assets/css/base.css (brand tokens + reset).

   Site Orientation is the toolbox-talk archetype cloned: a no-scroll
   View-Transitions crossfade
   WIZARD (portal.css §9C) whose sign-in reuses the shared roster pool + the
   full-screen drawn Signature sheet. This file owns ONLY what's specific to
   Site Orientation: the intro/step headers, the meta + site-info grids, the
   orientation-topics checklist, the acknowledgement note, the worker sign-in
   pool + walk-on popup, the conductor sign-off slot, the signature-gate notice,
   and the @media print stylesheet that flattens the wizard into a clean record.

   The shared top nav (portal.css §9B), the wizard layout + View-Transitions crossfade
   (§9C), the red focus ring (§6), the required-asterisk rules (§4) and the
   date picker affordance (§5) are ALL inherited — never re-declared here.
   Square corners everywhere (inherited). Structure mirrors toolbox-talk.css so
   the two forms stay identical; the `so-` classes are its `tt-` equivalents.
   ============================================================ */


/* ============================================================
   1. INTRO STEP (step 1) — title + meta grid + gate
   ============================================================ */
.so-intro-head { margin-bottom: var(--s7); }

/* Record-ID line is print-only (screen shows nothing). */
.so-record-id { display: none; }

/* The pinned Continue nav stays ON TOP of the scrolling content, solid white
   bar so scrolling content never bleeds through it. Mirrors weekly + toolbox. */
.so-form .wizard-nav { z-index: 6; background: var(--white); }

/* CONTENT BOUNDARIES — box ALL form content between the yellow guide lines
   (--gr-logo-x), IDENTICAL to the weekly + toolbox forms. */
.so-form .wizard-stage {
    /* 2026-07-20 — was calc(100vw - …). 100vw INCLUDES the vertical scrollbar; the
     document scrolls now, so on desktop that made the stage wider than its own
     container and produced a horizontal page scroll. 100% is the same width
     measured against the content box, which is what was always meant. */
  width: calc(100% - 2 * var(--gr-logo-x));
  max-width: none;
  /* Safe-area, not 0 (2026-07-25) — these pages ship viewport-fit=cover, and zeroing
     dropped the shared max(gutter, env(safe-area-inset-*)). On a notched iPhone in
     LANDSCAPE the content column and the Back button sat under the sensor housing:
     --gr-logo-x computes to ~51.8px against a 59px inset. Carried over from
     toolbox-talk.css, which got this on the same day. */
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
  /* clip, NOT hidden: `overflow-x:hidden` forces the other axis to compute to `auto`,
     which would make this stage a vertical scroll container again — exactly the nested
     scroller the rebuild removed. `clip` suppresses horizontal paint with no such
     side effect, which is all this ever wanted. (2026-07-20) */
  overflow-x: clip;
}
/* The page picker is absolute on laptop+ (portal.css §11b) and sticky on mobile
   (the pin block below) — either way anchor it to the yellow lines. (2026-07-18) */
.so-form .wizard-pagepick {
  width: auto; max-width: none;
  /* Safe-area, not 0 (2026-07-25) — these pages ship viewport-fit=cover, and zeroing
     dropped the shared max(gutter, env(safe-area-inset-*)). On a notched iPhone in
     LANDSCAPE the content column and the Back button sat under the sensor housing:
     --gr-logo-x computes to ~51.8px against a 59px inset. Carried over from
     toolbox-talk.css, which got this on the same day. */
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
}

/* 2026-07-21 — the action bar's gutter reset, hoisted OUT of the laptop media query below.
   It only ever applied at ≥641px, so on a phone this form's bar kept the shared 28px
   --portal-gutter padding while every other form had it zeroed — its Back/Continue sat 28px
   inside the fields above them. The bar lives inside .wizard-stage, which already carries the
   --gr-logo-x gutter, so re-insetting here double-gutters it. Measured at 430pt: buttons were
   landing at x=63.2 against a stage edge of x=35.2. */
.so-form .wizard-nav {
  width: auto; max-width: none;
  /* Safe-area, not 0 (2026-07-25) — these pages ship viewport-fit=cover, and zeroing
     dropped the shared max(gutter, env(safe-area-inset-*)). On a notched iPhone in
     LANDSCAPE the content column and the Back button sat under the sensor housing:
     --gr-logo-x computes to ~51.8px against a 59px inset. Carried over from
     toolbox-talk.css, which got this on the same day. */
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
}
@media (min-width: 641px) {
  /* the pinned nav is absolute only on laptop+ (sticky on mobile). */
  .so-form .wizard-nav {
/* 2026-07-20 — `left`/`right`/`transform:none` dropped from this rule. They existed to
     undo the shared bar's absolute centring (left:50% + translateX(-50%)); the bar is in
     normal flow now, so all three were inert. width/max-width/padding-0 are KEPT and are
     doing real work: the bar sits inside .wizard-stage, which already carries the
     --gr-logo-x gutter, so the bar should fill its parent exactly rather than re-inset
     itself and end up double-guttered. */
  width: auto; max-width: none;
    /* Safe-area, not 0 (2026-07-25) — these pages ship viewport-fit=cover, and zeroing
     dropped the shared max(gutter, env(safe-area-inset-*)). On a notched iPhone in
     LANDSCAPE the content column and the Back button sat under the sensor housing:
     --gr-logo-x computes to ~51.8px against a 59px inset. Carried over from
     toolbox-talk.css, which got this on the same day. */
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
  }
}
/* Top nav boxed to the yellow lines: logo on the LEFT line. Scoped to .so-nav. */
.so-nav .portal-nav-inner {
  width: calc(100% - 2 * var(--gr-logo-x));
  max-width: none;
  /* Safe-area, not 0 (2026-07-25) — these pages ship viewport-fit=cover, and zeroing
     dropped the shared max(gutter, env(safe-area-inset-*)). On a notched iPhone in
     LANDSCAPE the content column and the Back button sat under the sensor housing:
     --gr-logo-x computes to ~51.8px against a 59px inset. Carried over from
     toolbox-talk.css, which got this on the same day. */
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
}
.so-nav .portal-nav-logo { margin-left: 0; }

/* PINNED TITLES + NO SCROLLBAR (parity with weekly + toolbox) — the step title
   stays put; only the content beneath it scrolls. */
/* (2026-07-20 — the .wizard-stage scrollbar-hiding pair was removed here: the stage
   stopped being a scroller when the document took over scrolling, so there is no
   scrollbar left to hide. The horizontal rails keep their own.) */
.so-form .wizard-stage > .so-intro-head,
.so-form .wizard-stage > .so-step-head {
  background: var(--white);
  margin-bottom: 0;
  padding-bottom: var(--s5);
}
.so-form .wizard-stage > .so-intro-head + *,
.so-form .wizard-stage > .so-step-head + * { margin-top: var(--s2); }

/* PIN THE PAGE PICKER ON MOBILE too (2026-07-18). On laptop+ the picker is
   absolute against .wizard-step (portal.css §11b) so it never moves; on mobile it
   is an in-flow first child of the stage, so it scrolled away and let the title
   slide up before it stuck. Make the picker sticky and drop the title's stick line
   to just below it, so neither the picker, title, nor subtext move on the very
   first pixel of scroll — content scrolls under all three. (2026-07-20) */
@media (max-width: 640px) {
  .so-form .wizard-stage > .wizard-pagepick {
    background: var(--white);
    margin-bottom: 0;
    padding-bottom: var(--pick-trail);   /* white buffer replaces the margin, so scrolling content cannot bleed between the pinned picker and the pinned title */
  }
  /* (The head's sticky `top:` offset rule was removed here with the sticky itself — the
     whole rule went, selectors included: a selector list with no declaration block is
     invalid CSS and the parser discards it, taking the next brace with it.) */
}

/* Continue lands on the RIGHT yellow line on EVERY step — MOBILE too. */
@media (max-width: 640px) {
  .so-form .wizard-nav { justify-content: space-between; }
  .so-form .wizard-nav [data-wizard-next],
  .so-form .wizard-nav button[type="submit"] { order: 2; }   /* primary always rightmost */
}


/* ============================================================
   2. META GRID (step 1) — Date · Project/Site · Address · PM · Site supervisor
   Equal rows + bottom-pinned controls, so the cells read as one block. The 2-column
   template below only ever renders between 641px and the `.wizard-step .field`
   one-column rule in portal.css §11b, which spans every cell from 641px up; below
   640 this grid is one column too. Identical to the toolbox meta grid.
   ============================================================ */
.so-meta-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-auto-rows: 1fr;
  gap: var(--s4) var(--gr-gutter);
  margin-top: var(--s2);
}
.so-meta-grid .field { margin: 0; display: flex; flex-direction: column; }
.so-meta-grid .field > .input,
.so-meta-grid .field > .date-field { margin-top: auto; }


/* ============================================================
   3. INTERIOR STEP HEADERS (steps 2–5)
   ============================================================ */
.so-step-head { margin-bottom: var(--s6); }
.so-step-head .section-sub { margin-top: var(--s2); }

.so-subhead {
  font-size: var(--t-label);
  font-weight: 700;
  letter-spacing: -0.005em;
  color: var(--text);
  margin-bottom: var(--s3);
}

/* Section title line with a right-aligned link (Check all). */
.so-head-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s4);
}
/* ⚠ THE TITLE IS NO LONGER IN THIS ROW (2026-07-28). The row now pairs the SUB with
   "Check all"; the old `.so-head-row .section-h { margin: 0 }` went with it. The h2 sits in
   normal flow again on the full column and takes the shared `.section-h` margins like every
   other step's heading — which is the whole point: a control beside it can no longer squeeze
   it (it had been holding the title to 204px of 295px). */
.so-head-row .section-sub { margin: 0; }
/* Keep "Check all" on the SUB's line. The shared .btn-link vertical padding made this button
   taller than the line it shares, baseline-pushing the whole row; padding-block:0 zeroes that
   so the row height == the sub. The 44px tap area is the ::before, independent of padding. */
.so-head-row .so-check-all { white-space: nowrap; flex: 0 0 auto; min-height: 0; position: relative; padding-block: 0; }
/* Right bleed removed 2026-07-26 — same defect as toolbox's .tt-check-all::before: this
   button sits flush with the content column's right edge, so -10px right put 10px outside
   it and was this step's whole horizontal overflow (MEASURED +10 at 360 AND 390, nav clean
   at 0). The left + vertical bleed still carry the enlarged target, and the lost 10px were
   off-column against the screen gutter.
   44px, AND ASYMMETRIC ON PURPOSE (2026-07-27) — identical to toolbox's, same reason:
   -12/-12 over a 15px button hit-tested 39px, and the 5px shortfall can only be taken
   from BELOW because the page-picker's own tap overlay sits just above and is earlier
   in the DOM, so an overlap would silently hand its taps to this button. The nearest
   control below is 47px away. Verify with `node _BUILD/measure.mjs tap`. */
.so-head-row .so-check-all::before { content: ""; position: absolute; inset: -7px 0 -22px -10px; }


/* ============================================================
   4. SITE-INFO GRID (step 2) — prime contractor + emergency / first-aid
   A full-width prime-contractor field, then a 2-col grid of short fields, then
   a full-width evacuation-notes textarea.
   ============================================================ */
.so-info-full { margin-top: 0; }
.so-info-full + .so-info-grid { margin-top: var(--s5); }
.so-info-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s5) var(--gr-gutter);
}
.so-info-grid .field { margin: 0; }
.so-info-grid + .so-info-full { margin-top: var(--s5); }


/* ============================================================
   5. ORIENTATION TOPICS (step 3) — grouped smart checklist
   Four groups mapped to WorkSafeBC OHSR s.3.23(2). All groups open by default;
   a group folds up once fully checked (JS) and the next opens into view. Each
   group body is a 2-col checklist of .toggle-row items (portal.css). Group head
   mirrors the toolbox topic-block head (light stroke, chevron rotate — CSS only).
   ============================================================ */
.so-check-list { margin-top: var(--s3); }
.so-topic-group + .so-topic-group { margin-top: var(--s5); }

.so-group-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  width: 100%;
  padding: var(--s3) 0;
  margin-bottom: var(--s3);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--divider);   /* the light stroke under each group name */
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  appearance: none;
}
.so-group-title { flex: 1 1 auto; font-size: var(--t-body); font-weight: 700; color: var(--text); }
.so-group-status { flex: 0 0 auto; font-size: var(--t-hint); font-weight: 600; color: var(--text-2); white-space: nowrap; }
.so-topic-group.is-complete .so-group-status { color: #16A34A; }   /* green once fully checked */
.so-group-chev {
  flex: 0 0 auto;
  display: inline-flex;
  color: var(--text-2);
  transition: transform var(--dur) var(--ease-out);   /* down when open, right when collapsed */
}
.so-topic-group.is-collapsed .so-group-chev { transform: rotate(-90deg); }
.so-topic-group.is-collapsed .so-group-head { margin-bottom: 0; }
/* COLLAPSED-ROW VERTICAL CENTERING. When collapsed the head IS the whole visible
   row, bounded by its hairline divider. The base row had symmetric 12/12 padding,
   but the 20px inter-group margin sat ONLY above it, so between two hairlines the
   title/count/chevron read LOW (≈32px of space above vs ≈13px below). Fold that
   inter-group gap into equal top+bottom head padding and drop the top margin
   between consecutive collapsed rows, so each row's content is optically centered
   with equal space above and below. Open groups are untouched (keep their spacing);
   align-items:center on .so-group-head already centres the count + chevron. */
.so-topic-group.is-collapsed + .so-topic-group.is-collapsed { margin-top: 0; }
.so-topic-group.is-collapsed .so-group-head {
  padding-top: var(--s5);
  padding-bottom: var(--s5);
}

.so-group-body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s3);
}
.so-topic-group.is-collapsed .so-group-body { display: none; }


/* ============================================================
   6. WORKER SIGN-IN (step 4) — acknowledgement note + roster pool + walk-ons
   ============================================================ */
/* Acknowledgement statement — a calm neutral note (NOT an error box). Part of
   the printed record too (it's the compliance statement each signature covers).
   It lives INSIDE the sticky .so-step-head (§1), so the step title + sub + this
   note PIN together as one block while only the roster scrolls beneath. Plain
   in-flow block here; the 15px gap under it is the step-head's padding-bottom. */
.so-ack {
  margin-top: var(--s4);
  padding: var(--s4) var(--s5);
  background: var(--fill);
  box-shadow: inset 3px 0 0 var(--divider);
  font-size: var(--t-hint);
  line-height: 1.5;
  color: var(--text-2);
}
.so-ack p { margin: 0; }

/* Sign-in step: the sticky step-head now carries the note, so its padding-bottom
   IS the 15px gap under the gray note (opaque + pinned, roster scrolls under it).
   Zero the roster's own top margin so the landed gap is exactly 15px, not 15+20. */
.so-form .so-signin-step .wizard-stage > .so-step-head { padding-bottom: 15px; }
.so-signin-step .so-attend { margin-top: 0; }

/* REMOVED 2026-07-27 — `.so-attend-section`, `.so-attend-head` and their heading rule
   went with the two inline name grids they laid out. The step is now one Add button,
   a signed list and a roster popup, all styled from portal.css § SIGN-OFF BLOCK.
   Verified dead: zero references left in site-orientation.html/.js. */
.so-attend-count { font-size: var(--t-hint); font-weight: 600; color: var(--text-2); white-space: nowrap; }

.so-pool-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--s2);
}
/* Name button — ONE line, never wraps (filter-button size/feel). */
.so-pool-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 66px;              /* ~20px taller (was 46) — matches toolbox; content stays centred via align-items:center. Grows the grid DOWN only; the anchored title/dots don't move. */
  padding: 12px 9px 12px 14px;
  text-align: left;
}
.so-pool-chip-col { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.so-pool-chip-name {
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.so-pool-chip-title {
  font-size: 10px;
  font-weight: 400;
  color: var(--text-3);
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Conductor (the supervisor who ran it) — red * on their name; red stroke on the
   button until they sign, then it drops back to the resting look (mirrors toolbox). */
.so-pool-chip-star,
.so-conductor-star { color: var(--accent); font-weight: 800; margin-left: 2px; }
.so-pool-chip.is-conductor:not(.is-signed) { box-shadow: inset 0 0 0 1.5px var(--accent); }
/* Small "young/new" tag on a chip (accent-neutral). */
.so-pool-chip-young { color: var(--accent); font-weight: 700; }
/* Green check to the RIGHT of the name once signed. */
.so-pool-chip-check { flex: 0 0 auto; display: inline-flex; line-height: 0; color: #16A34A; }
.so-walkon-add-chip .so-pool-chip-name { color: var(--text-2); }

/* ---- Walk-on popup: name + company + young/new flag → Sign ---- */
/* Geometry is .vv-overlay's (portal.css) — fixed to the VISIBLE viewport, so the
   name/company fields centre above the keyboard instead of behind it. */
/* ⚠ THE TINT LIVES ON THE MODAL, NOT THE SCRIM CHILD (2026-07-28).
   This modal is a `.vv-overlay`, and .vv-overlay gained safe-area PADDING the same day. An
   absolutely-positioned child resolves `inset: 0` against its parent's PADDING box — so the
   scrim stopped covering the inset band and, in an installed PWA, the page showed through
   behind the notch and the home indicator while a modal was open.
   A background painted on the padded element itself covers its padding, which is why
   `.so-modal` and `.dr-note-modal` never had this problem — both already tint the overlay
   element directly. This brings the walk-on popups in line with them.
   The scrim child STAYS (it is the click-outside dismiss target, wired in the controller);
   it is simply transparent now and contributes no paint. */
.so-walkon-modal { z-index: 50; display: flex; align-items: center; justify-content: center; overscroll-behavior: contain; background: rgba(16, 24, 32, 0.55); }
.so-walkon-scrim { position: absolute; inset: 0; }
.so-walkon-panel {
  position: relative;
  width: min(360px, 90vw);
  background: var(--white);
  padding: var(--s5);
  box-shadow: 0 12px 40px rgba(16, 24, 32, 0.25);
}
.so-walkon-panel .so-subhead { margin-bottom: var(--s4); }
.so-walkon-panel .field + .field { margin-top: var(--s3); }
.so-walkon-young { margin-top: var(--s3); }
.so-walkon-modal-actions { display: flex; justify-content: flex-end; gap: var(--s3); margin-top: var(--s5); }
/* Sign button — FADED green by default, FULL green once BOTH name + company are filled. */
.so-walkon-add-btn {
  white-space: nowrap;
  background: rgba(22, 163, 74, 0.32);
  border: 1px solid transparent;
  color: #fff;
}
.so-walkon-add-btn:disabled { opacity: 1; cursor: not-allowed; }
.so-walkon-add-btn.is-ready,
.so-walkon-add-btn.is-ready:hover { background: #16A34A; border-color: #16A34A; color: #fff; cursor: pointer; }


/* ============================================================
   8. SIGNATURE GATE — needs-sign highlight + calm gap notice
   (drawn signature mandatory for ≥1 worker + the conductor). Only shown after
   an export attempt. Mirrors toolbox exactly.
   ============================================================ */
.so-sign-gap {
  margin-top: var(--s5);
  padding: var(--s3) var(--s4);
  background: var(--fill);
  box-shadow: inset 3px 0 0 var(--accent);
  color: var(--text);
}
.so-sign-gap:focus-visible { outline: none; box-shadow: inset 3px 0 0 var(--accent), 0 0 0 3px var(--accent-ring); }
/* Half the height, one accent — matches toolbox-talk.css (owner 2026-07-26). Heading
   + sub-line + red underlined links collapsed to a lead and plain-text gaps. */
.so-sign-gap .so-sign-gap-lead { font-size: var(--t-hint); font-weight: 700; color: var(--text); line-height: 1.4; }
.so-sign-gap ul { list-style: none; margin: 0; padding: 0; }
.so-sign-gap li { margin-top: var(--s1); font-size: var(--t-hint); color: var(--text-2); line-height: 1.4; }

/* Soft inset ring on a control that still needs a signature. */
.needs-sign { box-shadow: inset 0 0 0 1.5px var(--accent); }

/* Done page shows ONLY the exported filename as light subtext under the title. */
.so-done-file { word-break: break-word; }


/* ============================================================
   9. RESPONSIVE — laptop primary, mobile pleasant (640 / 1024 breakpoints only;
   NEVER the home page's 860/861). Matches weekly + toolbox.
   ============================================================ */
@media (max-width: 640px) {
  .so-meta-grid {
    grid-template-columns: 1fr;
    grid-auto-rows: auto;
    gap: var(--s5);
  }
  .so-meta-grid .field > .input,
  .so-meta-grid .field > .date-field { margin-top: 0; }
  .so-info-grid { grid-template-columns: 1fr; gap: var(--s5); }
  .so-group-body { grid-template-columns: 1fr; }      /* topics: 1 per row on phones */
  .so-pool-grid { grid-template-columns: 1fr 1fr; }   /* name buttons: 2 per row on phones */
}


/* ============================================================
   10. PRINT — the export path. report-export.js (Report.print) sets
   <html class="is-printing"> and fires window.print(). FLATTEN the wizard so
   the saved PDF is the whole record. @page letter 0.5in, 7.5in content width,
   10.5pt body — IDENTICAL to weekly + toolbox.
   ============================================================ */
.so-print-only { display: none; }

@media print {

  @page { size: letter; margin: 0.5in; }

  html, body { background: #fff; color: #191A1C; width: 7.5in; }
  .portal { font-size: 10.5pt; line-height: 1.5; }

  /* --- Hide all interactive chrome / screen-only affordances --- */
  [data-print-hide],
  .so-intro-head,
  .portal-nav,
  .wizard-nav,
  .so-sign-gap,
  .so-check-all,
  .so-attend,
  .so-walkon-modal,
  .btn,
  .btn-icon,
  .btn-link {
    display: none !important;
  }

  /* Print-only letterhead: small logo · document title · generated date · record id. */
  .so-print-only { display: block; }
  .so-print-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 24pt;
    padding-bottom: 10pt;
    margin-bottom: 16pt;
    border-bottom: 1px solid #DADADA;
  }
  .so-print-logo svg { width: 109px; height: auto; display: block; }
  .so-print-id { display: flex; flex-direction: column; align-items: flex-end; gap: 5pt; text-align: right; }
  .so-print-doctitle { white-space: nowrap; font-size: 15pt; font-weight: 800; letter-spacing: -0.01em; color: #191A1C; }
  .so-print-rec { font-size: 8.5pt; font-weight: 700; color: #646464; letter-spacing: 0.4px; }
  .so-print-gen { font-size: 8.5pt; font-weight: 500; color: #8A8A8A; }

  /* --- Flatten the wizard into document flow --- */
  .wizard { position: static !important; height: auto !important; overflow: visible !important; }
  .wizard-step {
    position: static !important;
    display: block !important;
    transform: none !important;
    opacity: 1 !important;
    visibility: visible !important;
    pointer-events: auto !important;
    padding: 0 !important;
    break-inside: auto;
  }
  .wizard-step.so-done-step { display: none !important; }   /* screen-only success state */
  .wizard-stage {
    width: 100% !important;
    max-width: none !important;
    max-height: none !important;
    overflow: visible !important;
    margin: 0 !important;
    padding: 0 !important;
  }
  .wizard-step + .wizard-step {
    border-top: 1px solid #ECECEC;
    margin-top: 32pt;
    padding-top: 24pt !important;
  }
  /* Sign-in starts a fresh page so signatures are never split across a boundary. */
  .wizard-step.so-signin-step {
    break-before: page; page-break-before: always;
    border-top: 0 !important; margin-top: 0 !important; padding-top: 0 !important;
  }
  .so-step-head { break-inside: avoid; page-break-inside: avoid; }
  .so-step-head { margin-bottom: 16pt; }

  /* --- Header band: title + record id --- */
  .so-intro-head { margin-bottom: 8pt; }
  .page-title { font-size: 20pt; letter-spacing: -0.01em; }
  .so-record-id {
    display: block !important;
    margin-top: 2pt;
    font-size: 9pt; font-weight: 700; color: #646464; letter-spacing: 0.4px;
  }
  .so-record-id[hidden] { display: block !important; }

  /* Sentence case, matching screen + the jsPDF export (owner 2026-07-25). */
  .section-h { font-size: 14pt; font-weight: 700; text-transform: none; letter-spacing: -0.01em; color: #191A1C; }
  .section-h, .so-step-head, .so-subhead { break-after: avoid; page-break-after: avoid; }
  .section-sub { display: none; }
  .required, .optional { display: none; }
  .so-subhead { font-size: 9.5pt; font-weight: 700; color: #8A8A8A; text-transform: none; letter-spacing: 0.5px; margin-bottom: 6pt; }

  /* Meta grid + info grid print as compact key/value blocks. */
  .so-meta-grid, .so-info-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-auto-rows: auto !important;
    gap: 10pt 24pt;
    margin: 0;
  }
  .so-meta-grid .field, .so-info-grid .field { display: block; }
  .so-meta-grid .field > .input,
  .so-meta-grid .field > .date-field { margin-top: 0 !important; }
  .so-info-full { margin: 0 0 10pt; }
  .label { font-size: 7.5pt; font-weight: 700; color: #8A8A8A; text-transform: none; letter-spacing: 0.6px; margin-bottom: 2pt; }
  .hint { display: none; }

  /* --- Flatten inputs to read as printed text --- */
  .input,
  .portal textarea,
  .portal select {
    box-shadow: none !important;
    background: transparent !important;
    border: 0 !important;
    padding: 0 !important;
    color: #191A1C !important;
    font-size: 11pt;
    min-height: 0;
    -webkit-appearance: none;
    appearance: none;
  }
  .portal textarea { resize: none; }
  .portal input[type="date"] { color: #191A1C !important; }
  .portal input[type="date"]::-webkit-calendar-picker-indicator { display: none !important; }
  .portal input[type="date"]::-webkit-inner-spin-button,
  .portal input[type="date"]::-webkit-clear-button { display: none !important; -webkit-appearance: none !important; appearance: none !important; }
  .portal input[type="date"]::-webkit-datetime-edit { padding: 0 !important; color: #191A1C !important; }
  .date-field-display { left: 0 !important; right: 0 !important; }

  /* --- Toggle rows (topics) flatten to printed ☑ / ☐ lines --- */
  .toggle-row {
    display: flex !important;
    align-items: center;
    gap: 6pt;
    min-height: 0 !important;
    padding: 1.5pt 0 !important;
    background: transparent !important;
    box-shadow: none !important;
    break-inside: avoid;
  }
  .toggle-row .toggle-label { font-size: 10.5pt; font-weight: 400; }
  .toggle-row .toggle-box {
    width: auto !important; height: auto !important;
    box-shadow: none !important; background: transparent !important;
    color: #191A1C !important;
    order: -1;
  }
  .toggle-row .toggle-box svg { display: none !important; }
  .toggle-row .toggle-box::before { content: "\2610"; font-size: 12pt; line-height: 1; }
  .toggle-row > input:checked ~ .toggle-box::before { content: "\2611"; }
  .toggle-row > input:checked ~ .toggle-box { color: #191A1C !important; }
  /* Topics: show EVERY group's items (flatten the on-screen collapse); the group
     name prints as a small subhead above its two-column checklist. */
  .so-topic-group + .so-topic-group { margin-top: 10pt; }
  .so-group-head { display: block; border-bottom: 0 !important; padding: 0 !important; margin: 0 0 4pt !important; }
  .so-group-chev, .so-group-status { display: none !important; }
  .so-group-title { font-size: 9.5pt; font-weight: 700; color: #8A8A8A; text-transform: none; letter-spacing: 0.5px; }
  .so-group-body { display: grid !important; grid-template-columns: 1fr 1fr; gap: 2pt 18pt; }

  /* --- Acknowledgement statement prints as plain body text --- */
  .so-ack {
    position: static !important;   /* never sticky in the printed record */
    background: transparent !important;
    box-shadow: none !important;
    padding: 0 !important;
    margin: 0 0 12pt !important;
    font-size: 9pt !important;
    color: #333 !important;
    font-style: italic;
  }

  /* --- Worker sign-in: on paper, hide the interactive name grids. The numbered sign-in
     table went with buildPrintTable, and the `.so-print-signin` reveal that outlived it
     was swept 2026-07-25 — nothing has produced that container since. --- */
  .so-attend { display: none !important; }

  /* --- Optional SITE-PHOTOS step — JUSTIFIED export grid (photos.js computeRows
     → --pfr/--prn). Rows fill the ENTIRE printable width, each photo scaled to its
     row's common height at its TRUE shape: WHOLE, never cropped, never letterboxed.
     break-inside:avoid keeps each photo (+ its caption) whole across page breaks.
     Mirrors the Weekly Report export block. [[portal-photos-never-crop-export]] --- */
  .so-photos-step .wizard-stage {
    width: 100% !important; max-width: none !important; margin: 0 !important; padding: 0 !important;
  }
  .so-photos-head { position: static !important; background: none !important; }
  .so-photos-step .section-body { margin: 0 !important; padding: 0 !important; }
  /* No photos attached → drop the whole photos section from the export. */
  .so-photos-step:not(:has(.photo-tile)) { display: none !important; }
  /* Hide the capture chrome; only the photos + captions print. */
  .photo-drop, .photo-take, .photo-remove { display: none !important; }
  .photo-grid {
    --pg-gap: 16px;
    display: flex; flex-wrap: wrap; align-items: flex-start;
    gap: var(--pg-gap); width: 100%; margin: 0 !important;
  }
  /* Same justified widths as on screen, re-pointed to the EXPORT grouping
     (--pfr/--prn) computed for the fixed letter-page width. */
  .photo-tile {
    flex: 0 0 calc((100% - (var(--prn) - 1) * var(--pg-gap) - 1px) * var(--pfr));
    max-width: calc((100% - (var(--prn) - 1) * var(--pg-gap) - 1px) * var(--pfr));
    min-width: 0; display: flex; flex-direction: column; gap: 0; margin: 0;
    break-inside: avoid; page-break-inside: avoid;
  }
  .photo-figure { box-shadow: none; border: 1px solid #DADADA; overflow: visible; height: auto !important; }
  .photo-thumb { display: block; width: 100%; height: auto; }   /* width-driven height = true aspect */
  .photo-num {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
    background: #E43338 !important; color: #fff !important;
  }
  /* On-screen caption is a single-line <input> that CLIPS long text — hide it and
     print the wrapping mirror <div> (photos.js) instead. */
  .photo-cap { display: none !important; }   /* was .photo-caption (the under-tile input) until 2026-07-28 */
  .photo-caption-print {
    display: block; text-align: left;
    padding: 4px 15px 20px 15px; margin: 0 !important;
    border: 1px solid #DADADA; border-top: 0;
    font-size: 8.5pt; line-height: 1.35; color: #6E6E6E;
    overflow-wrap: break-word; word-break: break-word; white-space: normal;
  }
  .photo-caption-print:empty { display: none !important; }
}
