/* ==========================================================================
   B4MONDAY Client Portal
   Cinematic Arc — warm, airy, cinematic, natural, emotional.
   ========================================================================== */

:root {
  --cream: #f5eae0;
  --ivory: #fbf7f3;
  --espresso: #5c4033;
  --bronze: #b07b52;
  --charcoal: #3a342f;
  --muted: rgba(58, 52, 47, 0.62);
  --hairline: rgba(92, 64, 51, 0.16);
  --hairline-soft: rgba(92, 64, 51, 0.08);
  --sage: #7d8a70;
  --clay: #b5573f;

  --display: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --ui: "Jost", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
  --body: "Lora", Georgia, serif;

  --shell-max: 620px;
  --nav-h: 68px;
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--cream);
  color: var(--charcoal);
  font-family: var(--body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overscroll-behavior-y: none;
}

body {
  min-height: 100svh;
  padding-bottom: calc(var(--nav-h) + env(safe-area-inset-bottom));
}

button, input, select, textarea { font: inherit; color: inherit; }
a { color: var(--bronze); text-decoration: none; }
a:hover { text-decoration: underline; }

/* --- Film sprocket motif -------------------------------------------------- */
.sprocket {
  height: 7px;
  background-image: repeating-linear-gradient(
    90deg,
    var(--espresso) 0 11px,
    transparent 11px 18px
  );
  opacity: 0.85;
}

/* --- Shell ---------------------------------------------------------------- */
.shell {
  max-width: var(--shell-max);
  margin: 0 auto;
  padding: 0 20px;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  background: rgba(245, 234, 224, 0.94);
  backdrop-filter: saturate(140%) blur(12px);
  border-bottom: 1px solid var(--hairline-soft);
  padding-top: env(safe-area-inset-top);
}

.topbar__inner {
  max-width: var(--shell-max);
  margin: 0 auto;
  padding: 14px 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.wordmark {
  font-family: var(--ui);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--espresso);
}

.wordmark small {
  display: block;
  font-size: 9px;
  letter-spacing: 0.18em;
  color: var(--bronze);
  margin-top: 3px;
}

/* --- Typography ----------------------------------------------------------- */
h1, h2, h3 { font-family: var(--display); font-weight: 400; color: var(--espresso); margin: 0; }
h1 { font-size: 33px; line-height: 1.16; letter-spacing: -0.01em; }
h2 { font-size: 24px; line-height: 1.25; }
h3 { font-size: 18px; line-height: 1.3; }

.eyebrow {
  font-family: var(--ui);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--bronze);
  margin-bottom: 10px;
}

p { line-height: 1.68; margin: 0 0 14px; }
.lede { font-size: 17px; color: var(--charcoal); }
.muted { color: var(--muted); }
.small { font-size: 13.5px; }

/* --- Cards ---------------------------------------------------------------- */
.card {
  background: var(--ivory);
  border: 1px solid var(--hairline);
  padding: 22px;
  margin-bottom: 14px;
}

.card--flush { padding: 0; overflow: hidden; }
.card__head { padding: 20px 22px 0; }
.card__body { padding: 22px; }

.row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 22px;
  border-top: 1px solid var(--hairline-soft);
}
.row:first-child { border-top: 0; }
.row__main { min-width: 0; flex: 1; }
/* display:block matters — the forms list builds these as <span> inside a
   <button>, so without it the title and the due date run together on one line. */
.row__title { display: block; font-family: var(--ui); font-size: 15px; font-weight: 500; color: var(--espresso); }
.row__meta { display: block; font-family: var(--ui); font-size: 12.5px; color: var(--muted); margin-top: 4px; }
.row__side { text-align: right; flex-shrink: 0; }

/* --- Hero ----------------------------------------------------------------- */
.hero { padding: 30px 0 8px; }
.hero__count {
  font-family: var(--display);
  font-size: 62px;
  line-height: 1;
  color: var(--espresso);
  letter-spacing: -0.02em;
}
.hero__count span {
  font-family: var(--ui);
  font-size: 12px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--bronze);
  display: block;
  margin-top: 10px;
}

/* --- Buttons -------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--ui);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 13px 22px;
  border: 1px solid var(--espresso);
  background: var(--espresso);
  color: var(--cream);
  cursor: pointer;
  transition: opacity 0.18s var(--ease), transform 0.18s var(--ease);
  text-decoration: none;
  width: 100%;
}
.btn:hover { opacity: 0.88; text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

.btn--ghost { background: transparent; color: var(--espresso); }
.btn--quiet {
  background: transparent;
  border-color: var(--hairline);
  color: var(--muted);
  letter-spacing: 0.08em;
  padding: 10px 16px;
}
.btn--inline { width: auto; }
.btn--sm { padding: 9px 14px; font-size: 11px; }

/* Two or three inline buttons sitting together under a card's copy — a primary
   action beside a quieter one. Wraps rather than squeezing on narrow phones. */
.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 16px;
}

/* --- Pills ---------------------------------------------------------------- */
.pill {
  display: inline-block;
  font-family: var(--ui);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 5px 10px;
  border: 1px solid var(--hairline);
  color: var(--muted);
  white-space: nowrap;
}
.pill--good { color: var(--sage); border-color: rgba(125, 138, 112, 0.4); }
.pill--warn { color: var(--bronze); border-color: rgba(176, 123, 82, 0.45); }
.pill--alert { color: var(--clay); border-color: rgba(181, 87, 63, 0.4); }

/* --- Stats ---------------------------------------------------------------- */
.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px; background: var(--hairline); border: 1px solid var(--hairline); margin-bottom: 14px; }
.stat { background: var(--ivory); padding: 18px 12px 16px; text-align: center; }
.stat__value { font-family: var(--display); font-size: 28px; color: var(--espresso); line-height: 1.05; }
.stat__label { font-family: var(--ui); font-size: 9.5px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted); margin-top: 9px; }
/* A hairline of bronze between number and label — enough to separate the two
   without adding another border to a screen that already has plenty. */
.stat__label::before {
  content: "";
  display: block;
  width: 16px;
  height: 1px;
  background: var(--bronze);
  opacity: 0.55;
  margin: 0 auto 8px;
}

/* --- Forms ---------------------------------------------------------------- */
.field { margin-bottom: 20px; }
.field label {
  display: block;
  font-family: var(--ui);
  font-size: 13px;
  font-weight: 500;
  color: var(--espresso);
  margin-bottom: 7px;
}
.field .help { font-family: var(--ui); font-size: 12px; color: var(--muted); margin: -3px 0 8px; line-height: 1.5; }
.field .req { color: var(--clay); }

