/* app.perdura.org — Perdura brand, tuned for older eyes (builder-ux-spec §1). */
:root {
  /* Perdura reskin (BUILDER-24 §6, short-term token swap): legacy names, plum
     values — forest=plum-600 (6.7:1 on cream, same as the old forest), forest-deep=
     plum-800, sage=plum-400 (3.2:1 — borders and marks only, never body text),
     sage-wash=wisteria. Semantic --danger/--warn/--success stay green/amber/ember;
     the honest rename to --accent-* waits for Phase 4. */
  /* Type stack as a token, because the `font:` SHORTHAND resets the family.
     Any rule written as `font: 600 .8rem/1.2 sans-serif` silently opts out of
     the body stack and renders in the generic sans (Helvetica on macOS), so the
     step nav was in a different typeface from the app around it. Naming it
     means a shorthand can say var(--sans) and stay in the system. */
  --sans:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
  --serif:ui-serif,Georgia,'Times New Roman',serif;
  --mono:ui-monospace,SFMono-Regular,Menlo,monospace;
  --forest:#5d4a78; --forest-deep:#2e2439; --sage:#8f7cc0; --sage-wash:#e7e1f2;
  --cream:#f5f2ea; --paper:#fbf9f4; --ink:#26332b; --muted:#59685d; --line:#e5dfd1;
  --clay:#8a5138; --ember:#a1332b;
  /* BUILDER-21 Part A: one named radius scale (same pixels as before, now
     consistent) — sm=inputs/.err, md=.btn, lg=.card. --radius kept as an
     alias of --radius-lg so nothing that reads it directly has to change. */
  --radius-sm:10px; --radius-md:12px; --radius-lg:14px; --radius:var(--radius-lg);
  /* Semantic aliases — same values as today's ad-hoc ones, now named + reused
     by .err/focus below instead of repeating the hex/color-mix per spot. */
  --danger:var(--ember); --danger-wash:#fbeae8; --danger-line:#f0cfcb; --success:#3d5c47;
  /* BUILDER-2 SW-1: a third semantic tier (warn) for "in progress" / "action
     needed but not broken" — was a one-off inline #c0842e before; named now so
     the publish banner's amber states share one source. */
  --warn:#96650f; --warn-wash:#faf1e0; --warn-line:#ecd8ab;
  /* LOOP 15A item 2: the ring is the full brand color. The 55% tint measured
     2.35:1 on the cream page, under the 3:1 WCAG 1.4.11 asks of a focus
     indicator; --forest is about 7.7:1. */
  --focus-ring:var(--forest);
  /* ONE page gutter for header + content (they were misaligned on phones:
     head .9rem vs wrap 1.25rem). Phones override to 1rem below. */
  --gutter:1.25rem; --pad-panel:1.1rem 1.3rem;
  /* LOOP 14A item 1: the spaces around `+` are load-bearing. CSS reads
     `.86rem+.2vw` as two numbers side by side (the `+` belongs to `.2vw`), so
     clamp() was invalid, every var(--step-*) fell back to the inherited 16px,
     and headings, labels, hints and body all rendered the same size. Held by
     test/loop14a-builder.test.ts and a computed-size check in smoke.spec.ts.
     --step--1's floor is 1rem, not the .9rem it was written with: every hint
     and label has rendered at 16px since the bug, and fixing the scale should
     make headings bigger, not make a hint smaller for a reader in glasses on a
     phone (reversible, 2026-09-26). */
  --step--1:clamp(1rem, .96rem + .2vw, 1.06rem); --step-0:clamp(1.125rem, 1.09rem + .16vw, 1.19rem);
  --step-1:clamp(1.35rem, 1.25rem + .5vw, 1.7rem); --step-2:clamp(1.7rem, 1.5rem + 1vw, 2.3rem);
}
/* Contrast follows the OS/browser setting — no in-app toggle. Text size is
   rem-based throughout, so browser zoom / font-size settings scale everything. */
