:root {
  --bg: #fafaf8;
  --navy: #0c2d6b;
  --accent: #1a4fa0;
  /* WCAG 2.2 AA r2 (order #box-orders C0BQD1PTJ68, ts 1790428680.253569): #9a6c1e on --bg
     measured 4.42:1 (axe-core color-contrast, needs 4.5:1 for normal-size text at
     .founding__eyebrow / .founding__copy--price / .link-slot). Darkened to the
     same hue/family, 5.28:1 measured — visually still "gold", never a copy/price change. */
  --gold: #8a611b;
  --text: #0d1117;
  --muted: #5b6472;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: "DM Sans", -apple-system, BlinkMacSystemFont, sans-serif;
}

.stage {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
}

.photo-panel {
  position: relative;
  flex: 0 0 44%;
  max-width: 44%;
  overflow: hidden;
}

@media (min-width: 769px) {
  /* Soft fade where the photo meets the content panel, replacing the bare
     edge with a deliberate blend into the panel background. */
  .photo-panel::after {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 12%;
    background: linear-gradient(to right, rgba(250, 250, 248, 0) 0%, var(--bg) 100%);
    pointer-events: none;
  }
}

.photo-panel__img {
  width: 100%;
  height: 100vh;
  height: 100dvh;
  object-fit: cover;
  object-position: center top;
  display: block;
}

.photo-panel__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  display: block;
}

.photo-panel__video[hidden] {
  display: none;
}

/* INTRO VIDEO FIRST (order ts 1790196424.667909, r6 re-post ts 1790278965.701139): the one-tap
   "Meet Grace" / "Watch again" target and the skip control, both overlaid on the portrait box.
   WHOLE-WEBSITE PREVIEW r2 AMENDMENT 2 (ts 1790354550.431499, Arthur: "she starts with closed
   eyes fix that... move the button, it now sits over her mouth"): moved off center-face to
   bottom-center over a soft gradient backdrop, thumb-sized on iPhone, never covering eyes/nose/
   mouth at any width. #meetGraceBtn and #watchAgainBtn share this class and slot — only one is
   ever visible at a time (script.js toggles `hidden`). */
.photo-panel__gradient {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 34%;
  z-index: 1;
  background: linear-gradient(to top, rgba(8, 12, 20, 0.62), rgba(8, 12, 20, 0));
  pointer-events: none;
}

.photo-panel__gradient[hidden] {
  display: none;
}

.photo-panel__cta {
  position: absolute;
  left: 50%;
  bottom: 1.4rem;
  transform: translateX(-50%);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
}

/* [BOX1] after-face-to-grace r1 found this rule's own closing brace missing here (box3's
   MERGE TRAIN defect, same commit family as the index.html/script.js/grace_service.py defects
   fixed elsewhere this lane) — it swallowed the next rule's opening and left
   .photo-panel__cta[hidden] with no body, so #startFaceToGraceBtn stayed visible after `hidden`
   was set (caught by test_entrypoint_after_ftg_r1.mjs). website functions bundle r2 (46c6a819,
   already landed on this train) fixed the identical defect too, by folding these shared
   properties into .photo-panel__cta directly and dropping .photo-panel__meet's own rule — but
   .photo-panel__meet still styles r1's own disclosed dead/unreachable duplicate #meetGraceBtn
   node (out of that lane's fix scope, cosmetic only), so it is kept here as a second selector
   rather than dropped, to carry r1's own screenshot-verified rendering forward unchanged. */
.photo-panel__cta,
.photo-panel__meet {
  padding: 0.85em 1.8em;
  border: none;
  border-radius: 999px;
  background: var(--navy);
  color: #fff;
  font-family: "DM Sans", -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 1.05rem;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 6px 24px rgba(12, 45, 107, 0.35);
  min-height: 44px;
}

.photo-panel__meet {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
}

.photo-panel__cta-icon {
  font-size: 0.85em;
  line-height: 1;
}

.photo-panel__cta[hidden],
.photo-panel__meet[hidden],
.photo-panel__skip[hidden],
.photo-panel__unmute[hidden] {
  display: none;
}

/* STEP 6 BRIDGE VIDEO GATE r1 (order ts 1791131014.543389): #ftgVideoUnmuteBtn, a one-way
   tap-to-unmute control for the muted-autoplay bridge video. Bottom-left so it never overlaps
   .photo-panel__skip (#ftgVideoSkipBtn), which stays bottom-right. */
