/* Your Wedding Brief: one stylesheet for every page.
   The visual language here is NOT new. It is the design the DW team reacted to
   on 6 Jul ("the team is obsessed"), lifted out of the old per-page <style>
   blocks and made shared. Do not re-theme it without evidence.
   Fonts are self-hosted so the page can run a strict CSP. */

/* ---------- fonts (self-hosted, no third-party requests) ---------- */
@font-face { font-family:'Lora'; font-style:normal; font-weight:400; font-display:swap; src:url('../assets/fonts/lora-400.woff2') format('woff2'); }
@font-face { font-family:'Lora'; font-style:normal; font-weight:500; font-display:swap; src:url('../assets/fonts/lora-500.woff2') format('woff2'); }
@font-face { font-family:'Lora'; font-style:italic; font-weight:400; font-display:swap; src:url('../assets/fonts/lora-400i.woff2') format('woff2'); }
@font-face { font-family:'Lora'; font-style:italic; font-weight:500; font-display:swap; src:url('../assets/fonts/lora-500i.woff2') format('woff2'); }
@font-face { font-family:'Lato'; font-style:normal; font-weight:400; font-display:swap; src:url('../assets/fonts/lato-400.woff2') format('woff2'); }
@font-face { font-family:'Lato'; font-style:normal; font-weight:700; font-display:swap; src:url('../assets/fonts/lato-700.woff2') format('woff2'); }

/* ---------- tokens ---------- */
:root{
  --bg:#fbf9f6; --bg-deep:#f4efe9; --card:#ffffff;
  --ink:#21302f; --ink-soft:#5f6d6b;
  /* --ink-faint was #8c9897 in the original hub. At 12-13px on this cream
     background that measured 2.83:1, well under the WCAG AA minimum of 4.5:1,
     and it carries real content ("About 5 minutes", "Chapter 1 of 5", field
     hints). Darkened to the least amount that clears AA so the de-emphasised
     tier survives. This is the ONLY colour changed from the original palette. */
  --ink-faint:#677473;
  --line:rgba(33,48,47,0.10); --line-soft:rgba(33,48,47,0.06);
  --teal:#6ebbbf; --teal-ink:#1d7479; --teal-wash:#e9f4f4; --teal-wash-2:#dcefef;
  --rose:#e37062; --rose-ink:#af3f37;
  --amber-wash:#fdf3e4; --amber-ink:#8a5a17;
  /* Ink for text sitting on a solid teal fill (primary buttons). Was
     hardcoded twice, 1-2 RGB units apart, between this file and the ported
     soundtrack.css (#12312f vs #10302f). One token now so it cannot drift. */
  --btn-primary-ink:#12312f;
  /* Second accent, scoped to the soundtrack chapter's Live/Speakers song
     tags. Teal already means "brand/primary" everywhere else, so tagging
     both Live and Speakers songs teal would make the two indistinguishable
     at a glance. Named and moved here from a bare hex found nowhere else in
     the palette; the colour itself is unchanged from the original ported
     design. */
  --speakers-wash:#ecebf6; --speakers-ink:#4a4585;
  --shadow:0 1px 2px rgba(33,48,47,.04), 0 14px 40px -22px rgba(33,48,47,.22);
  --shadow-lift:0 2px 4px rgba(33,48,47,.05), 0 20px 46px -20px rgba(29,116,121,.35);
  --radius:18px; --radius-sm:10px;
  --serif:'Lora',Georgia,'Times New Roman',serif;
  --sans:'Lato',-apple-system,BlinkMacSystemFont,system-ui,sans-serif;
  --measure:62ch;
}

/* ---------- base ---------- */
*{box-sizing:border-box;}
/* Any explicit `display` beats the hidden attribute, and this page carries a lot
   of display:flex components. Without this, an element set hidden in JS stays on
   screen while every scripted check reports it as hidden. */
[hidden]{display:none !important;}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;}
body{margin:0;font-family:var(--sans);color:var(--ink);background:var(--bg);line-height:1.5;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;position:relative;min-height:100vh;}
/* atmosphere: warm gradient wash + fine grain. A big part of why this does not
   read as "a form". Keep both layers. */