input[type="text"], input[type="email"], input[type="tel"], input[type="password"],
input[type="date"], input[type="time"], input[type="number"], select, textarea {
  width: 100%;
  padding: 12px 13px;
  border: 1px solid var(--hairline);
  background: #fff;
  font-family: var(--body);
  font-size: 16px; /* 16px avoids iOS zoom-on-focus */
  color: var(--charcoal);
  border-radius: 0;
  -webkit-appearance: none;
  appearance: none;
}
textarea { min-height: 110px; resize: vertical; line-height: 1.6; }
select { background-image: linear-gradient(45deg, transparent 50%, var(--bronze) 50%), linear-gradient(135deg, var(--bronze) 50%, transparent 50%); background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; padding-right: 38px; }

input:focus, select:focus, textarea:focus { outline: 2px solid var(--bronze); outline-offset: -1px; border-color: var(--bronze); }

.check { display: flex; align-items: flex-start; gap: 11px; }
.check input { width: 20px; height: 20px; margin: 1px 0 0; accent-color: var(--espresso); flex-shrink: 0; }
.check label { margin: 0; font-family: var(--body); font-size: 15px; font-weight: 400; color: var(--charcoal); line-height: 1.5; }

.form-section { border-top: 1px solid var(--hairline); padding-top: 24px; margin-top: 26px; }
.form-section:first-of-type { border-top: 0; padding-top: 0; margin-top: 0; }
.form-section h3 { margin-bottom: 18px; }

/* --- Alerts --------------------------------------------------------------- */
.alert {
  padding: 14px 16px;
  border-left: 3px solid var(--bronze);
  background: rgba(176, 123, 82, 0.08);
  font-family: var(--ui);
  font-size: 13.5px;
  line-height: 1.6;
  margin-bottom: 16px;
}
.alert--error { border-color: var(--clay); background: rgba(181, 87, 63, 0.08); color: var(--clay); }
.alert--good { border-color: var(--sage); background: rgba(125, 138, 112, 0.1); color: var(--sage); }
.alert ul { margin: 8px 0 0; padding-left: 18px; }

/* --- Bottom navigation ---------------------------------------------------- */
.nav {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  z-index: 40;
  background: rgba(251, 247, 243, 0.97);
  backdrop-filter: saturate(150%) blur(14px);
  border-top: 1px solid var(--hairline);
  padding-bottom: env(safe-area-inset-bottom);
}
.nav__inner {
  max-width: var(--shell-max);
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  height: var(--nav-h);
}
.nav button {
  border: 0;
  background: none;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  font-family: var(--ui);
  font-size: 9.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0 2px;
  transition: color 0.16s var(--ease);
  position: relative;
}
.nav button .glyph { font-size: 17px; line-height: 1; font-family: var(--display); }
.nav button[aria-current="page"] { color: var(--espresso); }
.nav button[aria-current="page"]::before {
  content: "";
  position: absolute;
  top: 0; left: 50%;
  transform: translateX(-50%);
  width: 26px; height: 2px;
  background: var(--bronze);
}
.nav .badge {
  position: absolute;
  top: 10px;
  right: 50%;
  margin-right: -20px;
  min-width: 16px; height: 16px;
  background: var(--clay);
  color: #fff;
  border-radius: 9px;
  font-size: 9px;
  line-height: 16px;
  text-align: center;
  padding: 0 4px;
  letter-spacing: 0;
}

/* --- Login ----------------------------------------------------------------
   A cinematic full-bleed entrance rather than a form on a page. The photo is
   optional: /portal/hero.jpg fills it when present, and the layered gradient
   underneath carries the screen on its own when it is not.
   -------------------------------------------------------------------------- */
.login {
  position: relative;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 48px 22px 34px;
  overflow: hidden;
  isolation: isolate;
}

.login__bg {
  position: absolute;
  inset: 0;
  z-index: -3;
  background-color: #2A1D16;
  /* Layer order matters: the photo is listed FIRST so it paints on top and
     hides the gradients beneath. Drop hero.jpg in and it takes over; leave it
     out and the gradient stack below shows through as a deliberate backdrop. */
  background-image:
    var(--hero-src),
    radial-gradient(118% 88% at 24% 10%, rgba(176, 123, 82, 0.55) 0%, transparent 60%),
    radial-gradient(120% 92% at 84% 92%, rgba(120, 78, 54, 0.62) 0%, transparent 58%),
    linear-gradient(168deg, #402E23 0%, #2A1D16 54%, #180F0C 100%);
  background-repeat: no-repeat;
  /* gallery-cover.jpg USED to sit under the hero as a fallback. It was removed:
     the backdrop is now a ~450KB six-frame mosaic, and until that finished
     downloading the fallback painted a completely different wedding across the
     screen for several seconds on every cold load. A fallback that is visible
     in the normal case is not a fallback, it is a bug. The gradient stack below
     is the real fallback — it needs no network at all, so there is nothing to
     flash. */
  background-size: contain, cover, cover, cover;
  background-position: center;
  transform: none;
  animation: driftIn 1.6s var(--ease) both;
}

@keyframes driftIn {
  from { opacity: 0; transform: scale(1.04); }
  to   { opacity: 1; transform: scale(1); }
}

/* Warm scrim + vignette: keeps text legible over any photograph. */
.login__scrim {
  position: absolute;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(82% 66% at 50% 44%, rgba(28, 19, 16, 0.08) 0%, rgba(28, 19, 16, 0.52) 100%),
    linear-gradient(180deg, rgba(28, 19, 16, 0.42) 0%, rgba(28, 19, 16, 0.1) 34%, rgba(28, 19, 16, 0.58) 100%);
}

/* Ghosted brand typography behind the card. */
.login__ghost {
  position: absolute;
  inset: 0;
  z-index: -1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  user-select: none;
  text-align: center;
  color: var(--cream);
}
.ghost { display: block; opacity: 0.085; white-space: nowrap; }
.ghost--name {
  font-family: var(--display);
  font-size: clamp(19px, 5.4vw, 44px);
  letter-spacing: 0.34em;
  text-transform: uppercase;
  margin-bottom: -0.18em;
}
.ghost--word {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(58px, 21vw, 210px);
  letter-spacing: 0.015em;
  line-height: 0.94;
  opacity: 0.11;
}
.ghost--script {
  font-family: var(--body);
  font-style: italic;
  font-size: clamp(22px, 6.6vw, 56px);
  margin-top: -0.16em;
  opacity: 0.13;
}
.ghost--services,
.ghost--role {
  font-family: var(--display);
  font-size: clamp(11px, 3vw, 25px);
  letter-spacing: 0.3em;
  text-transform: uppercase;
  margin-top: 1.5em;
  opacity: 0.1;
}
.ghost--role { margin-top: 0.6em; }

/* The card itself — no panel, just type floating on the image. */
.login__card {
  position: relative;
  width: 100%;
  max-width: 400px;
  text-align: center;
  animation: rise 0.7s var(--ease) 0.24s both;
}

/* A soft halo that sinks the decorative type wherever real copy sits on top.
   Without this the ghosted wordmark cuts straight through the body text — and
   it does so unpredictably, because the ghost is sized off the viewport. */
.login__card::before {
  content: "";
  position: absolute;
  inset: -22% -30%;
  z-index: -1;
  background: radial-gradient(
    closest-side,
    rgba(18, 12, 9, 0.9) 0%,
    rgba(18, 12, 9, 0.82) 48%,
    rgba(18, 12, 9, 0.5) 74%,
    transparent 100%
  );
  pointer-events: none;
}

.login__sprocket {
  height: 6px;
  width: 92px;
  margin: 0 auto 30px;
  background-image: repeating-linear-gradient(
    90deg, rgba(245, 234, 224, 0.68) 0 9px, transparent 9px 15px
  );
}

.login__title {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(38px, 11vw, 52px);
  line-height: 1.04;
  letter-spacing: 0.03em;
  color: var(--cream);
  margin: 0;
  text-shadow: 0 2px 26px rgba(0, 0, 0, 0.42);
}

.login__eyebrow {
  font-family: var(--ui);
  font-size: 10.5px;
  font-weight: 400;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--bronze);
  margin-top: 13px;
  padding-left: 0.42em;
}