.photo-panel__unmute {
  position: absolute;
  left: 1rem;
  bottom: 1rem;
  z-index: 2;
  padding: 0.5em 1.1em;
  border: 1px solid rgba(255, 255, 255, 0.7);
  border-radius: 999px;
  background: rgba(13, 17, 23, 0.72);
  color: #fff;
  font-family: "DM Sans", -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 0.85rem;
  cursor: pointer;
}

.photo-panel__skip {
  position: absolute;
  right: 1rem;
  bottom: 1rem;
  z-index: 2;
  padding: 0.5em 1.1em;
  border: 1px solid rgba(255, 255, 255, 0.7);
  border-radius: 999px;
  background: rgba(13, 17, 23, 0.35);
  /* WCAG 2.2 AA r2: this pill sits over live video, whose frames are not a fixed color axe-core
     can check — worst case (a bright frame, which this video's actual studio backdrop is close
     to) composited to ~2.3:1 for white text at the old 0.35 alpha. 0.72 composites to >=4.5:1
     against a fully white frame, so the pill stays legible for the video's real content range. */
  background: rgba(13, 17, 23, 0.72);
  color: #fff;
  font-family: "DM Sans", -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 0.85rem;
  cursor: pointer;
}

/* FACE TO GRACE AFTER PAYMENT r2 (order ts 1790351035.513679): a small caption over the portrait
   box, shown only while a live Face-to-Grace session is on screen. */
.photo-panel__status {
  position: absolute;
  left: 1rem;
  bottom: 1rem;
  z-index: 2;
  margin: 0;
  padding: 0.4em 0.9em;
  border-radius: 999px;
  background: rgba(13, 17, 23, 0.55);
  /* WCAG 2.2 AA r2: same video-background contrast fix as .photo-panel__skip above; 0.55
     composited to 4.18:1 worst case (just under the 4.5:1 floor), 0.72 clears it with margin. */
  background: rgba(13, 17, 23, 0.72);
  color: #fff;
  font-family: "DM Sans", -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 0.8rem;
}

.photo-panel__status[hidden] {
  display: none;
}

/* AFTER FACE TO GRACE r2 (order #box-orders ts 1790623286.000679): the correction + final
   screens shown after face_to_grace_end() responds.
   AMENDMENT 1 restyle (r3, order #box-orders ts 1790624130.027719, "same fonts, colors,
   spacing ... same thin-caps line style, no new components or colors. Reuse the landing
   page's CSS classes"): r2's version filled this panel with rgba(13,17,23,0.88) + #fff —
   a full-bleed near-black modal never seen anywhere else on this light (--bg/--text) page
   (the ONE existing near-black use, .photo-panel__status/.photo-panel__skip, is a small
   pill sitting ON TOP of a still-playing video, not a screen that replaces it). Rebuilt on
   the SAME tokens as the rest of the page: var(--bg) background, var(--text) body copy,
   .convo__title's Instrument Serif for the headline, .founding__eyebrow's thin-caps
   uppercase treatment for the label line, .photo-panel__meet's navy pill for the primary
   action, .link-slot__link's plain underlined text for the secondary one. No new color,
   no new font, no new button shape — only new spacing rules (padding/gap), named because
   a distinct screen still needs its own layout box.
   AMENDMENT 3(a) (order ts 1790626142.234429, Codex RED on e4a2f31d): "Grace's portrait
   (.photo-panel) stays visible on the correction and final screens. No opaque overlay
   covering her." The r3-style version above still used `inset: 0` + a flat `var(--bg)`
   fill — a solid box the FULL SIZE of .photo-panel, functionally the same full-bleed cover
   the CORE finding named even in the page's own light tokens. Bottom-anchored now instead
   (`bottom: 0` not `inset: 0`, capped at 62% of the panel's height) with a gradient that is
   fully transparent at its own top edge and only reaches solid var(--bg) in its bottom
   third — the SAME "small pill sitting on a still-playing video" pattern this file's own
   comment already names as the right shape, just tall enough to hold this screen's real
   content instead of one line. */