body::before{content:"";position:fixed;inset:0;z-index:-2;
  background:radial-gradient(1100px 520px at 78% -8%, rgba(110,187,191,.20), transparent 60%),
             radial-gradient(900px 500px at -6% 4%, rgba(227,112,98,.07), transparent 55%),
             linear-gradient(180deg,#fcfaf8 0%, var(--bg) 38%, var(--bg-deep) 100%);}
body::after{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;opacity:.035;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");}
a{color:var(--teal-ink);text-underline-offset:3px;}
.wrap{max-width:920px;margin:0 auto;padding:0 28px;}
.wrap--narrow{max-width:720px;}
.visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;}
/* outline, not box-shadow: box-shadow is a plain visual property that .card,
   .btn--primary and others also set for their own resting shadow, and at
   equal specificity the LAST box-shadow declaration in the cascade wins -
   silently erasing this ring (confirmed live: a focused .btn--primary showed
   a byte-identical box-shadow to its unfocused state). outline is a separate
   compositing layer nothing else here touches, so no component's own
   box-shadow - now or added later - can cancel it again. */
:focus-visible{outline:3px solid rgba(110,187,191,.45);outline-offset:2px;border-radius:6px;}
/* Skip link: off-screen until focused, then a real visible target. A permanently
   hidden skip link is worse than none, it exists only for keyboard users. */
.skip-link{position:absolute;left:12px;top:-60px;z-index:80;background:var(--card);color:var(--teal-ink);
  font-size:13px;font-weight:700;padding:10px 16px;border-radius:999px;border:1px solid var(--line);
  box-shadow:var(--shadow);text-decoration:none;transition:top .18s ease;}
.skip-link:focus{top:12px;}
main:focus{outline:none;}

/* ---------- header / footer ---------- */
.site-header{position:sticky;top:0;z-index:40;backdrop-filter:saturate(140%) blur(10px);
  background:rgba(251,249,246,.78);border-bottom:1px solid var(--line-soft);}
.site-header .wrap{display:flex;align-items:center;justify-content:space-between;gap:18px;padding-top:16px;padding-bottom:16px;}
.logo{display:flex;align-items:center;color:var(--ink);text-decoration:none;}
.logo svg{height:24px;width:auto;display:block;}
.logo svg path{fill:currentColor;}
main{padding-bottom:70px;}
.foot{padding:26px 0 60px;text-align:center;color:var(--ink-soft);font-size:12.5px;}
.foot .sep{margin:0 9px;opacity:.5;}
/* Storage blocked (private mode / quota). Must read as a warning, not as the
   usual quiet reassurance, because the couple's answers are NOT being kept. */
.foot .is-warning{color:var(--rose-ink);font-weight:700;}

/* ---------- page head ---------- */
.page-head{padding:54px 0 26px;}
.eyebrow{font-size:11.5px;font-weight:700;letter-spacing:.22em;text-transform:uppercase;color:var(--teal-ink);margin:0 0 14px;}
h1{font-family:var(--serif);font-weight:500;font-size:clamp(34px,6vw,50px);line-height:1.04;margin:0;letter-spacing:-.01em;color:var(--ink);}
h1 em{font-style:italic;color:var(--teal-ink);font-weight:500;}
.lede{font-family:var(--serif);font-style:italic;font-size:clamp(16px,2.4vw,19px);color:var(--ink-soft);margin:16px 0 0;max-width:52ch;}
.storage-note{margin:14px 0 0;font-size:13px;color:var(--ink-faint);max-width:52ch;}

/* ---------- identity card ---------- */
.profile{background:linear-gradient(180deg,#ffffff,#fcfbf9);border:1px solid var(--line);
  border-radius:var(--radius);box-shadow:var(--shadow);padding:22px 24px;margin:10px 0 30px;}
.profile h2{font-family:var(--serif);font-weight:500;font-size:19px;margin:0 0 3px;}
.profile > p{margin:0;font-size:13.5px;color:var(--ink-soft);}
.fields{display:flex;gap:14px;flex-wrap:wrap;margin-top:16px;}
.fields > .field{flex:1 1 200px;}

/* ---------- progress ---------- */
.progress{display:flex;align-items:center;gap:12px;margin:0 0 22px;}
.progress__track{display:flex;gap:4px;flex:1 1 auto;}
.progress__seg{height:5px;flex:1 1 0;border-radius:999px;background:var(--bg-deep);transition:background .3s ease;}
.progress__seg.is-done{background:var(--teal);}
.progress__seg.is-part{background:var(--teal-wash-2);}
.progress__label{font-size:12px;color:var(--ink-faint);white-space:nowrap;}

/* ---------- chapter cards ---------- */
.cards{display:grid;grid-template-columns:1fr 1fr;gap:22px;}
/* An odd chapter count (5, since soundtrack was promoted from a
   de-emphasised "Bonus" card to Chapter 5) leaves the last card alone with
   an empty cell beside it. Give it the full row instead. Matches nothing
   when the count is even, and is a no-op on the 1-column mobile grid. */
.cards > .card:last-child:nth-child(odd){grid-column:1 / -1;}
.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);
  padding:22px 22px 20px;display:flex;flex-direction:column;gap:10px;text-decoration:none;color:var(--ink);
  transition:transform .15s ease, box-shadow .2s ease, border-color .2s ease;}
a.card:hover{transform:translateY(-2px);border-color:var(--teal);box-shadow:var(--shadow-lift);}
.card .num{font-size:11px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:var(--ink-faint);}
.card h3{font-family:var(--serif);font-weight:500;font-size:21px;margin:0;}
.card h3 em{font-style:italic;color:var(--teal-ink);}
.card p{margin:0;font-size:13.5px;color:var(--ink-soft);flex:1 1 auto;}
.card-foot{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:6px;}
.mins{font-size:12px;color:var(--ink-faint);}

/* ---------- status pills ---------- */
.status{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-weight:700;letter-spacing:.04em;
  border-radius:999px;padding:5px 12px;}
.status.todo{background:var(--bg-deep);color:var(--ink-soft);}
.status.progress-pill{background:var(--amber-wash);color:var(--amber-ink);}
.status.done{background:var(--teal-wash);color:var(--teal-ink);}
.status svg{width:12px;height:12px;}

/* ---------- chapter bar ---------- */
.chapter-bar{display:flex;align-items:center;gap:14px;padding:18px 0 6px;}
/* Padding balanced by an equal negative margin: grows the focusable/clickable
   box to a 44px touch target (was ~19.5px, text-height only) without moving
   the link's visual position or changing the chapter bar's layout - the flex
   container only ever sees the margin box, which nets back to the original
   size. */
.chapter-bar .back{display:inline-flex;align-items:center;gap:6px;font-size:13px;font-weight:700;text-decoration:none;color:var(--teal-ink);
  padding:12px 10px;margin:-12px -10px;}
.chapter-bar .back svg{width:14px;height:14px;}
/* opacity:0 alone still leaves this in the accessibility tree, so a screen
   reader would announce "Saved" on a chapter page that saves nothing yet.
   visibility:hidden removes it properly; .is-on restores both. */
.saved-tick{margin-left:auto;display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--ink-faint);
  opacity:0;visibility:hidden;transition:opacity .25s ease, visibility .25s ease;}