@media (prefers-contrast: more){
  :root{
    --ink:#131a15; --muted:#2e3a32; --line:#8d867a;
    --cream:#ffffff; --paper:#ffffff; --sage-wash:#e2d9f0;
    --forest:#4a3b60; --forest-deep:#241c2e;
  }
  .mut{color:var(--muted, #2e3a32)}
}
*,*::before,*::after{box-sizing:border-box} *{margin:0}
body{font-family:var(--sans);font-size:var(--step-0);
  line-height:1.6;color:var(--ink);background:var(--cream);-webkit-font-smoothing:antialiased}
h1,h2,h3{font-family:var(--serif);font-weight:600;line-height:1.15;text-wrap:balance}
h1{font-size:var(--step-2)} h2{font-size:var(--step-1)}
/* The `hidden` attribute must always win.
   Any rule that sets `display` on a class beats the attribute's default
   display:none, so a hidden element renders anyway. That has now shipped three
   times: .unpub-confirm left a confirmation box permanently open, .assist left
   AI chips showing on the story step, and .save-pill put an empty lozenge in the
   corner of every page in the app.
   83 classes in this file set display and could do it again, so this is the one
   place to fix it rather than 83. `!important` is deliberate and is the standard
   treatment for [hidden]; the per-class guards below stay as a record of where
   it has already hurt. Asserted in nav.spec.ts. */
[hidden]{display:none !important}
p{text-wrap:pretty} a{color:var(--forest)} img{max-width:100%;display:block}
:focus-visible{outline:3px solid var(--focus-ring);outline-offset:2px;border-radius:4px}
.wrap{max-width:44rem;margin-inline:auto;padding:0 var(--gutter)}
.card{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);padding:clamp(1.4rem,4vw,2.2rem)}
label{display:block;font-weight:600;font-size:var(--step--1);margin-bottom:.35rem}
/* Text-like fields fill their row; checkboxes and radios must NOT — the global
   width:100% used to inflate them into a full-width box that shoved the label
   into a one-word column (email prefs, review options). Excluding them here
   fixes it at the source so every current and future checkbox/radio is correct
   without a per-spot override. */
input:not([type=checkbox]):not([type=radio]),textarea,select{font:inherit;color:var(--ink);width:100%;background:#fff;
  border:1px solid var(--line);border-radius:var(--radius-sm);padding:.75rem .9rem}
input[type=checkbox],input[type=radio]{accent-color:var(--forest);width:auto;flex:none;margin:0}
input:focus-visible,textarea:focus-visible{outline:2px solid var(--forest);outline-offset:1px;border-color:var(--forest)}
textarea{resize:vertical;min-height:10rem;line-height:1.7}
.field{margin-bottom:1.15rem} .opt{font-weight:400;color:var(--muted)}
/* LOOP 12A item 11: an empty story shows the fork first; the tools, the
   heading panel and their hints wait until there is text or a chosen path. */
.story-empty .story-tools-wrap, .story-empty #organize-hint, .story-empty #headings-panel,
.story-empty .hold-until-started { display: none; }
.tools-ai-note { font-size: var(--step--1); margin: 0 0 .5rem; }
/* Password with a Show control (LOOP 12A item 10). */
.reveal-row{display:flex;gap:.5rem;align-items:stretch} .reveal-row input{flex:1;min-width:0}
.reveal-btn{min-height:44px;padding:.5rem 1rem;flex:none} .reveal-btn[hidden]{display:none}
/* font-family:inherit (LOOP 14A item 3): a <button> takes the browser's own
   control font (Arial on Windows and Chrome), so every .btn was in a different
   typeface from the words around it. */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:.5rem;font-family:inherit;font-weight:600;
  font-size:var(--step-0);padding:.8rem 1.4rem;border-radius:var(--radius-md);border:0;cursor:pointer;
  background:var(--forest);color:#fff;text-decoration:none;min-height:44px}
/* LOOP 15A item 3: a busy button is aria-disabled (it keeps focus), and looks
   like a disabled one. */
.btn:hover{filter:brightness(1.08)} .btn:disabled,.btn[aria-disabled="true"]{opacity:.65;cursor:default;filter:none}
/* Ghost variants draw their stroke as an inset shadow, not a border — a border
   added 3px of height next to a solid .btn at the same padding. */
.btn-ghost{background:transparent;color:var(--forest);box-shadow:inset 0 0 0 1.5px color-mix(in srgb,var(--forest) 35%,#fff)}
.err{color:var(--danger);font-size:var(--step--1);background:var(--danger-wash);border:1px solid var(--danger-line);
  border-radius:var(--radius-sm);padding:.6rem .8rem;margin-bottom:1rem}
.err:empty{display:none}
.saved{color:var(--muted);font-size:var(--step--1)}
/* BUILDER-22: the date-echo's future/impossible-order note, tokenized —
   was an inline style.color='var(--ember,var(--ember, #a1332b))' set from JS. Color only,
   matching the original exactly (no other property changed = no visual
   regression beyond the token swap itself). */
.date-echo.warn{color:var(--danger)}
.steps{list-style:none;padding:0;display:flex;gap:.5rem;flex-wrap:wrap;margin:0 0 1.4rem}
.steps a{display:inline-flex;align-items:center;gap:.4rem;text-decoration:none;color:var(--muted);white-space:nowrap;
  font-size:var(--step--1);font-weight:600;padding:.4rem .8rem;border-radius:999px;background:var(--paper);border:1px solid var(--line)}
.steps .on a{background:var(--sage-wash);color:var(--forest);border-color:transparent}
.steps .done a{color:var(--forest);border-color:color-mix(in srgb,var(--sage) 60%,#fff)}
.steps .step-check{color:var(--forest)}
/* Five pills on one row down to small tablets — numbers dropped, so trim chrome.
   Trimmed chrome must not trim the finger: hold the 44px tap floor on touch sizes. */
@media (max-width:54rem){
  .steps{gap:.35rem}
  .steps a{padding:.35rem .6rem;gap:.3rem;font-size:calc(var(--step--1) * .94);min-height:2.75rem}
}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* Review checklist — mark | label + summary on one line, actions on their own
   quiet row beneath (never mid-sentence). */
.review-list{list-style:none;padding:0;margin:0;display:flex;flex-direction:column}
.review-list li{display:flex;gap:.7rem;align-items:flex-start;padding:.65rem 0;border-top:1px solid var(--line)}
.review-list li:first-child{border-top:0;padding-top:.15rem}
.review-mark{flex:none;width:1.4rem;height:1.4rem;border-radius:50%;display:inline-flex;align-items:center;
  justify-content:center;font-size:.85rem;font-weight:700;background:var(--cream);color:var(--muted);
  border:1px solid var(--line);transform:translateY(.15rem)}
.review-mark.ok{background:var(--sage-wash);color:var(--forest);border-color:transparent}
.review-body{display:flex;flex-direction:column;min-width:0}
.review-line{margin:0;display:flex;gap:.35rem .7rem;align-items:baseline;flex-wrap:wrap}
.review-step{font-weight:600;color:var(--forest);text-decoration:none}
.review-step:hover{text-decoration:underline}
.review-note{font-size:var(--step--1)}
.review-actions{display:flex;gap:.5rem;flex-wrap:wrap;margin:.55rem 0 .1rem}
.review-actions-lead{flex-basis:100%;font-size:var(--step--1);color:var(--muted)}
/* Quiet action chips; ✦ marks the AI one (same grammar as the story toolbox). */
/* 44px targets across the builder (LOOP 10 item 11): .chip was 34px. */
.chip{display:inline-flex;align-items:center;gap:.35rem;padding:.3rem .8rem;min-height:44px;border-radius:999px;
  border:1px solid var(--line);background:var(--paper);color:var(--forest);font:inherit;font-size:var(--step--1);
  font-weight:600;text-decoration:none;cursor:pointer}
.chip:hover{border-color:var(--sage);background:var(--sage-wash)}
.chip:focus-visible{outline:2px solid var(--forest);outline-offset:2px}
.chip:disabled,.chip[aria-disabled="true"]{opacity:.6;cursor:default}
.chip-ai{border-color:color-mix(in srgb,var(--clay) 40%,#fff);color:var(--clay)}
.chip-ai:hover{border-color:var(--clay);background:color-mix(in srgb,var(--clay) 8%,#fff)}

/* Live-status light — BUILDER-2 SW-1: the honest publish-state banner. State
   is word (#live-text) + SHAPE (this ::before glyph) + color, never color
   alone (ux-ui-practices.md, colorblind-safe). The dot classes themselves are
   set by the existing live-check/publish JS, unchanged — this only adds the
   glyph + the banner-level color response via :has(), so no script edits were
   needed to make the container itself reflect state. */
.live-status{display:flex;align-items:center;gap:.55rem;font-weight:600;margin:0}
.live-dot{width:.8rem;height:.8rem;border-radius:50%;background:var(--line);flex:none;position:relative}
.live-dot::before{content:"";position:absolute;inset:-.5rem;display:flex;align-items:center;justify-content:center;
  font-size:1rem;line-height:1}
.live-dot.up{background:var(--success);box-shadow:0 0 0 4px color-mix(in srgb,var(--success) 18%,transparent)}
.live-dot.up::before{content:"✓"}
.live-dot.building{background:#b8860b;animation:live-pulse 1.6s ease-in-out infinite}
.live-dot.building::before{content:"◐"}
.live-dot.down{background:var(--ember)}
.live-dot.down::before{content:"⚠"}
@keyframes live-pulse{50%{opacity:.45}}
@media (prefers-reduced-motion: reduce){.live-dot.building{animation:none}}

/* The banner itself — one prominent, honest status card at the top of a
   published Review page (relocated from the buried Settings card). Color
   follows the SAME classes the script already sets on #live-dot (:has(), no
   extra JS needed for this part) plus [data-unpub] (server-rendered from
   hasUnpublished) to tell "live & current" (success) apart from "live but you
   have unpublished edits" (warn) — both carry .live-dot.up, so color alone
   can't distinguish them; the word always does too ("Live and up to date" vs
   "You've made edits that aren't live yet"). .is-publishing is the one class
   the timer script toggles (renderTimer/finishTimer/stallTimer) so a fresh
   publish reads as "in progress," not the neutral default, before the first
   live-check tick lands. */
.pub-banner{border:1px solid var(--line);border-radius:var(--radius);padding:1.1rem 1.3rem;
  margin-bottom:1.2rem;background:var(--paper)}
.pub-banner:has(.live-dot.up){background:var(--sage-wash);border-color:transparent}
.pub-banner[data-unpub="1"]:has(.live-dot.up){background:var(--warn-wash);border-color:var(--warn-line)}
.pub-banner:has(.live-dot.building),.pub-banner.is-publishing{background:var(--warn-wash);border-color:var(--warn-line)}
.pub-banner:has(.live-dot.down){background:var(--danger-wash);border-color:var(--danger-line)}
.pub-banner[data-unpub="1"] .live-dot.up::before{content:"●"}
.pub-banner[data-unpub="1"] .live-status{color:var(--warn)}
.pub-banner:has(.live-dot.down) .live-status{color:var(--danger)}

/* Discouraged actions read as text, tinted toward caution */
.unpublish-link{color:var(--muted)}
.unpublish-link:hover{color:var(--ember)}
.unpub-line{margin-top:.9rem;margin-bottom:0}
/* The take-offline confirmation is hidden until the link is clicked. The
   [hidden] selector carries higher specificity than the base rule, so `hidden`
   always wins — never rely on an inline display that would defeat it (that bug
   left this box permanently open). */
.unpub-confirm{display:flex;gap:.6rem;flex-wrap:wrap;align-items:center;margin-top:.7rem}
.unpub-confirm[hidden]{display:none}
/* A committed legacy contact — a confirmed entry, not an editable form field */
.succ-committed{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;
  margin-bottom:.6rem;padding:.7rem .9rem;background:var(--sage-wash);border-radius:10px}
.succ-committed .succ-who{font-size:.95rem}
.succ-committed .succ-email{font-size:var(--step--1);word-break:break-all;margin-top:.05rem}
.succ-committed .succ-remove{flex:none;white-space:nowrap}

/* Locked web address, shown as text once the memorial is live */
.url-locked{display:flex;align-items:baseline;gap:.6rem;flex-wrap:wrap;margin:0;font-weight:600}
.url-locked a{color:var(--forest)}
.url-lock{font-size:var(--step--1);font-weight:400;color:var(--muted)}
.site-head{background:color-mix(in srgb,var(--cream) 88%,transparent);border-bottom:1px solid var(--line)}
.head-in{display:flex;align-items:center;gap:1rem;min-height:4.2rem;max-width:64rem;margin-inline:auto;padding:0 var(--gutter)}
.brand{display:inline-flex;align-items:center;gap:.5rem;text-decoration:none;color:var(--forest-deep);
  font-family:ui-serif,Georgia,serif;font-weight:600;font-size:1.15rem}
.brand svg{width:22px;height:26px}
/* Corp-site lockup: letterspaced caps beside the solid-cut P. */
.brand-wm{text-transform:uppercase;letter-spacing:.13em}
.head-right{margin-left:auto;display:flex;gap:1rem;align-items:center;font-size:var(--step--1)}
/* Bare header links (signed-out "Sign in") get a 44px hit area without moving:
   padding grows the target, the negative margin gives the space right back. */
.head-right>a:not(.head-dash){display:inline-flex;align-items:center;min-height:44px;
  padding:.35rem .5rem;margin:-.35rem -.5rem}
.text-size{min-width:44px;min-height:44px;border:1px solid var(--line);background:var(--paper);
  border-radius:10px;cursor:pointer;font-family:ui-serif,Georgia,serif;font-size:1.05rem;
  font-weight:600;color:var(--ink);display:inline-flex;align-items:center;justify-content:center}
.text-size:hover{border-color:var(--sage)}
main{padding:clamp(1.6rem,5vw,3rem) 0 4rem}
.mut{color:var(--muted)}
.arch-list{list-style:none;padding:0;display:flex;flex-direction:column;gap:.8rem}
/* (The old ".arch-list a" block rule is gone on purpose — it boxed every link
   inside the cards like a form input. Cards style themselves via .arch-item.) */
.pv{background:#fff;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}

/* Born/Passed pair: labels can wrap, inputs stay aligned on one baseline. */
/* Row gap matches .field's 1.15rem rhythm when the pair stacks on phones. */
.yr-row{display:grid;grid-template-columns:1fr 1fr;gap:1.15rem 1rem;align-items:end}
.yr-row label{min-height:1.5em}
@media (max-width:30rem){.yr-row{grid-template-columns:1fr}}
/* LOOP 25 item 3 (BUILDER-34): the Share card's three buttons. One even row
   when the card is wide enough for three with a wide font (35rem, measured:
   "Share by email" in Verdana needs about 11rem), one full-width column
   below that. Never two and one. */
.share-actions-wrap{container-type:inline-size}
.share-actions{display:grid;grid-template-columns:minmax(0,1fr);gap:.6rem}
.share-actions .btn{width:100%;padding-inline:.9rem;white-space:nowrap}
@container (min-width:35rem){.share-actions{grid-template-columns:repeat(3,minmax(0,1fr))}}
/* LOOP 25 item 1 (BUILDER-32): Basics' dates, every piece placed in ONE grid.
   Labels share row 1 and sit on its bottom edge, so a label that wraps keeps
   the two boxes level in row 2; the published-page choice spans row 4 at the
   card's width. One column on a phone, the choice under Born. */
.date-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);column-gap:1rem;align-items:end}
.date-grid>*{min-width:0}
.date-grid .dg-born-l{grid-area:1/1}.date-grid .dg-passed-l{grid-area:1/2}
.date-grid .dg-born-i{grid-area:2/1}.date-grid .dg-passed-i{grid-area:2/2}
.date-grid .dg-born-e{grid-area:3/1;align-self:start}.date-grid .dg-passed-e{grid-area:3/2;align-self:start}
.date-grid .dg-choice{grid-area:4/1/5/3}
/* The published-page choice: two radios whose labels wrap (a select clipped). */
.bdp-choice{border:0;margin:0;padding:0;min-width:0}
.bdp-choice legend{font-weight:600;font-size:var(--step--1);margin:0 0 .2rem;padding:0}
.bdp-choice label{display:flex;gap:.55rem;align-items:center;font-weight:400;font-size:var(--step-0);margin:0;min-height:44px}
@media (max-width:30rem){
  .date-grid{grid-template-columns:minmax(0,1fr)}
  .date-grid .dg-born-l{grid-area:1/1}.date-grid .dg-born-i{grid-area:2/1}.date-grid .dg-born-e{grid-area:3/1}
  .date-grid .dg-choice{grid-area:4/1}
  .date-grid .dg-passed-l{grid-area:5/1;margin-top:1.15rem}.date-grid .dg-passed-i{grid-area:6/1}.date-grid .dg-passed-e{grid-area:7/1}
}

/* Story blocks editor */
.blk{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);padding:1rem;margin-bottom:.9rem}
.blk-bar{display:flex;align-items:center;justify-content:space-between;margin-bottom:.6rem}
.blk-kind{font-size:var(--step--1);font-weight:600;color:var(--muted)}
.blk-btns{display:flex;gap:.4rem}
.bb{min-width:44px;min-height:44px;border:1px solid var(--line);background:#fff;border-radius:10px;
  font-size:1.05rem;cursor:pointer;color:var(--ink)}
.bb:hover{border-color:var(--sage)}
.blk img{max-height:220px;border-radius:10px;margin-bottom:.6rem}
.undo{position:fixed;left:50%;transform:translateX(-50%);bottom:1.2rem;background:var(--ink);color:#fff;
  padding:.7rem 1rem;border-radius:12px;display:flex;gap:.8rem;align-items:center;z-index:50;font-size:var(--step--1)}
.undo button{background:transparent;border:0;color:#a9c0ad;font-weight:700;cursor:pointer;font-size:inherit;min-height:44px}
/* The Undo toast stays until the next edit (LOOP 10 item 4), so the end of the
   page scrolls clear of it rather than hiding under it. */
body:has(.undo){padding-bottom:5.5rem}

/* AI assist panel (story step) */
.ai-panel{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);padding:var(--pad-panel);margin-bottom:1.2rem}
.ai-panel > summary{cursor:pointer;font-weight:600;color:var(--forest);list-style:none;min-height:44px;display:flex;align-items:center}
.ai-panel > summary::-webkit-details-marker{display:none}
.ai-panel > summary::before{content:"✦";margin-right:.5rem;color:var(--clay)}
.ai-panel[open] > summary{margin-bottom:.4rem}

/* Publish step */
/* SW-2 (review-page-ia.md §5): the custom-domain request as its own bordered
   callout in Settings, not one more plain field in the flow — same boxed
   language as .blk, so it reads as an offer worth noticing. */
.domain-callout{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  padding:1rem 1.1rem;margin:.9rem 0}
/* A committed domain request (has a status) is a read-only card, never an
   editable-looking form (ux-ui-practices.md) — reuses the same success/warn
   language as the publish banner so "requested/in-setup/live" reads
   consistently with every other status surface on this page. */
.domain-status{background:var(--sage-wash);border:1px solid transparent;border-radius:var(--radius);
  padding:.85rem 1rem;margin:.9rem 0;font-size:var(--step--1)}
.domain-status.pending{background:var(--warn-wash);border-color:var(--warn-line)}
.url-row{display:flex;align-items:stretch;border:1px solid var(--line);border-radius:10px;overflow:hidden;background:#fff}
.url-row input{border:0;border-radius:0;flex:1;min-width:0}
.url-row input:focus-visible{outline:2px solid var(--forest);outline-offset:-2px}
.url-suffix{display:flex;align-items:center;padding:0 .8rem;background:var(--band-sage,var(--sage-wash));color:var(--forest);font-weight:600;white-space:nowrap;font-size:var(--step--1)}
.slug-status{margin:.4rem 0 0}

/* Header dashboard link */
.head-dash { display: inline-flex; align-items: center; gap: .3rem; color: var(--forest);
  font-weight: 600; text-decoration: none; padding: .4rem .8rem; border-radius: 999px;
  border: 1px solid var(--line); min-height: 36px; font-size: var(--step--1); }
.head-dash:hover, .head-dash:focus-visible { background: var(--sage-wash); border-color: var(--sage); }
/* Header nav: icon + label at every size (LOOP 14A item 5). Phones used to get
   the icons alone, on the reasoning that a house and a person are universally
   understood. NN/g's icon-usability research says a label is what makes an
   icon usable, and "Account" behind a person outline is not a guess an older
   reader should have to make. On the narrowest phones the words stay and the
   icons go, because the words carry the meaning and the row has to fit. */
.head-dash svg { width: 17px; height: 17px; }
@media (max-width: 40rem) {
  :root { --gutter: 1rem; } /* header and content share the same 16px edge */
  .head-right { gap: .5rem; }
  .head-in { gap: .6rem; }
  .head-dash { padding: .5rem .75rem; min-height: 44px; border-radius: 12px; }
}
@media (max-width: 26rem) {
  .head-dash svg { display: none; }
  /* Measured at 375px (loop 14 screenshot pass): with both words the row ran
     4px past the edge. A little less padding and letter spacing fits it. */
  .head-in { gap: .4rem; }
  .head-right { gap: .35rem; }
  .head-dash { padding: .45rem .6rem; }
  .brand { font-size: 1.05rem; gap: .4rem; }
  .brand-wm { letter-spacing: .1em; }
}
/* LOOP 23 item 21: at 360px with a wider phone font (CI's Linux fallback,
   Verdana in a local probe) the row still ran 11px past the edge, and the
   whole page scrolled sideways. Tighter spacing covers the measured case; the
   brand is the one thing allowed to give if a font is wider still, so the
   page never scrolls whatever the phone's font. */
@media (max-width: 23rem) {
  .head-right { gap: .25rem; }
  .head-dash { padding: .45rem .5rem; }
  .brand-wm { letter-spacing: .06em; }
}
/* At 320px (the smallest phones) even that cut letters off the wordmark. A
   half word reads as broken, so there the P mark stands alone; the link keeps
   its "Perdura home" name. */
@media (max-width: 21rem) { .brand-wm { display: none; } }
.brand { min-width: 0; flex-shrink: 1; overflow: hidden; }
.head-right { flex-shrink: 0; }

/* Tagline starter-idea chips */
.tagline-ideas { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .6rem; align-items: center; }
/* LOOP 12A item 12: questions that help a family find their own line. */
.tagline-qs { margin-top: .5rem; font-size: var(--step--1); color: var(--muted); }
.tagline-qs p { margin: 0 0 .2rem; } .tagline-qs ul { margin: 0; padding-left: 1.1rem; }
.ideas-label { font-size: var(--step--1); color: var(--muted); margin-right: .2rem; }
/* min-height 44px (LOOP 9 item 17): the tap-target floor every other control
   here meets; these chips measured about 30px. */
.idea-chip { background: var(--paper); border: 1px solid var(--line); border-radius: 999px;
  min-height: 44px; padding: .35rem .8rem; font-size: var(--step--1); color: var(--ink); cursor: pointer;
  text-align: left; line-height: 1.3; transition: background .12s ease, border-color .12s ease; }
.idea-chip:hover, .idea-chip:focus-visible { background: var(--sage-wash); border-color: var(--sage); }

/* Sticky autosave pill — always visible, incl. mobile */
/* A quiet confirmation chip — NOT a primary-colored button, and never full-width
   (it used to cover the control beneath it). Distinct color so "Saved" never
   reads as an action; compact so it stays out of the way (Garrett 2026-07-16). */
.save-pill { position: fixed; right: 1rem; bottom: 1rem; z-index: 60;
  display: inline-flex; align-items: center; gap: .3rem; max-width: min(20rem, calc(100vw - 1.6rem));
  background: var(--sage-wash); color: var(--forest); border: 1px solid var(--sage);
  padding: .4rem .8rem; border-radius: 999px; font-size: var(--step--1); font-weight: 600;
  box-shadow: 0 4px 14px rgba(43,38,32,.14); }
.save-pill[data-kind="saving"] { background: var(--cream); color: var(--muted); border-color: var(--line); }
/* An error can wrap to multiple lines — the pill (999px) radius turns that into
   a blob. A clean rounded rectangle reads as a proper message, left-aligned. */
.save-pill[data-kind="err"] { background: #fbeae7; color: var(--ember, #a1332b); border-color: #e6b3ab;
  border-radius: 10px; text-align: left; }
@media (max-width: 40rem) { .save-pill { left: auto; right: .8rem; bottom: .8rem; } }

/* Inline form messages — success vs error */
.msg { font-size: var(--step--1); padding: .55rem .85rem; border-radius: 10px; margin-top: 1rem; }
.msg:empty { display: none; }
.msg-ok { background: var(--sage-wash); color: var(--forest); border: 1px solid var(--sage); }
.msg-err { background: #fbecea; color: var(--ember, #a1332b); border: 1px solid #e5b8b2; }

/* Memories inbox */
.mem-tabs{display:flex;gap:.4rem;flex-wrap:wrap;border-bottom:1px solid var(--line);margin-bottom:.4rem}
.mem-tab{background:transparent;border:0;border-bottom:2px solid transparent;color:var(--muted);
  font:inherit;font-weight:600;font-size:var(--step--1);padding:.6rem .7rem;cursor:pointer;min-height:44px}
.mem-tab[aria-selected="true"]{color:var(--forest);border-bottom-color:var(--forest)}
.mem-count{display:inline-block;min-width:1.4em;text-align:center;background:var(--sage-wash);color:var(--forest);
  border-radius:999px;padding:0 .4em;font-size:.8em;margin-left:.25rem}
.mem-card{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  padding:var(--pad-panel);margin-bottom:.9rem}
.mem-head{display:flex;justify-content:space-between;align-items:baseline;gap:1rem;margin-bottom:.5rem}
.mem-head .mut{font-size:var(--step--1)}
.mem-msg{white-space:pre-line;margin-bottom:.6rem}
.mem-photos{display:flex;flex-wrap:wrap;gap:.5rem;margin-bottom:.6rem;align-items:center}
.mem-photos img{width:88px;height:88px;object-fit:cover;border-radius:10px;border:1px solid var(--line)}
.mem-audio{width:100%;max-width:24rem}
.mem-video-file{width:100%;max-width:24rem;border-radius:10px;border:1px solid var(--line)}
.mem-video{font-size:var(--step--1);margin-bottom:.6rem}
.mem-actions{display:flex;gap:.6rem;align-items:center;flex-wrap:wrap;margin-top:.7rem}
.mem-actions .btn{padding:.55rem 1rem;font-size:var(--step--1);min-height:40px}
.mem-badge{font-size:var(--step--1);font-weight:600;color:var(--muted);background:var(--cream);
  border:1px solid var(--line);border-radius:999px;padding:.25rem .7rem}
.mem-badge.ok{color:var(--forest);background:var(--sage-wash);border-color:transparent}
.mem-badge.story{color:var(--clay);background:#f3e9e3;border-color:transparent}
.mem-badge.correction{color:var(--ember);background:var(--danger-wash);border-color:transparent}
.mem-texture{list-style:none;padding:.55rem .8rem;margin:0 0 .6rem;background:var(--sage-wash);
  border-radius:10px;font-size:var(--step--1)}
.mem-texture li{margin:.15rem 0}
.mem-check{display:flex;gap:.6rem;align-items:flex-start;margin-bottom:.7rem;font-weight:400}
.mem-check input{width:auto;margin-top:.25rem}
.btn-link{background:transparent;border:0;color:var(--forest);font:inherit;font-weight:600;
  font-size:var(--step--1);cursor:pointer;text-decoration:underline;text-underline-offset:2px;
  padding:.3rem 0;min-height:40px}
.btn-link:hover{color:var(--forest-deep)}
/* A removed row's Undo line, in the row's place (details, wishes songs). */
.row-undo{display:flex;gap:.7rem;align-items:center;flex-wrap:wrap;margin:0 0 .5rem;
  font-size:var(--step--1);color:var(--muted)}

/* Weave preview modal */
.wv-modal{position:fixed;inset:0;z-index:80;background:rgba(38,51,43,.45);
  display:flex;align-items:flex-start;justify-content:center;padding:1.2rem;overflow-y:auto}
.wv-modal[hidden]{display:none}
.wv-sheet{background:var(--paper);border-radius:var(--radius);max-width:40rem;width:100%;
  margin-block:auto;padding:1.3rem 1.4rem;box-shadow:0 18px 50px rgba(38,51,43,.3)}
.wv-sheet-head{display:flex;justify-content:space-between;align-items:center;gap:1rem;margin-bottom:.6rem}
.wv-close{background:transparent;border:0;font-size:1.2rem;color:var(--muted);cursor:pointer;min-height:40px;min-width:40px}
.wv-body{display:flex;flex-direction:column;gap:.55rem;margin:.4rem 0}
.wv{border:1px solid var(--line);border-radius:10px;padding:.7rem .85rem;background:#fff}
.wv p{font-size:var(--step--1)}
.wv-tag{display:inline-block;font-size:.75rem;font-weight:700;text-transform:uppercase;
  letter-spacing:.03em;margin-bottom:.3rem}
/* LOOP 10 item 8: no opacity on text. Faded to .72, the unchanged paragraphs
   fell under 4.5:1; they read as "background" by their panel instead. */
.wv.unchanged{background:var(--cream);border-style:dashed}
.wv.unchanged .wv-tag{color:var(--muted)}
.wv.edited .wv-tag{color:var(--clay)}
.wv.new .wv-tag{color:var(--forest)}
/* Inline word-level diff — the reader sees exactly the words that changed, in
   place: added words highlighted, removed words struck. No two-paragraph re-read. */
.wv ins{background:color-mix(in srgb,var(--forest) 15%,transparent);color:var(--forest);
  text-decoration:none;border-radius:3px;padding:0 .12em;font-weight:600}
.wv del{color:var(--muted);text-decoration:line-through;text-decoration-color:var(--clay);text-decoration-thickness:1px}
.wv-before{font-size:.82rem;color:var(--muted);font-style:italic;margin-top:.3rem}
/* Inline word-diff vs stacked before/after: strikethrough inside a sentence is
   hard to read on a phone (older eyes, small type), so narrow viewports get the
   clean new sentence + a quiet "was:" line instead. Renderers emit both. */
.wv-stack{display:none}
@media (max-width: 30rem){
  .wv .wv-diff{display:none}
  .wv .wv-stack{display:block}
}
.ai-gaps{border:1px solid var(--sage);border-radius:10px;padding:.6rem .85rem;margin-top:.7rem;background:var(--sage-wash)}
.ai-gaps-lead{font-weight:600;font-size:var(--step--1);color:var(--forest)}
.ai-gaps ul{margin:.3rem 0 0;padding-left:1.1rem;font-size:var(--step--1)}

/* Story step — grouped toolbox + panels (previously unstyled) */
.story-tools-wrap{display:flex;flex-direction:column;gap:.9rem;margin:.4rem 0 1rem;
  background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);padding:var(--pad-panel)}
.tools-label{font-size:.78rem;font-weight:600;color:var(--muted);margin-bottom:.45rem;
  text-transform:uppercase;letter-spacing:.04em}
/* LOOP 25 item 7: found by the width sweep. A wrapping flex row put two tools
   on one line and the third alone under them at 1280px, the shape Garrett
   flagged on the Share card (BUILDER-34). One even row of three where the
   group is wide enough, one column below that; a long label wraps inside its
   own button instead of pushing a button down. */
.story-tools-group{container-type:inline-size}
.story-tools{display:grid;grid-template-columns:minmax(0,1fr);gap:.6rem}
@container (min-width:36rem){.story-tools{grid-template-columns:repeat(3,minmax(0,1fr))}}
.story-tools .btn{padding:.6rem 1.05rem;font-size:var(--step--1);min-height:44px;white-space:normal;text-align:center;line-height:1.3}
/* LOOP 25 item 7: a step page's foot (Preview, Back, Next). It broke one and
   two at 360px on Details. One row where it fits with a wide font, one column
   below that. */
.foot-row-wrap{container-type:inline-size;margin-top:1.4rem}
.foot-row{display:grid;grid-template-columns:minmax(0,1fr);gap:.6rem}
@container (min-width:36rem){.foot-row{display:flex;flex-wrap:nowrap;gap:.8rem}}
/* Button grammar (consistent everywhere):
   .tool-magic  = AI writes something for you (gradient — the one "magic" accent)
   .btn-ai-ghost = secondary AI action (quiet, clay-tinted)
   .btn ghost/solid = mechanical actions and human decisions (no sparkle) */
.tool-magic{background:linear-gradient(120deg,var(--forest),var(--forest-deep))}
.btn-ai-ghost{background:transparent;color:var(--clay);box-shadow:inset 0 0 0 1.5px color-mix(in srgb,var(--clay) 40%,#fff)}
.btn-ai-ghost:hover{background:color-mix(in srgb,var(--clay) 8%,transparent);filter:none}
.tool-hint{font-size:var(--step--1);margin:.2rem 0 1rem;max-width:42rem}
.tool-hint:empty{display:none}
.import-panel{margin:0 0 1rem;padding:var(--pad-panel)}
.photos-panel{margin-bottom:1rem}
.photo-strip{display:flex;gap:.6rem;flex-wrap:wrap}
/* LOOP 14A item 8: wider chips (132px held "In the story" and "In the album"
   only at 11.5px) and, under 30rem, one photo per row with every control at
   the builder's hint size and the 44px floor. See the block after .photo-place. */
.photo-chip{position:relative;width:11rem;display:flex;flex-direction:column;gap:.35rem}
/* BUILDER-16 Phase 3: an explicit display on .photo-chip ties the UA's
   [hidden]{display:none} on specificity — author wins, so `chip.hidden = true`
   silently did nothing until this rule outranks it. Found live via QA (a
   collapsed chip stayed visible even though .hidden was set correctly). */
.photo-chip[hidden]{display:none}
.photo-chip img{width:100%;height:120px;object-fit:cover;border-radius:10px;border:1px solid var(--line)}
.photo-cap{width:100%;font:inherit;font-size:var(--step--1);padding:.35rem .5rem;border:1px solid var(--line);border-radius:8px;background:var(--paper)}
/* opacity:1 on purpose: Firefox fades placeholders by default, and the muted
   color is already as light as text can be here (LOOP 10 item 8). */
.photo-cap::placeholder{color:var(--muted);opacity:1}
/* The caption box's visible label (it had only an aria-label). */
.photo-cap-l{font-size:.8rem;font-weight:600;color:var(--ink);margin-bottom:-.15rem}
/* A 44px target around the same 28px circle (LOOP 10 item 11): the button is
   transparent and the circle is drawn behind the ✕ by ::before, inside the
   button's own stacking context (isolation), so it never slips under the chip. */
.photo-x{position:absolute;top:-16px;right:-16px;width:44px;height:44px;border:0;background:transparent;
  isolation:isolate;cursor:pointer;font-size:.85rem;line-height:1;color:var(--ink)}
.photo-x::before{content:"";position:absolute;inset:8px;border-radius:50%;border:1px solid var(--line);background:#fff;z-index:-1}
.photo-x:hover{color:var(--ember)}
.photo-x:hover::before{border-color:var(--ember)}
.ai-preview{margin-top:.4rem}
/* Checkbox row inside an AI preview — the global input{width:100%} would blow it
   out, so pin the box to auto width and lay the label out beside it. */
.ai-fill-opt{display:flex;gap:.5rem;align-items:flex-start;margin:.95rem 0 .1rem;font-size:var(--step--1);cursor:pointer;line-height:1.5}
.ai-fill-opt input[type="checkbox"]{flex:none;width:auto;min-height:0;margin:.2rem 0 0}
.ai-preview-body{background:#fff;border:1px solid var(--line);border-radius:10px;
  padding:.9rem 1rem;margin-top:.7rem;max-height:24rem;overflow:auto;display:flex;flex-direction:column;gap:.7rem}
.ai-preview-body p{font-size:var(--step--1)}
.step-foot{display:flex;gap:1rem;align-items:center;justify-content:space-between;flex-wrap:wrap;margin-top:1.6rem}
.foot-preview{font-weight:600;font-size:var(--step--1)}

/* "Add a few at once" bulk entry (details step) */
.bulk-panel{margin-top:.7rem;background:var(--cream);border:1px solid var(--line);border-radius:10px;padding:.9rem 1rem}
.bulk-panel textarea{min-height:6rem;font-size:var(--step--1)}
.bulk-preview{list-style:none;padding:0;margin:.5rem 0 0;display:flex;flex-direction:column;gap:.25rem;font-size:var(--step--1)}
.bulk-preview li{background:#fff;border:1px solid var(--line);border-radius:8px;padding:.35rem .6rem}
.bulk-preview:empty{display:none}
.bulk-panel .btn{padding:.55rem 1rem;font-size:var(--step--1);min-height:40px}

/* Story orientation strip + sources cards + merge CTA */
.story-status{display:flex;gap:.4rem .5rem;flex-wrap:wrap;align-items:center;
  font-size:var(--step--1);margin:-.5rem 0 1rem}
.story-status > *:not(:first-child)::before{content:"·";margin-right:.5rem;color:var(--line)}
.story-status .btn-link{padding:.2rem 0;min-height:44px}
.src-card{border-top:1px solid var(--line);padding-top:.7rem;margin-top:.7rem}
.src-title{font-size:var(--step--1);margin-bottom:.4rem}
.src-content{white-space:pre-wrap;font-size:var(--step--1);background:var(--cream);
  border:1px solid var(--line);border-radius:8px;padding:.7rem;max-height:32rem;overflow:auto}
.src-content.clamped{max-height:9.5rem;overflow:hidden;position:relative}
.src-content.clamped::after{content:"";position:absolute;left:0;right:0;bottom:0;height:3rem;
  background:linear-gradient(transparent,var(--cream))}
.src-more{margin-top:.3rem}
.src-merge{background:var(--sage-wash);border-radius:10px;padding:.9rem 1rem;margin-top:.8rem}

/* Voices on the memorial (story step) */
.voice-row{display:flex;gap:.8rem;align-items:flex-start;background:var(--paper);
  border:1px solid var(--line);border-radius:var(--radius);padding:.8rem 1rem;margin-bottom:.7rem}
.voice-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:.5rem}
.voice-title{font-weight:600}
.voice-main audio{width:100%;max-width:26rem}
.voice-btns{display:flex;gap:.4rem}
.bb.del:hover{border-color:var(--ember);color:var(--ember)}
@media (max-width:30rem){.voice-row{flex-direction:column}.voice-btns{align-self:flex-end}}

/* Voice recording state */
.rec-btn.rec-on{background:var(--danger-wash, #fbeae8);border-color:var(--ember);color:var(--ember);animation:rec-pulse 1.6s ease-in-out infinite}
@keyframes rec-pulse{50%{box-shadow:0 0 0 6px rgba(161,51,43,.12)}}
@media (prefers-reduced-motion: reduce){.rec-btn.rec-on{animation:none}}
/* .btn's display:inline-flex otherwise beats the bare [hidden] UA rule —
   the same footgun documented above for .unpub-confirm/.theme-modal (found
   here 2026-07-25 building the plan interview's per-card mic button). */
.rec-btn[hidden]{display:none}

/* Theme chooser */
.theme-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: .6rem; margin-top: .5rem; }
.theme-card { background: var(--paper); border: 2px solid var(--line); border-radius: 12px; padding: .5rem;
  cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: .4rem; }
.theme-card[data-active="true"] { border-color: var(--forest); }
.theme-swatch { width: 100%; height: 42px; border-radius: 8px; border: 1px solid var(--line);
  display: flex; align-items: flex-end; padding: 5px; }
.theme-swatch > span { width: 40%; height: 12px; border-radius: 4px; }
.theme-name { font-size: var(--step--1); font-weight: 600; color: var(--ink); }
@media (max-width: 40rem) { .theme-grid { grid-template-columns: repeat(2, 1fr); } }

/* Inbox weave = secondary AI action (clay, per the button grammar) */
.mem-weave{color:var(--clay)}
.mem-weave:hover{color:#6e3f2a}

/* Quiet countdown, only in the last 15% of a long field */
.char-left{font-size:.78rem;color:var(--muted);align-self:center;white-space:nowrap}

/* Read-through Q&A: answer questions where they're asked */
.qa-list{margin:.4rem 0 0;padding:0;list-style:none;display:flex;flex-direction:column;gap:.9rem}
.qa-item{border-top:1px solid color-mix(in srgb,var(--line) 60%,transparent);padding-top:.7rem}
.qa-issue{font-weight:600;margin-bottom:.45rem}
.qa-opts{display:flex;flex-direction:column;gap:.4rem;align-items:flex-start}
.qa-opts .btn{font-size:var(--step--1);padding:.5rem .9rem;min-height:40px;text-align:left;white-space:normal}
.qa-answer{display:flex;flex-direction:column;gap:.5rem}
.qa-answer textarea{min-height:3.2rem;font-size:var(--step--1)}
.qa-answer .btn{align-self:flex-start;font-size:var(--step--1);padding:.5rem 1rem;min-height:40px}

/* ── Rules merged from the previously-served copy (dashboard cards, skip link,
   photo strip, AI preview, etc.) — one file now, so styles can't fork again. ── */

.skip-link{position:absolute;left:.5rem;top:-3rem;z-index:100;background:var(--forest);color:#fff;
  padding:.6rem 1rem;border-radius:10px;font-weight:600;text-decoration:none;transition:top .15s ease}

.skip-link:focus{top:.5rem}

/* One cohesive card per archive: name (left) + Memories (right), one row that
   wraps on narrow screens. */
/* Interview mode toggle — one at a time (gentle default) / all at once (power path) */
/* 44px tall, in the builder's own type (was 600 .8rem/1 sans-serif, about 29px;
   LOOP 10 item 11). */
.iv-mode{border:0;background:none;border-radius:999px;padding:.4rem .85rem;min-height:44px;font:600 var(--step--1)/1.2 var(--sans);color:var(--muted);cursor:pointer}
.iv-mode.on{background:var(--forest, #5d4a78);color:var(--cream, #f5f2ea)}
.iv-mode:focus-visible{outline:2px solid var(--forest, #5d4a78);outline-offset:2px}

/* Front door — the two paths, side by side (v2 mockup screen 1). Real radios;
   the card is the label. Selection reads via border + wash, works in grayscale. */
.doors{display:grid;grid-template-columns:1fr;gap:.7rem;border:0;padding:0;margin:0}
@media (min-width: 40rem){.doors{grid-template-columns:1fr 1fr}}
.door{display:flex;cursor:pointer;border:1.5px solid var(--line);border-radius:12px;background:#fff;padding:.9rem 1rem;transition:border-color .15s ease,background .15s ease}
.door input{position:absolute;opacity:0;pointer-events:none}
.door-body{display:flex;flex-direction:column;gap:.2rem;font-size:var(--step--1)}
.door-body strong{font-size:1rem;color:var(--forest)}
.door-who{font-weight:600;font-size:.72rem;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
.door-desc{line-height:1.45}
/* 16px, not .74rem (11.84px). The a11y floor measured this as BODY COPY at
   390px — "Free to start · plan ahead" is six words, past the four-word label
   exemption, so it is something a person reads rather than a chip they glance
   at. CLAUDE.md: buyers "skew older, grieving". 11.84px is not a lint nit for
   that reader. BR-1's floor is 16px and it is arithmetic, not taste. */
.door-badge{align-self:flex-start;margin-top:.45rem;font-weight:600;font-size:1rem;border-radius:999px;padding:.3rem .65rem;background:var(--sage-wash);color:var(--forest)}
.door:has(input:checked){border-color:var(--forest);background:var(--sage-wash)}
.door:has(input:focus-visible){outline:2px solid var(--forest);outline-offset:2px}
.arch-item{background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);
  padding:var(--pad-panel);display:flex;align-items:center;justify-content:space-between;gap:.6rem 1.5rem;flex-wrap:wrap;
  transition:border-color .12s ease}

.arch-item:hover{border-color:var(--sage)}
/* An archive that came to them through succession — carried for someone who has
   passed. A quiet distinct frame, never loud (legacy-accounts UX, 2026-07-19). */
.arch-item-care{border-color:var(--sage);box-shadow:0 0 0 1px var(--sage);background:color-mix(in srgb, var(--sage) 4%, var(--paper))}
.arch-care-badge{font:600 .72rem/1 sans-serif;letter-spacing:.04em;background:var(--sage-wash, #e7e1f2);color:var(--forest, #5d4a78);border:1px solid var(--sage);border-radius:999px;padding:.28rem .6rem;white-space:nowrap}

.arch-info{flex:1 1 16rem;min-width:0}

.arch-head-row{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap}

.arch-main{text-decoration:none;color:var(--ink);min-width:0}
.arch-main strong{font-size:1.12rem;letter-spacing:-.01em}

.arch-main:hover strong{text-decoration:underline}

.arch-status{font-size:.7rem;font-weight:700;letter-spacing:.03em;text-transform:uppercase;padding:.14rem .55rem;border-radius:999px}

.arch-status.live{background:var(--sage-wash);color:var(--forest)}

.arch-status.draft{background:var(--cream);color:var(--muted);border:1px solid var(--line)}

.arch-next{font-size:var(--step--1);margin:.25rem 0 0}

.arch-next a{color:var(--forest)}

.arch-actions{display:flex;gap:1.1rem;flex-wrap:wrap;align-items:center;font-size:var(--step--1);font-weight:600}

.arch-actions a{color:var(--forest);text-decoration:none;white-space:nowrap;display:inline-flex;align-items:center;gap:.3rem;
  border:1px solid var(--line);border-radius:10px;padding:.5rem 1rem;min-height:40px;background:transparent}

.arch-actions a:hover{border-color:var(--sage);background:var(--sage-wash)}

.arch-actions a.btn-sm{border-color:var(--forest)}

.arch-actions a.btn-sm:hover{background:var(--forest);border-color:var(--forest);filter:brightness(1.08)}

.tool-magic:hover{filter:brightness(1.06)}

.ai-preview-body p:last-child{margin-bottom:0}

.ai-gaps li{margin-bottom:.2rem}

.wv:last-child{border-bottom:0}

.foot-preview:hover{text-decoration:underline}


/* Dashboard: pending-memories badge on an archive row */
.arch-badge { display: inline-block; background: var(--clay); color: #fff; border-radius: 999px; font-size: .75rem;
  font-weight: 700; padding: .1rem .5rem; margin-left: .5rem; vertical-align: middle; }


/* Look & feel step — theme gallery + preview modal */
.wrap-wide { max-width: 60rem; }

/* LOOP 24 item 16 (BUILDER-26 C2.9, display-and-nav-hardening.md): the
   read-through's change views ran at 34rem, about fifty characters, so a
   reworded paragraph and its "was:" line wrapped into a column. On a desktop
   the surface takes a wider measure; the page's own prose keeps the 44rem it
   reads well at, and phones are unchanged. Diff surfaces only, never the editor. */
.diff-surface { max-width: 34rem; }
@media (min-width: 64rem) {
  .wrap-diff { max-width: 54rem; }
  .wrap-diff > h1, .wrap-diff > p { max-width: 44rem; }
  .wrap-diff .diff-surface { max-width: none; }
}

.theme-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: 1.1rem; }

.theme-tile { position: relative; display: flex; flex-direction: column; }

.theme-pick { display: flex; flex-direction: column; text-align: left; padding: 0; cursor: pointer;
  background: var(--paper); border: 2px solid var(--line); border-radius: var(--radius); overflow: hidden; width: 100%; }

.theme-pick:hover { border-color: var(--sage); }

.theme-tile[data-active="true"] .theme-pick { border-color: var(--forest); box-shadow: 0 0 0 3px color-mix(in srgb, var(--forest) 20%, #fff); }

.theme-shot { display: block; aspect-ratio: 4 / 5; overflow: hidden; background: var(--cream); border-bottom: 1px solid var(--line); }

.theme-shot img { width: 100%; height: 100%; object-fit: cover; object-position: top center; }

.theme-meta { padding: .7rem .85rem .85rem; }

/* BUILDER-24 §4.1: every card-as-choice shows "chosen" in FORM as well as
   colour (WCAG 1.4.1). The theme tiles already did it with a ✓; these three
   conveyed it by colour alone. Pseudo-content so no markup or JS changes —
   `aria-pressed` already carries the state programmatically, and each of these
   keeps it in sync. */
.pm-opt.on .pm-t::before,
.photo-place.on::before,
.iv-mode.on::before { content: "✓ "; content: "✓ " / ""; font-weight: 700; }
/* LOOP 39 item 15 (walk K18): the mark was read as part of the button's name
   ("check mark In the album"); the alternative text after the slash is empty,
   so it is seen and not said. The first `content` is for a browser without it. */
.theme-check { color: var(--forest); opacity: 0; font-size: .9em; }

.theme-tile[data-active="true"] .theme-check { opacity: 1; }

/* 16px, not --step--1 (13.33px measured at 390). Five theme descriptions, each
   6-10 words — the sentence that tells someone what a theme will feel like, on
   the step where they choose how their memorial looks. Body copy by any reading. */
.theme-desc { display: block; color: var(--muted); font-size: 1rem; margin-top: .15rem; line-height: 1.45; }

/* In the caption row, in normal flow (LOOP 14A item 3). It was an absolute pill
   at the tile's bottom-right, first over the name at the top, then over the
   description at the foot: wherever it floated it covered the words that say
   what the look is. Now it sits under them, as its own row, and covers nothing. */
.theme-preview-btn { position: static; align-self: flex-start; margin-top: .5rem; background: var(--paper);
  color: var(--forest); border: 1px solid var(--line); border-radius: 999px; padding: .4rem 1rem;
  font: inherit; font-size: var(--step--1); font-weight: 600; cursor: pointer; min-height: 44px; }

.theme-preview-btn:hover, .theme-preview-btn:focus-visible { border-color: var(--sage); background: var(--sage-wash); }


.theme-modal { position: fixed; inset: 0; z-index: 80; background: rgba(38,51,43,.55);
  display: flex; align-items: center; justify-content: center; padding: clamp(.5rem, 2vw, 1.5rem); }

.theme-modal[hidden] { display: none; }
  /* class display:flex would otherwise beat [hidden] */
.theme-modal-in { background: var(--cream); border-radius: var(--radius); width: min(72rem, 100%); height: min(90vh, 100%);
  display: flex; flex-direction: column; overflow: hidden; box-shadow: 0 20px 60px rgba(38,51,43,.4); }

.theme-modal-bar { display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .8rem 1rem; border-bottom: 1px solid var(--line); flex-wrap: wrap; }

.theme-modal-note { margin: 0; padding: .5rem 1rem 0; }

.theme-modal-frame { flex: 1; min-height: 0; padding: .6rem 1rem 1rem; }

.theme-modal-frame iframe { width: 100%; height: 100%; border: 1px solid var(--line); border-radius: 10px; background: #fff; }

/* Wishes: radio CARDS that wrap on any width (the inline version broke on
   phones). Radio + body, body wraps, selection reads as a filled card. */
.wish-style{display:flex;gap:.7rem;align-items:flex-start;border:1px solid var(--line);border-radius:12px;
  padding:.8rem 1rem;cursor:pointer;background:var(--paper)}
/* The global input{width:100%} rule hits RADIOS too — the little circle's
   layout box swallowed the whole row and squeezed the text to width 0
   (found by computed-style probe: bodyW=0, x beyond the card). */
.wish-style input{margin-top:.3rem;flex:none;width:auto}
.wish-style .ws-body{display:block;min-width:0}
.wish-style .ws-body b{display:block}
.wish-style .ws-body .mut{font-size:var(--step--1)}
.wish-style.on{border-color:var(--forest);background:var(--sage-wash)}

/* Dashboard hierarchy: ONE primary action per card; the rest are quiet links. */
.arch-actions .btn-sm{background:var(--forest);color:#fff;border-radius:10px;padding:.5rem 1rem;min-height:40px;
  display:inline-flex;align-items:center;font-weight:600;text-decoration:none}
.arch-actions .btn-sm:hover{filter:brightness(1.08);text-decoration:none}

/* Current-page state for header nav: filled pill, consistent icons that never
   disappear — position is shown, not implied by absence. */
.head-dash.head-on{background:var(--sage-wash);border-color:transparent;color:var(--forest-deep)}

.wish-fold > summary{list-style:none}
.wish-fold > summary::-webkit-details-marker{display:none}
.wish-fold > summary::after{content:"▾";float:right;color:var(--muted);transition:transform .15s}
.wish-fold[open] > summary::after{transform:rotate(180deg)}

/* Photo placement segmented toggle: quiet two-state, the ON side filled. */
.photo-place{flex:1;font:inherit;font-size:.85rem;font-weight:600;padding:.28rem .3rem;border:1px solid var(--line);
  background:var(--paper);color:var(--muted);cursor:pointer;min-height:40px;line-height:1.2}
.photo-place:first-of-type{border-radius:8px 0 0 8px}
.photo-place:last-of-type{border-radius:0 8px 8px 0;border-left:0}
.photo-place.on{background:var(--sage-wash);color:var(--forest);border-color:var(--sage)}
.photo-hero,.photo-anchor{font-size:.85rem}
.photo-cap-l{font-size:.85rem}
@media (max-width:30rem){
  .photo-strip{flex-direction:column;gap:1.1rem}
  .photo-chip{width:100%}
  .photo-chip img,.photo-chip.photo-missing .photo-fallback{height:auto;aspect-ratio:4/3;max-height:16rem}
  .photo-place,.photo-hero,.photo-anchor,.photo-cap-l,.photo-cap{font-size:var(--step--1)}
  .photo-place,.photo-hero,.photo-mv,.photo-anch{min-height:44px}
  /* The remove button hangs off the chip's corner; at full width that corner
     is the page edge, so it tucks in. */
  .photo-x{top:-10px;right:-6px}
}

/* Photo placement: the up-front woven-vs-gallery choice + numbering + budget */
.photo-mode{display:grid;grid-template-columns:1fr 1fr;gap:.6rem;margin-bottom:.8rem}
.pm-opt{text-align:left;border:1.5px solid var(--line);background:var(--paper);border-radius:12px;
  padding:.7rem .85rem;cursor:pointer;display:flex;flex-direction:column;gap:.15rem;font:inherit}
.pm-opt .pm-t{font-weight:600;font-size:.92rem;color:var(--ink)}
.pm-opt .pm-d{font-size:var(--step--1);color:var(--muted)}
.pm-opt.on{border-color:var(--forest);box-shadow:0 0 0 3px color-mix(in srgb,var(--forest) 13%,transparent)}
.pm-opt:hover{border-color:var(--sage)}
.photo-budget{display:flex;align-items:center;gap:.5rem .7rem;flex-wrap:wrap;background:var(--sage-wash);
  border-radius:10px;padding:.55rem .8rem;margin-bottom:.85rem;font-size:var(--step--1);color:var(--ink)}
.photo-budget b{font-weight:700}
.photo-budget .btn-link{margin-left:auto;white-space:nowrap}
.photo-num{position:absolute;top:.35rem;left:.35rem;z-index:2;background:var(--forest);color:#fff;
  font-size:.72rem;font-weight:700;min-width:1.3rem;height:1.3rem;border-radius:7px;display:grid;
  place-items:center;padding:0 .32rem;box-shadow:0 1px 3px rgba(0,0,0,.28)}
.photo-cat{position:absolute;top:.35rem;right:.35rem;z-index:2;background:rgba(255,255,255,.94);
  border:1px solid var(--line);border-radius:7px;font-size:.85rem;line-height:1.3;padding:.02rem .28rem;box-shadow:0 1px 3px rgba(0,0,0,.15)}
@media(max-width:30rem){.photo-mode{grid-template-columns:1fr}}

/* Visual arrange view: the story as it reads, photos between the paragraphs */
.arrange{border:1px solid var(--line);border-radius:14px;background:var(--paper);padding:.4rem clamp(.6rem,3vw,1.4rem) 1rem;margin-bottom:1rem}
.arr-para{font-family:Georgia,'Iowan Old Style',serif;font-size:1rem;line-height:1.7;color:#38312a;margin:.2rem 0;padding:.2rem 0}
.arr-gap{display:flex;flex-direction:column;gap:.5rem;margin:.25rem 0;align-items:stretch}
/* The between-paragraphs "place a photo" affordance — the primary way to weave a
   photo into the story, so it earns a clearer, more tappable presence (was small
   and easy to miss). Still quiet until hovered/focused. */
.arr-slot{align-self:center;display:inline-flex;align-items:center;min-height:40px;border:1px dashed var(--line);
  background:var(--cream);color:var(--muted);border-radius:999px;padding:.45rem 1.15rem;font:inherit;font-size:.85rem;
  font-weight:500;cursor:pointer;transition:border-color .12s,background .12s}
.arr-slot:hover,.arr-slot:focus-visible{border-color:var(--sage);color:var(--forest);background:var(--sage-wash)}
/* A gap that already holds a photo keeps its slot quieter by its frame, never
   by fading its words (was opacity .5, under 3:1; LOOP 10 item 8). */
.arr-gap.has .arr-slot{background:transparent;border-style:dotted}
.arr-photo{margin:0;position:relative;border-radius:12px;overflow:hidden;border:1px solid var(--line);background:#fcf8ef}
/* contain, not cover: the arrange view is a PREVIEW of the page, and the page
   shows the whole photo (see .inline-photo on the site — no crop). cover here
   hid the top/sides of tall photos and made people think the page would crop
   them too (Garrett, 2026-07-20). contain scales the whole photo to fit the
   space cap instead; the cream mat fills any letterbox, so a portrait reads as
   framed — which is exactly how the published memorial mats it. */
.arr-photo img{display:block;width:100%;height:auto;max-height:22rem;object-fit:contain}
.arr-ctl{position:absolute;top:.5rem;right:.5rem;display:flex;gap:.4rem}
/* 44px touch targets (was 32) — the overlay move/remove controls were small and
   easy to miss on a phone (Garrett, 2026-07-20). */
.arr-ctl button{width:2.75rem;height:2.75rem;border-radius:10px;border:0;background:rgba(35,30,24,.62);color:#fff;cursor:pointer;
  font-size:1.2rem;line-height:1;display:grid;place-items:center;backdrop-filter:blur(2px)}
.arr-ctl button:hover{background:rgba(35,30,24,.82)}
.arr-ctl button:disabled{opacity:.35;cursor:default}
.arr-pick{display:flex;gap:.5rem;flex-wrap:wrap;align-items:center;padding:.6rem;background:var(--sage-wash);border-radius:10px}
.arr-pick-p{padding:0;border:2px solid transparent;border-radius:9px;background:none;cursor:pointer;line-height:0}
.arr-pick-p img{width:60px;height:60px;object-fit:cover;border-radius:8px;border:1px solid var(--line)}
.arr-pick-p:hover img,.arr-pick-p:focus-visible img{border-color:var(--forest)}

/* Dashboard hub rows — quiet, informative, brand-toned. */
.arch-rows{width:100%;border-top:1px solid var(--line);margin-top:.85rem;padding-top:.7rem;display:flex;flex-direction:column;gap:.35rem}
.arch-row{margin:0;font-size:var(--step--1);color:var(--muted)}
.arch-row a{color:var(--forest)}
.arch-row-k{display:inline-block;font-size:.68rem;font-weight:600;letter-spacing:.07em;text-transform:uppercase;color:var(--forest);margin-right:.5rem}
.linklike{background:none;border:0;padding:0;font:inherit;color:var(--forest);text-decoration:underline;cursor:pointer}
.copy-ok{color:var(--forest);font-weight:600;margin-left:.35rem}
.arch-item-helping{background:var(--cream)}

/* Family invite rows — email + status on the left, quiet retract on the right. */
.inv-row{display:flex;justify-content:space-between;align-items:baseline;gap:.6rem 1rem;flex-wrap:wrap;font-size:var(--step--1);padding:.35rem 0;border-bottom:1px solid var(--line)}
.inv-row:last-child{border-bottom:0}
.inv-who{display:flex;gap:.5rem;align-items:baseline;flex-wrap:wrap;min-width:0}
.inv-del{background:none;border:0;padding:.15rem .35rem;font:inherit;font-size:var(--step--1);color:var(--clay);cursor:pointer;text-decoration:underline;white-space:nowrap}
.inv-del:hover{color:#8a2b2b}
.inv-del:disabled,.inv-del[aria-disabled="true"]{opacity:.5;cursor:default}

/* Dashboard "For you" region — surfaces pre-need planning as a first-class,
   discoverable offering (not a buried link). */
.foryou{margin:0 0 2rem}
.foryou-links{display:grid;gap:.7rem;grid-template-columns:repeat(auto-fit,minmax(15rem,1fr))}
.foryou-card{display:flex;flex-direction:column;gap:.25rem;padding:.9rem 1.1rem;border:1px solid var(--line);
  border-radius:var(--radius);background:var(--paper);text-decoration:none;color:var(--ink);transition:border-color .12s ease,background .12s ease}
.foryou-card:hover{border-color:var(--sage);background:var(--sage-wash)}
.foryou-card strong{color:var(--forest)}
.foryou-card .mut{font-size:var(--step--1)}

.people-list{list-style:none;padding:0;margin:0}

/* ── Mobile tap-target floor (Garrett 2026-07: "honest tap targets"). Desktop
   keeps its quieter density; on phone widths every button-shaped control meets
   the 44px floor. Inline-in-prose links (.linklike) are exempt per WCAG 2.5.8's
   inline exception; .photo-x / .photo-place are reported, not resized, because
   44px would break their compact compositions. ── */
@media (max-width:40rem){
  .chip,.idea-chip,.btn-link,.mem-actions .btn,.bulk-panel .btn,
  .qa-opts .btn,.qa-answer .btn,.arch-actions a,.arch-actions .btn-sm,
  .theme-preview-btn,.wv-close,.story-status .btn-link,.arr-slot{min-height:44px}
  .wv-close{min-width:44px}
  .arr-ctl button{width:2.75rem;height:2.75rem}
}

/* BUILDER-25 D4 — "Shown in" on a woven memory. Quiet by design: it is a
   statement of fact the owner may want to act on, not an alert. Values read as
   plain chips so "the story · the tributes" is scannable at a glance. */
.mem-shown{display:flex;flex-wrap:wrap;gap:.4rem .55rem;align-items:center;margin:.6rem 0 0;
  font-size:var(--step--1);color:var(--muted)}
.mem-shown-k{font-weight:600;color:var(--ink)}
.mem-shown-v{background:var(--sage-wash);color:var(--forest);border-radius:999px;padding:2px 10px;font-weight:600}
.mem-shown-off{background:var(--cream);border:1px solid var(--line);border-radius:999px;padding:2px 10px}

/* BUILDER-23 1a — textareas that grow with their content instead of becoming a
   nested scroll region. A box that scrolls inside a page that also scrolls hides
   whatever sits below it, and on a phone the thumb lands in the wrong one. */
textarea.autogrow{overflow-y:hidden;resize:none;field-sizing:content}

/* BUILDER-23 1b — ONE AI grammar. Exactly two presentations exist:
   • panel action  — the ✨ button inside a "want a hand?" panel; starts something
   • inline assist — .assist, a quiet chip beside the content it acts on
   Nothing else. The story step had three dialects (dark button, ghost button,
   underlined sparkle link) for jobs at the same altitude, so the AI read as
   scattered rather than as one helper. Ghost-button padding, chip shape, and it
   never competes with the primary action on the screen. */
.assist{display:inline-flex;align-items:center;gap:.35rem;min-height:44px;
  padding:.4rem .9rem;border-radius:999px;border:1.5px solid var(--line);
  background:var(--paper);color:var(--forest);font:inherit;font-size:var(--step--1);
  font-weight:600;cursor:pointer;text-decoration:none}
.assist:hover,.assist:focus-visible{border-color:var(--sage);background:var(--sage-wash)}
.assist:disabled,.assist[aria-disabled="true"]{opacity:.6;cursor:default}
/* `display:inline-flex` above beats the UA stylesheet's [hidden]{display:none},
   so an assist told to hide stayed visible — caught by looking at the render,
   not by reading the code. Any component that sets `display` must restate this. */
.assist[hidden]{display:none}

/* LOOP 14A item 4: Details rows label every box. From 30rem up the labels on
   rows after the first are visually hidden (the first row's labels head the
   column); on a phone the boxes wrap and every row shows its own. */
@media (min-width:30rem){
  .d-col-label-rest{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
}
/* LOOP 14A item 4: a legacy contact's three boxes, each with its own visible
   label; three across on a wide screen, stacked on a phone. */
.succ-fields{display:grid;grid-template-columns:1fr 1fr .8fr;gap:.5rem;border:0;padding:0;margin:0;min-width:0}
.succ-fields label{margin-bottom:.2rem}
@media (max-width:30rem){.succ-fields{grid-template-columns:1fr}}
/* Which person a stack of boxes belongs to. On a wide screen the two rows read
   as two people; stacked on a phone, six boxes ran together (loop 14 screenshot
   pass), so the legend shows there and the second person gets a rule above. */
.succ-legend{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
@media (max-width:30rem){
  .succ-legend{position:static;width:auto;height:auto;margin:0 0 .4rem;overflow:visible;clip:auto;white-space:normal;
    font-weight:600;font-size:var(--step--1);color:var(--muted)}
  .succ-row + .succ-row{border-top:1px solid var(--line);padding-top:.8rem;margin-top:.9rem}
}

/* LOOP 26 item 5 (Frame 6): who's replied, under each service on Details.
   A guest's name, address and note are a stranger's words of any length, so
   every line wraps (an unbroken address included) and nothing sets a width. */
.guest-list{background:var(--sage-wash);border-color:transparent;margin-top:1rem}
.guest-h{font-size:1.05rem;margin:0 0 .15rem}
.guest-sub{font-size:var(--step--1);margin:0 0 .8rem}
.guest-status:empty{display:none}
.guest-status{margin:0 0 .8rem}
.guest-warn{margin:0 0 .4rem}
.guest-group + .guest-group{margin-top:1rem}
.guest-svc{font-size:1rem;margin:0 0 .2rem;overflow-wrap:anywhere}
.guest-tally{font-weight:600;margin:0 0 .3rem}
.guest-empty{font-size:var(--step--1);margin:0}
.guest-rows{list-style:none;margin:0;padding:0}
.guest-row{padding:.5rem 0;border-top:1px solid var(--line);min-width:0}
.guest-who{display:flex;flex-wrap:wrap;gap:.1rem .6rem;align-items:baseline}
.guest-name,.guest-note,.guest-meta{overflow-wrap:anywhere}
.guest-answer{color:var(--muted)}
.guest-note{margin:.2rem 0 0}
.guest-meta{font-size:var(--step--1);margin:.15rem 0 0}
/* The quiet "Report a problem" link at the foot of signed-in pages (2026-10-10). */
.report-foot{max-width:64rem;margin:0 auto;padding:0 var(--gutter) 2rem;text-align:center}
.report-link{font:inherit;font-size:var(--step--1);color:var(--muted);background:none;border:0;padding:.6rem .4rem;min-height:44px;text-decoration:underline;text-underline-offset:.2em;cursor:pointer}
.report-link:hover,.report-link:focus-visible{color:var(--forest)}
.report-link[hidden]{display:none}