/* AFTER FACE TO GRACE r4 FINAL-SCREEN HEADLINE (order #box-orders ts 1790632281.985539, EDGE:
   "the success heading clipped above the viewport while the long vCard failure text overlays the
   portrait"): `justify-content: flex-end` on a `max-height` + `overflow-y: auto` column flex
   container is the exact cause — when content (the "ok" scenario's 4-line list) exceeds
   max-height, a flex-end container's overflow extends UPWARD past the box's own top and the
   initial scroll position shows only the tail, leaving the eyebrow/title (the first children)
   permanently scrolled out of the visible/scrollable region instead of reachable. flex-start
   (the default; explicit here for clarity) keeps the heading pinned at the box's own top at
   scrollTop 0 in every case — short content (the correction screen; unaffected, still visually
   bottom-anchored since its content never approaches max-height so there is no leftover space to
   distribute) and long content alike, with any overflow now scrolling DOWNWARD and reachable.
   AFTER FACE TO GRACE r5 (order ts 1790638368.013799, Codex EDGE NO on r4 52df69d9): the 74%
   height r4 chose to avoid scrolling instead reached UP into the portrait's own face — a
   390x844 face box (upper 60% of the portrait's height, centered at 60% of its width; see
   test_entrypoint_after_ftg_r5.mjs's faceBoxOf()) always extends to ~294px on this viewport, and
   a panel anchored to the bottom at 74% height starts at ~127px, well inside that box. Capped at
   37% instead — the panel's own top now sits at ~181px BELOW the face box's ~294px bottom edge
   with margin to spare — and the "ok" scenario's 4-line list (the one case that needed the 74%
   headroom) now scrolls per this rule's own flex-start/overflow-y:auto mechanism rather than
   growing the box upward into the face; DO item 4 permits either ("put the panel below the face
   ... or scroll it") and scrolling is what a smaller, face-clearing box requires for long content.
   Padding trimmed (2.5rem -> 1.25rem top) to reclaim room in the now much shorter box; the
   gradient's opaque stop raised 62% -> 80% so short-box text (now packed closer to the panel's
   own top, inside what used to be pure fade zone) renders on a solid background, not a
   still-fading one. */
.ftg-screen {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  max-height: 37%;
  z-index: 3;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: 0.9rem;
  padding: 1.25rem 1.5rem 1.25rem;
  background: linear-gradient(to top, var(--bg) 0%, var(--bg) 80%, rgba(250, 250, 248, 0) 100%);
  color: var(--text);
  font-family: "DM Sans", -apple-system, BlinkMacSystemFont, sans-serif;
  overflow-y: auto;
}

.ftg-screen[hidden] {
  display: none;
}

.ftg-screen__eyebrow {
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold);
  font-weight: 600;
  margin: 0 0 0.4rem;
}

.ftg-screen__title {
  font-family: "Instrument Serif", Georgia, serif;
  font-weight: 400;
  font-size: clamp(1.6rem, 4vw, 2.1rem);
  line-height: 1.15;
  margin: 0;
  color: var(--text);
}

.ftg-screen__label {
  font-size: 0.82rem;
  color: var(--muted);
}

/* AMENDMENT 3(b): the correction screen's input/submit are now .field/.field__input/
   .field__submit (index.html, reusing #graceForm's own markup) — .ftg-screen__input/
   __actions/__submit are dead, removed rather than left unreferenced. */

.ftg-screen__skip {
  padding: 0;
  border: none;
  background: transparent;
  color: var(--gold);
  font-family: inherit;
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-decoration: underline;
  cursor: pointer;
}

.ftg-screen__list {
  margin: 0;
  padding-left: 1.1rem;
  font-size: 0.95rem;
  line-height: 1.4;
  color: var(--muted);
}

.content-panel {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 2.5rem 4vw 2rem;
  min-height: 100vh;
  min-height: 100dvh;
}

.tagline {
  align-self: flex-end;
  margin: 0;
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 500;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  column-gap: 1em;
  row-gap: 0.35em;
}

.tagline__item {
  white-space: nowrap;
}

.convo {
  margin: auto 0;
  max-width: 34rem;
}

.convo__title {
  font-family: "Instrument Serif", Georgia, serif;
  font-weight: 400;
  font-size: clamp(2.75rem, 5vw, 4.25rem);
  margin: 0 0 0.75rem;
  line-height: 1.05;
  color: var(--text);
}

.convo__subtitle {
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  color: var(--muted);
  margin: 0 0 1.5rem;
}

.convo__line {
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  line-height: 1.5;
  color: var(--text);
  margin: 0 0 2.75rem;
  min-height: 1.6em;
  transition: opacity 0.2s ease;
}