.login__tagline {
  font-family: var(--body);
  font-style: italic;
  font-size: 15.5px;
  line-height: 1.7;
  color: rgba(245, 234, 224, 0.9);
  margin: 26px auto 34px;
  max-width: 22em;
  text-shadow: 0 1px 18px rgba(0, 0, 0, 0.5);
}

/* Inline sign-in form, sitting directly on the photograph. */
.login__form { margin-top: 4px; text-align: left; }

.field--dark { margin-bottom: 16px; }
.field--dark label {
  font-family: var(--ui);
  font-size: 10px;
  font-weight: 400;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(245, 234, 224, 0.72);
  margin-bottom: 8px;
}
.field--dark input {
  background: rgba(20, 13, 10, 0.46);
  border: 1px solid rgba(245, 234, 224, 0.24);
  color: var(--cream);
  font-family: var(--body);
  padding: 14px 15px;
  backdrop-filter: blur(3px);
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}
.field--dark input::placeholder { color: rgba(245, 234, 224, 0.34); }
.field--dark input:focus {
  outline: none;
  border-color: var(--bronze);
  background: rgba(20, 13, 10, 0.62);
}
/* Chrome's autofill styling is otherwise a bright blue slab on a dark form. */
.field--dark input:-webkit-autofill,
.field--dark input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--cream);
  -webkit-box-shadow: 0 0 0 1000px #241812 inset;
  caret-color: var(--cream);
}

