/* =====================================================================
   ACCOUNT PAGES inside the static open book.
   These pages reuse the About Us / Contact Us layout exactly: the same
   book shell, the same .sb-* page classes. Everything below only styles
   form controls on the right-hand page and the supporting copy on the
   left, so no new layout concept is introduced.
   ===================================================================== */

/* Auth forms carry more content than About/Contact, so the right page
   scrolls internally rather than the book growing. */
.page-auth-book .sb-scroll { padding-right: 10px; }

/* ---- left page supporting copy ---- */
.sb-caption {
  max-width: 88%;
  font-size: clamp(11.5px, .8vw, 13px);
  line-height: 1.6;
  color: #6f5c44;
  text-align: center;
}
.sb-caption p { margin: 0 0 6px; }

/* ---- form controls ---- */
.sb-field-label {
  font-size: 12px; font-weight: 600; letter-spacing: .6px;
  text-transform: uppercase; color: #7d6a50; padding: 0;
}
.sb-field > span em { font-style: normal; text-transform: none; opacity: .7; }
.req { color: var(--page-accent); margin-left: 2px; }

.sb-field select {
  font-family: var(--font-ui);
  padding: 10px 12px; border-radius: 7px;
  border: 1px solid rgba(120, 86, 44, .38);
  background: rgba(255, 252, 244, .72);
  color: var(--page-ink); width: 100%; box-sizing: border-box; outline: none;
  transition: border-color .16s, box-shadow .16s;
}
.sb-field select:focus {
  border-color: var(--page-accent);
  box-shadow: 0 0 0 3px rgba(123, 51, 53, .13);
}