/* WEBSITE iPHONE WALK BATCH r1 (order ts 1790697811.273239), SCREEN 2: the confirm-back recap,
   two aligned rows (label / value / check) instead of one prose line whose inline "✓" wrapped
   onto its own line at 390px. Grid keeps label and value columns aligned between the two rows;
   nowrap + ellipsis on the value cell means the check mark itself never wraps, even for a long
   email address. */
.confirm-summary {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  margin: 0 0 1.75rem;
}

/* LOOK BACK, disclosed pre-existing-pattern bug caught live on the deployed preview (real
   iPhone-viewport screenshot showed empty Phone/Email rows on a FRESH page load, before any
   step reached confirm): an author-stylesheet class rule beats the UA's native `[hidden] {
   display: none}` regardless of specificity order, so `.confirm-summary { display: flex }` alone
   left the element visible whenever `hidden` was set. Same fix already applied elsewhere in this
   file (.photo-panel__video[hidden], .photo-panel__gradient[hidden], .photo-panel__status[hidden])
   — every hidden-toggled element needs its own `[hidden]` override, this one had none. */
.confirm-summary[hidden] {
  display: none;
}

/* WALK VIDEO r2 (order ts 1790794288.182389): the real end-of-Part-A completion control, shown
   only on the paid-onboarding path (script.js). Same pill treatment as .confirm-summary__value
   would get if it were a CTA — normal document flow, not the photo panel's absolute overlay. */
.confirm-summary__continue {
  margin-top: 0.3rem;
  padding: 0.7em 1.8em;
  border: none;
  border-radius: 999px;
  background: var(--navy);
  color: #fff;
  font-family: "DM Sans", -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
  min-height: 44px;
}

.confirm-summary__continue[hidden] {
  display: none;
}

.confirm-summary__row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 0.5rem;
  width: 100%;
  max-width: 22rem;
  font-size: 1rem;
}

.confirm-summary__label {
  color: var(--muted);
  text-align: right;
}

.confirm-summary__value {
  color: var(--text);
  font-weight: 500;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.confirm-summary__check {
  color: var(--accent);
  white-space: nowrap;
}

/* WEBSITE iPHONE WALK BATCH r2 AMENDMENT 1 (order ts 1790705340.086899), SCREEN F item 3: the
   Phone/Email rows are now <button> elements (index.html) so they are tappable — reset every
   native button chrome so a value reads exactly as the old plain <span> did, plus a pointer
   cursor and a focus ring for keyboard users. */
.confirm-summary__value--editable {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  cursor: pointer;
  font: inherit;
}

.confirm-summary__value--editable:hover,
.confirm-summary__value--editable:focus-visible {
  text-decoration: underline;
  outline: none;
}

/* The in-place edit input that replaces a tapped row's button — sized/positioned to sit in the
   exact same grid cell (same column as .confirm-summary__value), never a layout jump. */
.confirm-summary__edit-input {
  grid-column: 2;
  width: 100%;
  min-width: 0;
  border: 1px solid var(--accent);
  border-radius: 6px;
  padding: 0.1rem 0.4rem;
  font: inherit;
  font-size: 1rem;
  color: var(--text);
  background: #fff;
}

/* WEBSITE iPHONE WALK BATCH r2 (order ts 1790704698.939539), SCREEN B: "Part A · Question 2 of
   10" — the post-payment onboarding step label, same quiet/eyebrow treatment as
   .founding__eyebrow so it reads as this page's own progress convention rather than a new one. */
.onboarding-progress {
  margin: 0 0 0.5rem;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--gold);
  text-align: center;
}

.onboarding-progress[hidden] {
  display: none;
}

/* ONBOARDING PART A Q3-Q10 TYPED STEPS r1 (order ts 1790818668.737249): the one generic step
   screen reused for every Q3..Q10 step. Buttons reuse .confirm-summary__continue's exact pill
   treatment -- same CTA shape already proven on this page, never a second button style. */
.part-a-step[hidden] {
  display: none;
}

.part-a-step__buttons {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  align-items: center;
  width: 100%;
  max-width: 22rem;
  margin: 0.3rem 0;
}

.part-a-step__btn {
  width: 100%;
  padding: 0.7em 1.8em;
  border: none;
  border-radius: 999px;
  background: var(--navy);
  color: #fff;
  font-family: "DM Sans", -apple-system, BlinkMacSystemFont, sans-serif;
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
  min-height: 44px;
}

.part-a-step__status {
  margin: 0.3rem 0 0;
  font-size: 0.85rem;
  color: var(--muted);
  text-align: center;
}