/* Warm brass button, at home on a photograph. */
.btn--gold {
  background: var(--bronze);
  border-color: var(--bronze);
  color: #241812;
  font-weight: 500;
  letter-spacing: 0.2em;
  padding: 15px 22px;
  box-shadow: 0 10px 34px rgba(0, 0, 0, 0.3);
}
.btn--gold:hover { opacity: 1; background: #c0895c; border-color: #c0895c; }

.login__note {
  font-family: var(--ui);
  font-size: 12.5px;
  line-height: 1.75;
  color: rgba(245, 234, 224, 0.62);
  margin: 20px auto 0;
  max-width: 25em;
}
.login__note a { color: var(--bronze); }

#loginMsg:not(:empty) { margin-bottom: 18px; text-align: left; }

.login__rule {
  height: 1px;
  width: 64px;
  margin: 30px auto;
  background: rgba(245, 234, 224, 0.22);
}

.login__help {
  font-family: var(--ui);
  font-size: 12.5px;
  line-height: 1.85;
  color: rgba(245, 234, 224, 0.55);
  margin: 0;
}
.login__help a { color: var(--bronze); text-decoration: none; }
.login__help a:hover { text-decoration: underline; }

.login__footer {
  position: relative;
  margin-top: 40px;
  font-family: var(--ui);
  font-size: 10px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: rgba(245, 234, 224, 0.38);
  text-align: center;
}

/* The error state inherits the dark treatment rather than fighting it. */
.login .alert--error {
  background: rgba(181, 87, 63, 0.18);
  color: #F0C9BC;
  border-color: #B5573F;
  text-align: left;
}
.login .center.small.muted { color: rgba(245, 234, 224, 0.6); }

/* --- Utility -------------------------------------------------------------- */
.stack > * + * { margin-top: 10px; }
.center { text-align: center; }
.hidden { display: none !important; }
.spinner {
  width: 22px; height: 22px;
  border: 2px solid var(--hairline);
  border-top-color: var(--bronze);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
  margin: 40px auto;
}
@keyframes spin { to { transform: rotate(360deg); } }

.view { animation: rise 0.32s var(--ease) both; padding-top: 6px; }
@keyframes rise { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }

/* Empty states carry a lot of this portal — a couple sees "nothing here yet"
   more often than they see data. Worth making them feel composed rather than
   like a missing element. */
.empty {
  text-align: center;
  padding: 46px 24px 40px;
  color: var(--muted);
  font-family: var(--ui);
  font-size: 14px;
  line-height: 1.6;
  background: var(--ivory);
  border: 1px solid var(--hairline);
}
.empty .glyph {
  font-family: var(--display);
  font-size: 42px;
  color: var(--bronze);
  opacity: 0.4;
  display: block;
  margin-bottom: 6px;
  line-height: 1;
}
/* A short sprocket rule under the glyph, tying the empty state back to the
   film motif rather than leaving it as bare text. */
.empty .glyph::after {
  content: "";
  display: block;
  width: 54px;
  height: 5px;
  margin: 16px auto 0;
  background-image: repeating-linear-gradient(90deg, var(--bronze) 0 7px, transparent 7px 12px);
  opacity: 0.5;
}

.divider { height: 1px; background: var(--hairline); margin: 26px 0; }

/* --- Photo band ------------------------------------------------------------
   A single photograph, framed top and bottom by the sprocket motif, so the
   interior screens carry some of the same weight as the login backdrop. Falls
   back to the warm gradient if the file is ever missing — same approach as the
   login screen, so a deleted image degrades rather than breaks.
   -------------------------------------------------------------------------- */
.band {
  position: relative;
  margin: 22px 0 20px;
  background-color: #2a1d16;
  background-image:
    linear-gradient(174deg, rgba(42, 29, 22, 0) 44%, rgba(42, 29, 22, 0.72) 100%),
    var(--band-src, none),
    linear-gradient(150deg, #402e23 0%, #2a1d16 62%, #180f0c 100%);
  background-size: cover, cover, cover;
  background-position: center, center 42%, center;
  background-repeat: no-repeat;
  aspect-ratio: 3 / 2;
  max-height: 232px;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 20px 22px 18px;
  overflow: hidden;
  animation: bandIn 0.9s var(--ease) both;
}
@keyframes bandIn { from { opacity: 0; } to { opacity: 1; } }

.band::before,
.band::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 7px;
  background-image: repeating-linear-gradient(90deg, var(--cream) 0 11px, transparent 11px 18px);
  opacity: 0.62;
}
.band::before { top: 0; }
.band::after { bottom: 0; }

.band__count {
  font-family: var(--display);
  font-size: 60px;
  line-height: 0.94;
  color: var(--cream);
  letter-spacing: -0.02em;
  text-shadow: 0 2px 26px rgba(24, 15, 12, 0.6);
}
.band__count span {
  display: block;
  font-family: var(--ui);
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(245, 234, 224, 0.82);
  margin-top: 11px;
  text-shadow: 0 1px 12px rgba(24, 15, 12, 0.7);
}
.band__label {
  font-family: var(--ui);
  font-size: 10px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: rgba(245, 234, 224, 0.9);
  text-shadow: 0 1px 12px rgba(24, 15, 12, 0.7);
}
.band--tall { max-height: 280px; }

.contact-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.contact-grid .btn { padding: 12px 6px; font-size: 10.5px; letter-spacing: 0.08em; }

@media (min-width: 700px) {
  h1 { font-size: 38px; }
  .hero__count { font-size: 72px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ==========================================================================
   DARK EDITORIAL THEME
   Brings the signed-in portal in line with b4m.studio: dark warm ground,
   photography given room, pull quotes, and the eyebrow + serif-with-italic
   heading pattern the site uses throughout.

   Deliberately appended as an override block rather than rewritten inline, so
   reverting is a single file swap (styles.light.bak.css).

   Scoped away from .login — that screen was already dark and already works.
   ========================================================================== */

:root {
  --ink:        #14100d;   /* page ground, warm near-black */
  --ink-raised: #1e1813;   /* cards, rows */
  --ink-high:   #271f19;   /* inputs, hovered surfaces */
  --line:       rgba(240, 230, 220, 0.13);
  --line-soft:  rgba(240, 230, 220, 0.07);
  --text:       #f0e6dc;
  --text-dim:   rgba(240, 230, 220, 0.6);
  --gold:       #c9925f;   /* bronze, lifted for legibility on dark */
  --sage-lift:  #9aa88b;
  --clay-lift:  #d97a5c;
}

html, body { background: var(--ink); color: var(--text); }

/* --- Chrome --------------------------------------------------------------- */
.topbar {
  background: rgba(20, 16, 13, 0.86);
  border-bottom: 1px solid var(--line-soft);
}
.wordmark { color: var(--text); }
.wordmark small { color: var(--gold); }

/* The sprocket strip above the wordmark, and the sign-out button, both live
   OUTSIDE .view — so they need explicit dark treatment or they vanish into
   the background. */
.topbar .sprocket {
  background-image: repeating-linear-gradient(90deg, var(--gold) 0 11px, transparent 11px 18px);
  opacity: 0.55;
}
.topbar .btn--quiet {
  background: transparent;
  border-color: rgba(240, 230, 220, 0.24);
  color: rgba(240, 230, 220, 0.78);
}
.topbar .btn--quiet:hover {
  background: rgba(240, 230, 220, 0.07);
  color: var(--text);
  opacity: 1;
}

.nav {
  background: rgba(20, 16, 13, 0.94);
  border-top: 1px solid var(--line);
}
.nav button { color: var(--text-dim); }
.nav button[aria-current="page"] { color: var(--text); }
.nav button[aria-current="page"]::before { background: var(--gold); }

/* --- Type ----------------------------------------------------------------- */
.view h1, .view h2, .view h3 { color: var(--text); }
.view .eyebrow { color: var(--gold); }
.view .muted, .view .small.muted { color: var(--text-dim); }
.view p { color: rgba(240, 230, 220, 0.78); }

/* The site's signature heading: roman with one italic phrase carrying the
   emphasis. Used for screen titles so the portal reads in the same voice. */
.view h1 em, .headline em {
  font-style: italic;
  color: var(--gold);
}

/* --- Surfaces ------------------------------------------------------------- */
.view .card {
  background: var(--ink-raised);
  border: 1px solid var(--line);
}
.view .row { border-top: 1px solid var(--line-soft); }
.view .row__title { color: var(--text); }
.view .row__meta { color: var(--text-dim); }

.view .stats { background: var(--line); border: 1px solid var(--line); }
.view .stat { background: var(--ink-raised); }
.view .stat__value { color: var(--text); }
.view .stat__label { color: var(--text-dim); }
.view .stat__label::before { background: var(--gold); opacity: 0.7; }

.view .empty {
  background: var(--ink-raised);
  border: 1px solid var(--line);
  color: var(--text-dim);
}
.view .empty .glyph { color: var(--gold); opacity: 0.55; }

.view .divider { background: var(--line); }
.view .sprocket { background-image: repeating-linear-gradient(90deg, var(--gold) 0 11px, transparent 11px 18px); opacity: 0.5; }

/* --- Pills ---------------------------------------------------------------- */
.view .pill { border-color: var(--line); color: var(--text-dim); }
.view .pill--good  { color: var(--sage-lift); border-color: rgba(154, 168, 139, 0.45); }
.view .pill--warn  { color: var(--gold);      border-color: rgba(201, 146, 95, 0.5); }
.view .pill--alert { color: var(--clay-lift); border-color: rgba(217, 122, 92, 0.45); }

/* --- Buttons -------------------------------------------------------------- */
.view .btn {
  background: var(--gold);
  border-color: var(--gold);
  color: #1a130e;
}
.view .btn--ghost {
  background: transparent;
  border-color: rgba(240, 230, 220, 0.28);
  color: var(--text);
}
.view .btn--ghost:hover { background: rgba(240, 230, 220, 0.06); opacity: 1; }
.view .btn--quiet {
  background: transparent;
  border-color: var(--line);
  color: var(--text-dim);
}

/* --- Forms ----------------------------------------------------------------
   The one place restraint matters more than atmosphere. A 17-question
   questionnaire gets high-contrast fields and generous labels — legibility
   beats mood when someone is typing on a phone at a venue.
   -------------------------------------------------------------------------- */
.view .field label { color: var(--text); }
.view .field .help { color: var(--text-dim); }
.view .field .req { color: var(--clay-lift); }

.view input[type="text"], .view input[type="email"], .view input[type="tel"],
.view input[type="password"], .view input[type="date"], .view input[type="time"],
.view input[type="number"], .view select, .view textarea {
  background: var(--ink-high);
  border: 1px solid rgba(240, 230, 220, 0.2);
  color: var(--text);
}
.view input::placeholder, .view textarea::placeholder { color: rgba(240, 230, 220, 0.32); }
.view input:focus, .view select:focus, .view textarea:focus {
  outline: 2px solid var(--gold);
  border-color: var(--gold);
  background: #2c231c;
}
.view .check label { color: rgba(240, 230, 220, 0.86); }
.view .check input { accent-color: var(--gold); }
.view .form-section { border-top: 1px solid var(--line); }

/* --- Alerts --------------------------------------------------------------- */
.view .alert {
  background: rgba(201, 146, 95, 0.12);
  border-left-color: var(--gold);
  color: rgba(240, 230, 220, 0.9);
}
.view .alert--good  { background: rgba(154, 168, 139, 0.14); border-left-color: var(--sage-lift); color: var(--sage-lift); }
.view .alert--error { background: rgba(217, 122, 92, 0.14);  border-left-color: var(--clay-lift); color: #f2c4b4; }

/* --- Pull quote -----------------------------------------------------------
   Real couples' words, carried over from the testimonials on b4m.studio. The
   portal is the one place a couple is already emotionally invested; a line
   from someone who has been through it belongs here more than on a sales page.
   -------------------------------------------------------------------------- */
.quote {
  position: relative;
  margin: 26px 0;
  padding: 30px 24px 26px;
  background: linear-gradient(158deg, rgba(201, 146, 95, 0.09) 0%, rgba(20, 16, 13, 0) 70%);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.quote::before {
  content: "\201C";
  position: absolute;
  top: 2px;
  left: 18px;
  font-family: var(--display);
  font-size: 68px;
  line-height: 1;
  color: var(--gold);
  opacity: 0.28;
}
.quote__text {
  position: relative;
  font-family: var(--body);
  font-style: italic;
  font-size: 16.5px;
  line-height: 1.72;
  color: rgba(240, 230, 220, 0.9);
  margin: 0 0 16px;
}
.quote__who {
  font-family: var(--ui);
  font-size: 10px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--gold);
}
.quote__who span { display: block; color: var(--text-dim); letter-spacing: 0.16em; margin-top: 5px; }
.quote__stars { color: var(--gold); font-size: 11px; letter-spacing: 0.3em; margin-bottom: 12px; }

/* --- Section marker -------------------------------------------------------
   The site breaks its long scroll with a small centred label between bands.
   Same device here, so screens read as chapters rather than one long list.
   -------------------------------------------------------------------------- */
.marker {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 30px 0 18px;
  font-family: var(--ui);
  font-size: 10px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--gold);
}
.marker::before, .marker::after {
  content: "";
  height: 1px;
  background: var(--line);
  flex: 1;
}

/* Photo bands sit flush against a dark ground now, so the sprocket edges read
   in gold rather than cream. */
.band::before, .band::after {
  background-image: repeating-linear-gradient(90deg, var(--gold) 0 11px, transparent 11px 18px);
  opacity: 0.55;
}
.band { margin: 24px 0 22px; }

/* Placement variants. Each screen gets its own crop and proportion so the
   photography reads as a sequence rather than the same banner repeated. */
.band--wide   { aspect-ratio: 16 / 9; max-height: 210px; }
.band--slim   { aspect-ratio: 3 / 1;  max-height: 150px; }
.band--square { aspect-ratio: 1 / 1;  max-height: 300px; }
.band--soft   { aspect-ratio: 4 / 3;  max-height: 260px; }

/* A slim band carries a label, not a headline — tighten the padding so the
   text sits on the photograph rather than floating in a gap below it. */
.band--slim { padding: 14px 18px 12px; }
.band--slim .band__label { font-size: 9.5px; letter-spacing: 0.3em; }

/* --- Side photo columns ----------------------------------------------------
   Photography alongside the content rather than stacked above it. A tall 2:3
   column bleeds to the screen edge on its own side, which reads as editorial
   rather than as a banner someone dropped in.

   Sides alternate down the app (home left, forms right, pay left…) so scrolling
   between tabs has some rhythm to it.
   -------------------------------------------------------------------------- */
.split {
  display: grid;
  grid-template-columns: 122px minmax(0, 1fr);
  gap: 18px;
  align-items: stretch;
  margin: 22px 0 24px;
}

.split__photo {
  position: relative;
  background-color: #2a1d16;
  background-image: var(--split-src, none), linear-gradient(150deg, #402e23, #180f0c);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  aspect-ratio: 2 / 3;
  animation: bandIn 0.9s var(--ease) both;
  /* Bleed past the shell's 20px gutter to the screen edge. */
  margin-left: -20px;
  min-height: 100%;
}

/* The sprocket motif turned on its side, on the inboard edge only — the
   outboard edge runs off-screen so a strip there would just be clipped. */
.split__photo::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  width: 7px;
  background-image: repeating-linear-gradient(180deg, var(--gold) 0 11px, transparent 11px 18px);
  opacity: 0.5;
}

.split__body { min-width: 0; align-self: center; }
.split__body > :last-child { margin-bottom: 0; }

/* Mirrored: photo on the right, bleeding off the right edge instead. */
.split--right { grid-template-columns: minmax(0, 1fr) 122px; }
.split--right .split__photo {
  grid-column: 2;
  grid-row: 1;
  margin-left: 0;
  margin-right: -20px;
}
.split--right .split__body { grid-column: 1; grid-row: 1; }
.split--right .split__photo::after { right: auto; left: 0; }

/* The countdown lives in a side layout too — big numeral against the photo. */
.split__count {
  font-family: var(--display);
  font-size: 58px;
  line-height: 0.92;
  color: var(--text);
  letter-spacing: -0.02em;
}
.split__count span {
  display: block;
  font-family: var(--ui);
  font-size: 10.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--gold);
  margin-top: 10px;
}

/* A little more room for the photograph once there is width to spare. */
@media (min-width: 560px) {
  .split { grid-template-columns: 190px minmax(0, 1fr); gap: 26px; }
  .split--right { grid-template-columns: minmax(0, 1fr) 190px; }
  .split__count { font-size: 72px; }
}

/* Tick list for collection contents — a plain <ul> reads as a wall of bullets
   at this size, so the marker is a small gold rule instead. */
.tick { list-style: none; margin: 0; padding: 0; }
.tick li {
  position: relative;
  padding-left: 20px;
  margin-top: 9px;
  font-family: var(--ui);
  font-size: 13px;
  line-height: 1.55;
  color: rgba(240, 230, 220, 0.8);
}
.tick li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 9px;
  width: 11px;
  height: 1px;
  background: var(--gold);
  opacity: 0.8;
}
/* ==========================================================================
   THE PHOTOGRAPH IS THE PAGE
   ==========================================================================
   Previous attempt put a large soft-edged photograph at the top of each screen
   with the content stacked underneath it in a dark panel. Structurally that is
   still "a picture and then a page" — two areas with a boundary between them,
   which is the thing being objected to.

   This is the other model, the one a photographer's own site uses: one
   photograph filling the entire viewport, fixed, and the whole interface
   floating on top of it. Content scrolls over the picture. Every surface the
   content sits on is translucent and blurred, so the image reads through it.
   There is no photo region and no content region — there is one image with an
   interface on it.

   Legibility is held by two things working together: a fixed veil over the
   photograph, and blurred translucent panels under anything set small. Large
   display type may sit directly on the picture; body copy may not.
   ========================================================================== */

/* The view's entrance used to translate on the Y axis. A transform on an
   ancestor becomes the containing block for position:fixed descendants, which
   would peg the photograph to the 620px content column instead of the
   viewport. Fade only — this is load-bearing, not cosmetic. */
.view { animation: viewIn 0.4s var(--ease) both; }
@keyframes viewIn { from { opacity: 0; } to { opacity: 1; } }

/* --- The veil -------------------------------------------------------------
   One gradient, reused by both photo layers. Lighter across the upper half so
   the picture is genuinely open there, heavier towards the foot where the
   longest runs of copy end up.
   -------------------------------------------------------------------------- */
:root {
  /* The backdrop is a composed mosaic (see tools/build-photos.py). Two builds:
     a landscape one for windows and a portrait one for phones, swapped below.
     Everything that paints the backdrop reads this, so the swap happens once. */
  --hero-src: url("/portal/hero.jpg");

  --veil: linear-gradient(
    180deg,
    rgba(14, 10, 8, 0.58) 0%,
    rgba(14, 10, 8, 0.5) 22%,
    rgba(14, 10, 8, 0.55) 48%,
    rgba(14, 10, 8, 0.66) 76%,
    rgba(14, 10, 8, 0.78) 100%
  );
  --panel: rgba(18, 13, 10, 0.42);
  --panel-strong: rgba(18, 13, 10, 0.6);
  --panel-line: rgba(240, 230, 220, 0.17);
  --blur: blur(18px) saturate(118%);
}

/* --- Base layer: every signed-in screen has a photograph -------------------
   Screens without their own image (a questionnaire, the admin roster) would
   otherwise fall back to flat near-black with translucent panels floating on
   nothing. This guarantees there is always a picture underneath.
   -------------------------------------------------------------------------- */
#app::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  background-color: var(--ink);
  background-image: var(--veil), var(--hero-src);
  background-size: cover, contain;
  /* Near-centred. hero.jpg is the mansion entrance and its subject sits just
     above the middle of the frame, so the crop is kept close to centre. The
     old value here was 62%, chosen for a previous photo that had a dark
     treeline across its top; against this one that pushed the window down far
     enough to clip the top of the doorway arch on wide, short viewports and
     fill the screen with empty steps. */
  background-position: center, center 48%;
  background-repeat: no-repeat;
  pointer-events: none;
}