.saved-tick.is-on{opacity:1;visibility:visible;}
.saved-tick.is-warning{color:var(--rose-ink);font-weight:700;}
/* The couple's names, carried through from the home card so no chapter has to
   ask who they are a second time. */
.identity-summary{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;margin:12px 0 0;font-size:13px;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;color:var(--teal-ink);}
.identity-summary:empty{display:none;}
/* Soundtrack: shown in place of the couple fields once the home card has
   supplied the names, so nobody is asked who they are twice. */
.couple-from-brief{margin:0;font-size:13.5px;color:var(--ink-soft);}

/* ---------- form sections + fields ---------- */
.section{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);
  padding:26px 26px 24px;margin:0 0 22px;}
.section > h2{font-family:var(--serif);font-weight:500;font-size:22px;margin:0 0 4px;}
.section > h2 em{font-style:italic;color:var(--teal-ink);}
.section__blurb{margin:0 0 18px;font-size:13.5px;color:var(--ink-soft);max-width:var(--measure);}
.field{display:flex;flex-direction:column;gap:6px;margin:0 0 16px;max-width:var(--measure);}
.field__label{font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--teal-ink);}
.field__optional{font-weight:400;letter-spacing:0;text-transform:none;color:var(--ink-faint);font-size:11.5px;margin-left:6px;}
.field__hint{font-size:12.5px;color:var(--ink-faint);margin:0;}
.field__error{font-size:12.5px;color:var(--rose-ink);margin:0;}
/* Form-level note under the chapter actions, shown when Mark as done finds
   something malformed. Matches the field error tone rather than shouting. */
.form-note{font-size:13.5px;color:var(--rose-ink);font-weight:700;margin:0 0 10px;}
input[type=text],input[type=email],input[type=tel],input[type=date],select,textarea{
  font-family:var(--sans);font-size:14px;color:var(--ink);background:#fff;border:1px solid var(--line);
  border-radius:var(--radius-sm);padding:10px 13px;outline:none;width:100%;
  transition:border-color .15s ease, box-shadow .15s ease;}