.part-a-step__status[hidden] {
  display: none;
}

.field {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  background: #fff;
  border: 1px solid #e3e2dd;
  border-radius: 999px;
  padding: 0.4rem 0.4rem 0.4rem 0.9rem;
  box-shadow: 0 10px 30px rgba(12, 45, 107, 0.08);
  max-width: 26rem;
}

/* WCAG 2.2 AA r2: #graceInput itself sits flush/transparent inside this pill with its own
   outline suppressed below, so the visible focus indicator (2.4.7) lives on the pill container
   instead — a keyboard user tabbing into the name/email/mobile field sees this ring appear. */
.field:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(26, 79, 160, 0.45), 0 10px 30px rgba(12, 45, 107, 0.08);
}

/* AMENDMENT 5(c) (order ts 1790607342.014759, reply ts 1790628871.715269) — Arthur on a real
   iPhone: "A YELLOW highlight on the name box. Remove it (iOS autofill/focus styling). The focus
   state uses the page's own navy/gold tokens, and nothing yellow."

   THERE WAS NO RULE HERE AT ALL BEFORE THIS ONE, which is why the yellow appeared: with the
   autofill pseudo-class unstyled, iOS Safari and Chrome paint their own #FAFFBD-ish background on
   any field they consider autofilled, and it overrides the .field pill's white. `background-color`
   CANNOT undo it — WebKit and Blink both ignore background on an autofilled input — so the only
   override that works is an INSET box-shadow large enough to cover the box, which is what this is.
   -webkit-text-fill-color is needed for the same reason: the UA repaints the text colour too.

   The 5000px inset spread is the documented idiom, not a magic number: the shadow must be at least
   as large as the field can ever get, and a fixed px height would leak yellow at the edges the
   moment the pill wraps on a narrow phone. */
#graceInput:-webkit-autofill,
#graceInput:-webkit-autofill:hover,
#graceInput:-webkit-autofill:focus,
#graceInput:-webkit-autofill:active,
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
  -webkit-box-shadow: 0 0 0 1000px #ffffff inset;
  box-shadow: 0 0 0 1000px #ffffff inset;
  -webkit-text-fill-color: var(--text);
  caret-color: var(--accent);
  /* The UA also runs a one-off background transition on autofill; a long delay parks it on ours. */
  transition: background-color 5000s ease-in-out 0s;
}

/* iOS also paints a grey/yellow TAP HIGHLIGHT over the touched element for a moment. It is a
   separate mechanism from autofill and needs its own removal, or the field flashes on every tap
   into it — which is what a visitor reads as "the box went yellow when I touched it". The visible
   focus indicator is unaffected: it lives on .field:focus-within above (WCAG 2.2 AA 2.4.7). */
.field,
.field__input,
#graceInput,
.field__mic,
.photo-panel__cta {
  -webkit-tap-highlight-color: rgba(12, 45, 107, 0.12);  /* navy at 12%, never yellow */
}

.field__mic {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: none;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  border-radius: 50%;
}

/* AMENDMENT 11 (order ts 1790633371.625509, amendment ts 1790633403.744209): script.js hides this
   button with the `hidden` ATTRIBUTE — while Grace's video plays, and whenever there is no
   transcriber capability at all (the r10 "never a dead control" rule). `[hidden] { display: none }`
   lives in the USER-AGENT stylesheet, and any AUTHOR rule beats the UA sheet regardless of
   specificity or source order, so the `display: flex` above silently won and the button stayed on
   screen with hidden===true. Measured in a real browser by entrypoint_ios_fallback_r2.py:
   hidden===true and offsetParent!==null at the same instant, on all three profiles.
   This file already carries the same guard for .photo-panel__video, .field, .photo-panel__cta,
   .photo-panel__skip and .gift-redeem__form — .field__mic was the one that was missed. */
.field__mic[hidden] {
  display: none;
}
.field__mic:hover,
.field__mic:focus-visible {
  color: var(--accent);
}

/* WCAG 2.2 AA r2: a color-only change is not a visible focus indicator (2.4.7) — this button
   sits inside the white .field pill, so an accent ring (7.9:1 vs #fff, axe-core computed) is
   always visible regardless of the mic icon's own state color. */