/* --- Per-screen photograph ------------------------------------------------
   Same element the old layout used, so app.js is untouched — but it is no
   longer part of the document flow at all. It fills the viewport, sits behind
   everything, and does not scroll.
   -------------------------------------------------------------------------- */
.split {
  display: block;
  position: static;
  margin: 0;
  grid-template-columns: none;
}

.split__photo {
  position: fixed;
  inset: 0;
  z-index: -1;
  width: 100%;
  height: 100%;
  min-height: 0;
  max-height: none;
  margin: 0;
  left: auto;
  transform: none;
  aspect-ratio: auto;

  background-color: var(--ink);
  /* The veil fills; the photograph FITS. `contain` shows the whole frame
     instead of cropping it to the viewport — a landscape photo in a landscape
     window is then almost edge to edge, and nothing gets beheaded. */
  background-image: var(--veil), var(--split-src, none);
  background-size: cover, contain;
  background-position: center, center;
  background-repeat: no-repeat;

  opacity: 1;
  -webkit-mask-image: none;
  mask-image: none;
  pointer-events: none;
  animation: photoIn 1.1s var(--ease) both;
}
@keyframes photoIn { from { opacity: 0; } to { opacity: 1; } }

/* The film-strip rule down the side belonged to the old boxed photo. */
.split__photo::after { display: none; }