textarea{min-height:96px;resize:vertical;line-height:1.55;}
select{appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%235f6d6b' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 13px center;padding-right:34px;}
input:focus,select:focus,textarea:focus{border-color:var(--teal);box-shadow:0 0 0 3px rgba(110,187,191,.22);}
input::placeholder,textarea::placeholder{color:var(--ink-faint);}
.field.has-error input,.field.has-error textarea,.field.has-error select{border-color:var(--rose);}

/* Conditional reveals. The field IS in the DOM, carrying the `hidden` attribute
   until its controlling answer matches; it is not absent. Anything reading the
   form must therefore filter by visibility (see isVisible in brief-schema.mjs),
   which both the client and the server do. */
.conditional{animation:reveal .28s ease both;}
.field[hidden]{display:none;}
@keyframes reveal{from{opacity:0;transform:translateY(-4px);}to{opacity:1;transform:none;}}

/* ---------- repeatable rows ---------- */
.rows{display:flex;flex-direction:column;gap:14px;}
.row{border:1px solid var(--line);border-radius:var(--radius-sm);padding:16px;background:#fdfcfb;position:relative;}
.row__head{display:flex;align-items:center;justify-content:space-between;margin:0 0 12px;}
.row__title{font-size:11px;font-weight:700;letter-spacing:.18em;text-transform:uppercase;color:var(--ink-faint);}
.row__grid{display:flex;flex-wrap:wrap;gap:12px;}
.row__grid > .field{flex:1 1 200px;margin:0;}

/* ---------- buttons ---------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:var(--sans);font-size:14px;
  font-weight:700;letter-spacing:.01em;border-radius:999px;padding:12px 22px;border:1px solid transparent;
  cursor:pointer;text-decoration:none;transition:transform .12s ease, box-shadow .2s ease, background .2s ease;}
.btn:active{transform:translateY(1px);}
/* teal fill always takes DARK ink. White on this teal fails contrast. */
.btn--primary{background:var(--teal);color:var(--btn-primary-ink);box-shadow:0 10px 24px -14px rgba(29,116,121,.9);}
.btn--primary:hover{background:#63b1b5;}
.btn--ghost{background:transparent;color:var(--teal-ink);border-color:var(--line);}
.btn--ghost:hover{border-color:var(--teal);background:rgba(110,187,191,.08);}
/* min-height, not just padding: a <button data-remove-row> and an
   <a class="btn--quiet"> (Edit, on the review screen) pick up different
   default line-heights from the browser's own form-control reset, so
   identical padding rendered them at 39px and 43px respectively. min-height
   pins both to the same 44px touch target regardless of that gap. */
.btn--quiet{background:transparent;color:var(--ink-soft);padding:10px 14px;min-height:44px;}
.btn--danger{background:transparent;color:var(--rose-ink);border-color:rgba(175,63,55,.28);}
.btn--danger:hover{background:rgba(227,112,98,.08);}
.btn[disabled]{opacity:.45;pointer-events:none;}
.actions{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:26px;}
.actions--split{justify-content:space-between;}

/* ---------- review ---------- */
.review-group{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);
  box-shadow:var(--shadow);padding:22px 24px;margin:0 0 18px;}
.review-group__head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin:0 0 14px;}
.review-group__head h2{font-family:var(--serif);font-weight:500;font-size:20px;margin:0;}
.review-list{display:grid;grid-template-columns:minmax(140px,34%) 1fr;gap:10px 20px;margin:0;font-size:14px;}
.review-list dt{color:var(--ink-faint);font-size:12.5px;overflow-wrap:anywhere;}
.review-list dd{margin:0;color:var(--ink);white-space:pre-wrap;overflow-wrap:anywhere;}
.review-list dd.is-empty{color:var(--ink-faint);font-style:italic;}
.review-empty{font-size:13.5px;color:var(--ink-soft);margin:0;}

/* ---------- review: still to come ---------- */
/* The chase list. This is the part of the product the metric rests on, so it
   leads the page and is styled to be read first, not skimmed past. */
.still-to-come{background:linear-gradient(180deg,#fffdf9,#fdf7ee);border:1px solid rgba(175,63,55,.18);
  border-radius:var(--radius);box-shadow:var(--shadow);padding:24px 26px;margin:0 0 24px;}
.still-to-come h2{font-family:var(--serif);font-weight:500;font-size:21px;margin:0 0 10px;}
.still-to-come p{margin:0 0 8px;font-size:14px;color:var(--ink-soft);}
.still-to-come ul{margin:0 0 14px;padding-left:20px;font-size:14px;color:var(--ink);}
.still-to-come li{margin:2px 0;}
.still-to-come .muted{color:var(--ink-faint);font-size:13px;}
.still-to-come.is-complete{background:linear-gradient(180deg,#fbfefe,#f2fafa);border-color:rgba(29,116,121,.22);}

.review-section{margin:0 0 18px;}
.review-section:last-child{margin-bottom:0;}
.review-section h3{font-size:11px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  color:var(--teal-ink);margin:0 0 8px;}
.review-group__head .status{margin-left:auto;}
.review-group__head .btn--quiet{margin-left:10px;}

.send-card{text-align:left;}
/* Sticky send card (tester issue #19, and the UI spec's "sticky summary bar"
   that was never built). The review page is long by design: it deliberately
   lists every unanswered question so the couple sees exactly what the planner
   will. That makes the one action they came for scroll out of reach, which is
   what prompted the "add a back to top button" request. A back-to-top button
   is a workaround for the CTA being far away; keeping the CTA present removes
   the need for it.
   Only sticks where there is room to spare. Measured: the card is 227px, so on
   a 594px window it would occupy 38% of the screen the couple is trying to
   READ, and 57% on a landscape phone. 700px keeps it at roughly a quarter,
   which real phones (812) and desktops clear comfortably while short and
   landscape viewports correctly fall back to a normal in-flow card. */
@media (min-height:700px){
  .send-card{position:sticky;bottom:0;z-index:2;
    background:var(--card);border-top:1px solid var(--line);
    box-shadow:0 -8px 20px -18px rgba(22,33,31,.5);}
}
@media (prefers-reduced-motion:reduce){.send-card{position:static;}}
.submit-status{margin:12px 0 0;font-size:13.5px;color:var(--ink-soft);min-height:1.2em;}
.submit-status.is-warning{color:var(--rose-ink);font-weight:700;}

.danger-zone{margin-top:34px;padding-top:22px;border-top:1px solid var(--line);}

/* ---------- toast ---------- */
.toast{position:fixed;left:50%;bottom:24px;transform:translateX(-50%) translateY(12px);z-index:60;
  background:var(--ink);color:#f7fbfa;font-size:13.5px;padding:12px 18px;border-radius:999px;
  box-shadow:0 18px 40px -18px rgba(0,0,0,.6);opacity:0;pointer-events:none;transition:opacity .25s ease, transform .25s ease;}
.toast.is-on{opacity:1;transform:translateX(-50%) translateY(0);}

/* ---------- responsive ---------- */
@media (max-width:680px){
  .wrap{padding:0 18px;}
  .page-head{padding:34px 0 20px;}
  .cards{grid-template-columns:1fr;}
  .section{padding:20px 18px;}
  .review-list{grid-template-columns:1fr;gap:2px 0;}
  .review-list dd{margin:0 0 10px;}
  .btn{padding:13px 22px;}
  /* Touch targets: 10px padding gives a 38px control, short of the 44px in
     docs/UI-SPEC.md 6. Mobile only, so the desktop design is unchanged. */
  /* min-height rather than padding: date inputs and selects box differently, so
     padding alone lands some controls a pixel or two short. */
  input[type=text],input[type=email],input[type=tel],input[type=date],select{padding:12px 13px;min-height:44px;}
  .skip-link{padding:13px 16px;}
}

/* Print. Applies to every chapter, not just the soundtrack: "Download PDF" falls
   back to window.print() if pdf-lib cannot load, and a couple printing their
   brief should get the answers, not the navigation chrome. */
@media print{
  .site-header,.foot,.skip-link,.chapter-bar,.actions,.progress,.toast{display:none !important;}
  body::before,body::after{display:none;}
  body{background:#fff;}
  main{padding-bottom:0;}
  .section,.review-group,.profile{box-shadow:none;border:1px solid #ddd;break-inside:avoid;}
  a{color:inherit;text-decoration:none;}
}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;transition-duration:.001ms !important;scroll-behavior:auto !important;}
  a.card:hover{transform:none;}
}

/* Overflow backstop (tester issues #5, #7). Nothing in a couple's own answers
   is guaranteed to contain a space: pasted playlist URLs, long hyphenated
   names and run-on words all arrived unbroken and pushed the page sideways.
   Reproduced with a 130-character URL, which made the review page scroll
   horizontally. Applied to the containers that render answers, not globally,
   so deliberate scroll regions are untouched. */
.review-group,.card,.section,.profile{min-width:0;}
.review-group p,.card p,.section__blurb{overflow-wrap:anywhere;}