.field__mic:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* DICTATION: NEVER DEAD r10 (order ts 1790278957.601769, ADD 6): IDLE = the rule above, no
   class. LISTENING = a gentle pulse. HEARING YOU = a larger pulse, both class-driven only by the
   recognizer's own onstart/onspeechstart/onsoundstart/onspeechend/onend events in script.js so
   the UI can never say "listening" once the mic has actually been released. */
.field__mic--listening,
.field__mic--hearing {
  color: var(--accent);
}

.field__mic--listening {
  animation: grace-mic-pulse-listening 1.6s ease-in-out infinite;
}

.field__mic--hearing {
  animation: grace-mic-pulse-hearing 0.9s ease-in-out infinite;
}

@keyframes grace-mic-pulse-listening {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.08); }
}

@keyframes grace-mic-pulse-hearing {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.22); }
}

/* Reduced-motion users get the color change above with no animation at all. */
@media (prefers-reduced-motion: reduce) {
  .field__mic--listening,
  .field__mic--hearing {
    animation: none;
  }
}

/* DICTATION: NEVER DEAD r10 (order ts 1790278957.601769, ADD 6): IDLE = the rule above, no
   class. LISTENING = a gentle pulse. HEARING YOU = a larger pulse, both class-driven only by the
   recognizer's own onstart/onspeechstart/onsoundstart/onspeechend/onend events in script.js so
   the UI can never say "listening" once the mic has actually been released. */
.field__mic--listening,
.field__mic--hearing {
  color: var(--accent);
}

.field__mic--listening {
  animation: grace-mic-pulse-listening 1.6s ease-in-out infinite;
}

.field__mic--hearing {
  animation: grace-mic-pulse-hearing 0.9s ease-in-out infinite;
}

@keyframes grace-mic-pulse-listening {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.08); }
}

@keyframes grace-mic-pulse-hearing {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.22); }
}

/* Reduced-motion users get the color change above with no animation at all. */
@media (prefers-reduced-motion: reduce) {
  .field__mic--listening,
  .field__mic--hearing {
    animation: none;
  }
}

/* DICTATION: NEVER DEAD r10 (order ts 1790278957.601769, ADD 6): IDLE = the rule above, no
   class. LISTENING = a gentle pulse. HEARING YOU = a larger pulse, both class-driven only by the
   recognizer's own onstart/onspeechstart/onsoundstart/onspeechend/onend events in script.js so
   the UI can never say "listening" once the mic has actually been released. */
.field__mic--listening,
.field__mic--hearing {
  color: var(--accent);
}

.field__mic--listening {
  animation: grace-mic-pulse-listening 1.6s ease-in-out infinite;
}

.field__mic--hearing {
  animation: grace-mic-pulse-hearing 0.9s ease-in-out infinite;
}

@keyframes grace-mic-pulse-listening {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.08); }
}

@keyframes grace-mic-pulse-hearing {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.22); }
}

/* Reduced-motion users get the color change above with no animation at all. */
@media (prefers-reduced-motion: reduce) {
  .field__mic--listening,
  .field__mic--hearing {
    animation: none;
  }
}

/* DICTATION: NEVER DEAD r10 (order ts 1790278957.601769, ADD 6): IDLE = the rule above, no
   class. LISTENING = a gentle pulse. HEARING YOU = a larger pulse, both class-driven only by the
   recognizer's own onstart/onspeechstart/onsoundstart/onspeechend/onend events in script.js so
   the UI can never say "listening" once the mic has actually been released. */
.field__mic--listening,
.field__mic--hearing {
  color: var(--accent);
}

.field__mic--listening {
  animation: grace-mic-pulse-listening 1.6s ease-in-out infinite;
}

.field__mic--hearing {
  animation: grace-mic-pulse-hearing 0.9s ease-in-out infinite;
}

@keyframes grace-mic-pulse-listening {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.08); }
}

@keyframes grace-mic-pulse-hearing {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.22); }
}

/* Reduced-motion users get the color change above with no animation at all. */
@media (prefers-reduced-motion: reduce) {
  .field__mic--listening,
  .field__mic--hearing {
    animation: none;
  }
}

.field__input {
  flex: 1;
  border: none;
  outline: none;
  font-family: inherit;
  font-size: 16px;
  color: var(--text);
  background: transparent;
  min-width: 0;
}

/* WEBSITE iPHONE WALK BATCH r2 AMENDMENT 2(a) (order ts 1790705674.246469): "the placeholder hint
   is too faint to read on iPhone: raise its contrast to the body-text grey" — #9aa0a6 measured
   too light for Burton on a real device; var(--muted) is the same grey the rest of this page's
   body copy already reads at (e.g. .confirm-summary__label). */