.sb-hint { margin: 2px 0 0; color: #8d7c64; }
.sb-error { font-size: 12.5px; color: var(--page-accent); font-weight: 500; }
/* DEDUPLICATED Aug 2026: mw.css declared colour, margin and font-size for
   .sb-lede at the SAME specificity (0,1,0) and simply won on load order, so
   the values that used to sit here never applied to anything. The winning
   values were moved here and mw.css's copies deleted; computed output is
   byte-identical, proved with tools/style_oracle.py. See HANDOVER.md A6. */
.sb-lede { color: #5b4a37; }

/* ---- prominent status alert (replaces plain .sb-note for real feedback:
   validation errors, login failures, save confirmations) ---- */
.sb-alert {
  display: block; text-align: center; padding: 11px 16px; margin: 2px 0;
  border-radius: 8px; border: 1px solid transparent;
  font-size: 13.5px; font-weight: 600; line-height: 1.4;
}
.sb-alert-error { color: #8c2626; background: #fbeaea; border-color: #e6b6b6; }
.sb-alert-success { color: #1f5c33; background: #e9f5ec; border-color: #b6ddc2; }

/* The payment confirmation that REPLACES the form on success. It used to be a
   line in the status slot under the submit button — below the fold on a form
   long enough to scroll, and gone again in under a second, which is why it was
   reported as missing. Given room and weight so it reads as an answer rather
   than a footnote. */
/* COMPACT. This was 18px padding with a 1.14rem heading and a 1.02rem body,
   which for two short lines produced a panel far larger than the message it
   carried. Trimmed to the size of what it actually says; still centred, still
   the only thing on the page once the form is hidden. */
.pf-done {
  display: block;
  max-width: 460px;
  margin: 0 auto;
  padding: 13px 16px;
  font-size: .94rem;
  line-height: 1.45;
  text-align: center;
}
.pf-done strong { display: block; font-size: 1.02rem; margin-bottom: 3px; }
.pf-done span { display: block; }
/* The one part the customer has to keep. Monospaced so the characters are
   unambiguous when it is read out or typed into an email. */
.pf-ref {
  margin-top: 7px;
  font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  font-size: .88rem;
  letter-spacing: .3px;
}
/* The unresolved outcome is not a success and not a failure: the payment may
   have gone through and we do not know yet. Amber says "in progress" where the
   green success panel would overclaim and a red one would be wrong. */
.pf-unresolved { color: #6b4b13; background: #fdf6e8; border-color: #e2c98d; }
/* "We checked, and no money was taken — please pay again." Same amber as the
   unresolved panel above, deliberately: it is the resolution of that same
   event, and giving it the green success colour would read as a confirmation
   of something that has not happened yet. Sits above the summary rather than
   replacing it, so the customer can still see what they are buying. */
.pay-retry-notice {
  color: #6b4b13; background: #fdf6e8; border-color: #e2c98d;
  margin-bottom: 12px; text-align: left;
}

/* ---- password visibility toggle ---- */
.sb-input-wrap { position: relative; display: flex; align-items: center; }
.sb-field .sb-input-wrap input { width: 100%; padding-right: 42px; box-sizing: border-box; }
.sb-pw-toggle {
  /* #5 Vertically centred against the input rather than offset from the top:
     it was sitting noticeably low in the field. Anchoring to the input's own
     centre keeps it aligned whatever the field height or font size. */
  position: absolute; top: 50%; right: 8px; transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0;
  background: none; border: 0; cursor: pointer;
  color: #8d7c64; border-radius: 6px;
}
.sb-pw-toggle:hover { color: var(--page-accent); background: rgba(123, 51, 53, .07); }
.sb-pw-toggle svg { width: 19px; height: 19px; }
.sb-pw-toggle .sb-eye-slash { display: none; }
.sb-pw-toggle.is-visible .sb-eye-open { display: none; }
.sb-pw-toggle.is-visible .sb-eye-slash { display: block; }

.sb-inline-link {
  margin: -2px 0 0; font-size: 13.5px; text-align: right;
}
.sb-inline-link a, .sb-alt a { color: var(--page-accent); }

.sb-alt {
  margin: 18px 0 0; padding-top: 16px; border-top: 1px solid rgba(120, 86, 44, .16);
  font-size: 13.5px; color: #6f5c44;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}

/* Login specifically: tighter than the general form density above —
   only 2 fields plus 2 links, so the same gap used for longer forms
   (registration) reads as noticeably looser here than it should. */
/* LOGIN FORM RHYTHM — one place, deliberately tight.
   The Login button and the "Don't have an account? Register Here" line sat far
   below the fields because four separate gaps stacked up: the form's own gap,
   the Forgot-password line's margin, the button's margin, and .sb-alt's
   margin PLUS its padding above the divider. Each was small; together they
   pushed the button most of a page down. Tightened as a set so the whole
   group reads as one form. */

.sb-alt-btn {
  display: inline-block; padding: 8px 18px; border-radius: 22px;
  border: 1.5px solid var(--page-accent); color: var(--page-accent);
  text-decoration: none; font-weight: 500; font-size: 14px;
  transition: background .16s, color .16s;
}
.sb-alt-btn:hover { background: var(--page-accent); color: #fff; }
.sb-sep { opacity: .45; }

/* =====================================================================
   REGISTRATION

   .reg-row IS GONE. It was a dt/dd definition row that register.php had also
   been reusing as a field-pair wrapper — two different components under one
   class name, which is why the pairs kept collapsing (the dt/dd mobile rule
   `flex-direction: column` was stacking First Name and Last Name on every
   phone). .sb-row replaced the pairing job; the dt/dd job had no callers left.
   Verified before removal: zero occurrences of "reg-row" in any .php or .js
   file in the tree. Removing it takes the name out of circulation so it cannot
   be reused for a third meaning.
   ===================================================================== */

/* ---- night mode ---- */
.reader-room.night .sb-caption { color: #e8dcc8; }
.reader-room.night .sb-field-label { color: #b0a189; }

/* ---- responsive ---- */
@media (max-width: 860px){
  .sb-submit { width: 100%; text-align: center; }
}

/* On phones the static book hides its left page. That is right for About /
   Contact (a decorative logo plate), but the auth pages carry real content
   there — the plans copy, the welcome text and the quote. So for these
   pages the spread stacks vertically instead of dropping a page. */
@media (max-width: 860px){
  .page-auth-book .static-book .page-left { display: block; }
  .page-auth-book .static-book .book-open {
    grid-template-columns: 1fr;
    aspect-ratio: auto;
    max-height: none;
    height: auto;
  }
  .page-auth-book .static-book .page-left,
  .page-auth-book .static-book .page-right {
    position: relative;
    border-radius: 6px;
    min-height: 0;
  }
  /* The spine sits between two columns; with one column it has no meaning. */
  .page-auth-book .static-book .book-spine-shadow,
  .page-auth-book .static-book .page-left::before,
  .page-auth-book .static-book .page-right::before { display: none; }
  /* Stacked pages size to their content rather than scrolling internally. */
  .page-auth-book .sb-scroll { overflow: visible; }
  /* =====================================================================
     A LEFT PAGE THAT IS NOTHING BUT THE LOGO IS DROPPED ON A PHONE.

     On a spread the plate fills the left page, which costs nothing — that page
     exists either way. Stacked on a phone it becomes real vertical space ABOVE
     the thing the visitor came for, repeating a brand mark the sticky header is
     already showing 68px higher. It measured ~190px on a 390px phone: the img
     `clamp(104px, 11vw, 176px)` hits its 104px FLOOR at that width, then rule,
     imprint and 48px of padding.

     Keyed on CONTENT, not on page names. `:has(.sb-logo-plate)` asks the only
     question that matters — "is this left page purely decorative?" — so About,
     Contact, Login and Register all lose it, while any left page carrying real
     content keeps it: the auth welcome copy, and `.gb-art` cover artwork on
     info books that have their own picture. Add a cover image in the Admin and
     that page's left side comes back automatically, with no rule to update.

     `:has()` is already relied on for the reader's root scroll lock, so this
     adds no new support requirement. */
  .page-auth-book .static-book .page-left:has(.sb-logo-plate) { display: none; }

  /* =====================================================================
     A STATIC BOOK IS STILL A BOOK, AND IT SHOULD LOOK LIKE ONE.

     `height: auto` above makes the stacked spread size to its content. That is
     right for a long page (Register) and wrong for a short one: Login and the
     module info books ended two-thirds of the way down the screen and left a
     wide band of empty room below, reading as a small card rather than a book.

     A floor tied to the viewport gives them book proportions without capping
     anything: pages with more content than this still grow past it, so nothing
     is compressed and nothing scrolls internally that did not before. The
     subtraction is the header plus the room's own padding — the same chrome
     the reader accounts for — and it is `svh` for the reason recorded in
     A8ter: one stable number per device in every browser.

     This is a MINIMUM. It cannot squeeze content, only stop a short book from
     collapsing. */
  .page-auth-book .static-book .book-open {
    min-height: calc(100vh - var(--site-header-h) - 120px);
    min-height: calc(100svh - var(--site-header-h) - 120px);
  }
}

/* =====================================================================
   .sb-row — THE SHARED FORM ROW.

   One component for every "these two fields belong together" pair on the site:
   Register, Profile, Contact, and the create/edit forms. Two up by default,
   collapsing to one column only on the very narrowest phones, where two inputs
   genuinely cannot both be usable.

   IT IS DELIBERATELY NOT CALLED .reg-row. That name WAS already taken in this
   file by a dt/dd definition row (the plan summary), and register.php had been
   reusing it as a field-pair wrapper — so the dt/dd mobile rule
   `.reg-row { flex-direction: column }` was stacking First Name and Last Name
   on every phone. Two different components under one class name; the collision
   is why the pair never stayed side by side.

   `min-width: 0` on the children is required, not decorative: grid items
   default to `min-width: auto` and an input's min-content width would other-
   wise push the row wider than the phone. Same defect class as the homepage
   room column.

   `.sb-row.is-stacked` opts a row out where a field needs the full width. */
.sb-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  align-items: start;
}
.sb-row > * { min-width: 0; }
/* AND THE CONTROL INSIDE IT, NOT JUST THE CELL.
   `min-width: 0` on the grid item stops the TRACK growing, which is why the row
   never pushed the page wide. It does nothing for the control itself: a native
   `input[type="date"]` carries a UA intrinsic minimum that `width: 100%` cannot
   override, so on a real phone the widget painted past its own cell and touched
   the field beside it. Measured arithmetic at 393px: the cell is ~160px and a
   16px native date widget is ~150-165px — marginal, which is why it reads as a
   slight overlap rather than a gross one, and why it appears on a device and not
   in a desktop engine (Blink-desktop draws a narrow text-style date field).

   The 16px is deliberate and must stay — it is the iOS focus-zoom fix (A15) —
   so the control is made shrinkable instead. */
.sb-row .sb-field input,
.sb-row .sb-field select { min-width: 0; }
.sb-row.is-stacked { grid-template-columns: 1fr; }
@media (max-width: 340px) {
  .sb-row { grid-template-columns: 1fr; }
}

/* A ROW CARRYING A PASSWORD TOGGLE NEEDS THE FULL WIDTH ON A PHONE.
   `.sb-pw-toggle` reserves 42px of padding inside its input. Paired at 390px
   that leaves ~130px to type a password into, and a mistyped password is a
   worse outcome than one more row of scrolling. Keyed on the toggle being
   present rather than on which form it is, so it holds wherever the pattern is
   reused. */
@media (max-width: 430px) {
  .sb-row:has(.sb-pw-toggle) { grid-template-columns: 1fr; }
}

/* A ROW CARRYING A NATIVE DATE CONTROL TAKES THE FULL WIDTH ON A PHONE.

   `input[type="date"]` is a NATIVE WIDGET, and every platform sizes it
   differently: iOS Safari lays out its own date text, Android Chrome renders a
   wider control again, and both carry an intrinsic minimum that neither
   `width: 100%` nor `min-width: 0` can always shrink past — the two remedies
   already tried. Chasing a width that fits every engine is chasing a number
   that does not exist, which is why the field kept touching its neighbour on
   some devices and not others.

   So the row stops asking it to share. Keyed on the CONTROL being present —
   exactly as the password-toggle rule above is — rather than on a device, a
   browser or a form name, so it holds wherever a date field is paired. Plain
   text inputs still pair happily; they shrink predictably everywhere.

   BOTH pairing components are named, and `time` alongside `date`: .pf-grid pairs
   the same way .sb-row does (Add Special Event and In Memory Of pair two date
   fields through it), and a native time picker has exactly the same
   irreducible-width problem. One policy, stated once, for every row that pairs a
   native picker. */
@media (max-width: 430px) {
  .sb-row:has(input[type="date"]),
  .pf-grid:has(input[type="date"]),
  .sb-row:has(input[type="time"]),
  .pf-grid:has(input[type="time"]) { grid-template-columns: 1fr; }
}

/* Forms: tighter rhythm and smaller controls. */
.sb-form { gap: clamp(7px, .8vw, 11px); }

/* TYPOGRAPHY FLOOR (Aug 2026): field labels are read while filling a form, so
   they take the 12px floor. Letter-spacing dropped from .5px to .4px so the
   label occupies the same width it did at 11px and no field re-wraps. */
.sb-field > span, .sb-field-label { font-size: 12px; letter-spacing: .4px; }
.sb-field input, .sb-field select, .sb-field textarea {
  font-size: 13.5px; padding: 7px 10px; border-radius: 6px;
}
.sb-field textarea { min-height: 64px; }
.sb-submit {
  font-size: 14px; padding: 9px 24px; margin-top: 2px;
}
.sb-note, .sb-hint { font-size: 12px; }   /* typography floor */
.sb-lede { margin: 0 0 18px; font-size: 13.5px; }   /* deduplicated — see note above */

/* Select placeholders can be long — keep them from crowding the field. */
.sb-field select { font-size: 13px; }

/* Section headings inside a page (About Us: Our Vision / Our Mission). */
.sb-subtitle {
  margin: clamp(8px, 1vw, 13px) 0 4px;
  font-family: var(--font-display); font-weight: 700;
  font-size: clamp(15px, 1.25vw, 19px);
  color: var(--page-accent); letter-spacing: .2px;
}
.sb-subtitle:first-of-type { margin-top: 2px; }
.sb-body strong { font-weight: 600; }

/* =====================================================================
 * LOGIN GROUP — Forgot password / Login / Register Here as one unit.
 *
 * PLACED LAST AND WRITTEN AS .sb-form.login-form ON PURPOSE.
 * The previous attempt used `.login-form { gap: 3px }` earlier in this file,
 * but `.sb-form { gap: clamp(7px,.8vw,11px) }` appears later with the SAME
 * specificity — so the later rule won and the gap never changed. That is why
 * the button kept sitting low however many times the value was adjusted.
 * `.sb-form.login-form` is two classes on one element, so it wins on
 * specificity regardless of order, and being last removes the ambiguity too.
 * ===================================================================== */
.sb-form.login-form { gap: 3px; }
.sb-form.login-form .sb-inline-link { margin: 0; }
.sb-form.login-form .sb-submit { margin-top: 2px; }
.sb-form.login-form .sb-alt { margin-top: 8px; padding-top: 8px; }
.sb-form.login-form .sb-note { margin-top: 10px; }

/* =====================================================================
 * LOGIN — LAYOUT, NOT DECORATION
 *
 * The colour direction, the background and the book are approved and are not
 * touched here. What was wrong was the arrangement: a 3px flex gap with every
 * element carrying its own margin, so the form read as four controls dropped
 * onto a page rather than as one designed unit. There was no rhythm to it, the
 * two fields and the two secondary links all had the same visual weight, and
 * the submit button sat wherever the accumulated margins left it.
 *
 * THREE GROUPS, STATED AS SUCH:
 *   1  the credentials      — the fields, one rhythm, equal weight
 *   2  the primary action   — Login, with its two helpers on the line above
 *   3  the alternative      — registering, separated by a rule because it is
 *                             a different intention, not a smaller version of
 *                             the same one
 *
 * Sizes come from the touch floor and the typography floor this project
 * already uses (44px targets, 12px minimum type), so nothing here shrinks to
 * buy space. Written as `.sb-form.login-form` for the specificity reason
 * recorded above, and placed after it so both are obvious.
 * ===================================================================== */

/* ---- 1. the credentials ---- */
.sb-form.login-form { gap: 0; }
.sb-form.login-form > .sb-field { margin-bottom: 11px; }
.sb-form.login-form .sb-field { gap: 5px; }
.sb-form.login-form .sb-field > span {
  font: 600 11px/1 var(--font-ui, Poppins, sans-serif);
  letter-spacing: .7px; text-transform: uppercase; color: #7d6a4e;
}
/* One field height everywhere, and it is a comfortable target rather than the
   minimum one. The password field keeps its 42px of room for the toggle. */
.sb-form.login-form .sb-field input {
  width: 100%; box-sizing: border-box;
  min-height: 44px; padding: 11px 13px;
  font-size: 14px; line-height: 1.2;
  border: 1px solid rgba(120, 86, 44, .34);
  border-radius: 9px;
  background: #fffdf8; color: #33281c;
  transition: border-color .15s, box-shadow .15s, background .15s;
}
.sb-form.login-form .sb-field input::placeholder { color: #a2937c; }
.sb-form.login-form .sb-field input:hover { border-color: rgba(120, 86, 44, .5); }
/* FOCUS IS VISIBLE AND IT IS THE BRAND COLOUR. A ring rather than a colour
   swap, so the state reads the same on both fields and does not depend on
   noticing a one-pixel border change. */
.sb-form.login-form .sb-field input:focus,
.sb-form.login-form .sb-field input:focus-visible {
  outline: none;
  border-color: var(--maroon, #7b3335);
  box-shadow: 0 0 0 3px rgba(123, 51, 53, .16);
  background: #fff;
}
.sb-form.login-form .sb-field input:-webkit-autofill { -webkit-text-fill-color: #33281c; }

/* The toggle is a real target, centred on the field's new height. */
.sb-form.login-form .sb-pw-toggle {
  width: 40px; height: 40px; right: 3px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 8px;
}

/* Errors sit WITH the field they belong to, above the next one, so a message
   never appears at the bottom of the form describing something at the top. */
.sb-form.login-form .sb-error { margin: 5px 0 0; line-height: 1.35; }

/* ---- 2. the primary action ---- */
/* Remember me and Forgot password share one line and are aligned to it, which
   is what stops them reading as two more form controls. */
.sb-form.login-form .login-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
  margin: 1px 0 15px;
}
.sb-form.login-form .login-remember {
  display: inline-flex; align-items: center; gap: 7px;
  font: 500 12.5px/1 var(--font-ui, Poppins, sans-serif); color: #5f5140;
  cursor: pointer; min-height: 24px;
}
.sb-form.login-form .login-remember input {
  width: 16px; height: 16px; min-height: 0; margin: 0; padding: 0;
  accent-color: var(--maroon, #7b3335); cursor: pointer;
}
.sb-form.login-form .sb-inline-link {
  margin: 0; font: 500 12.5px/1 var(--font-ui, Poppins, sans-serif);
}
.sb-form.login-form .sb-inline-link a { text-decoration: none; }
.sb-form.login-form .sb-inline-link a:hover { text-decoration: underline; }

/* THE BUTTON IS THE WIDTH OF THE FORM. A 140px pill under two full-width
   fields is the detail that made this read as a default form: the primary
   action should be as wide as the thing it submits. */
.sb-form.login-form .sb-submit {
  display: block; width: 100%; min-width: 0; margin: 0;
  min-height: 46px; padding: 12px 20px;
  font: 700 14px/1 var(--font-ui, Poppins, sans-serif);
  letter-spacing: .3px; border-radius: 10px;
  box-shadow: 0 6px 16px -8px rgba(123, 51, 53, .85);
}
.sb-form.login-form .sb-submit:focus-visible {
  outline: 2px solid var(--maroon, #7b3335); outline-offset: 3px;
}

/* ---- 3. the alternative ---- */
/* A rule, not a gap: registering is a different intention and should look
   like one without a second button competing with Login. */
.sb-form.login-form .sb-alt {
  margin: 16px 0 0; padding: 14px 0 0;
  border-top: 1px solid rgba(120, 86, 44, .2);
  text-align: center;
  font: 500 12.5px/1.5 var(--font-ui, Poppins, sans-serif); color: #6b5c46;
}
.sb-form.login-form .sb-alt a { font-weight: 700; text-decoration: none; }
.sb-form.login-form .sb-alt a:hover { text-decoration: underline; }
.sb-form.login-form .sb-note {
  margin: 8px 0 0; text-align: center;
  font: 400 11.5px/1.4 var(--font-ui, Poppins, sans-serif); color: #8d7d64;
}

/* ---- narrow phones ----
   Not a shrunken desktop: the two secondary controls stop competing for one
   line and take a row each, which is more comfortable to tap than two small
   targets pushed to opposite edges of a 320px page. */
/* 430px, from the documented scale in tools/breakpoints.py (large phone).
   An invented 400px was written here first and gate 36 rejected it — correctly:
   the threshold was chosen to suit this one form rather than taken from the
   scale the rest of the site steps on. 430 is the right side of the same
   decision and is where every other phone rule in this project turns. */
@media (max-width: 430px) {
  .sb-form.login-form .login-row {
    flex-direction: column; align-items: flex-start; gap: 9px;
    margin-bottom: 13px;
  }
  .sb-form.login-form > .sb-field { margin-bottom: 9px; }
  .sb-form.login-form .sb-field input { min-height: 44px; font-size: 14px; }
}

/* =====================================================================
 * FORM DENSITY STANDARD  (Registration, Profile, Create Couple Diary)
 * Defined once so every static-book form uses space the same way and new
 * forms inherit it rather than being tuned individually.
 * ===================================================================== */

/* Content starts nearer the top instead of drifting down the page. */
.page-static-book .sb-body { padding-top: 0; }
.page-static-book .sb-title { margin-bottom: 2px; }
.page-static-book .sb-title-rule { margin-bottom: 8px; }
/* #1 CORRECTION — .sb-form IS a flex column: reader.css declares
   `.sb-form { display: flex; flex-direction: column; gap: ... }` and
   reader.css is loaded on every static-book page. (An earlier comment here
   claimed it had no display declaration anywhere, which is what led two
   rounds of float-based avatar layouts to fail silently — float has no
   effect on flex items.) The margins below are kept: they stack with the
   flex gap, and together they produce the compact rhythm these forms
   currently render with. */
.sb-form > .sb-field,
.sb-form > .pf-grid,
.sb-form > .sb-note { margin-bottom: 7px; }
.sb-form > .sb-field:last-of-type { margin-bottom: 0; }
.sb-field { gap: 2px; }
.sb-hint { font-size: 12px; line-height: 1.35; }   /* typography floor */

/* Two short fields per row where the page offers the width. */
.pf-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 12px; }
@media (max-width: 620px){
  /* KEPT TWO UP. This collapse is what stacked First Name and Last Name on
     every phone — the same decision .sb-row now makes once for the whole site.
     Two short text inputs are perfectly usable side by side at 320px; the
     narrow-phone fallback lives in .sb-row's own 340px rule, so profile and
     register behave identically instead of each deciding for itself. */
  .pf-grid { grid-template-columns: 1fr 1fr; }
  .pf-grid > * { min-width: 0; }
}

/* Actions: compact and never full-width. */
.sb-form .sb-submit {
  display: block; width: auto; margin: 8px auto 0;
  padding: 8px 26px; font-size: 13px; min-width: 140px;
}

/* ---- Registration: photo control ----
   Layout and sizing live in ONE place: mw.css (.reg-top / .reg-avatar-*),
   which loads last and therefore always wins. Competing float-based rules
   here were dead on arrival — .sb-form is a flex column (reader.css), and
   float has no effect on flex items — but still fought mw.css on sizes.
   Do not re-add avatar rules in this file. */

/* ---- Password requirements: ONE compact line ----
   A four-item grid stacked and wrapped on narrow pages and read as clutter.
   A single hint line states the rule set in one place, and turns green once
   every part is satisfied — so it still gives live feedback without costing
   four rows of height. */
/* PRESENTATION ONLY — the validation contract is unchanged. password-rules.js
   puts `.is-met` on each <li> and `.is-complete` on the list; nothing below asks
   for anything else, so the live behaviour is exactly what it was.

   The single dot-separated line was legible but gave no per-rule feedback worth
   reading: everything greened at once and a partly-satisfied password looked the
   same as an empty one. Each rule now carries its own indicator, so a visitor
   can see WHICH requirement is still outstanding.

   Two columns, because four stacked rows was the height problem the one-line
   version was solving. At two columns the card is two rows tall and each rule
   still has its own state. */
/* LIGHTER. The box was a bordered, filled panel under the password field —
   the same visual weight as the field itself, for a hint. It now reads as a
   caption: no border, no fill, tighter gutters. The 2x2 grid stays, because
   four stacked rows was the height problem in the first place, and so does the
   12px floor: this is read while typing a password. */
.pw-rules {
  list-style: none; margin: 5px 0 0; padding: 0 2px;
  display: grid; grid-template-columns: 1fr 1fr; gap: 1px 10px;
  border: 0;
  background: none;
  font-size: 12px; line-height: 1.3; color: #8a7757;
  transition: color .2s;
}
/* ================= THE FIRST LINES MUST LINE UP =================
   Measured at 1280: "Min 8 characters" and "1 special character" wrap to two
   lines in this column while "1 uppercase" and "1 number" do not. A grid row
   is as tall as its tallest cell, so with `align-items: center` the SHORT
   items floated to the middle of a 31px row while the wrapped ones started at
   the top — four requirements, four different first-line positions. That is
   the misalignment; nothing was wrong with any single rule.

   Aligning to the start makes every first line begin at the same offset
   whether the label wraps or not. The indicator then needs its own nudge,
   because `flex-start` would otherwise pin an 11px dot to the top of a ~16px
   line instead of centring it on that line. */
.pw-rules li {
  display: flex; align-items: flex-start; gap: 5px; min-width: 0;
}
/* The indicator is one element in two states, so nothing shifts as it changes:
   an outline ring becomes a filled tick at the same size. */
.pw-rules li::before {
  content: '';
  /* (line-height 1.3 x 12px = 15.6px, less the 11px dot, halved) */
  margin-top: 2px;
  flex: 0 0 auto; width: 11px; height: 11px; border-radius: 50%;
  border: 1.5px solid rgba(150, 120, 70, .5); background: none;
  transition: background .18s, border-color .18s;
}
.pw-rules li.is-met {
  color: #2f7d4f;
}
.pw-rules li.is-met::before {
  border-color: #2f7d4f; background: #2f7d4f no-repeat center/7px 7px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M1.6 6.3l3 3 5.8-6.6' fill='none' stroke='%23fff' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* Every rule satisfied: the card itself confirms it, so the state is readable
   without re-reading four lines. */
/* Every rule satisfied. Without a box to tint, the confirmation is carried by
   the ticks themselves, which are already green — so this only lifts the
   remaining label colour rather than repainting a panel that no longer exists. */
.pw-rules.is-complete { color: #2f7d4f; }
/* THE 2x2 GRID HOLDS ON PHONES — collapsing it to one column was the height
   problem, not the fix. Four rows of ~15px plus padding is what made the
   component tall; two rows is half of it. The labels are short enough to sit
   beside an 11px indicator at 10.5px in half a book page, so the grid only
   gives way on the very narrowest screens, where two columns genuinely cannot
   hold a legible label. */
@media (max-width: 340px) {
  .pw-rules { grid-template-columns: 1fr; }
}

/* ---- Login: Remember me shares the row with Forgot password ---- */
.login-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin: 2px 0 0;
}
.login-row .sb-inline-link { margin: 0; }
.login-remember {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12.5px; color: #6f5c44; cursor: pointer;
}
.login-remember input { width: 14px; height: 14px; accent-color: var(--page-accent, #7b3335); }

/* ---- Profile: compact and higher up ---- */
.page-profile .pf-avatar-block { margin-bottom: 6px; }
.page-profile .sb-form { gap: 6px; }   /* deduplicated — mw.css declared 6px and won */
.page-profile .pf-actions { margin-top: 8px; }

/* ---- Add Event: the two optional diary checkboxes ----
   Reuses .login-remember for the control itself (one checkbox style in the
   project); these rules only stack them as their own rows within the form,
   which the login row does not need. */
.page-event-create .ev-check-row {
  display: flex; gap: 18px; flex-wrap: wrap; margin: 2px 0 4px;
}
.page-event-create .ev-check { display: inline-flex; margin: 0; }
.page-event-create .ev-couple-pick[hidden] { display: none; }
.page-event-create .ev-range-hint.is-limit { color: var(--maroon, #7b3335); font-weight: 600; }

/* ---- Special Events: the date rule, as an information box ---------------
   The 28-day limit is this form's one hard rule and the live day count is the
   only confirmation that a range is valid. Both were grey 11px hints under a
   field. A bordered box with an icon and a badge makes them noticeable without
   turning them into a warning. */
.page-event-create .ev-range-box {
  display: flex; align-items: center; gap: 10px;
  margin: -2px 0 16px;
  padding: 10px 13px;
  border: 1px solid rgba(150, 120, 70, .42);
  border-left: 3px solid var(--gold, #c9a24e);
  border-radius: 0 7px 7px 0;
  background: #f8f2e6;
}
.page-event-create .ev-range-ic { flex: 0 0 auto; display: grid; place-items: center; }
.page-event-create .ev-range-ic svg { width: 16px; height: 16px; fill: #a07a52; }
.page-event-create .ev-range-box .ev-range-hint {
  flex: 1 1 auto; margin: 0;
  font-size: 12px; line-height: 1.45; color: #6b5940;
}
.page-event-create .ev-range-count {
  flex: 0 0 auto;
  padding: 4px 11px; border-radius: 12px;
  background: var(--maroon, #7b3335); color: #fff;
  font: 700 11.5px/1 var(--font-ui, Poppins, sans-serif);
  letter-spacing: .03em;
}
/* At the ceiling, the whole box says so rather than one recoloured word. */
.page-event-create .ev-range-box.is-limit {
  border-left-color: var(--maroon, #7b3335);
  background: #fbf0ee;
}
.page-event-create .ev-range-box.is-limit .ev-range-hint { color: #8c2f2f; font-weight: 600; }

/* A pending message is working, not finished. The spinner says so, and it
   stays until the navigation replaces the page. */
.sb-alert.is-pending::before {
  content: '';
  display: inline-block;
  width: 11px; height: 11px;
  margin-right: 8px;
  vertical-align: -1px;
  border: 2px solid rgba(107, 89, 64, .3);
  border-top-color: var(--maroon, #7b3335);
  border-radius: 50%;
  animation: mw-pending-spin .7s linear infinite;
}
@keyframes mw-pending-spin { to { transform: rotate(360deg); } }

/* =====================================================================
   CREATE-FORM DENSITY STANDARD

   The three creation forms are long because every field is a full-width row
   with a label above it and a hint below. No field is removed here — the
   rhythm between them is tightened, and the pairs that belong together sit on
   one row through the existing .pf-grid.

   Scoped to the three create pages only. The same classes are used by Login,
   Register and Profile, which are short already and are left alone.
   ===================================================================== */
.page-couple-create .sb-form,
.page-memory-create .sb-form,
.page-event-create .sb-form { gap: 0; }

.page-couple-create .sb-form > .sb-field,
.page-memory-create .sb-form > .sb-field,
.page-event-create .sb-form > .sb-field { margin-bottom: 9px; }

.page-couple-create .sb-form > .pf-grid,
.page-memory-create .sb-form > .pf-grid,
.page-event-create .sb-form > .pf-grid { margin-bottom: 9px; gap: 4px 14px; }

/* The label sits tight to its control; the hint tucks under it rather than
   claiming a line of its own. */
.page-couple-create .sb-field > span:first-child,
.page-memory-create .sb-field > span:first-child,
.page-event-create .sb-field > span:first-child { margin-bottom: 3px; }

.page-couple-create .sb-hint,
.page-memory-create .sb-hint,
.page-event-create .sb-hint { margin-top: 3px; line-height: 1.3; }

/* The upload zone is the tallest thing on these forms and does not need to be. */
.page-couple-create .cc-drop,
.page-memory-create .cc-drop,
.page-event-create .cc-drop { padding: 12px 12px; min-height: 0; }

.page-couple-create .sb-submit,
.page-memory-create .sb-submit,
.page-event-create .sb-submit { margin-top: 6px; }

/* Billing address section heading on the payment page. auth.css owns the
   .sb-* form components, so it owns this too — putting it in mw.css would
   split one form's styling across two files. */
.pay-section-head {
  margin: 16px 0 2px;
  font: 700 13px/1.3 var(--font-ui, Poppins, sans-serif);
  color: var(--maroon, #7b3335);
  border-top: 1px solid #ece0c8;
  padding-top: 13px;
}

/* Upgrade breakdown headings and the explanatory note beneath the summary.
   auth.css owns the payment page's .pay-* components. */
.pay-section-title {
  margin: 0 0 10px;
  font: 700 12px/1.3 var(--font-ui, Poppins, sans-serif);
  letter-spacing: .6px;
  text-transform: uppercase;
  color: var(--maroon, #7b3335);
}

/* TYPOGRAPHY FLOOR: this paragraph is the explanation of the amount being
   charged, so it sits at 12px rather than below it. */
.pay-note {
  margin: 10px 0 0;
  font: 400 12px/1.6 var(--font-ui, Poppins, sans-serif);
  color: #6b5628;
  background: #fdf6e6;
  border: 1px solid #e3d0a4;
  border-radius: 7px;
  padding: 10px 12px;
}

/* "What happens next" on the renewal payment page. Sentences, not a table: this
   is the part a customer reads to DECIDE, and the table above it is the part
   they read to CHECK. Parchment and gold, like every other explanatory panel. */
.pay-explain {
  margin: 12px 0 0;
  padding: 12px 14px;
  background: #fdf6e6;
  border: 1px solid #e3d0a4;
  border-left: 3px solid #c9a24e;
  border-radius: 7px;
}

.pay-explain-head {
  margin: 0 0 8px;
  font: 700 12px/1.3 var(--font-ui, Poppins, sans-serif);
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--maroon, #7b3335);
}

/* TYPOGRAPHY FLOOR: read while deciding whether to pay. 12px, not below. */
.pay-explain p {
  margin: 0 0 7px;
  font: 400 12px/1.6 var(--font-ui, Poppins, sans-serif);
  color: #3c2f1e;
}
.pay-explain p:last-child { margin-bottom: 0; }

.pay-losing-head { margin-top: 10px !important; font-weight: 600; }

.pay-losing {
  margin: 0 0 7px;
  padding-left: 18px;
  font: 400 12px/1.6 var(--font-ui, Poppins, sans-serif);
  color: var(--maroon, #7b3335);
}

.pay-losing-note { color: #6b5628 !important; }