/* --- The copy that used to sit beside the photo ---------------------------
   It now floats on it. Small type never sits on bare picture, so this gets the
   same translucent treatment as every other surface.
   -------------------------------------------------------------------------- */
.split__body {
  position: relative;
  margin: 0 0 22px;
  padding: 22px 20px 20px;
  background: var(--panel);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  border: 1px solid var(--panel-line);
}
.split__body > :last-child { margin-bottom: 0; }

.split__count { line-height: 1; }
.split__count span { margin-top: 12px; }

/* --- Every surface becomes translucent ------------------------------------ */
.view .card,
.view .empty,
.view .quote {
  background: var(--panel);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  border: 1px solid var(--panel-line);
}

/* .stats paints the hairlines between cells through its own gap, so it stays
   a thin line colour rather than a panel; the cells carry the glass. */
.view .stats {
  background: rgba(240, 230, 220, 0.16);
  border: 1px solid var(--panel-line);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.view .stat {
  background: var(--panel);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
}

.view .row { border-top: 1px solid rgba(240, 230, 220, 0.1); }
.view .divider { background: rgba(240, 230, 220, 0.14); }
.view .form-section { border-top: 1px solid var(--panel-line); }

/* Alerts sit on the picture unaided, and one of them is how a couple learns
   they have questionnaires outstanding. Denser than a normal panel. */
.view .alert {
  background: rgba(46, 32, 20, 0.66);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
}
.view .alert--good  { background: rgba(38, 46, 32, 0.7); }
.view .alert--error { background: rgba(66, 30, 22, 0.72); color: #f7d3c6; }

/* Typing is the one activity where mood loses to legibility outright. Fields
   stay close to solid — a blurred photograph behind a text cursor is not a
   design decision, it is a usability fault. */
.view input[type="text"], .view input[type="email"], .view input[type="tel"],
.view input[type="password"], .view input[type="date"], .view input[type="time"],
.view input[type="number"], .view select, .view textarea {
  background: rgba(26, 19, 14, 0.9);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border-color: rgba(240, 230, 220, 0.26);
}
.view input:focus, .view select:focus, .view textarea:focus {
  background: rgba(30, 22, 16, 0.96);
}

/* --- Chrome --------------------------------------------------------------- */
.topbar {
  background: rgba(16, 12, 9, 0.52);
  -webkit-backdrop-filter: blur(20px) saturate(120%);
  backdrop-filter: blur(20px) saturate(120%);
  border-bottom: 1px solid rgba(240, 230, 220, 0.1);
}
/* The nav carries 10px labels, so it holds a little more of its own ground. */
.nav {
  background: rgba(16, 12, 9, 0.66);
  -webkit-backdrop-filter: blur(22px) saturate(120%);
  backdrop-filter: blur(22px) saturate(120%);
  border-top: 1px solid rgba(240, 230, 220, 0.13);
}

/* --- Type on the picture --------------------------------------------------
   Display type is allowed to sit on bare photograph. It gets a shadow so it
   holds against a bright highlight — a white dress, a blown sky — rather than
   relying on the veil alone.
   -------------------------------------------------------------------------- */
.view h1, .view h2, .view h3,
.view .eyebrow,
.split__count {
  text-shadow: 0 1px 3px rgba(8, 5, 4, 0.72), 0 2px 26px rgba(8, 5, 4, 0.86);
}
.view p, .view .muted, .view .small { text-shadow: 0 1px 10px rgba(8, 5, 4, 0.6); }

/* Section markers are bare by design. Lift them off the picture. */
.marker {
  color: #e6b184;
  text-shadow: 0 1px 3px rgba(8, 5, 4, 0.8), 0 1px 14px rgba(8, 5, 4, 0.9);
}
.marker::before, .marker::after { background: rgba(240, 230, 220, 0.28); }

/* --- The sprocket, made to read as a mark rather than a fault -------------
   Full-bleed along the top of the screen a dashed strip is indistinguishable
   from a broken dashed border. The sign-in screen already does this properly:
   a short centred perforation that is obviously an ornament. Match it.
   -------------------------------------------------------------------------- */
.topbar .sprocket,
.view .sprocket {
  width: 88px;
  height: 6px;
  margin-left: auto;
  margin-right: auto;
  background-image: repeating-linear-gradient(90deg, var(--gold) 0 9px, transparent 9px 15px);
  opacity: 0.62;
}
.topbar .sprocket { margin-top: 10px; margin-bottom: 2px; }

/* --- Breathing room -------------------------------------------------------
   With no photo occupying vertical space, the first screenful is mostly
   picture with the copy floating low on it — which is the intent.
   -------------------------------------------------------------------------- */
.view { padding-top: 10px; }
.shell { padding-bottom: 28px; }

@media (min-width: 700px) {
  .split__body { padding: 28px 30px 26px; }
}

/* Phones: the veil does more work, because there is less room to fall back on
   and the copy sits closer to the middle of the frame. */
/* Phones get the portrait build of the mosaic. The landscape one `contain`s
   down to a thin band on a tall screen, which makes four photographs unreadably
   small; the portrait build stacks the same four frames instead. */
@media (max-width: 700px) {
  :root { --hero-src: url("/portal/hero-portrait.jpg"); }
}

@media (max-width: 480px) {
  :root {
    --veil: linear-gradient(
      180deg,
      rgba(14, 10, 8, 0.56) 0%,
      rgba(14, 10, 8, 0.52) 24%,
      rgba(14, 10, 8, 0.6) 52%,
      rgba(14, 10, 8, 0.72) 78%,
      rgba(14, 10, 8, 0.82) 100%
    );
    --panel: rgba(18, 13, 10, 0.5);
  }
  .split__body { padding: 20px 17px 18px; }
}

/* iOS gives fixed backgrounds a hard time inside scrolling containers, and a
   handful of Android browsers still ship without backdrop-filter. Without it a
   0.42-alpha panel over a photograph is unreadable, so those browsers get an
   opaque surface and keep the full-bleed picture behind everything else. */
@supports not ((-webkit-backdrop-filter: blur(2px)) or (backdrop-filter: blur(2px))) {
  :root {
    --panel: rgba(16, 12, 9, 0.93);
    --panel-strong: rgba(16, 12, 9, 0.96);
  }
  .topbar { background: rgba(16, 12, 9, 0.95); }
  .nav { background: rgba(16, 12, 9, 0.96); }
  .view .alert { background: rgba(46, 32, 20, 0.95); }
}

/* --- Sign-in: unchanged in structure, softened earlier -------------------- */
.login__bg { transform: scale(1.06); }

.login__scrim {
  background:
    radial-gradient(
      88% 74% at 50% 42%,
      rgba(24, 16, 13, 0) 0%,
      rgba(24, 16, 13, 0.16) 58%,
      rgba(24, 16, 13, 0.46) 100%
    ),
    linear-gradient(
      180deg,
      rgba(24, 16, 13, 0.4) 0%,
      rgba(24, 16, 13, 0.06) 26%,
      rgba(24, 16, 13, 0.1) 46%,
      /* The small print all lives in the bottom third, and on this photograph
         that third is bright grass and concrete. Weight the wash there. */
      rgba(24, 16, 13, 0.46) 74%,
      rgba(24, 16, 13, 0.78) 100%
    );
}

/* Was a 90%-opaque black disc behind the form. Now a wide soft pool — enough
   to seat the type, transparent enough to read the photograph through. */
.login__card::before {
  inset: -26% -38%;
  background: radial-gradient(
    closest-side,
    rgba(16, 11, 8, 0.72) 0%,
    rgba(16, 11, 8, 0.6) 42%,
    rgba(16, 11, 8, 0.34) 70%,
    rgba(16, 11, 8, 0.12) 88%,
    transparent 100%
  );
}

.login__title { text-shadow: 0 2px 30px rgba(8, 5, 4, 0.82), 0 1px 3px rgba(8, 5, 4, 0.5); }
.login__eyebrow { text-shadow: 0 1px 14px rgba(8, 5, 4, 0.9); }
.login__tagline { text-shadow: 0 1px 20px rgba(8, 5, 4, 0.9), 0 1px 3px rgba(8, 5, 4, 0.6); }
.field--dark label { text-shadow: 0 1px 12px rgba(8, 5, 4, 0.9); }

/* These sit lowest on the screen, over the brightest part of the photograph,
   and one of them is the only instruction a first-time client gets. */
.login__note,
.login__help,
.login__footer {
  text-shadow: 0 1px 3px rgba(8, 5, 4, 0.9), 0 1px 16px rgba(8, 5, 4, 0.95);
}
.login__note { color: rgba(248, 240, 233, 0.88); }
.login__help { color: rgba(248, 240, 233, 0.8); }
.login__footer { color: rgba(248, 240, 233, 0.56); }
.login__note a,
.login__help a { color: #e0a97a; }
.login__rule { background: rgba(248, 240, 233, 0.3); }

.field--dark input {
  background: rgba(16, 11, 8, 0.55);
  border-color: rgba(245, 234, 224, 0.32);
  backdrop-filter: blur(6px) saturate(115%);
}
.field--dark input:focus { background: rgba(16, 11, 8, 0.7); }

/* The warning on the sign-in screen must never be the thing that gets lost. */
.login .alert--error {
  background: rgba(74, 28, 20, 0.86);
  border-color: #d97a5c;
  color: #ffdcd0;
  backdrop-filter: blur(6px);
  text-shadow: none;
}
.login .alert--good {
  background: rgba(38, 46, 32, 0.86);
  backdrop-filter: blur(6px);
  text-shadow: none;
}

/* Studio-side questionnaire reader — question/answer pairs.
   Long free-text answers must wrap rather than push the card sideways on a
   phone, hence overflow-wrap: anywhere. white-space: pre-wrap keeps the line
   breaks a couple typed into a textarea. */
.qa { border-top: 1px solid var(--hairline); padding: 14px 0; }
.qa:first-of-type { border-top: 0; padding-top: 0; }
.qa:last-of-type { padding-bottom: 0; }
.qa__q {
  font-family: var(--ui);
  font-size: 11.5px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
  line-height: 1.5;
}
.qa__a {
  margin-top: 6px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--espresso);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.qa__a--empty { color: var(--muted); font-style: italic; }

/* --- Sign-in v2: atmospheric backdrop (2026-09-27) ------------------------
   Replaces the sharp six-frame mosaic behind the sign-in form with ONE photo
   (the Sundial Bridge ceremony, gallery-cover.jpg) filling the screen, pushed
   soft, dark and warm. The form reads as type on a mood rather than a form on
   a collage, and the ghosted brand lettering finally shows through.

   Scoped to .login only — #app::before and every signed-in screen still use
   hero.jpg / side-*.jpg exactly as before.

   gallery-cover.jpg is only 744px wide. That is fine here precisely because it
   is blurred; do NOT reuse this rule for a sharp backdrop.
   -------------------------------------------------------------------------- */
.login__bg {
  inset: -48px;                       /* hide the blur's soft edges off-screen */
  background-color: #1E1510;
  background-image:
    url("/portal/gallery-cover.jpg"),
    radial-gradient(118% 88% at 24% 10%, rgba(176, 123, 82, 0.55) 0%, transparent 60%),
    linear-gradient(168deg, #402E23 0%, #2A1D16 54%, #180F0C 100%);
  background-size: cover, cover, cover;
  background-position: center 38%, center, center;
  filter: blur(14px) sepia(0.75) saturate(1.15) hue-rotate(-8deg) brightness(0.6);
  transform: scale(1.04);
}

/* Espresso wash + warm top glow + dark floor, as in the mock. */
.login__scrim {
  background:
    radial-gradient(70% 55% at 30% 0%, rgba(176, 123, 82, 0.42) 0%, transparent 70%),
    radial-gradient(50% 45% at 78% 30%, rgba(176, 123, 82, 0.22) 0%, transparent 70%),
    linear-gradient(rgba(92, 58, 38, 0.22), rgba(92, 58, 38, 0.22)),
    radial-gradient(90% 70% at 50% 45%, rgba(22, 15, 12, 0.10) 0%, rgba(22, 15, 12, 0.55) 100%),
    linear-gradient(180deg,
      rgba(22, 15, 12, 0.30) 0%,
      rgba(22, 15, 12, 0.18) 30%,
      rgba(22, 15, 12, 0.50) 62%,
      rgba(22, 15, 12, 0.92) 100%);
}

/* The soft dark "hill" across the lower third. Purely decorative. */
.login::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 36%;
  z-index: -2;
  pointer-events: none;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1440 120' preserveAspectRatio='none'%3E%3Cpath d='M0 62 C 240 18, 420 96, 720 58 S 1180 6, 1440 44 L1440 120 L0 120 Z' fill='%23170F0C'/%3E%3C/svg%3E") top / 100% 90px no-repeat,
    linear-gradient(#170F0C, #170F0C) 0 88px / 100% 100% no-repeat;
  opacity: 0.9;
  filter: blur(1.5px);
}

/* With the busy photo gone the ghosted type can be a touch more present. */
.ghost { opacity: 0.12; }
.ghost--word { opacity: 0.16; }
.ghost--script { opacity: 0.15; }

/* Less halo needed now the backdrop is calm. */
.login__card::before {
  background: radial-gradient(
    closest-side,
    rgba(16, 11, 8, 0.55) 0%,
    rgba(16, 11, 8, 0.38) 50%,
    rgba(16, 11, 8, 0.12) 85%,
    transparent 100%
  );
}

/* Inputs: near-solid dark fields like the mock, not glass. */
.field--dark input {
  background: rgba(14, 9, 7, 0.72);
  border-color: rgba(245, 234, 224, 0.22);
  backdrop-filter: none;
}
.field--dark input:focus { background: rgba(14, 9, 7, 0.86); }

@media (prefers-reduced-motion: reduce) {
  .login__bg { animation: none; }
}