.field__input::placeholder {
  color: var(--muted);
}

.field__input:disabled {
  color: var(--muted);
}

.field__submit {
  flex: 0 0 auto;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  border: none;
  background: var(--navy);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.field__submit:hover,
.field__submit:focus-visible {
  background: var(--accent);
}

/* WCAG 2.2 AA r2: outline drawn outside the pill (offset), so it shows against the white
   .field background (7.9:1) rather than against this button's own navy/accent fill. */
.field__submit:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.field__submit:disabled,
.field__mic:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.field__error {
  max-width: 26rem;
  color: #b3261e;
  font-size: 0.85rem;
  margin: 0.6rem 0 0;
}

.consent {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 0.55rem;
  max-width: 26rem;
  margin: 0.9rem 0 0;
  text-align: left;
}

/* WEBSITE iPHONE WALK BATCH r2 AMENDMENT 2(b) (order ts 1790705674.246469): "the checkbox row
   shakes once + turns the border navy + shows 'Tap to agree before we continue' under it, and the
   page scrolls it into view." nudgeConsent() (script.js) adds/removes these two classes. */
@keyframes consent-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-6px); }
  40% { transform: translateX(6px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(4px); }
}

.consent--attention {
  padding: 0.55rem 0.65rem;
  margin: 0.7rem -0.65rem 0;
  border: 1.5px solid var(--navy);
  border-radius: 10px;
}

.consent--shake {
  animation: consent-shake 0.4s ease-in-out;
}

.consent__hint {
  flex: 1 0 100%;
  margin: 0.25rem 0 0;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--navy);
}

.consent__hint[hidden] {
  display: none;
}

.consent__checkbox {
  flex: 0 0 auto;
  width: 1.05rem;
  height: 1.05rem;
  margin-top: 0.15rem;
  accent-color: var(--navy);
}

.consent__label {
  font-size: 0.72rem;
  line-height: 1.4;
  color: var(--muted);
}

.consent__label a {
  color: var(--muted);
  text-decoration: underline;
}

.consent__label a:hover,
.consent__label a:focus-visible {
  color: var(--accent);
}

/* WCAG 2.2 AA r2: same 2.4.7 gap as the field controls above — Terms/Privacy/SMS links had no
   visible focus indicator at all before this. */
.consent__label a:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.founding {
  max-width: 26rem;
  margin: 1.25rem 0 0;
}

.founding__eyebrow {
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold);
  font-weight: 600;
  margin: 0 0 0.4rem;
}

.founding__copy {
  font-size: 0.95rem;
  line-height: 1.4;
  color: var(--muted);
  margin: 0 0 0.35rem;
  /* AMENDMENT ts=1790295726.360509, item 2: no single word left orphaned on its own line
     (e.g. "up." alone) at narrow widths — balances line lengths without touching the text
     itself, so test_waitlist_copy.mjs's exact-copy assertions stay pinned to the real string. */
  text-wrap: balance;
}

/* AMENDMENT ts=1790296079.079349 ("I want to keep it more clean"): the price line is bold,
   the only visual weight change in the founding block — three plain lines otherwise. */
.founding__copy--price {
  font-weight: 700;
  color: var(--gold);
}

.link-slot {
  align-self: center;
  font-size: 0.68rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
}

.link-slot__link {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid currentColor;
}

/* WAITLIST: SIGN-UP DEAD-ENDS AFTER THE CHECKBOX r2 (order item 3): the sign-up front door's own
   "Lock in $N a month now" step is a single button, not the plain uppercase link the OTHER
   showCheckoutLink caller (engine-conversation checkout, untouched) still uses. */
.link-slot__link--button {
  display: inline-block;
  text-transform: none;
  letter-spacing: normal;
  font-size: 1rem;
  font-weight: 600;
  padding: 0.75em 1.6em;
  border-radius: 999px;
  border-bottom: none;
  background: var(--navy);
  color: #fff;
  box-shadow: 0 6px 24px rgba(12, 45, 107, 0.25);
}

.link-slot__quiet {
  margin: 0.5rem 0 0;
  font-size: 0.72rem;
  color: var(--muted);
  text-transform: none;
  letter-spacing: normal;
}

.field[hidden] {
  display: none;
}

@media (max-width: 768px) {
  .stage {
    flex-direction: column;
  }

  .photo-panel {
    flex: 0 0 auto;
    max-width: 100%;
    width: 100%;
  }

  .photo-panel__img {
    height: 58vh;
    height: 58dvh;
  }

  .content-panel {
    flex: 1;
    padding: 1.75rem 6vw 1.5rem;
    text-align: center;
    align-items: center;
    min-height: 0;
  }

  .tagline {
    order: 3;
    align-self: center;
    margin-top: 1.5rem;
    justify-content: center;
  }

  .convo {
    order: 1;
    margin: auto 0 1.25rem;
    max-width: 100%;
  }

  .field {
    order: 2;
    margin: 0 auto;
  }

  .consent {
    text-align: left;
    margin-left: auto;
    margin-right: auto;
    margin-top: 0.5rem;
  }

  .founding {
    text-align: center;
    margin-left: auto;
    margin-right: auto;
    margin-top: 0.65rem;
  }

  .founding__copy {
    margin: 0 0 0.2rem;
  }

  .link-slot {
    order: 5;
  }
}

/* WEBSITE E2E UNION r1 (order ts=1790284891.908009): the required consent checkbox
   (Terms/Privacy/SMS-privacy links) and founding-member copy, combined below the field on a
   narrow phone viewport, pushed total page height past the 58vh photo + prior mobile design's
   scroll budget — tightened here rather than at 768px, since 375px+ already clears it. */
@media (max-width: 374px) {
  .convo__subtitle {
    display: none;
  }

  .founding__eyebrow {
    margin: 0 0 0.2rem;
  }
}

/* WEBSITE E2E UNION r1 (order ts=1790284891.908009): the required consent checkbox
   (Terms/Privacy/SMS-privacy links) and founding-member copy, combined below the field on a
   narrow phone viewport, pushed total page height past the 58vh photo + prior mobile design's
   scroll budget — tightened here rather than at 768px, since 375px+ already clears it. */
@media (max-width: 374px) {
  .convo__subtitle {
    display: none;
  }

  .founding__eyebrow {
    margin: 0 0 0.2rem;
  }
}

/* CHECKOUT COLLECTS PHONE + GIFT REDEMPTION r1/r2 (order ts 1790376676.284759, item 2): a full-
   screen overlay shown only when script.js's initGiftRedeem() finds a ?gift= param — never
   competing with the main .stage layout, never shown by default. */
.gift-redeem {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
}

/* DICTATION BUTTON PROVEN ON IPHONE AND DESKTOP r1: author-origin CSS of equal specificity
   always beats the UA [hidden] default regardless of source order, so the `hidden` attribute
   alone (index.html) never actually hid this overlay -- every sibling conditional element in
   this file (.photo-panel__video[hidden], .field[hidden], even this overlay's own
   .gift-redeem__form[hidden]) already carries this exact guard. */
.gift-redeem[hidden] {
  display: none;
}

.gift-redeem__card {
  max-width: 22rem;
  width: 100%;
}

.gift-redeem__title {
  font-family: "Instrument Serif", Georgia, serif;
  font-size: 1.8rem;
  color: var(--navy);
  margin: 0 0 0.5rem;
}

.gift-redeem__line {
  color: var(--muted);
  font-size: 0.95rem;
  margin: 0 0 1.2rem;
}

.gift-redeem__form {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.gift-redeem__form[hidden] {
  display: none;
}

.gift-redeem__input {
  border: 1px solid #d8dbe0;
  border-radius: 0.6rem;
  padding: 0.7rem 0.9rem;
  font-family: inherit;
  font-size: 16px;
  color: var(--text);
  background: #fff;
}

.gift-redeem__input::placeholder {
  color: #9aa0a6;
}

.gift-redeem__submit {
  margin-top: 0.4rem;
  border: none;
  border-radius: 0.6rem;
  padding: 0.8rem;
  background: var(--navy);
  color: #fff;
  font-family: inherit;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
}

.gift-redeem__submit:hover,
.gift-redeem__submit:focus-visible {
  background: var(--accent);
  outline: none;
}

/* WCAG 2.2 AA r2: same 2.4.7 fix as the front-door submit button. */
.gift-redeem__submit:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.gift-redeem__submit:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.gift-redeem__error {
  color: #b3261e;
  font-size: 0.85rem;
  margin: 0.8rem 0 0;
}

.gift-redeem__success {
  color: var(--navy);
  font-size: 0.95rem;
  margin: 0.8rem 0 0;
}
