/* ==========================================================================
   site.css — reindeer.gift new prototype
   Loaded after main.css/plugins.css: overrides Oomph's split-panel layout
   with a centered content window, adds the snow background, and defines
   the redesigned gift/comment card component.
   ========================================================================== */

/* Oomph's own scrollbar-thumb color (main.css) is #9e2525 — the same red
   used for delete/unclaim actions elsewhere, applied here as a generic
   sitewide default that has nothing to do with "danger", it just clashes
   with the gold/green palette everywhere a scrollbar shows up (most
   noticeably in the comment textareas). Firefox uses scrollbar-color
   instead of the ::-webkit-scrollbar-* pseudo-elements, so both are
   needed for the same fix to apply across browsers. The track/background
   (also themed gray by default, main.css) goes transparent rather than
   another color — a solid gray block behind a gold thumb looked like its
   own separate, unstyled UI element rather than part of the page. */
::-webkit-scrollbar-thumb {
  background-color: #c9a51c;
}

::-webkit-scrollbar-track,
::-webkit-scrollbar {
  background: transparent;
}

* {
  scrollbar-color: #c9a51c transparent;
}

/* ---------- Loading spinner / retry (comments, messages inbox, an open
   thread) — shown only while the very first fetch for that list is in
   flight, see loadComments()/loadConversations()/openThread() in site.js. */

.loading-spinner {
  display: flex;
  justify-content: center;
  padding: 2.5rem 0;
}

.loading-spinner__circle {
  width: 2.4rem;
  height: 2.4rem;
  border: 3px solid rgba(44, 52, 43, 0.15);
  border-top-color: #c9a51c;
  border-radius: 50%;
  animation: site-spin 0.8s linear infinite;
}

/* Same spinner, sized for its own dedicated screen (the Connecting
   home-state) rather than the compact inline use every other
   .loading-spinner is for. */
.loading-spinner--large {
  padding: 0;
  margin-bottom: 2rem;
}

.loading-spinner--large .loading-spinner__circle {
  width: 5rem;
  height: 5rem;
  border-width: 5px;
}

/* .btn has no margin-top of its own (only margin-right, for sitting
   inline next to siblings elsewhere) — without this it sits flush
   against the "Connecting…" heading right above it. display:block +
   width:fit-content + auto side-margins is the same fix (and the same
   underlying cause — h1 is inline-block, and with nothing else between
   it and this button, text-align:center centers the pair rather than
   the button alone) as .home-back needed on Email/Verify Email above;
   .btn is inline-block too, so it needed it independently confirmed
   here (measured the same ~237px offset before this rule existed). */
.home-state--connecting .btn {
  display: block;
  width: fit-content;
  margin-top: 7rem;
  margin-left: auto;
  margin-right: auto;
}

.home-state--suspended__logout-btn {
  margin-top: 4rem;
}

@keyframes site-spin {
  to {
    transform: rotate(360deg);
  }
}

.loading-error {
  text-align: center;
  padding: 2.5rem 1.5rem;
  color: #6b6555;
  font-size: 1.3rem;
}

.loading-error__retry {
  display: inline-block;
  margin-top: 0.75rem;
  background: none;
  border: 1px solid rgba(44, 52, 43, 0.2);
  border-radius: 20px;
  padding: 0.4rem 1.2rem;
  font-size: 1.15rem;
  font-weight: 600;
  color: #6b6555;
  cursor: pointer;
}

.loading-error__retry:hover {
  color: #2c342b;
  border-color: #2c342b;
}

/* Only shown while the Hub home-state is active (see site.js) */
.home-state--hub-only {
  display: none;
}
.home-state--hub-only.is-visible {
  display: block;
}

/* Center to match the rest of the centered-content design language
   (the theme's own default here is left-aligned, a leftover from the
   removed split-panel layout). */
.main__dataline {
  text-align: center;
}

/* ---------- Fullscreen snow background (behind everything) ---------- */

#snow-js {
  position: fixed !important;
  top: 0; left: 0; right: 0; bottom: 0;
  width: 100% !important;
  height: 100% !important;
  z-index: 0;
  pointer-events: none;
  background: radial-gradient(ellipse at 50% 0%, #3a4638 0%, #232920 55%, #171c15 100%);
}

/* Let the home screen's own backgrounds go transparent so snow shows through */
.main,
.main__content {
  background-color: transparent !important;
}

/* ---------- Transition model: Home is a static base layer; sections are drawers
   that slide in from the right over it, and slide back out to reveal it again.
   (Oomph's original behavior slid Home away instead and faded sections in in
   place — this replaces that, and also fixes the old -50%/50% half-width slide
   values, which were calibrated for the removed two-column layout.) ---------- */

@media only screen and (min-width: 1200px) {
  .menu {
    width: 100%;
  }
}

/* Home never moves */
.main.move-out,
.main.move-in {
  -webkit-transform: none !important;
  -moz-transform: none !important;
  -ms-transform: none !important;
  transform: none !important;
}

/* The hamburger menu shouldn't do its own competing slide-away when a section
   opens (that reads as "a second thing flying across the screen"). It already
   fades via opacity (menu has `transition: all 0.6s`), so just drop its own
   slide and let it disappear cleanly while the section slides in on top. */
.menu.move-out {
  -webkit-transform: none !important;
  -moz-transform: none !important;
  -ms-transform: none !important;
  transform: none !important;
}

/* Sections rest off-screen to the right, and slide to cover the screen when open */
.inner {
  -webkit-transform: translateX(100%);
  -moz-transform: translateX(100%);
  -ms-transform: translateX(100%);
  transform: translateX(100%);
}
.inner.animate-in {
  -webkit-transform: translateX(0);
  -moz-transform: translateX(0);
  -ms-transform: translateX(0);
  transform: translateX(0);
}

/* While closing, .animate-in is still present alongside .animate-out/.to-back
   for part of the sequence — without !important here, the rule above (equal
   specificity, later in source order) would win and pin the section in place,
   so only opacity would visibly change instead of it sliding back out. */
.inner.animate-out,
.inner.to-back {
  -webkit-transform: translateX(100%) !important;
  -moz-transform: translateX(100%) !important;
  -ms-transform: translateX(100%) !important;
  transform: translateX(100%) !important;
}

/* ---------- Home hub ---------- */

/* .main itself normally gets its height from a `height: 100%` chain rooted
   at html/body, which can independently carry the same mobile-viewport bug
   as raw `vh` units (both were historically sized against the largest
   possible viewport, address bar collapsed). Since .main scrolls internally
   (overflow-y: auto from the theme), any extra height here becomes exactly
   the dead scroll space below correctly-centered content. Override it
   directly with the same corrected-height fallback chain used below. */
.main--centered {
  height: 100vh;
  height: calc(var(--vh, 1vh) * 100);
  height: 100dvh;
  scrollbar-gutter: stable both-edges;
}

/* The "scrolls internally" comment above is only true up to 1199px — the
   theme itself sets `.main { overflow: hidden; }` at 1200px+ (main.css),
   which was invisible while Hub content always fit in one screen. Now that
   it can genuinely be taller than the viewport (more tiles, the wish-list
   button), that hidden overflow makes anything past the fold completely
   unreachable, not just awkward to find, no amount of scrolling helps.
   Restoring auto here doesn't change anything when content already fits;
   it only matters once it doesn't. */
@media only screen and (min-width: 1200px) {
  .main--centered {
    overflow-y: auto;
    padding-right: 1.25rem;
    box-sizing: border-box;
  }
}

/* main.css also switches .main__dataline (the account bar) to
   position:absolute at this same breakpoint, pinned near the bottom of
   .main's own fixed-height box. That's fine exactly as long as content
   never grows past one screen — once it does, the bar stays pinned in
   place while the real content scrolls underneath it, hovering over
   whatever happens to land there (the countdown, now) instead of sitting
   after it like a normal page footer would. Putting it back in normal
   flow is what actually matches "naturally at the bottom of the page". */
@media only screen and (min-width: 1200px) {
  .main--centered .main__dataline {
    position: relative;
    bottom: auto;
    left: auto;
    /* Negative on purpose — .hub__stage's own bottom padding (6rem) was
       sized to pull short content (Login/Landing forms) up off
       dead-center, not as spacing before this bar. Safe to adjust here
       rather than there since this element is home-state--hub-only
       (invisible on every screen except Hub), so it can't affect any
       other screen's layout. Nets out to a much smaller, more
       intentional-looking gap above the bar. */
    margin-top: -3rem;
  }
}

/* Real content, not a margin, on purpose — see the HTML comment next to
   this element for why. flex-shrink: 0 keeps it from being squeezed away
   to nothing by the flex column it sits in. */
.hub__bottom-spacer {
  flex-shrink: 0;
  height: 2rem;
}

/* Sibling of .hub__stage, not inside it — see the HTML comment above
   this element for why (renders on every state, not just one). Plain
   and quiet on purpose (Xavier's call: "just simple text and links") —
   no card, no background, nothing competing with the actual page
   content above it. */
.site-footer {
  flex-shrink: 0;
  text-align: center;
  padding: 1.5rem 1.5rem 2rem;
  font-size: 1.2rem;
}

.site-footer__links a {
  color: rgba(255, 255, 255, 0.75);
  text-decoration: none;
}

.no-touch .site-footer__links a:hover {
  color: #ffffff;
  text-decoration: underline;
}

.site-footer__divider {
  color: rgba(255, 255, 255, 0.4);
  margin: 0 0.6rem;
}

.site-footer__copyright {
  margin: 0.6rem 0 0;
  color: rgba(255, 255, 255, 0.5);
  font-size: 1.1rem;
}

/* ---------- Standalone stub pages (terms/privacy/support.html) ----------
   Deliberately not part of the index.html app shell — no home-state
   machine, no site.js — these are plain, always-reachable pages (a
   Privacy Policy can never live behind a login gate). Reuses
   .background-dark/.headline__text and the same .site-footer above for
   visual consistency, in a simple flex column so the footer sits at
   the bottom of the viewport the same way it does on index.html. */
/* main.css sets html/body to overflow:hidden site-wide, assuming the SPA
   shell's own inner .main scroll container (overflow-y:auto) handles all
   scrolling. These standalone pages have no such container, so without
   this override any content taller than the viewport is just unreachable. */
.static-page-body,
html:has(.static-page-body) {
  overflow-y: auto !important;
}

.static-page {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}

.static-page__container {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  max-width: 640px;
  width: 100%;
  margin: 0 auto;
  padding: 4rem 1.5rem;
}

.static-page__logo {
  display: inline-block;
  margin-bottom: 3rem;
}

/* A real button rather than a plain underlined link — Xavier's own
   request: the theme's own big uppercase-Cinzel .btn is sized for a
   hero CTA (see main.css), too loud for a quiet "go back" action tucked
   under a document's worth of body copy, so this is a smaller bordered
   pill in the site's own gold accent instead of borrowing that class. */
.static-page__back {
  display: inline-block;
  margin-top: 2.5rem;
  padding: 0.9rem 2.2rem;
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 999px;
  color: #ffffff;
  text-decoration: none;
  font-size: 1.3rem;
  font-weight: 600;
  transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.no-touch .static-page__back:hover {
  background-color: #c9a51c;
  border-color: #c9a51c;
  color: #2c342b;
}

/* Modifier for pages with real body copy (Terms/Privacy) instead of a
   short centered blurb (Support) — left-aligned document flow instead of
   vertically-centered hero content, and wide enough to read comfortably. */
.static-page__container--doc {
  justify-content: flex-start;
  align-items: stretch;
  text-align: left;
  max-width: 720px;
}

.static-page__container--doc > .static-page__logo,
.static-doc__title,
.static-doc__updated {
  align-self: center;
  text-align: center;
}

.static-doc__updated {
  margin: 0.5rem 0 0;
  font-family: "Mulish", sans-serif;
  font-size: 1.2rem;
  color: rgba(255, 255, 255, 0.5);
}

/* Sits above the regular .static-doc flow, visually set apart from the
   rest of the document — this is a standing commitment the whole page
   is built around, not just one more paragraph in "What We Collect." */
.static-doc__promise {
  margin-top: 2rem;
  padding: 1.5rem 1.75rem;
  border: 1px solid rgba(201, 165, 28, 0.35);
  border-radius: 10px;
  background: rgba(201, 165, 28, 0.06);
}

.static-doc__promise p {
  margin: 0 0 1rem;
  font-size: 1.36rem;
  color: #f5efe3;
}

/* The caveat line, deliberately more muted than the promise above it —
   still fully readable, but reads as a subordinate honesty note rather
   than competing with the actual commitment for attention. */
.static-doc__promise p:last-child {
  margin-bottom: 0;
  color: rgba(245, 239, 227, 0.65);
}

.static-doc {
  margin-top: 3rem;
}

.static-doc__heading {
  margin: 2.6rem 0 0.8rem;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 1.9rem;
  color: #c9a51c;
}

.static-doc__heading:first-child {
  margin-top: 0;
}

.static-doc__subheading {
  margin: 1.8rem 0 0.6rem;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 1.55rem;
  color: #f5efe3;
}

/* A subsection groups an h3 with everything under it (paragraphs,
   lists) so the indent applies to the whole block, not just the
   heading — a bare margin on .static-doc__subheading alone would
   indent the title but leave its own body text flush with the parent
   heading, reading as broken rather than nested. */
.static-doc__subsection {
  padding-left: 1.5rem;
}

.static-doc__subsection::before {
  content: "";
  display: block;
  width: 2rem;
  height: 2px;
  background: rgba(201, 165, 28, 0.5);
}

.static-doc__subsection .static-doc__subheading {
  margin-top: 0.8rem;
}

/* Replaces the plain "┈┈┈" text dividers that used to sit between the
   Third-Party Services call-outs — a real rule matching the doc's own
   muted palette instead of a character that looked like a stray leftover. */
.static-doc__divider {
  margin: 1.5rem 0;
  border: none;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}

.static-doc p {
  margin: 0 0 1.2rem;
}

.static-doc ul {
  margin: 0 0 1.2rem;
  padding-left: 1.8rem;
  /* plugins.css resets all `ul { list-style: none }` site-wide (a
     Bootstrap-ism this theme relies on for its own custom-bulleted nav/
     card lists) — restore real bullets here since this is plain body
     copy, not a styled component. */
  list-style: disc;
  font: normal 400 1.4rem/1.6 "Mulish", sans-serif;
  color: rgba(255, 255, 255, 0.8);
}

.static-doc li {
  margin-bottom: 0.4rem;
}

.static-doc a {
  color: #c9a51c;
  text-decoration: underline;
}

.no-touch .static-doc a:hover {
  color: #f5efe3;
}

.static-page__container--doc .static-page__back {
  align-self: center;
}

/* .main--centered is on the <section>, but .main__header/.hub__stage/
   .main__dataline are children of the inner .container-fluid.fullheight,
   not direct children of the section — the flex container needs to go
   there, or it never actually applies to them. */
.main--centered > .container-fluid {
  display: flex;
  flex-direction: column;
  min-height: 100vh; /* absolute fallback if JS is disabled (has the mobile bug) */
  min-height: calc(var(--vh, 1vh) * 100); /* JS-measured real visible height (site.js) — the most reliable cross-browser fix */
  min-height: 100dvh; /* wins over both above when the browser supports it natively, no JS needed */
}

/* The theme floats .main__header out of flow (position: absolute) at
   >=1200px, so it hovers over content instead of scrolling away with it —
   causing it to overlap the text underneath once a home-state's own content
   scrolls (each state manages its own scroll now, see .home-state.is-active
   below). Xavier wants it to stay in normal flow and scroll away with the
   page instead ("fixed on the page, not fixed on the screen"), so keep it
   position: relative at all sizes rather than letting the theme float it. */
.main--centered .main__header {
  position: relative;
}

/* The wordmark is a fixed-size SVG (main.css sets .logo img { width: auto },
   so it renders at its natural ~260px and never shrinks), while
   #menu-trigger sits pinned to the right edge of the same row. On a
   phone there isn't room for both: at 375px the logo ran to 298px and
   the trigger starts at 290px, putting the hamburger lines on top of the
   "S" in GIFTS, and at 320px the overlap grew to 52px. Same root cause
   as the .section-header/.section-close collision fixed earlier —
   nothing reserved space for a control positioned outside the text's own
   flow.
   Reserving that space on .logo (whose box already shares its right edge
   with the trigger) and letting the image shrink to fit is self-limiting:
   max-width only bites once the box is narrower than the logo's natural
   size, so wider screens are untouched. Scoped to phones anyway, since
   that's the range where the collision was actually measured. */
@media only screen and (max-width: 767px) {
  .main--centered .main__header .logo {
    padding-right: 62px;
  }

  .main--centered .main__header .logo img {
    max-width: 100%;
  }
}

/* .hub__stage no longer manages its own scroll or absolute-positions its
   children — only one .home-state is ever in the DOM's layout at a time
   (the rest are display:none), so the whole page (header included) scrolls
   together as one normal unit via .main's own overflow-y: auto, and the
   header genuinely scrolls away with the content instead of hovering fixed
   above a separately-scrolling pane. */
.hub__stage {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Asymmetric on purpose: less top padding than bottom pulls the
     auto-centered content up a bit, closer to the header, rather than
     sitting at a strict mathematical center that read as "too low". */
  padding: 1rem 1.5rem 6rem;
}

/* ---------- Home states (Landing / Login / Activate / Hub) ----------
   Only .is-active is shown; the rest are fully removed from layout
   (display: none) so an inactive state's content — e.g. Hub's tall
   headline + 4 tiles — can't inflate the page's height while hidden.
   site.js coordinates display:none/block around the opacity fade, since
   display can't itself be transitioned. */

.home-state {
  display: none;
  opacity: 0;
  width: 100%;
  transition: opacity 0.4s ease-in-out;
}

.home-state.is-active {
  display: flex; /* centers .hub__content horizontally via justify-content below */
  justify-content: center;
  opacity: 1;
  /* Main-axis auto-margin centering within .hub__stage's column flex —
     degrades gracefully to top-alignment if content is taller than the
     available space, instead of overflowing symmetrically off both edges. */
  margin-top: auto;
  margin-bottom: auto;
}

.hub__content {
  max-width: 640px;
  width: 100%;
  text-align: center;
}

/* A bit more room for the Hub state specifically, since it has 4 tiles to fit */
.home-state--hub .hub__content {
  max-width: 760px;
}

/* Oomph's .countdown is built to absolutely-position itself over a full-height
   hero section; here it sits inline in the Hub's centered content flow instead. */
.hub-countdown {
  position: static;
  -webkit-transform: none;
     -moz-transform: none;
      -ms-transform: none;
          transform: none;
  width: auto;
  margin-top: 1.5rem; /* now sits right under the greeting H1, not below the grid — tighter, more connected spacing */
}

/* Login / Activate UI */

.auth__buttons {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 360px;
  margin: 2.5rem auto 0;
}

.btn-auth {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  padding: 0.9rem 1.5rem;
  border-radius: 30px;
  border: none;
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 1.52rem;
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.btn-auth:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25);
}

/* Real logo files instead of an icon font — sidesteps the font-loading
   race that caused Google/Yahoo's icons to intermittently render as
   missing-glyph boxes (that font only loads once the Login screen becomes
   visible, and sometimes lost the race against the ~3s font-display:block
   grace period). Fixed height, auto width so Yahoo's wider mark isn't
   squished into a square like Google's. */
.btn-auth__icon {
  height: 2rem;
  width: auto;
}

.btn-auth--google {
  background: #fff;
  color: #232920;
}

.btn-auth--yahoo {
  background: #6001d2;
  color: #fff;
}

.btn-auth--email {
  background: transparent;
  color: #f5efe3;
  border: 1px solid rgba(245, 239, 227, 0.5);
}

/* Deliberately quieter and smaller than Change Avatar right above it —
   this is the secondary/undo action, not something to give equal visual
   weight to. Targets the id, not the .btn-auth--reset class, because
   .account-avatar .btn-auth (two classes, further down this file) gives
   every button in this panel a solid dark fill and outweighs a single-
   class selector regardless of source order — an id is the only way to
   reliably win against it. */
#avatar-set-default-btn {
  min-width: 0;
  background: transparent;
  color: #6b6555;
  border: 1px solid rgba(44, 52, 43, 0.25);
  padding: 0.6rem 1.2rem;
  font-size: 1.3rem;
}

#avatar-set-default-btn:hover {
  background: transparent;
  color: #2c342b;
  border-color: rgba(44, 52, 43, 0.45);
}

.avatar-set-default-wrap {
  position: relative;
  display: inline-block;
}

.avatar-color-popover {
  position: absolute;
  top: calc(100% + 0.6rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  background: #fffdf8;
  border: 1px solid rgba(44, 52, 43, 0.18);
  border-radius: 0.6rem;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  padding: 1.2rem;
  width: max-content;
  max-width: 22rem;
  text-align: center;
}

.avatar-color-popover__title {
  margin: 0 0 0.8rem;
  font-size: 1.3rem;
  color: #6b6555;
}

.avatar-color-swatches {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.6rem;
  margin-bottom: 1rem;
}

.avatar-color-swatch {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  border: 2px solid transparent;
  padding: 0;
  cursor: pointer;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12);
}

.avatar-color-swatch.is-selected {
  border-color: #ffffff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12), 0 0 0 3px rgba(255, 255, 255, 0.6);
}

.avatar-color-swatch--gold {
  background: #c9a51c;
}
.avatar-color-swatch--cranberry {
  background: #a13d42;
}
.avatar-color-swatch--copper {
  background: #b5651d;
}
.avatar-color-swatch--burgundy {
  background: #7a2438;
}
.avatar-color-swatch--plum {
  background: #6b4a6b;
}
.avatar-color-swatch--blueberry {
  background: #5678a0;
}
.avatar-color-swatch--midnight-blue {
  background: #2f4468;
}
.avatar-color-swatch--teal {
  background: #327d70;
}

.contact-signature {
  margin-top: 1rem;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 1.6rem;
  color: #c9a51c;
}

/* main.css's generic `p span { color: #ffffff; }` would otherwise win over
   this span just inheriting .contact-signature's gold — match its
   specificity with the same p-span selector shape instead of !important. */
p.contact-signature span.contact-signature__title {
  font-style: italic;
  color: #c9a51c;
}

.activate-form,
.email-auth-form,
.contact-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 360px;
  margin: 2.5rem auto 0;
}

.activate-form input,
.email-auth-form input,
.contact-form input,
.contact-form textarea {
  padding: 0.85rem 1.2rem;
  border-radius: 30px;
  border: 1px solid rgba(245, 239, 227, 0.35);
  background: rgba(245, 239, 227, 0.08);
  color: #f5efe3;
  font-size: 1.52rem;
  font-family: inherit;
}

.contact-form textarea {
  border-radius: 20px;
  resize: none;
}

.activate-form input::placeholder,
.email-auth-form input::placeholder,
.contact-form input::placeholder,
.contact-form textarea::placeholder {
  color: rgba(245, 239, 227, 0.6);
}

/* Honeypot — present in the DOM for bots to find and fill, invisible and
   unreachable for real visitors (off-screen, not just display:none, since
   some bots skip display:none fields specifically to look more human). */
.contact-form__hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Purely visual — the actual value is still whatever was typed (lowercase
   included); activate.php uppercases it server-side regardless. This just
   sets the right expectation while typing, since that's how the code is
   actually checked/stored. */
#activate-invite-code {
  text-transform: uppercase;
}

/* Same checkbox treatment as .stay-signed-in on the Login screen. */
.contact-form__anonymous {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  color: rgba(245, 239, 227, 0.8);
  font-family: "Mulish", sans-serif;
  font-size: 1.44rem;
  cursor: pointer;
  user-select: none;
}

/* Two separate generic rules silently apply to every <input> inside a
   <form>, checkbox included, unless explicitly reset here: .contact-form
   input's own 0.85rem/1.2rem padding, and (the actual cause of the
   vertical misalignment) main.css's own blanket `form input { margin: 0
   0 2rem; }`. That 2rem bottom margin doesn't visibly add whitespace
   (the checkbox is the last thing before the warning text, which has its
   own spacing), but align-items:center on the flex row centers each
   item's full margin box, not just its visible square — a lopsided
   0/2rem margin shifts the visible checkbox upward relative to the icon
   and text next to it, which had no such margin to correct for. */
.contact-form__anonymous input[type="checkbox"] {
  flex-shrink: 0;
  width: 1.6rem;
  height: 1.6rem;
  margin: 0;
  padding: 0;
  border: none;
  border-radius: 0;
  background: none;
  accent-color: #c9a51c;
  cursor: pointer;
}

.contact-form__anonymous .fa-user-secret {
  flex-shrink: 0;
  font-size: 1.5rem;
  color: #c9a51c;
}

.contact-form__anonymous span {
  line-height: 1;
}

.contact-form input:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.contact-form__anonymous-warning {
  margin: -0.5rem 0 0;
  color: #e0a45c;
  font-family: "Mulish", sans-serif;
  font-size: 1.28rem;
  text-align: center;
}

.activate-form .btn,
.email-auth-form .btn,
.contact-form .btn {
  margin-top: 0.5rem;
}

.email-auth-actions {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.email-auth-actions .btn {
  /* Oomph's base .btn assumes buttons sit inline with manual right-margin
     spacing; that only strips via :last-of-type, which left the first
     button uneven once these are gap-spaced instead. Neutralize it and
     let flex handle spacing in both the stacked and side-by-side layouts. */
  margin-right: 0;
}

@media (min-width: 640px) {
  .email-auth-actions {
    flex-direction: row;
  }

  .email-auth-actions .btn {
    /* Equal width regardless of "Sign In" vs "Sign Up" text width. */
    flex: 1 1 0;
    padding-left: 0;
    padding-right: 0;
  }
}

.home-back {
  display: inline-block;
  margin-top: 1.75rem;
  background: none;
  border: none;
  color: rgba(245, 239, 227, 0.7);
  font-size: 1.44rem;
  cursor: pointer;
  text-decoration: underline;
}

.home-back:hover {
  color: #f5efe3;
}

.stay-signed-in {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 1.25rem;
  color: rgba(245, 239, 227, 0.8);
  font-family: "Mulish", sans-serif;
  font-size: 1.44rem;
  cursor: pointer;
  user-select: none;
}

.stay-signed-in input[type="checkbox"] {
  width: 1.6rem;
  height: 1.6rem;
  accent-color: #c9a51c;
  cursor: pointer;
}

/* Unlike the other auth screens, Verify Email's button sits directly after
   its paragraph with no wrapping form/button-group element (.auth__buttons /
   .email-auth-form / .activate-form), so it never picked up the same
   margin-top those give — match it here for the same visual gap. */
#verify-email-continue {
  margin-top: 2.5rem;
}

.forgot-password-link {
  display: inline-block;
  margin-top: 1rem;
  color: rgba(245, 239, 227, 0.6);
  font-size: 1.36rem;
  text-decoration: underline;
}

/* Both Verify Email (resend link + back button) and Email (forgot-password
   link + back button) have two of these links stacked below their form —
   being inline-block by default otherwise lets them sit side by side if
   there's room, which reads as cramped rather than as two distinct actions.
   Force each onto its own line here without changing how they behave on
   Login/Forgot Password/Activate (those only ever have one such link, so
   this was never visible there). display:block alone isn't enough — a
   block box no longer centers via the parent's text-align (that only
   affects inline-level content), and drifts to the left edge instead.
   width:fit-content + auto side-margins restores the centering without
   touching each element's own margin-top. */
.home-state--verify-email .forgot-password-link,
.home-state--verify-email .home-back,
.home-state--email .forgot-password-link,
.home-state--email .home-back {
  display: block;
  width: fit-content;
  margin-left: auto;
  margin-right: auto;
}

.forgot-password-link:hover {
  color: #f5efe3;
}

.form-message {
  display: none;
  max-width: 360px;
  margin: 1.5rem auto 0;
  font: normal 400 1.44rem/1.5 "Mulish", sans-serif;
  color: #f5efe3;
}

.form-message.is-visible {
  display: block;
}

.form-message.is-error {
  color: #ff8a80;
}

/* .form-message's default colors (#f5efe3/#ff8a80) assume the dark auth
   screens it was built for — the Account section's avatar/photo area
   sits on a white .background-light panel instead, where that off-white
   text would be nearly invisible. */
.form-message--on-light {
  color: #2c342b;
}

.form-message--on-light.is-error {
  color: #9e2525;
}

.btn-auth:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.btn-auth:disabled:hover {
  transform: none;
  box-shadow: none;
}

/* ---------- Site-wide error banner ---------- */

.site-error-banner {
  position: fixed;
  top: 0.75rem;
  left: 50%;
  transform: translateX(-50%) translateY(-0.5rem);
  z-index: 1000;
  display: flex;
  align-items: center;
  gap: 0.9rem;
  max-width: calc(100% - 2rem);
  background: #5c2018;
  border: 1px solid #c9a51c;
  border-radius: 8px;
  padding: 0.75rem 1rem;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out;
}

.site-error-banner[hidden] {
  display: flex; /* keep transitioning instead of the attribute's default display:none */
}

.site-error-banner.is-visible {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
  pointer-events: auto;
}

.site-error-banner__text {
  font: normal 400 1.52rem/1.4 "Mulish", sans-serif;
  color: #ffffff;
}

.site-error-banner__close {
  flex-shrink: 0;
  border: none;
  background: transparent;
  color: rgba(255, 255, 255, 0.7);
  font-size: 2.24rem;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}

.site-error-banner__close:hover {
  color: #ffffff;
}

.hub__grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.25rem;
  margin-top: 3rem;
}

/* 3-across gives a clean 2-row layout for 6 tiles (was 4-across, back when
   there were only 4 tiles to show) */
@media (min-width: 640px) {
  .hub__grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (max-width: 479px) {
  .hub__grid {
    grid-template-columns: 1fr;
  }
}

/* ---------- Hamburger menu: resized for 6 entries ----------
   The theme's own sizing (main.css) was built for a dramatic 3-4-item
   marketing nav — link text alone scales up to 7rem (70px) at large
   desktop widths, plus 1.4rem vertical padding per item. That's fine for
   4 items but pushes 6 past the viewport height, forcing a scroll on
   desktop where none is wanted (mobile scrolling is fine, per Xavier).
   Overriding both the font sizes AND the padding together, since the
   mismatch is the scale itself, not just spacing. */
.navigation li {
  padding: 1rem 0;
}
/* main.css sets `.navigation li { display: block; }` directly, which
   beats the browser's own `[hidden] { display: none }` rule the same
   way this exact trap has bitten this codebase repeatedly elsewhere
   (see AGENTS.md) — without this, the 3 admin-only `<li hidden>`
   entries render regardless of their hidden attribute, exactly the bug
   Xavier caught live (visible with Admin Mode off, or as a non-admin).
   Verified via computed style that .hidden was true on the element but
   display was still "block" before this fix. */
.navigation li[hidden] {
  display: none;
}
.navigation li span {
  font-size: 2.2rem;
}
.navigation li a {
  font-size: 3.6rem;
}
@media only screen and (min-width: 768px) {
  .navigation li a {
    font-size: 4rem;
  }
}
@media only screen and (min-width: 1200px) {
  .navigation li span {
    font-size: 2.4rem;
  }
}
@media only screen and (min-width: 1400px) {
  .navigation li a {
    font-size: 4.6rem;
  }
}

/* Real bug, caught by Xavier live: adding the 3 admin-only entries (10
   total now, up from 7) pushed the whole nav list taller than the menu
   panel — main.css centers .navigation with `position: absolute; top:
   50%; transform: translateY(-50%)`, which has no concept of overflow
   at all, so once the block got taller than the viewport, centering it
   pushed the first item (About) up past the top edge with no way to
   scroll back up to it. Per Xavier's own explicit call not to resize
   the menu items to compensate, this switches the centering mechanism
   itself instead: .menu__content becomes a plain (row-direction) flex
   container, and .navigation — back to normal static positioning
   instead of main.css's absolute+transform — gets `margin: auto 0` to
   center itself vertically within it.
   Deliberately NOT `align-items: center` on the container (a first
   attempt at this fix used that, and Xavier reported the exact same
   "About" cut off symptom persisting) — that's a well-known flexbox/
   overflow trap: centering an over-tall flex item via the *parent's*
   align-items positions part of it at a negative offset relative to
   the scroll container, and scrollTop can never go negative, so that
   leading portion becomes permanently unreachable no matter how you
   scroll — the exact bug this was supposed to fix, just relocated.
   Auto margins on the *item* itself don't have that problem — when
   content overflows, the auto margins collapse to 0 instead of forcing
   a negative offset, so the full block including "About" starts
   reachable at scrollTop 0. Applies regardless of admin status — a
   non-admin's shorter 7-item list still centers exactly as before,
   this only changes behavior once there's enough content to actually
   overflow. */
.menu__content {
  display: flex;
  overflow-y: auto;
}
.navigation {
  position: static;
  transform: none;
  margin: auto 0;
}

/* main.css only defines staggered entrance timing up through the 5th nav
   item (built for a max of 5); 6th through 10th need the same treatment
   following the same +0.1s-per-item pattern (7th-10th added alongside
   the 3 admin-only entries below). */
.menu.animate-in .transition-el-6 {
  transition: opacity 0.3s 1.2s, transform 0.3s 1.2s;
}
.menu.animate-in .transition-el-7 {
  transition: opacity 0.3s 1.3s, transform 0.3s 1.3s;
}
.menu.animate-in .transition-el-8 {
  transition: opacity 0.3s 1.4s, transform 0.3s 1.4s;
}
.menu.animate-in .transition-el-9 {
  transition: opacity 0.3s 1.5s, transform 0.3s 1.5s;
}
.menu.animate-in .transition-el-10 {
  transition: opacity 0.3s 1.6s, transform 0.3s 1.6s;
}
/* 11th (Invites) was already using this class with no rule defined at
   all — it silently never animated in, just appeared static. 12th
   (Testing) added alongside the fix, same +0.1s pattern. */
.menu.animate-in .transition-el-11 {
  transition: opacity 0.3s 1.7s, transform 0.3s 1.7s;
}
.menu.animate-in .transition-el-12 {
  transition: opacity 0.3s 1.8s, transform 0.3s 1.8s;
}

/* Same orange as the admin-only Hub tile icons (#hub-admin-history-trigger
   etc. above) — text color only, no size/padding changes, per Xavier's
   explicit "don't resize the menu options" call. Needs the compound
   .navigation li a selector (not just .menu-link--admin alone) to
   actually beat main.css's own .navigation li a color rule — a bare
   single-class selector loses that specificity fight and silently
   renders as the theme's default white/cream. */
.navigation li a.menu-link--admin {
  color: #b45f06;
}

/* Super-admin's own color, distinct from the plain-admin orange above —
   Xavier's own call, so Invites/Testing read as a stricter tier at a
   glance rather than blending in with History/Moderation/User Images.
   Tuned to roughly the same brightness as #b45f06 so it carries the
   same visual weight against .menu's dark background, not washed out
   or overpowering next to it. Same specificity reasoning as
   .menu-link--admin above. */
.navigation li a.menu-link--super-admin {
  color: #9b4f9e;
}

.hub__tile {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  padding: 2rem 1rem;
  border-radius: 14px;
  background: rgba(245, 239, 227, 0.06);
  border: 1px solid rgba(201, 165, 28, 0.35);
  color: #f5efe3;
  text-decoration: none;
  transition: transform 0.3s ease, background 0.3s ease, border-color 0.3s ease;
}

/* Same [hidden]-vs-author-stylesheet issue as .admin-reason-overlay/
   #giftlist-suspend-btn — .hub__tile sets display: flex directly,
   which otherwise overrides [hidden]'s display: none regardless of
   the attribute. An id selector always wins on specificity regardless
   of source order, so this reliably wins without !important.
   #hub-invites-trigger was added after this rule already existed and
   got missed — exactly the bug it caught for the other three, just not
   caught for this one: Xavier found it showing up with Admin Mode off. */
#hub-admin-history-trigger[hidden],
#hub-moderation-trigger[hidden],
#hub-user-images-trigger[hidden],
#hub-invites-trigger[hidden],
#hub-testing-trigger[hidden] {
  display: none;
}

/* Count of unread threads (not messages) — set from session.php on page
   load (before My Messages has ever been opened) and kept live after
   that by loadConversations() itself, see site.js. */
.hub__tile-badge {
  position: absolute;
  top: -0.6rem;
  right: -0.6rem;
  min-width: 1.7rem;
  height: 1.7rem;
  padding: 0 0.4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: #c9a51c;
  color: #2c342b;
  font-family: "Mulish", sans-serif;
  font-size: 1.15rem;
  font-weight: 700;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}

.hub__tile-badge[hidden] {
  display: none;
}

/* Second, independent badge on the Moderation tile only — opposite
   corner, distinct color, for "an already-moderated item has been
   edited by its owner since you last checked" as opposed to the
   existing gold badge's "there's a new moderated/reported item." Same
   [hidden]-vs-display id-selector trap as everywhere else in this
   file, so it gets its own override below rather than relying on the
   shared .hub__tile-badge[hidden] rule (which it also matches, but
   duplicating the guard here keeps this block self-contained). */
.hub__tile-badge--edited {
  top: -0.6rem;
  left: -0.6rem;
  right: auto;
  background: #3a7ca5;
  color: #f5efe3;
}

.hub__tile-badge--edited[hidden] {
  display: none;
}

.hub__tile:hover {
  transform: translateY(-6px);
  background: rgba(201, 165, 28, 0.12);
  border-color: #c9a51c;
  color: #f5efe3;
}

.hub__tile-icon {
  font-size: 3.2rem;
  color: #c9a51c;
  margin-bottom: 0.25rem;
}

/* Admin-only tiles get the same orange as .gc-btn--moderate's gavel
   elsewhere on the site, instead of the usual gold every other tile
   icon uses — a quick visual "this one's an admin option" cue, Xavier's
   own request. Icon color only (size/spacing untouched) — these ids are
   already the exact set gated by isAdmin && Admin Mode in
   applyAdminMode(), so nothing else needs to change for this to only
   ever show for an admin with Admin Mode on. */
#hub-admin-history-trigger .hub__tile-icon,
#hub-moderation-trigger .hub__tile-icon,
#hub-user-images-trigger .hub__tile-icon {
  color: #b45f06;
}

/* Super-admin tiles get their own color instead — same
   .navigation li a.menu-link--super-admin reasoning: Invites/Testing
   are a stricter tier than plain admin, so they read as visually
   distinct rather than blending in with the three above. */
#hub-invites-trigger .hub__tile-icon,
#hub-testing-trigger .hub__tile-icon {
  color: #9b4f9e;
}

.hub__tile-title {
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 1.84rem;
}

.hub__tile-text {
  font-size: 1.36rem;
  opacity: 0.75;
}

/* Standalone featured button — deliberately separate from .hub__grid, not
   just a bigger tile within it. My Wish List is the single most frequent
   action on this page, so it gets its own prominent spot above the grid
   rather than competing equally with everything else. */
.hub__hero-btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.9rem;
  width: 100%;
  margin-top: 3rem;
  padding: 1.6rem 2rem;
  border-radius: 20px;
  background: #c9a51c;
  color: #2c342b;
  text-decoration: none;
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 2rem;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.hub__hero-btn i {
  font-size: 2.2rem;
  transition: color 0.2s ease, text-shadow 0.2s ease;
}

.hub__hero-btn:hover {
  transform: translateY(-3px);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.3);
  color: #2c342b;
}

/* Just the star, not the button's own text — Xavier's request. White
   plus a soft two-layer text-shadow glow (tight + wide) is what actually
   reads as "lit up" rather than just a color swap; a single-layer
   shadow looks flat by comparison. */
.hub__hero-btn:hover i {
  color: #ffffff;
  text-shadow: 0 0 8px rgba(255, 255, 255, 0.9), 0 0 18px rgba(255, 255, 255, 0.6);
}

/* Testing Checklist — same shape as .hub__hero-btn, deliberately
   lighter-weight (outline, not solid fill) so it doesn't compete with
   My Wish List for primary visual attention. It's a temporary layer
   over the real site (hidden outright once testing mode is off), so it
   shouldn't look like a permanent fixture. */
/* Same [hidden]-vs-author-stylesheet issue as .hub__tile above
   (#hub-admin-history-trigger[hidden] etc.) — .hub__hero-btn sets
   display: flex directly, which otherwise overrides [hidden]'s
   display: none regardless of the attribute. This is the one
   .hub__hero-btn that's ever actually toggled hidden/shown by JS
   (My Wish List never is), so it's the one that needs this. */
#hub-testing-checklist-trigger[hidden] {
  display: none;
}

/* Same issue, different element — .testing-questionnaire-start-btn also
   sets display: inline-flex directly. #testing-mode-begin-btn is the
   one use of that class actually toggled hidden/shown by JS (the
   Checklist page's own use of it is only ever disabled, never hidden);
   .testing-questionnaire-secondary-btn (End's own class) sets no
   display of its own, so #testing-mode-end-btn doesn't need this. */
#testing-mode-begin-btn[hidden] {
  display: none;
}

.hub__hero-btn--testing {
  background: transparent;
  border: 2px solid #c9a51c;
  color: #f5efe3;
  margin-top: 1.6rem;
}

.hub__hero-btn--testing i {
  color: #c9a51c;
}

.hub__hero-btn--testing:hover {
  color: #f5efe3;
  background: rgba(201, 165, 28, 0.12);
}

.hub__hero-btn-badge {
  display: inline-flex;
  align-items: center;
  font-family: "Mulish", sans-serif;
  font-size: 1.3rem;
  font-weight: 700;
  background: #c9a51c;
  color: #2c342b;
  border-radius: 20px;
  padding: 0.2rem 0.9rem;
}

.hub__hero-btn-badge i {
  margin-left: 0.3rem;
  color: #2c342b;
}

/* .hub__hero-btn:hover i's own glow (further up this file) is meant for
   the button's own leading icon, not this badge's — without this
   override it'd turn white and glow against the badge's gold
   background on hover, unreadable rather than "lit up." */
.hub__hero-btn:hover .hub__hero-btn-badge i {
  color: #2c342b;
  text-shadow: none;
}

/* Same corner-badge convention as .hub__tile-badge elsewhere on Hub,
   just its own class rather than reusing that one directly — .hub__tile
   is a relative-positioned square tile with room in its actual corner;
   .hub__hero-btn is a full-width pill button, so top/right sit inset
   from its rounded corner rather than overlapping it edge-on. Icon
   only, no count — pulled out of the flex row entirely (it used to sit
   inline next to "My Wish List," which visibly threw off that text's
   own centering) and dropped down to just the icon after that, since a
   labeled pill read as more than this flat "something's new" signal
   needs to say. Dark green, not the site's usual gold — the button's
   own background is already solid gold, so gold-on-gold repeats the
   exact invisible-dot mistake this replaced. */
.hub__hero-btn-badge--comments {
  position: absolute;
  top: 50%;
  right: 1.2rem;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: #2c342b;
  box-shadow: 0 0 0 3px rgba(44, 52, 43, 0.25);
}
.hub__hero-btn-badge--comments i {
  font-size: 1.1rem;
  color: #f5efe3;
}
.hub__hero-btn:hover .hub__hero-btn-badge--comments i {
  color: #f5efe3;
  text-shadow: none;
}

.hub__hero-btn-badge--comments[hidden] {
  display: none;
}

.hub__hero-btn-dot[hidden] {
  display: none;
}

/* ---------- Testing Checklist page ---------- */

.testing-checklist-intro {
  margin: 0 0 1.4rem;
  font-size: 1.6rem;
  line-height: 1.6;
  color: #6b6555;
}

.testing-checklist-progress {
  margin: 0 0 0.6rem;
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 1.8rem;
  color: #2c342b;
}

/* Same track/fill shape as the site's other progress-style bars — a
   quick "how far along" glance before ever reading a single item. */
.testing-checklist-progress-track {
  width: 100%;
  height: 10px;
  border-radius: 10px;
  background: rgba(44, 52, 43, 0.08);
  overflow: hidden;
  margin: 0 0 1.8rem;
}

.testing-checklist-progress-fill {
  height: 100%;
  border-radius: 10px;
  background: linear-gradient(90deg, #c9a51c, #e0c25a);
  transition: width 0.4s ease;
}

.testing-checklist-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

/* Same card language as .gc-card elsewhere on the site (white surface,
   rounded corners, soft shadow) instead of bare text rows — Xavier's
   own quality bar for list items generally. */
.testing-checklist-item {
  display: flex;
  flex-direction: column;
  padding: 1.1rem 1.4rem;
  background: #fff;
  border-radius: 12px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.06);
  font-size: 1.6rem;
  line-height: 1.5;
  color: #2c342b;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.testing-checklist-item__row {
  display: flex;
  align-items: center;
  gap: 1.2rem;
}

/* A short "here's how" for anyone unsure where a task even lives —
   collapsed by default so the list stays scannable for testers who
   don't need it. Own accordion per item, not tied to done/not-done. */
.testing-checklist-item__hint-toggle {
  flex-shrink: 0;
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.4rem 0.9rem;
  border: 1px solid rgba(44, 52, 43, 0.15);
  border-radius: 14px;
  background: transparent;
  color: #8a7f5f;
  font-family: "Mulish", sans-serif;
  font-size: 1.2rem;
  font-weight: 700;
  cursor: pointer;
  transition: border-color 0.2s ease, color 0.2s ease;
}

.testing-checklist-item__hint-toggle i {
  font-size: 1rem;
  transition: transform 0.2s ease;
}

.testing-checklist-item__hint-toggle:hover,
.testing-checklist-item__hint-toggle.is-open {
  border-color: #c9a51c;
  color: #7a5c00;
}

.testing-checklist-item__hint-toggle.is-open i {
  transform: rotate(180deg);
}

.testing-checklist-item__hint {
  max-height: 0;
  overflow: hidden;
  margin: 0;
  padding-top: 0;
  border-top: 1px solid transparent;
  font-size: 1.4rem;
  line-height: 1.6;
  color: #6b6555;
  transition: max-height 0.3s ease, margin-top 0.3s ease, padding-top 0.3s ease;
}

.testing-checklist-item__hint.is-open {
  max-height: 20rem;
  margin-top: 1rem;
  padding-top: 1rem;
  border-top-color: rgba(44, 52, 43, 0.08);
}

.testing-checklist-item__hint-guide-link {
  color: #7a5c00;
  font-weight: 700;
  text-decoration: underline;
}

.testing-checklist-item__hint-guide-link:hover {
  color: #c9a51c;
}

.testing-checklist-item__icon-wrap {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3.6rem;
  height: 3.6rem;
  border-radius: 50%;
  background: rgba(184, 174, 154, 0.16);
}

.testing-checklist-item__icon {
  font-size: 1.9rem;
  color: #b8ae9a;
}

.testing-checklist-item--done {
  background: #f6faf6;
  box-shadow: 0 3px 10px rgba(74, 122, 79, 0.1);
}

.testing-checklist-item--done .testing-checklist-item__icon-wrap {
  background: rgba(74, 122, 79, 0.14);
}

.testing-checklist-item--done .testing-checklist-item__icon {
  color: #4a7a4f;
}

.testing-checklist-item--done .testing-checklist-item__text {
  color: #6b8a6e;
  text-decoration: line-through;
}

/* Step 1/Step 2 headers on the checklist page — a title plus a check
   graphic that only appears once that step is actually done (state
   driven entirely by renderTestingChecklist() in site.js). */
.testing-checklist-step-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin: 0 0 1.2rem;
}

.testing-checklist-step-title {
  margin: 0;
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 2rem;
  color: #2c342b;
}

.testing-checklist-step-check {
  font-size: 2.6rem;
  color: #4a7a4f;
  line-height: 1;
}

.testing-checklist-questionnaire-status-label {
  margin: 0 0 0.4rem;
  font-family: "Mulish", sans-serif;
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #7a5c00;
}

.testing-checklist-questionnaire-status {
  margin: 0 0 1.6rem;
  font-size: 1.6rem;
  line-height: 1.5;
  color: #6b6555;
}

.testing-questionnaire-buttons {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.2rem;
}

/* Stacked rather than the shared class's default row — four same-
   weight buttons in a row read as a cramped, hard-to-scan strip; one
   per line is easier to pick out.  */
.testing-reminder-preview-buttons {
  flex-direction: column;
  align-items: stretch;
}

/* Same visual weight as .hub__hero-btn (My Wish List / Testing
   Checklist on Hub) rather than the plain .btn-small every other admin/
   form action on the site uses — this is the one action this whole
   page exists to lead someone to, so it gets the same "primary CTA"
   treatment as those, not a small text-link-looking button buried at
   the bottom of a list. */
.testing-questionnaire-start-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.9rem;
  padding: 1.4rem 2.6rem;
  border: none;
  border-radius: 20px;
  background: #c9a51c;
  color: #2c342b;
  text-decoration: none;
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 2rem;
  cursor: pointer;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.testing-questionnaire-start-btn i {
  font-size: 2.2rem;
}

.testing-questionnaire-start-btn:hover {
  transform: translateY(-3px);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.25);
}

.testing-questionnaire-start-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}

/* Secondary action next to the primary CTA (Start New Questionnaire,
   shown only alongside "Continue Questionnaire") — deliberately quieter
   than the primary button since it's the less-common path and starts a
   destructive confirm flow, not something to visually compete with
   Continue for attention. */
.testing-questionnaire-secondary-btn {
  padding: 1rem 1.8rem;
  border: 1px solid rgba(44, 52, 43, 0.25);
  border-radius: 20px;
  background: transparent;
  color: #6b6555;
  font-family: "Mulish", sans-serif;
  font-weight: 600;
  font-size: 1.5rem;
  cursor: pointer;
  transition: border-color 0.2s ease, color 0.2s ease;
}

.testing-questionnaire-secondary-btn:hover {
  border-color: #6b6555;
  color: #2c342b;
}

.testing-checklist-edit-hint {
  margin: 1rem 0 0;
  font-size: 1.4rem;
  font-style: italic;
  color: #8a7f5f;
}

.testing-checklist-support-link {
  margin: 1.4rem 0 0;
  font-size: 1.4rem;
  color: #6b6555;
}

.testing-questionnaire-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-top: 1.2rem;
}

.testing-questionnaire-form label.testing-questionnaire-question-text {
  display: block;
  margin-bottom: 0.8rem;
}

.testing-questionnaire-form textarea {
  width: 100%;
  padding: 1rem 1.2rem;
  border-radius: 8px;
  border: 1px solid rgba(44, 52, 43, 0.2);
  background: #fff;
  color: #2c342b;
  font-size: 1.6rem;
  font-family: "Mulish", sans-serif;
  resize: none;
}

/* .comment-form__error's site-wide default (1.2rem) is sized for a
   short inline note under a small comment box — too easy to miss next
   to this form's own much larger question text and buttons. */
.testing-questionnaire-form #testing-questionnaire-error {
  font-size: 1.5rem;
}

.testing-questionnaire-form__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.2rem;
  margin-top: 0.6rem;
}

.testing-progress-list {
  display: flex;
  flex-direction: column;
  gap: 1.2rem;
  margin-top: 1.2rem;
}

.testing-progress-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.4rem;
  padding: 1.2rem 1.4rem;
  border-radius: 10px;
  background: #fff;
  border: 1px solid rgba(44, 52, 43, 0.12);
}

.testing-progress-row__name {
  flex: 0 0 140px;
  font-weight: 700;
  font-size: 1.4rem;
  color: #2c342b;
}

.testing-progress-row__bar {
  flex: 1 1 200px;
  min-width: 160px;
}

.testing-progress-row__bar-label {
  display: block;
  margin-top: 0.4rem;
  font-size: 1.2rem;
  color: #6b6555;
}

.testing-progress-row__status {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex: 0 0 auto;
}

/* Same fa-circle-check green (#4a7a4f) the checklist page's own Step
   1/2 headers use for "done" — an empty outline circle fills the same
   slot for "not done" instead of hiding it entirely, since here the
   point is comparing testers against each other, not just marking one
   person's own completed step. */
.testing-progress-row__check {
  font-size: 2.2rem;
  line-height: 1;
  color: #c7c1b0;
}

.testing-progress-row__check--done {
  color: #4a7a4f;
}

.testing-progress-row__date {
  font-size: 1.25rem;
  color: #6b6555;
  white-space: nowrap;
}

.testing-progress-row__view-btn {
  padding: 0.6rem 1.3rem;
  border-radius: 16px;
  border: 1px solid #c9a51c;
  background: transparent;
  color: #7a5c00;
  font-family: "Mulish", sans-serif;
  font-weight: 700;
  font-size: 1.25rem;
  cursor: pointer;
  white-space: nowrap;
}

.testing-progress-row__view-btn:hover {
  background: #c9a51c;
  color: #2c342b;
}

.testing-questionnaire-section-title {
  margin: 3.2rem 0 1.8rem;
  padding-bottom: 1rem;
  border-bottom: 2px solid rgba(122, 92, 0, 0.25);
  font-family: "Playfair Display", serif;
  font-size: 2.3rem;
  color: #7a5c00;
}

.testing-questionnaire-section-title:first-child {
  margin-top: 0;
}

.testing-questionnaire-question {
  padding: 0;
  margin: 0 0 2.2rem;
  border: none;
}

.testing-questionnaire-question:last-child {
  margin-bottom: 0;
}

/* Shared by the <legend> (multiple-choice) and the <label> (typed) —
   one rule for how prominent a question reads, regardless of which
   kind of question it is. Sized and weighted to read as the thing
   being asked, not a caption above the real content. */
.testing-questionnaire-question-text {
  padding: 0;
  font-family: "Mulish", sans-serif;
  font-size: 1.7rem;
  font-weight: 400;
  color: #2c342b;
  line-height: 1.4;
}

.testing-questionnaire-options {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-top: 0.6rem;
}

/* Stacked full-width rows rather than wrapped pills — pills sized to
   their own label text (e.g. "Easy" vs. "No, something clearly went
   wrong") read as an inconsistent, uneven grid. A row's width is
   always the container's width regardless of label length, so every
   option reads at the same size without needing to fake it. */
.testing-questionnaire-option {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  padding: 0.8rem 1.1rem;
  border-radius: 8px;
  border: 1px solid rgba(44, 52, 43, 0.15);
  background: #fff;
  color: #2c342b;
  font-size: 1.35rem;
  font-weight: 400;
  cursor: pointer;
}

.testing-questionnaire-option input {
  flex-shrink: 0;
  width: 1.8rem;
  height: 1.8rem;
  margin: 0;
  accent-color: #c9a51c;
  cursor: pointer;
}

.testing-questionnaire-option:has(input:checked) {
  border-color: #c9a51c;
  background: rgba(201, 165, 28, 0.1);
}

/* Notice — a quiet strip under the tile grid, deliberately not a bright
   banner competing with the tiles above it (Xavier's own spec: "out of
   the way by design," no dismiss control needed because of that). "Quiet"
   here means restrained, not low-contrast — first pass reused this
   site's light-panel muted-text color (#6b6555), which reads fine on the
   cream panels it was designed for but was nearly invisible against the
   Hub's own dark background. Second pass borrowed .hub__tile's own
   dark-background box treatment (background tint + gold border) to fix
   that, which then read as its own clickable tile — a different problem.
   Landed on plain text sitting directly on the dark background instead,
   no box at all: cream text (#f5efe3), gold label/icon (#c9a51c) for
   contrast and legibility, but nothing that could be mistaken for
   another tile. text-align:left overrides .hub__content's own centered
   text (same reasoning .hub__chat below already has to work around).
   `:not([hidden])` on the display rule is load-bearing, not decorative —
   without it this authored `display: flex` (author CSS always wins over
   the browser's own built-in `[hidden] { display: none }`, even at
   equal specificity) would keep rendering the whole strip, box and all,
   the moment the `hidden` attribute is set; only .hub-notice-text's own
   empty content would actually disappear. Same bug class that already
   bit .gc-btn and others elsewhere in this file. */
.hub-notice:not([hidden]) {
  position: relative;
  display: block;
  margin-top: 1.5rem;
  /* Reserves room so a wide line of text doesn't visually run under the
     absolutely-positioned admin controls in the corner — see those
     below. */
  padding-right: 4.5rem;
  font-size: 1.5rem;
  /* Explicit rather than the default "normal" — updateHubNoticeMultiline()
     in site.js reads this back via getComputedStyle() to measure whether
     the text wrapped, and some browsers report "normal" back as that
     literal string rather than a resolved pixel value, which would
     break the math. */
  line-height: 1.4;
  color: #f5efe3;
  text-align: left;
}
/* Renders literal newlines in the message as real line breaks — .text()
   in site.js writes them into the DOM as plain text, and text nodes
   collapse whitespace (including \n) by default without this. */
.hub-notice__body {
  white-space: pre-line;
}
/* Multi-line mode (toggled by renderHubNotice() in site.js, based on
   whether the message contains a newline): Xavier's own spec is "Notice"
   on its own line, message indented on the line(s) under it — not a
   hanging indent under the message's own first line. .hub-notice__body
   stays one continuous inline run for the default (single-line) case so
   "Notice [icon] message" reads as one sentence, but here .hub-notice__text
   switches to display:block, which is what pushes it onto a fresh line
   below the label — padding-left then indents that whole block (every
   wrapped/newline'd line alike) under roughly where "Notice" sits above
   it. Single-line notices need none of this — plain left-aligned text
   inline with the label already reads fine on one line. */
.hub-notice--multiline .hub-notice__text {
  display: block;
  padding-left: 5rem;
}
/* Marquee mode (an admin-toggled checkbox in the edit form, independent
   of single vs. multi-line content — see hub-notice-marquee in site.js):
   forces everything onto one scrolling line regardless of how many
   newlines the message actually has, so it wins over --multiline's
   hanging indent rather than combining with it. "Notice [icon]" stays
   put as a fixed label; only the message text itself scrolls, in its
   own overflow-hidden viewport so the label never gets covered mid-
   scroll. padding-left:100% on the text is the classic marquee trick —
   it starts the text one full viewport-width off to the right (a
   percentage padding is resolved against the containing block's own
   width, so this works at any notice width without JS measuring
   anything), and the animation slides it left until the same distance
   has passed underneath, so it exits fully on the left before looping. */
.hub-notice--marquee .hub-notice__body {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding-left: 0;
  text-indent: 0;
}
.hub-notice--marquee .hub-notice__label {
  flex-shrink: 0;
}
.hub-notice--marquee .hub-notice__text {
  display: block;
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
}
.hub-notice--marquee .hub-notice__text-inner {
  display: inline-block;
  white-space: nowrap;
  padding-left: 100%;
  animation: hub-notice-marquee 16s linear infinite;
}
@keyframes hub-notice-marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-100%); }
}
.hub-notice__icon {
  display: inline-block;
  color: #c9a51c;
  font-size: 1.3rem;
}
/* First-time-seeing-this-message indicator (users.seen_notice, per
   get_site_notice.php): the bullhorn keeps swinging continuously, not a
   periodic burst-then-rest — Xavier's own follow-up feedback was that a
   pause between bursts left "moments when it looks normal," which was
   exactly the window someone could glance over and miss it in. So this
   never actually stops moving while is-new is set: it sways back and
   forth every 1.6s (no idle keyframe at all) with a matching glow pulse
   layered on the same cycle, animated together via the two-animation
   shorthand below. Pivoted near the mouthpiece (left/narrow end of the
   glyph) so it reads as the horn swinging on that point, not spinning
   around its own center. */
.hub-notice__icon.is-new {
  transform-origin: 25% 65%;
  animation:
    hub-notice-icon-ring 1.6s ease-in-out infinite,
    hub-notice-icon-glow 1.6s ease-in-out infinite;
}
@keyframes hub-notice-icon-ring {
  0%, 100% { transform: rotate(-14deg); }
  50% { transform: rotate(14deg); }
}
@keyframes hub-notice-icon-glow {
  0%, 100% { filter: drop-shadow(0 0 2px rgba(255, 245, 214, 0.5)); }
  50% { filter: drop-shadow(0 0 8px rgba(255, 245, 214, 1)); }
}
/* Same "keep it always moving" fix applied to the sparkle aura: each
   star's own twinkle cycle is short (1.2s) with no idle plateau, and
   the three are staggered a third of a cycle apart so at least one of
   them is near full brightness at any given instant — there's no
   moment where the whole aura goes dark together. Three separate <i>
   elements (not a single repeated shape) so each can be positioned and
   time-delayed independently for a scattered look rather than one
   obvious blinking dot. They're plain siblings of #hub-notice-icon
   inside .hub-notice__icon-wrap, activated purely via the `~` sibling
   selector off .hub-notice__icon.is-new — no separate JS toggle needed
   per star, the one class on the icon drives all of it. aria-hidden
   since they're decorative on top of an icon that already carries its
   own sr-only "New notice" text elsewhere in this markup. */
.hub-notice__icon-wrap {
  position: relative;
  display: inline-block;
}
.hub-notice__sparkle {
  position: absolute;
  font-size: 0.5em;
  color: #fff5d6;
  /* Invisible at rest — the 0.15 "dim" floor lives in the keyframes
     below and only applies once .is-new's animation is actually
     running; without a separate resting opacity: 0 here, that floor
     value leaked in permanently and left faint stars visible even on a
     message that's already been seen. */
  opacity: 0;
  pointer-events: none;
}
.hub-notice__sparkle--1 {
  top: -0.4em;
  right: -0.6em;
}
.hub-notice__sparkle--2 {
  bottom: -0.35em;
  left: -0.65em;
  font-size: 0.4em;
}
.hub-notice__sparkle--3 {
  top: 0.15em;
  left: -0.85em;
  font-size: 0.32em;
}
.hub-notice__icon.is-new ~ .hub-notice__sparkle {
  animation: hub-notice-sparkle-twinkle 1.2s ease-in-out infinite;
}
.hub-notice__icon.is-new ~ .hub-notice__sparkle--1 {
  animation-delay: 0s;
}
.hub-notice__icon.is-new ~ .hub-notice__sparkle--2 {
  animation-delay: 0.4s;
}
.hub-notice__icon.is-new ~ .hub-notice__sparkle--3 {
  animation-delay: 0.8s;
}
@keyframes hub-notice-sparkle-twinkle {
  0%, 100% {
    opacity: 0.15;
    transform: scale(0.5) rotate(0deg);
  }
  50% {
    opacity: 1;
    transform: scale(1.2) rotate(20deg);
  }
}
/* Visually hidden, not display:none — screen readers still announce
   "New notice" the moment this leaves the DOM's hidden state, since the
   ring animation above carries no meaning for anyone who can't see it. */
.hub-notice__new-badge {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.hub-notice__label {
  font-weight: 600;
  color: #c9a51c;
}
/* :not([hidden]) is load-bearing here too — same bug as .hub-notice
   itself above (an authored `display: flex` silently defeats the
   `hidden` attribute), just a second instance of it: a non-admin (or
   Admin Mode off) was still seeing the edit/clear buttons because this
   rule's bare `display: flex` kept rendering them regardless of the
   `hidden` property JS was setting on this exact element. Absolutely
   positioned (not part of the text flow at all now) so it sits in a
   fixed corner regardless of whether the text above is one plain line
   or hanging-indented across several. */
.hub-notice__admin-controls:not([hidden]) {
  position: absolute;
  top: 0;
  right: 0;
  display: flex;
  gap: 0.5rem;
}
.hub-notice__edit-btn,
.hub-notice__clear-btn {
  background: none;
  border: none;
  padding: 0.2rem;
  font-size: 1.3rem;
  color: #f5efe3;
  opacity: 0.75;
  cursor: pointer;
  transition: opacity 0.15s ease;
}
.hub-notice__edit-btn:hover,
.hub-notice__clear-btn:hover {
  opacity: 1;
}

.hub-notice-edit-form {
  margin-top: 1.5rem;
  text-align: left;
}
.hub-notice-edit-form textarea {
  /* Same box treatment as .wishlist-item-editor-reason-other elsewhere
     in this file — a real white box with dark text, not just "whatever
     the browser default is." Without an explicit background/color here,
     the field inherited transparent/no background (no visible box at
     all) and this section's own inherited dark-panel text color, which
     defaults toward black — illegible typed text on the Hub's dark
     background with nothing behind it to contrast against either way. */
  display: block;
  width: 100%;
  height: 7.5rem;
  padding: 0.9rem 1.2rem;
  border-radius: 8px;
  border: 1px solid rgba(44, 52, 43, 0.2);
  background: #fff;
  color: #2c342b;
  font-size: 1.36rem;
  line-height: 1.4;
  font-family: "Mulish", sans-serif;
  resize: none;
}
.hub-notice-edit-form__counter {
  margin: 0.4rem 0 0;
  font-size: 1.2rem;
  color: #f5efe3;
  opacity: 0.65;
  text-align: right;
}
.hub-notice-edit-form__counter.is-limit {
  color: #e0a05c;
  opacity: 1;
}
.hub-notice-edit-form__marquee {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.6rem;
  font-size: 1.3rem;
  color: #f5efe3;
  cursor: pointer;
}
/* The actual checkbox is kept for real toggle semantics (keyboard,
   screen readers, form state) but visually replaced by the pill+thumb
   track below it — sr-only rather than display:none so it stays
   focusable and clicking anywhere in the <label> (the native browser
   behavior for a label wrapping an input) still toggles it. */
.hub-notice-edit-form__marquee .hub-notice-toggle__input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.hub-notice-toggle__track {
  position: relative;
  flex-shrink: 0;
  display: inline-block;
  width: 3.4rem;
  height: 1.8rem;
  border-radius: 999px;
  background: rgba(245, 239, 227, 0.25);
  transition: background 0.15s ease;
}
.hub-notice-toggle__thumb {
  position: absolute;
  top: 0.2rem;
  left: 0.2rem;
  width: 1.4rem;
  height: 1.4rem;
  border-radius: 50%;
  background: #f5efe3;
  transition: transform 0.15s ease;
}
.hub-notice-toggle__input:checked ~ .hub-notice-toggle__track {
  background: #c9a51c;
}
.hub-notice-toggle__input:checked ~ .hub-notice-toggle__track .hub-notice-toggle__thumb {
  transform: translateX(1.6rem);
  background: #2c342b;
}
/* Keyboard-only affordance — the track itself has no native focus ring
   since the real focusable element is the sr-only checkbox underneath. */
.hub-notice-toggle__input:focus-visible ~ .hub-notice-toggle__track {
  outline: 2px solid #c9a51c;
  outline-offset: 2px;
}
/* .btn-small--cancel's site-wide default is a muted underlined text
   link (#6b6555, meant for light panels) — barely visible on the Hub's
   dark background, and Xavier asked for "a proper button" here anyway
   rather than the dismissive link style. Bordered like .btn-small--preview
   elsewhere in this file, just in the Hub's own light-on-dark palette. */
.hub-notice-edit-form .btn-small--cancel {
  background: transparent;
  color: #f5efe3;
  border: 1px solid rgba(245, 239, 227, 0.35);
  text-decoration: none;
}
.hub-notice-edit-form .btn-small--cancel:hover {
  background: rgba(245, 239, 227, 0.12);
  color: #f5efe3;
}
.hub-notice-edit-form__actions {
  display: flex;
  gap: 0.6rem;
  justify-content: center;
  margin-top: 0.6rem;
}

/* Chat, sitting directly on the Hub page below the tile grid rather
   than behind a tile — .hub__content has text-align:center for the
   headline content above it, which would otherwise center every line of
   chat text and the form controls too. */
.hub__chat {
  margin-top: 3rem;
  text-align: left;
}

.hub__chat-divider {
  height: 1px;
  margin-bottom: 2rem;
  background: linear-gradient(to right, transparent, rgba(201, 165, 28, 0.5), transparent);
}

.hub__chat-title {
  /* The theme's own h3 { color: #0A0A0A } (main.css) has no h3.light
     counterpart the way h1.light exists — only h1 got that treatment —
     so the .light class on this element was never actually doing
     anything for color. Set directly instead of relying on it. */
  color: #f5efe3;
  text-align: center;
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 2rem;
  margin-bottom: 1.25rem;
}

/* The standalone .inner sections get their white card look for free from
   the theme's own .panel class (background + rounding + shadow) — there's
   no such wrapper here, so .comment-thread needs its own version of that
   same treatment to read as a card floating on the dark Hub background. */
.comment-thread--panel {
  background: #fff;
  border-radius: 18px;
  overflow: hidden;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}

/* Hub Chat specifically, not every .comment-list on the site (item/
   activity comment threads stay their normal unbounded height, that's
   fine there) — Xavier's own call: an active Chat will only ever grow
   over the course of a season, and letting it keep stretching the
   whole Hub page forever pushes the account bar further and further
   out of reach. Capped into its own contained, internally-scrolling box
   instead, same idea as a real chat window — nothing else on the site
   does this yet, so there's no existing pattern to match, just a
   reasonable "show a handful of messages" height.

   Padding lives on .hub__chat-scroll-wrap (index.html), a plain
   non-scrolling wrapper around .comment-list, NOT on .comment-list
   itself — confirmed live (a zoomed-in screenshot showing the scrollbar
   flush against the card's rounded corner despite .comment-list having
   its own padding) that padding on a scrolling element only shifts its
   *content* inward, the scrollbar still renders flush against that same
   element's own edge regardless of how much padding it has. Putting the
   padding one level up, on an ancestor that doesn't itself scroll, is
   what actually insets the scrollbar from the card's true edge. */
.hub__chat-scroll-wrap {
  padding: 1rem 1.5rem 1rem 1rem;
}
.hub__chat .comment-list {
  max-height: 60rem;
  overflow-y: auto;
}

/* ---------- Centered content sections (About / Gift Lists / My Claims) ---------- */

.inner--centered .inner__content {
  float: none;
}

.inner {
  scrollbar-gutter: stable both-edges;
}

.inner--centered .panel {
  border-radius: 18px;
  margin: 0 auto 2.5rem;
  max-width: 100%;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
}

.inner--centered .inner__block.panel {
  padding: 3rem 0;
}

.inner--centered .inner__block.panel:first-of-type {
  border-radius: 0 0 18px 18px;
}

@media (min-width: 1200px) {
  .inner--centered .inner__block.panel {
    padding: 3.5rem 0;
  }
}

@media only screen and (min-width: 1200px) {
  .inner__content {
    scrollbar-gutter: stable both-edges;
    padding-right: 1.25rem;
    box-sizing: border-box;
  }
}

/* ---------- Accordion (About section) ---------- */

.accordion-item {
  border-bottom: 1px solid #eee6d6;
}

.accordion-item:last-child {
  border-bottom: none;
}

.accordion-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  background: none;
  border: none;
  padding: 1.25rem 0;
  cursor: pointer;
  text-align: left;
}

.accordion-header .section__subtitle {
  margin: 0;
}

/* These two accordion titles used the plain default text size (no
   .section__subtitle class like the About accordions above) — too
   small next to the actual section titles above them. Matches
   .wishlist-section-title's own font exactly rather than introducing
   a new size. */
.wishlist-hidden-accordion .accordion-header span,
.wishlist-pending-removals-accordion .accordion-header span {
  font-family: "Cinzel", serif;
  font-size: 1.8rem;
}

.accordion-icon {
  flex-shrink: 0;
  color: #c9a51c;
  font-size: 1.4rem;
  transition: transform 0.2s ease;
}

.accordion-item.is-open .accordion-icon {
  transform: rotate(90deg);
}

.accordion-panel {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease;
}

.accordion-item.is-open .accordion-panel {
  max-height: 30rem;
}

.accordion-panel .section__text {
  padding-bottom: 1.25rem;
}

/* User Images admin page — deliberately its own class names, not a
   reuse of .accordion-item/.accordion-header/.accordion-panel above,
   since this is a single-open accordion (opening one owner's group
   closes any other) rather than the independently-toggleable behavior
   those classes are wired to elsewhere (see renderUserImages()'s own
   comment in site.js). Same visual language (border, chevron rotate)
   as the shared accordion, just under its own selectors so the two
   toggle behaviors never collide on one delegated click handler. */
.user-images-owner-group {
  border-bottom: 1px solid #eee6d6;
}

.user-images-owner-group:last-child {
  border-bottom: none;
}

.user-images-owner-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  width: 100%;
  background: none;
  border: none;
  padding: 1.25rem 0;
  cursor: pointer;
  text-align: left;
  font-family: "Cinzel", serif;
  font-size: 1.8rem;
  color: #2c342b;
}

.user-images-owner-group.is-open .accordion-icon {
  transform: rotate(90deg);
}

/* Plain display:none/block, not the shared .accordion-panel's
   max-height animation — that trick's own fixed 30rem cap works for a
   few lines of FAQ text but not an unbounded photo grid (an account
   can hold up to 200 images, see upload_user_image.php's own limit).
   The thumb grid inside (.user-image-thumbs) keeps its own flex/grid
   display untouched — see the comment on this wrapper in site.js. */
.user-images-owner-panel {
  display: none;
  padding-bottom: 1.25rem;
}

.user-images-owner-group.is-open .user-images-owner-panel {
  display: block;
}

/* ---------- Gift list "whose list" tile menu ---------- */

/* Fixed 2-wide grid of large tiles, not the small auto-fill grid this
   used before the Gift List view became its own real page — with
   fewer, more deliberate destinations to choose from (one per family
   member) each one can afford to be a big, easy target rather than a
   dense row of small icons. Single column once there's no room for two
   at a reasonable size (matches the existing 768px breakpoint used
   elsewhere in this file). */
.giftlist-menu {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.5rem;
}

@media (max-width: 768px) {
  .giftlist-menu {
    grid-template-columns: 1fr;
  }
}

.giftlist-menu__tile {
  position: relative;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  text-align: left;
  text-decoration: none;
  color: inherit;
  padding: 1.75rem 2rem;
  border-radius: 16px;
  background: #fff;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.06);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

/* A non-email visual cue that this person's list has changed since it
   was last opened — a small dot rather than reusing .hub__tile-badge's
   numeric-count shape, since there's nothing to count here (one list,
   either seen or not). */
.giftlist-menu__tile--unseen::after {
  content: "";
  position: absolute;
  top: 1rem;
  right: 1rem;
  width: 0.9rem;
  height: 0.9rem;
  border-radius: 50%;
  background: #c9a51c;
  box-shadow: 0 0 0 3px rgba(201, 165, 28, 0.25);
}

.giftlist-menu__tile:hover {
  transform: translateY(-4px);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.12);
  color: inherit;
}

.giftlist-menu__tile img {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  object-fit: cover;
  border: 3px solid #c9a51c;
  flex-shrink: 0;
}

.giftlist-menu__tile--me {
  border: 2px solid #c9a51c;
}

.giftlist-menu__name {
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 2rem;
}

/* Per-person font pack on the "whose list?" picker tiles — each tile
   shows a different person, so this can't reuse the shared
   .wishlist-font-pack--X ancestor-class system (that one toggles a
   single pack onto a whole view root for "the one person you're
   currently browsing," see its own comment further down this file).
   Dedicated compound selectors directly on .giftlist-menu__name instead
   — same font-family values as each pack's title role elsewhere
   (.gc-title etc.), just applied per-tile rather than per-view. No
   "basic" rule needed: .giftlist-menu__name's own default above is
   already Cinzel, identical to what "basic" would set. Class is added
   in renderGiftlistMenu() (site.js), using each person's own
   wishlist_font_pack from get_giftlist_users.php. */
.giftlist-menu__name.wishlist-font-pack--fancy {
  font-family: "Quintessential", cursive;
  /* Bumped again — 1.1em still read as too small live on the real page,
     even though it compared favorably against Niconne at that size in
     isolation. */
  font-size: 1.4em;
}
.giftlist-menu__name.wishlist-font-pack--quirky {
  /* Mountains of Christmas reads noticeably smaller than the other
     packs' title fonts at an identical rem value — same optical-size
     mismatch already documented for this font pack's other roles
     elsewhere on the site, just never compensated for here since this
     is a new spot for it. Bumped, not left at the shared 2rem base. */
  font-family: "Mountains of Christmas", cursive;
  font-size: 2.3rem;
}
.giftlist-menu__name.wishlist-font-pack--sketchy {
  font-family: "Kalam", cursive;
  font-weight: 700;
}
.giftlist-menu__name.wishlist-font-pack--rustic {
  font-family: "Rye", serif;
}
.giftlist-menu__name.wishlist-font-pack--bold {
  font-family: "Anton", sans-serif;
  letter-spacing: 0.5px;
}

.giftlist-menu__tile small {
  display: block;
  color: #c9a51c;
  font-style: italic;
  font-size: 1.28rem;
  margin-top: 0.25rem;
}

/* ---------- Gift / comment card component ---------- */

/* Fixed 2 columns at every width, not auto-fill and not a responsive
   step-down — the design call is that grid vs. list is the owner's
   presentation choice (per section) and column count isn't a separate
   knob on top of that, so it stays constant from mobile to desktop
   rather than reading as a different layout depending on screen size. */
.gc-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.5rem;
}

@media (max-width: 479px) {
  .gc-grid {
    gap: 0.75rem;
  }

  .gc-grid .gc-card {
    padding: 0.85rem;
  }

  .gc-grid .gc-title {
    font-size: 1.4rem;
  }

  .gc-grid .gc-desc {
    font-size: 1.2rem;
  }

  .gc-grid .gc-meta {
    font-size: 1.05rem;
  }

  .gc-grid .gc-footer {
    margin-top: 0.75rem;
  }

  .gc-grid .gc-btn {
    font-size: 1.1rem;
    padding: 0.35rem 0.7rem;
  }

  .gc-grid .gc-badge {
    font-size: 1.05rem;
    padding: 0.3rem 0.6rem;
  }
}

.gc-card {
  position: relative;
  background: #fff;
  border-radius: 16px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.07);
  padding: 1.25rem;
  transition: transform 0.25s ease, box-shadow 0.25s ease;
  display: flex;
  flex-direction: column;
}

/* SortableJS's own state classes during a drag — a faint highlight on
   the placeholder slot and the element actually being dragged, so the
   interaction has some visual feedback beyond the browser's bare
   default. */
.sortable-ghost {
  opacity: 0.4;
}

.sortable-chosen {
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.18);
}

.gc-card:hover {
  box-shadow: 0 14px 30px rgba(0, 0, 0, 0.12);
}

/* The lift (translateY) itself is scoped to just the two draggable Edit
   Mode card types, not every .gc-card — Xavier's own read: it reads as
   a drag-and-drop affordance (reinforcing the grab/grabbing cursor
   above), which makes sense on cards you can actually pick up and
   reorder, but feels wrong on the plain read-only cards (My Wish List's
   own view, someone else's Gift List, My Claims) that are just a
   click-through to a detail page and were never draggable in the first
   place. The box-shadow-only hover above still applies everywhere as a
   plain "this is interactive" cue. */
.wishlist-item-card:hover,
.wishlist-note-card:hover {
  transform: translateY(-4px);
}

/* The whole tile opens the item's detail page (see the delegated click
   handler in site.js) — a pointer cursor over the card background is
   what actually signals that, since the hover-lift above already
   applies to every .gc-card regardless of whether it's clickable. */
.giftlist-item-card {
  cursor: pointer;
}

/* Same reasoning as .giftlist-item-card above — clicking a My Claims
   tile jumps to that item's own detail page. */
.my-claims-card {
  cursor: pointer;
}

/* My Wish List's own editing cards — the whole tile opens the item
   editor (or, for a note, its inline edit form) and is also the
   press-and-hold drag target (see initWishlistSortable() in site.js),
   so `grab` reads truer here than a plain pointer would. */
.wishlist-item-card,
.wishlist-note-card {
  cursor: grab;
}

.wishlist-item-card:active,
.wishlist-note-card:active {
  cursor: grabbing;
}

/* The read view's own card class (see renderWishlistItemCard() in
   site.js) — not draggable outside Edit Mode, just a click-through to
   the item's detail page, same as .giftlist-item-card above. */
.wishlist-item-card-readonly {
  cursor: pointer;
}

/* The one item per section an owner has flagged as "I really want this"
   (see toggle_wishlist_draft_item_star.php's own exclusivity logic) —
   needs to visibly pop out of a grid/list of otherwise-equal cards, not
   just carry a small badge that only reads up close. A warm gold glow
   around the whole card does that at a glance; the badge (added below,
   on .gc-media) is what actually explains why once you look closer. */
.gc-card--starred {
  position: relative;
  box-shadow: 0 0 0 2px #c9a51c, 0 6px 20px rgba(201, 165, 28, 0.35);
}
.gc-card--starred:hover {
  box-shadow: 0 0 0 2px #c9a51c, 0 14px 32px rgba(201, 165, 28, 0.4);
}
/* Xavier's own follow-up (round 1): the static ring felt "underwhelming"
   for what starring is supposed to convey — wanted something flashier.
   Round 2 followed almost immediately: that stronger glow (36px/10px,
   opacity swinging 0.4-1) then read as too strong/attention-grabbing on
   its own, so this is dialed back to a light, steady presence, with the
   sparkles below (.gc-card-sparkle) doing most of the "special" work
   instead — also just a better fit for something literally called
   "starred". Still a separate ::after layer animating only opacity
   rather than animating .gc-card--starred's own box-shadow directly —
   animating box-shadow itself is a known jank/perf risk (forces a
   repaint of the blur every frame), and it would fight with the plain
   :hover box-shadow bump above (a running animation always wins over a
   static value for the same property on the same element). inset:0
   (not outset) keeps the blur flush with the card's own edge — an
   earlier attempt at -10px left a visible gap where the blur hadn't
   built up to anything yet. No z-index — a negative one here (also an
   earlier attempt) pushed the glow behind some ancestor's own opaque
   background further up this deeply-nested tree, since
   .gc-card--starred itself never establishes its own stacking context;
   this pseudo-element has no fill of its own, so there's nothing to
   hide behind by leaving it in normal paint order instead. */
.gc-card--starred::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: 0 0 20px 4px rgba(201, 165, 28, 0.45);
  opacity: 0.35;
  pointer-events: none;
  animation: gc-card-starred-aura-pulse 4s ease-in-out infinite;
  /* Set inline (as a CSS custom property, not a plain animation-delay —
     see the same reasoning on .gc-card-sparkle below) by
     renderWishlistItemCard()/renderGiftlistItemCard() in site.js at the
     moment each card is built, computed from the real wall-clock time
     rather than left at its default (element-creation-relative) 0s. */
  animation-delay: var(--gc-aura-delay, 0s);
}
@keyframes gc-card-starred-aura-pulse {
  0%, 100% { opacity: 0.3; }
  50% { opacity: 0.55; }
}
/* Scattered around the card's edges (loosely — not evenly spaced, real
   sparkle scatter reads more natural than a neat ring), same twinkling
   technique as the Hub Notice bullhorn's own sparkle aura elsewhere in
   this file: real <i> elements (added in renderGiftlistItemCard() and
   renderWishlistItemCard() in site.js, only when item.starred), not
   ::before/::after — a single element only gets two pseudo-elements,
   nowhere near enough for a scattered handful of independently
   positioned/timed sparkles. Continuous but gentle at rest —
   .gc-card--starred-flash below (the on-appear moment) temporarily
   swaps in a much bigger, brighter burst on these same elements. */
/* First pass at this (cream #fff5d6, no glow, brief peak-then-gone
   twinkle, 5 sparkles) read as "barely visible" per Xavier's own
   testing — plain cream text has almost no contrast against this card's
   own white/cream background, and a razor-thin opacity peak meant a
   given sparkle was only actually bright for an instant. Switched to
   solid gold with a soft white drop-shadow glow (pops against both the
   card's light background and a busier product photo alike), given a
   held plateau instead of a spike so each one stays visible for a real
   stretch of its cycle, and expanded from 5 to 8 sparkles so more are
   glinting at any given moment. */
/* animation-delay is deliberately a *second*, separate declaration
   here (referencing a CSS custom property), not folded into the
   shorthand above or hardcoded per sparkle below — Xavier's own
   follow-up: whenever someone else's Gift List polls in a live update
   (pollGiftlistDetail() in site.js, every 15s), the whole card grid gets
   silently rebuilt from scratch, which recreates these sparkles as
   brand-new elements — and a brand-new element always starts its
   animation at 0%, so the twinkle visibly "reset" every time that
   happened, jarring anyone actually watching it. --sparkle-delay is set
   inline per-element at render time (renderWishlistItemCard()/
   renderGiftlistItemCard() in site.js), computed from the real
   wall-clock time rather than time-since-this-element-was-created, so a
   freshly rebuilt sparkle picks up wherever the shared cycle actually
   is instead of restarting it. Using a custom property rather than a
   literal inline animation-delay matters here specifically because the
   flash burst below (.gc-card--starred-flash .gc-card-sparkle) needs
   its own animation-delay to stay a plain 0s, always starting clean —
   a literal inline style would apply to *any* animation running on the
   element regardless of which one, custom properties only take effect
   where a rule explicitly references them. */
.gc-card-sparkle {
  position: absolute;
  color: #c9a51c;
  filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.85));
  font-size: 1.3rem;
  opacity: 0;
  pointer-events: none;
  animation: gc-card-sparkle-twinkle 2.6s ease-in-out infinite;
  animation-delay: var(--sparkle-delay, 0s);
}
.gc-card-sparkle--1 { top: -0.55rem; left: 14%; }
.gc-card-sparkle--2 { top: 22%; right: -0.6rem; font-size: 1.1rem; }
.gc-card-sparkle--3 { bottom: -0.55rem; left: 58%; }
.gc-card-sparkle--4 { bottom: 12%; left: -0.6rem; font-size: 1.15rem; }
.gc-card-sparkle--5 { top: 48%; left: -0.55rem; font-size: 1rem; }
.gc-card-sparkle--6 { top: -0.5rem; right: 38%; font-size: 1.05rem; }
.gc-card-sparkle--7 { bottom: -0.55rem; right: -0.55rem; }
.gc-card-sparkle--8 { top: 68%; right: -0.55rem; font-size: 0.95rem; }
@keyframes gc-card-sparkle-twinkle {
  0%, 100% { opacity: 0; transform: scale(0.4) rotate(0deg); }
  20% { opacity: 1; transform: scale(1) rotate(15deg); }
  70% { opacity: 0.9; transform: scale(0.95) rotate(10deg); }
}
/* The on-appear burst version — overrides the gentle continuous twinkle
   above for exactly as long as .gc-card--starred-flash is applied to
   the card (see watchWishlistStarredCard() in site.js), then reverts on
   its own once that class comes off. Same 3s duration as the card's own
   flash keyframe below so both finish in lockstep rather than one
   visibly outlasting the other. */
.gc-card--starred-flash .gc-card-sparkle {
  animation: gc-card-sparkle-flash 3s ease-out 1;
}
/* Xavier's own follow-up: worried the whole flash could still be missed
   if you're not looking at that exact instant. Rise (0-18%) and settle
   (55-100%) keep roughly their original absolute timing from the 1.8s
   version rather than stretching uniformly to 3s — a slower rise/fall
   would read as sluggish — so the extra 1.2s all goes into holding the
   bright peak longer (18%-55%) instead. */
@keyframes gc-card-sparkle-flash {
  0% { opacity: 0; transform: scale(0.3) rotate(0deg); }
  18% { opacity: 1; transform: scale(1.7) rotate(20deg); }
  55% { opacity: 1; transform: scale(1.35) rotate(-12deg); }
  100% { opacity: 0.8; transform: scale(1) rotate(0deg); }
}
/* One-time "just appeared" flash — toggled on/off by
   watchWishlistStarredCard() in site.js the first time a starred card
   actually scrolls into view in a given browser session (sessionStorage,
   not tied to the account — meant to catch the eye once per visit, not
   persist as a permanent record). Unlike the aura above, this animates
   the card's own box-shadow/transform directly — safe here specifically
   because it's a short, single-iteration burst (not an indefinitely
   running animation), so there's no ongoing jank cost and no lasting
   conflict with the plain :hover rule once it finishes and the class is
   removed. */
/* First pass at this (1.1s total, peaking briefly at 30%) came and went
   too fast to actually register as anything per Xavier's own testing —
   stretched to 1.8s with a held plateau (25%-45%), then to a full 3s
   after a further follow-up worried the whole thing could still get
   missed if you weren't looking at that exact instant. Same "extend the
   hold, not the rise/fall" approach as the sparkle keyframe above — the
   plateau below covers 15%-70% of the full 3s (~1.65s held at peak),
   while the rise and fall keep close to their original ~0.45s/~1s
   absolute timing from the 1.8s version so the animation still feels
   snappy to start and end rather than sluggish throughout. */
@keyframes gc-card-starred-flash {
  0% {
    box-shadow: 0 0 0 2px #c9a51c, 0 6px 20px rgba(201, 165, 28, 0.35);
    transform: scale(1);
  }
  15% {
    box-shadow: 0 0 0 5px #fff5d6, 0 0 55px 16px rgba(255, 245, 214, 0.95);
    transform: scale(1.07);
  }
  70% {
    box-shadow: 0 0 0 5px #fff5d6, 0 0 55px 16px rgba(255, 245, 214, 0.95);
    transform: scale(1.07);
  }
  100% {
    box-shadow: 0 0 0 2px #c9a51c, 0 6px 20px rgba(201, 165, 28, 0.35);
    transform: scale(1);
  }
}
.gc-card--starred-flash {
  animation: gc-card-starred-flash 3s ease-out 1;
}

.gc-card--gift.is-claimed-by-me {
  border: 2px solid #c9a51c;
}

.gc-media {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: 10px;
  overflow: hidden;
  margin-bottom: 1rem;
  background: #f4f1e6;
}

.gc-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Shown in place of a real cover photo when an item has none — a small
   centered gift icon rather than nothing at all, so every card in a
   grid (.gc-media) and every item detail page's own gallery
   (.wishlist-preview-gallery__main) keeps the same shape/height whether
   or not it has a photo yet. Same masked-icon technique as the pattern
   swatches (see .wishlist-pattern-swatch--gift::before), just muted
   rather than the swatches' bold gold — this is an empty state, not a
   decoration, so it should read as quieter than a real photo, not
   compete with one. */
.gc-media--placeholder,
.wishlist-preview-gallery__main--placeholder {
  position: relative;
}
.gc-media--placeholder::before,
.wishlist-preview-gallery__main--placeholder::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: #c2b492;
  -webkit-mask-image: url('../img/icons/gift.svg');
  mask-image: url('../img/icons/gift.svg');
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: 42% 42%;
  mask-size: 42% 42%;
}

/* Sits in .gc-media's own corner (see .gc-media's `position: relative`
   above) on both the clickable View Mode toggle (.wishlist-star-btn,
   the owner's own read-view card only — starring is live/immediate, not
   an Edit Mode/draft action) and the plain read-only badge
   (.wishlist-star-badge, viewer cards) — same position/size/shape for
   both so the two read as the same signal wherever this item shows up.
   Unstarred sits low-key (translucent dark circle, pale outline) so an
   un-clicked toggle doesn't read as its own decoration; starred switches
   to solid gold, matching .gc-card--starred's own glow. */
.wishlist-star-btn,
.wishlist-star-badge {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  width: 2.3rem;
  height: 2.3rem;
  display: block;
  z-index: 1;
}
/* Replaced the fa-star icon font, then a font-glyph-inside-a-CSS-circle
   version of this, with a single self-contained SVG (WISHLIST_STAR_SVG
   in site.js) that draws the circle AND the star together in one shared
   viewBox — see that variable's own comment for the full reasoning. The
   upshot for this file: there's no separate circle to size or center the
   star inside any more, no padding math, no percentage-on-both-axes
   sizing quirk. This element just scales the whole SVG as one unit
   (width/height above), and the two colors below (circle background,
   star foreground) are the only thing this file still controls. */
.wishlist-star-icon {
  display: block;
  width: 100%;
  height: 100%;
}
.wishlist-star-icon__circle {
  fill: rgba(44, 52, 43, 0.45);
  transition: fill 0.15s ease;
}
.wishlist-star-icon__star {
  fill: rgba(255, 255, 255, 0.75);
  transition: fill 0.15s ease;
}
.wishlist-star-btn {
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  transition: transform 0.15s ease, opacity 0.15s ease;
}
/* Unstarred only — an un-clicked toggle should read as quieter/lower-key
   than a starred one, not just a different color. Full opacity on hover
   and once actually starred, so it's never faint at the moment someone's
   actually looking at or interacting with it. */
.wishlist-star-btn:not(.is-starred) {
  opacity: 0.8;
}
.wishlist-star-btn:hover {
  transform: scale(1.08);
  opacity: 1;
}
.wishlist-star-btn:hover .wishlist-star-icon__circle {
  fill: rgba(44, 52, 43, 0.65);
}
.wishlist-star-btn:hover .wishlist-star-icon__star {
  fill: #ffffff;
}
.wishlist-star-btn.is-starred .wishlist-star-icon__circle,
.wishlist-star-badge .wishlist-star-icon__circle {
  fill: #c9a51c;
}
.wishlist-star-btn.is-starred .wishlist-star-icon__star,
.wishlist-star-badge .wishlist-star-icon__star {
  fill: #ffffff;
}
.wishlist-star-btn.is-starred:hover .wishlist-star-icon__circle {
  fill: #b5931a;
}

/* Opposite corner from .wishlist-star-btn/.wishlist-star-badge (top-right,
   scoped to $media not the whole card) — same "two badges might land on
   the same card, give the second one its own corner" reasoning as
   .hub__tile-badge--edited elsewhere in this file. Sits on the card
   itself, not the media block, since an item with no image still needs
   somewhere for this to show. */
.wishlist-item-comment-badge {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  min-width: 2.2rem;
  height: 2.2rem;
  padding: 0 0.7rem;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  border-radius: 999px;
  background: #c9a51c;
  color: #2c342b;
  font-family: "Mulish", sans-serif;
  font-size: 1.4rem;
  font-weight: 700;
  z-index: 1;
}

.wishlist-item-comment-badge i {
  font-size: 1.2rem;
}

/* Flat "this item has a conversation" indicator for browsing cards
   (renderGiftlistItemCard) — deliberately not the bold gold "unseen
   count" pill above, which reads as an urgent personal notification and
   would be misleading here (it's neither urgent nor personal, just
   informational). Same low-key translucent-dark-circle language the
   unstarred .wishlist-star-badge already uses elsewhere on this exact
   card, for the same "quiet until you look for it" reasoning. */
.wishlist-item-comment-badge--flat {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  width: 2.2rem;
  height: 2.2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(44, 52, 43, 0.55);
  border: 1px solid rgba(245, 239, 227, 0.35);
  color: #f5efe3;
  font-size: 1.2rem;
  z-index: 1;
}

.gc-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
}

.gc-title {
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 1.84rem;
  color: #2c342b;
  margin: 0;
  line-height: 1.3;
  /* .gc-title is a literal <h4> (site.js), which inherits main.css's
     sitewide h4 { text-transform: uppercase } — invisible on Cinzel's
     own base look (an all-caps-leaning serif to begin with) but became
     a real readability problem once other font packs (e.g. Fancy's
     Cinzel Decorative, which has no true lowercase glyphs at all) made
     every item title read as shouted regardless of how the name was
     actually typed. Reset here for every pack, not just Fancy —
     Xavier's call that item titles specifically should never be forced
     uppercase, full stop. */
  text-transform: none;
  /* truncateForCard() (site.js) already caps this at 80 characters, but
     that's a character count, not a visual guarantee — 80 characters of
     short words can still wrap past 4 lines on a narrow card (confirmed:
     the "Very Long Item Name" test title wrapped to 7 lines), making that
     one card tower over its neighbours in the grid regardless of what
     anyone typed. This caps it by rendered line count instead, which
     holds regardless of word length or card width. Shared by every card
     type (My Wish List, Gift Lists browsing, My Claims) since they all
     use this one class. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  line-clamp: 4;
  overflow: hidden;
}

.gc-qty {
  flex-shrink: 0;
  font-family: "Cinzel", serif;
  font-weight: 700;
  color: #c9a51c;
  font-size: 1.6rem;
  background: #f4f1e6;
  border-radius: 20px;
  padding: 0.15rem 0.6rem;
}

.gc-desc {
  font-size: 1.52rem;
  color: #444;
  margin: 0.5rem 0 0;
}

.gc-meta {
  font-size: 1.25rem;
  font-style: italic;
  color: #9a9384;
  margin-top: 0.5rem;
}

/* Unpublished indicator — Xavier's request, edit mode only (read view/
   live viewers never see an unpublished item at all, so the
   distinction is meaningless there). Started as a Live+Unpublished
   pair; Xavier decided live (the default, unremarkable state) didn't
   need a badge of its own, so only Unpublished remains — the element
   is omitted entirely rather than hidden when something is live.
   Reuses gold, the color this session already settled on for
   "new/draft/content-adding" actions (Add Item/Note/Section), rather
   than introducing a new color. */
.wishlist-item-status-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: 1.1rem;
  font-weight: 700;
  font-family: "Mulish", sans-serif;
  border-radius: 20px;
  padding: 0.2rem 0.7rem;
  white-space: nowrap;
  /* On the note card and section title, this badge is a *direct* flex
     child of a `flex-direction: column` container with the default
     `align-items: stretch` — that stretches it to the container's full
     cross-axis width regardless of its own `display: inline-flex`. On
     the item card it's nested one level deeper (inside .gc-body, a
     plain block div), so it never hit this. align-self keeps it sized
     to its own content in every context, not just the ones that
     happened not to trigger it. */
  align-self: flex-start;
}
.wishlist-item-status-badge--draft {
  background: rgba(201, 165, 28, 0.15);
  color: #8a701a;
}

/* The gold-tinted badge above is tuned for the cream .background-mute
   a section header normally sits on — sitting directly on a saturated
   accent color instead (a section's own .wishlist-section--color-*),
   its low-opacity gold background just reads as a muddy smear rather
   than a legible badge. Scoped to the section header specifically, not
   every .wishlist-item-status-badge--draft everywhere — an item card's
   own copy of this badge always sits on the card's plain white
   background regardless of its section's color, so it never needed
   this override to begin with. */
.wishlist-section--color-evergreen .wishlist-section-header .wishlist-item-status-badge--draft,
.wishlist-section--color-cranberry .wishlist-section-header .wishlist-item-status-badge--draft,
.wishlist-section--color-copper .wishlist-section-header .wishlist-item-status-badge--draft,
.wishlist-section--color-slate .wishlist-section-header .wishlist-item-status-badge--draft,
.wishlist-section--color-plum .wishlist-section-header .wishlist-item-status-badge--draft,
.wishlist-section--color-blueberry .wishlist-section-header .wishlist-item-status-badge--draft,
.wishlist-section--color-teal .wishlist-section-header .wishlist-item-status-badge--draft {
  background: rgba(245, 239, 227, 0.2);
  color: #f5efe3;
}

.wishlist-section--color-gold .wishlist-section-header .wishlist-item-status-badge--draft,
.wishlist-section--color-mauve .wishlist-section-header .wishlist-item-status-badge--draft {
  background: rgba(44, 52, 43, 0.15);
  color: #2c342b;
}

.gc-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 1rem;
  flex-wrap: wrap;
}

.gc-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 1.31rem;
  font-weight: 600;
  padding: 0.35rem 0.75rem;
  border-radius: 20px;
}

.gc-badge--open {
  background: #f4f1e6;
  color: #6b6555;
}

.gc-badge--partial {
  background: #fbe8c6;
  color: #8a5a00;
}

.gc-badge--full {
  background: #2c342b;
  color: #c9a51c;
}

/* Deliberately dull/gray, not any of the above's warmer tones — this
   isn't claim progress at all, it's "there's nothing to progress right
   now" (item is moderated, edited-awaiting-review, archived, or
   removed by its owner). See refreshGiftlistCardClaims()'s own comment
   for the full list of states this covers. */
.gc-badge--frozen {
  background: rgba(107, 101, 85, 0.15);
  color: #6b6555;
}

.gc-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  border: none;
  border-radius: 20px;
  padding: 0.4rem 0.9rem;
  font-size: 1.36rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease, transform 0.15s ease;
}

.gc-btn:active {
  transform: translateY(1px);
}

.gc-btn--claim {
  background: #c9a51c;
  color: #2c342b;
}

.gc-btn--claim:hover {
  background: #b3910c;
}

.gc-btn--unclaim {
  background: transparent;
  color: #9e2525;
  border: 1px solid #9e2525;
}

.gc-btn--unclaim:hover {
  background: #9e2525;
  color: #fff;
}

/* Distinct from --claim/--unclaim on purpose — those two are wired to the
   mock claim/unclaim toggle in site.js (`.gc-btn--claim` click handler), so
   reusing either class here would make an "Edit" button silently flip
   itself into "Unclaim" when clicked. */
.gc-btn--edit {
  background: transparent;
  color: #2c342b;
  border: 1px solid rgba(44, 52, 43, 0.35);
}

.gc-btn--edit:hover {
  background: #2c342b;
  color: #fff;
}

/* ---------- Wish List font packs ----------
   A whole-list typography choice (Xavier's call — list-wide, not
   per-section like color/pattern), live/immediate same as the headline
   (see update_wishlist_font_pack.php), not an Edit Mode/draft concern.
   Self-hosted on purpose (Xavier's preference) — every family below
   except Cinzel/Mulish/Great Vibes (already loaded site-wide via
   main.css's own Google Fonts @import, reused here as-is rather than
   duplicated) ships as a local woff2 in fonts/, one weight/style per
   role since each is only ever used for short English UI text, never
   full-page reading.
   Applied via a .wishlist-font-pack--X class toggled onto the whole
   top-level section — #wishlist for the owner, #works for a browsing
   family member (see applyWishlistFontPack() in site.js) — rather than
   a narrower view-specific root, since the item detail page, the
   editor's own Preview, and even Edit Mode's "Editing My Wish List"
   title are all sibling views of the read/detail view, not descendants
   of it, and Xavier wants the pack applied everywhere, not just the
   read view. Covers three roles through its descendants: title
   (.gc-title item names, .wishlist-section-title section headers,
   .wishlist-preview-detail__name item detail/preview names,
   .wishlist-panel-title/#giftlist-detail-title the big list-level page
   titles, #wishlist-item-view-title/#giftlist-item-title the item-level
   page header shown above that same name a second time — added later,
   after Xavier noticed the two didn't match once item titles stopped
   being forced uppercase (see .gc-title's own comment) — shared class/id
   names between the owner's own views and a Gift List's own render
   functions), headline (#wishlist-headline-text /
   #giftlist-detail-subtitle, the personal subtitle line, plus
   .wishlist-panel-subtitle — Edit Mode's own static "Nothing here is
   visible..." line, a completely different element from the read
   view's headline that got missed on the first pass), and note
   (.gc-comment-text). #giftlist-picker-view (the "whose list?" tile
   grid) is a sibling of #giftlist-detail-view too, so a stale class
   can linger there after browsing back from someone's list — harmless,
   since nothing in the picker's own markup (.giftlist-menu__name)
   matches any of these selectors. "Basic" isn't just "no class" —
   .section__subtitle's own sitewide default (main.css) is Great Vibes,
   so Basic still needs its own rule to pull the headline back to
   something plain instead of inheriting that. A plain classical serif
   (EB Garamond, tried Lora first — too curvy and too bold next to
   Cinzel's own restrained letterforms), not Mulish — Xavier's call after seeing it live: a sans headline next to
   Cinzel's serif title read as mismatched, not just "less fancy." */
@font-face {
  font-family: "EB Garamond";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/eb-garamond-400i.woff2") format("woff2");
}
@font-face {
  font-family: "Playfair Display";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/playfair-display-600.woff2") format("woff2");
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/cormorant-garamond-400i.woff2") format("woff2");
}
@font-face {
  font-family: "Mountains of Christmas";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/mountains-of-christmas-700.woff2") format("woff2");
}
@font-face {
  font-family: "Finger Paint";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/finger-paint-400.woff2") format("woff2");
}
@font-face {
  font-family: "Schoolbell";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/schoolbell-400.woff2") format("woff2");
}
@font-face {
  font-family: "Kalam";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/kalam-700.woff2") format("woff2");
}
@font-face {
  font-family: "Caveat";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/caveat-600.woff2") format("woff2");
}
@font-face {
  font-family: "Reenie Beanie";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/reenie-beanie-400.woff2") format("woff2");
}
@font-face {
  font-family: "Rye";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/rye-400.woff2") format("woff2");
}
@font-face {
  font-family: "Amarante";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/amarante-400.woff2") format("woff2");
}
@font-face {
  font-family: "Josefin Slab";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/josefin-slab-400.woff2") format("woff2");
}
@font-face {
  font-family: "Anton";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/anton-400.woff2") format("woff2");
}
@font-face {
  font-family: "Oswald";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/oswald-600.woff2") format("woff2");
}
@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/poppins-400.woff2") format("woff2");
}
@font-face {
  font-family: "Quintessential";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/quintessential-400.woff2") format("woff2");
}

/* "So-and-So's List" — .wishlist-panel-title/#giftlist-detail-title are
   bare <h2> elements with no class-based override of their own outside
   the font-pack rules below, so they'd otherwise inherit main.css's
   sitewide h2 { text-transform: uppercase } same as every other page
   title did before this file's item-title fixes. Xavier's call: drop it
   here too, universally (not just for Fancy, which already had its own
   override once it moved to Niconne) — this was the last remaining
   forced-uppercase spot on a title that shows real content (a person's
   own name) rather than a generic label. */
.wishlist-panel-title,
#giftlist-detail-title {
  text-transform: none;
}

.wishlist-font-pack--basic #wishlist-headline-text,
.wishlist-font-pack--basic #giftlist-detail-subtitle,
.wishlist-font-pack--basic .wishlist-panel-subtitle {
  font-family: "EB Garamond", serif;
  font-weight: 400;
  font-style: italic;
}

.wishlist-font-pack--fancy .gc-title,
.wishlist-font-pack--fancy .wishlist-section-title,
.wishlist-font-pack--fancy .wishlist-preview-detail__name {
  /* Went through Great Vibes here first (broke on the uppercase
     .wishlist-panel-title/#giftlist-detail-title — an illegible tangle),
     then tried a text-transform: none exception to keep Great Vibes
     anyway, then Abril Fatface/Playfair Display Black as bolder
     all-caps-friendly alternatives — none of those landed either.
     Cinzel Decorative won out at the time: an ornate serif rather than
     a connecting script, so it survived being forced uppercase without
     any special-casing — but it turned out to have no true lowercase
     glyphs at all, reading as shouted everywhere, not just the
     page-title scale (the actual bug this whole file's "no forced
     uppercase on item titles" fixes are about, see .gc-title's own
     comment). Tried Niconne next — a genuine connecting script,
     exactly the category that broke here before, so it leaned on the
     text-transform: none below rather than picking a font that merely
     survives all-caps. Xavier's own call after comparing Niconne
     against Style Script/Allura/Jim Nightshade/Felipa live — the others
     either tangled in caps or read as the wrong mood (gothic/craft-store)
     for this pack. Niconne itself then turned out too optically small
     to fix without inflating it to 1.7em, at which point Xavier called
     it: rather than keep scaling a font that fundamentally doesn't fit
     the role, swapped to Quintessential — confirmed live it reads
     noticeably bigger/bolder than Niconne at the exact same unscaled
     size, while still holding up fine in uppercase.

     Split into its own rule (was one shared block with the big page
     titles below) once Xavier reported the two needed genuinely
     different amounts of boost — 1.4em looked right here (item-card
     scale) but nowhere near enough on the much bigger page titles. */
  /* font-weight: 400 explicitly — .gc-title's own base rule (below)
     sets font-weight: 600 for Cinzel's sake, and this pack's title
     roles never reset it. Quintessential only ships one real weight
     (400, its own @font-face above) so requesting 600 doesn't pick a
     bolder cut, it makes the browser fake-bold the 400 outlines —
     confirmed live via getComputedStyle: this role was computing
     font-weight 600 against a font with no 600 file, while the panel
     title (h2's own 500, closer to 400) escaped the same distortion
     mostly unnoticed. That's what actually read as "two different
     fonts" — same font-family the whole time, just one synthetically
     bolded into looking like a different, blockier script. */
  font-family: "Quintessential", cursive;
  font-weight: 400;
  text-transform: none;
  font-size: 1.4em;
  /* Quintessential's swashy descenders (the tail on a lowercase "g",
     the flourish on a capital "S") extend further below the baseline
     than .gc-title's own line-height (1.3) budgets for — invisible on
     Cinzel's own restrained letterforms, but real ink was getting
     clipped by .gc-title's -webkit-line-clamp/overflow:hidden pair
     (confirmed live: "Luxury Hardside Luggage Set" showing a hard
     horizontal cut straight through the descenders) even on a single
     line, since line-clamp's box height comes from generic line
     metrics, not this specific font's actual glyph extents. More
     headroom per line fixes it without changing the 4-line cap itself. */
  line-height: 1.55;
}

/* em turned out to be the wrong tool for both groups below — confirmed
   live it doesn't scale relative to this same element's own default
   size the way it reads; it resolves against the INHERITED (parent)
   font-size, which for these elements is just the small ambient
   body-text size, not h2/h3's own real 28-70px+ sizes. That's why
   "2.1em" was actually rendering SMALLER than the unstyled default
   (measured live: 29px vs the real ~50px h2 baseline) no matter how far
   it got bumped — it was never multiplying the right number. Switched
   to explicit rem values instead, each one this element's own real base
   (main.css) times a genuine 1.6x — reliable regardless of ancestor
   context, and correctly proportional at every viewport width, not
   just one. Two separate groups, not one shared rule, because
   .wishlist-panel-title/#giftlist-detail-title are h2 (4.4rem/5rem/7rem
   across 3 breakpoints) while #wishlist-item-view-title/#giftlist-item-title
   are h3 (2.8rem/3.2rem across 2) — a shared rule would apply one
   group's breakpoint values to the other. */
.wishlist-font-pack--fancy .wishlist-panel-title,
.wishlist-font-pack--fancy #giftlist-detail-title {
  /* font-weight: 400, not left at h2's own 500 — same fake-bold
     mismatch as .gc-title's 600 above, just closer to Quintessential's
     real 400 so it read as "mostly fine" rather than obviously wrong.
     Explicit now so all three Fancy title roles request the exact
     weight that's actually loaded. */
  font-family: "Quintessential", cursive;
  font-weight: 400;
  text-transform: none;
  font-size: 5.28rem;
}
@media only screen and (min-width: 768px) {
  .wishlist-font-pack--fancy .wishlist-panel-title,
  .wishlist-font-pack--fancy #giftlist-detail-title {
    font-size: 6rem;
  }
}
@media only screen and (min-width: 1400px) {
  .wishlist-font-pack--fancy .wishlist-panel-title,
  .wishlist-font-pack--fancy #giftlist-detail-title {
    font-size: 8.4rem;
  }
}
.wishlist-font-pack--fancy #wishlist-item-view-title,
.wishlist-font-pack--fancy #giftlist-item-title {
  font-family: "Quintessential", cursive;
  font-weight: 400;
  text-transform: none;
  font-size: 5.04rem;
}
@media only screen and (min-width: 768px) {
  .wishlist-font-pack--fancy #wishlist-item-view-title,
  .wishlist-font-pack--fancy #giftlist-item-title {
    font-size: 5.76rem;
  }
}
.wishlist-font-pack--fancy #wishlist-headline-text,
.wishlist-font-pack--fancy #giftlist-detail-subtitle,
.wishlist-font-pack--fancy .wishlist-panel-subtitle {
  /* Great Vibes, freed up once the title moved to Cinzel Decorative —
     the headline is never forced uppercase (.section__subtitle carries
     no text-transform), so the script that broke as a title works fine
     here, and reads as the more overtly "fancy" of the two roles. */
  /* Regular weight explicitly, not inherited — Great Vibes has no real
     bold cut, so a leftover font-weight: 600 here (from when this rule
     was Playfair Display) was making the browser synthesize a fake
     bold, which just thickens/blurs a script font instead of reading
     as intentional weight. */
  /* Bumped up (Xavier's call: thin + smallish reads as the hardest to
     read of the six) — Great Vibes' delicate strokes need real size to
     stay legible, unlike a bolder face at the same nominal size. */
  font-family: "Great Vibes", cursive;
  font-weight: 400;
  font-size: 3.9rem;
}
.wishlist-font-pack--fancy .gc-comment-text {
  /* rem, not em — .gc-comment-text's own base size is 1.9rem, but a
     more-specific rule's font-size doesn't compose with a less-specific
     rule's font-size the way it might read; it fully replaces it, so an
     em value here computes against the inherited parent size instead
     (14px), not against 1.9rem. Was quietly rendering smaller than
     Basic instead of the intended "a bit bigger, compensating for
     Cormorant Garamond's own smaller optical size than Mulish's." Same
     fix applied to Quirky/Sketchy below, the other two packs with this
     same bug.
     Sizes across all three were originally picked independently while
     each pack was being tuned in isolation (2.2/2.1/2.35rem), which
     read as arbitrary once seen side by side — Xavier's call to tighten
     into a clearer scheme instead: Basic/Rustic/Bold's clean sans/slab
     fonts stay at the 1.9rem baseline (no compensation needed), and the
     two genuinely thin/delicate scripts here (this one, Sketchy's
     Reenie Beanie) share one bumped size rather than two arbitrarily
     different ones. Quirky's Schoolbell — casual but not especially
     thin — split the difference. */
  font-family: "Cormorant Garamond", serif;
  font-style: italic;
  font-size: 2.3rem;
}

.wishlist-font-pack--quirky .gc-title,
.wishlist-font-pack--quirky .wishlist-section-title,
.wishlist-font-pack--quirky .wishlist-preview-detail__name,
.wishlist-font-pack--quirky .wishlist-panel-title,
.wishlist-font-pack--quirky #giftlist-detail-title,
.wishlist-font-pack--quirky #wishlist-item-view-title,
.wishlist-font-pack--quirky #giftlist-item-title {
  /* Griffy first — looked charming in a small mockup but turned to
     mush at real UI sizes (illegible small, weird once forced uppercase
     big), same category of problem Great Vibes had as Fancy's title.
     Then Henny Penny, also reasonable but Xavier ultimately preferred
     this one — a bold, chunky script that still reads as festive
     without a connecting-script's fragility at extreme sizes. */
  font-family: "Mountains of Christmas", cursive;
}
.wishlist-font-pack--quirky #wishlist-headline-text,
.wishlist-font-pack--quirky #giftlist-detail-subtitle,
.wishlist-font-pack--quirky .wishlist-panel-subtitle {
  /* 0.9 * 3.4rem (the headline's own desktop base, main.css). This ID
     selector out-specifies site.css's own mobile shrink rule
     (.section-header .section__subtitle, class-only), so without the
     matching @media override below this stayed at the desktop size on
     phones — confirmed live: 30.6px here vs. every other pack's
     correctly-shrunk 20px. */
  font-family: "Finger Paint", cursive;
  font-size: 3.06rem;
}
.wishlist-font-pack--quirky .gc-comment-text {
  font-family: "Schoolbell", cursive;
  font-size: 2.4rem;
}

.wishlist-font-pack--sketchy .wishlist-panel-title,
.wishlist-font-pack--sketchy #giftlist-detail-title,
.wishlist-font-pack--sketchy #wishlist-item-view-title,
.wishlist-font-pack--sketchy #giftlist-item-title {
  font-family: "Kalam", cursive;
  font-weight: 700;
}
/* Panel-title role (the big "So-and-So's List" heading) explicitly
   boosted, same rem-not-em approach as Fancy's own fix above — left at
   the bare native h2 size until Xavier reported it reading
   inconsistently between "My Wish List" (short, own page, looks big)
   and "Ursula the Undergrad's List" (much longer, squeezed next to the
   avatar on the Gift List detail header, so the same font-size reads
   noticeably smaller there). Same 1.2x multiplier as Fancy for parity
   between packs, same 3-breakpoint rem values matching h2's own
   4.4rem/5rem/7rem base (main.css). */
.wishlist-font-pack--sketchy .wishlist-panel-title,
.wishlist-font-pack--sketchy #giftlist-detail-title {
  font-size: 5.28rem;
}
@media only screen and (min-width: 768px) {
  .wishlist-font-pack--sketchy .wishlist-panel-title,
  .wishlist-font-pack--sketchy #giftlist-detail-title {
    font-size: 6rem;
  }
}
@media only screen and (min-width: 1400px) {
  .wishlist-font-pack--sketchy .wishlist-panel-title,
  .wishlist-font-pack--sketchy #giftlist-detail-title {
    font-size: 8.4rem;
  }
}
.wishlist-font-pack--sketchy .gc-title,
.wishlist-font-pack--sketchy .wishlist-section-title,
.wishlist-font-pack--sketchy .wishlist-preview-detail__name {
  /* .gc-title also has its own mobile shrink at 479px (.gc-grid
     .gc-title, site.css) — same specificity as this rule (two classes
     each), so without the matching @media override below this would
     win on every viewport (later in source), leaving a grid-layout
     item's title at 1.9rem/19px on a small phone instead of the
     intended 1.4rem/14px. Same category of bug as the headline fix
     above, just never confirmed live since there's no grid-layout item
     on the test account right now. */
  /* Permanent Marker (too bold/heavy), Indie Flower (too light),
     Architects Daughter and Neucha (both tried after Kalam, same
     complaint either way) — landed back on Kalam Bold, the one Xavier
     actually preferred; the perceived gap below it (see the negative
     margin-top on the headline rule below) turned out to be the real
     fix, not the font choice itself. */
  font-family: "Kalam", cursive;
  font-weight: 700;
  font-size: 1.9rem;
}
.wishlist-font-pack--sketchy #wishlist-headline-text,
.wishlist-font-pack--sketchy #giftlist-detail-subtitle,
.wishlist-font-pack--sketchy .wishlist-panel-subtitle {
  /* Kalam's own vertical metrics leave more empty space below its ink
     than Cinzel's do, even at an identical measured box-gap to the
     element below (confirmed directly: both packs computed to the same
     17px gap) — so the *font*, not the CSS, was the source of the
     "too far apart" feeling. Compensated here with a negative margin
     pulling the headline up, specifically for Sketchy — an exception,
     not a general fix, since every other pack's default 1.7rem
     top-margin (main.css's .section__subtitle) reads correctly as-is. */
  /* 1.3 * 3.4rem, same ID-vs-mobile-shrink specificity issue as
     Quirky's headline above — see the @media override below. */
  margin-top: -0.8rem;
  font-family: "Caveat", cursive;
  font-weight: 600;
  font-size: 4.42rem;
}
.wishlist-font-pack--sketchy .gc-comment-text {
  font-family: "Reenie Beanie", cursive;
  font-size: 2.4rem;
}

/* Quirky/Sketchy's headline font-size overrides above use ID selectors
   (#wishlist-headline-text/#giftlist-detail-subtitle), which out-
   specify site.css's own mobile shrink for .section__subtitle (a
   class-only selector, .section-header .section__subtitle /
   .giftlist-detail-heading .section__subtitle, also at this same
   max-width: 767px breakpoint) — so without a matching override here,
   these two packs' headlines stayed at their full desktop size on a
   phone instead of shrinking, confirmed live (30.6px/44.2px instead of
   the ~20px every other pack correctly gets). Same ratios (0.9x/1.3x)
   applied to the mobile base (2rem, not the desktop 3.4rem) instead of
   a flat carry-over of the desktop pixel value. Sketchy's own negative
   margin-top is scaled down to match (0.8rem * 2.6/4.42) — the visual
   overhang it's compensating for shrinks proportionally with the font
   size, not by a fixed amount. */
@media only screen and (max-width: 767px) {
  .wishlist-font-pack--quirky #wishlist-headline-text,
  .wishlist-font-pack--quirky #giftlist-detail-subtitle,
  .wishlist-font-pack--quirky .wishlist-panel-subtitle {
    font-size: 1.8rem;
  }
  .wishlist-font-pack--sketchy #wishlist-headline-text,
  .wishlist-font-pack--sketchy #giftlist-detail-subtitle,
  .wishlist-font-pack--sketchy .wishlist-panel-subtitle {
    font-size: 2.6rem;
    margin-top: -0.47rem;
  }
}

@media only screen and (max-width: 479px) {
  .wishlist-font-pack--sketchy .gc-grid .gc-title {
    font-size: 1.4rem;
  }
}

.wishlist-font-pack--rustic .gc-title,
.wishlist-font-pack--rustic .wishlist-section-title,
.wishlist-font-pack--rustic .wishlist-preview-detail__name,
.wishlist-font-pack--rustic .wishlist-panel-title,
.wishlist-font-pack--rustic #giftlist-detail-title,
.wishlist-font-pack--rustic #wishlist-item-view-title,
.wishlist-font-pack--rustic #giftlist-item-title {
  /* Cabin Sketch first — Xavier's call that it didn't quite land after
     seeing it live. Rye instead: a western/woodcut stamp face, one of
     the two originally-shortlisted Rustic title candidates. */
  font-family: "Rye", serif;
}
.wishlist-font-pack--rustic #wishlist-headline-text,
.wishlist-font-pack--rustic #giftlist-detail-subtitle,
.wishlist-font-pack--rustic .wishlist-panel-subtitle {
  /* Vollkorn/Bitter first — restrained, classic serifs that read as
     "ordinary" next to Rye's own rugged western character once Xavier
     saw them live together. Amarante/Josefin Slab were Rye's original
     pairing from the first round of Rustic exploration — reinstated
     now that Rye won out as the title. */
  font-family: "Amarante", serif;
}
.wishlist-font-pack--rustic .gc-comment-text {
  font-family: "Josefin Slab", serif;
}

.wishlist-font-pack--bold .gc-title,
.wishlist-font-pack--bold .wishlist-section-title,
.wishlist-font-pack--bold .wishlist-preview-detail__name,
.wishlist-font-pack--bold .wishlist-panel-title,
.wishlist-font-pack--bold #giftlist-detail-title,
.wishlist-font-pack--bold #wishlist-item-view-title,
.wishlist-font-pack--bold #giftlist-item-title {
  font-family: "Anton", sans-serif;
  letter-spacing: 0.5px;
}
.wishlist-font-pack--bold #wishlist-headline-text,
.wishlist-font-pack--bold #giftlist-detail-subtitle,
.wishlist-font-pack--bold .wishlist-panel-subtitle {
  /* Shrunk a touch (Xavier's call) — Oswald's uppercase+letter-spacing
     already reads as loud/assertive at the default size. */
  font-family: "Oswald", sans-serif;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-size: 2.89rem;
}
.wishlist-font-pack--bold .gc-comment-text {
  font-family: "Poppins", sans-serif;
}

.wishlist-font-pack-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  justify-content: center;
}
.wishlist-font-pack-swatch {
  border: 2px solid transparent;
  border-radius: 10px;
  background: #f4f1e6;
  padding: 0.6rem 0.5rem;
  cursor: pointer;
  min-width: 92px;
  text-align: center;
}
.wishlist-font-pack-swatch:hover {
  background: #ede4c9;
}
.wishlist-font-pack-swatch.is-selected {
  border-color: #c9a51c;
  background: #fbf3d8;
}
.wishlist-font-pack-swatch__sample {
  display: block;
  font-size: 1.6rem;
  color: #2c342b;
  margin-bottom: 0.2rem;
}
.wishlist-font-pack-swatch__label {
  display: block;
  font-size: 1.1rem;
  color: #6b6555;
}

/* Each swatch previews its own pack using its own title font, same
   self-previewing idea as the color/pattern swatches above — a plain
   text label alone ("Quirky") wouldn't tell you anything about what the
   pack actually looks like. */
/* One shared size (the 1.6rem base above) for every swatch sample —
   these had each picked up their own ad hoc font-size while being
   tuned one at a time during font selection (16/14/17/14/16/16px
   across the six), which read as a real inconsistency once seen
   side by side rather than deliberate visual balancing. Font-family
   and weight still vary per pack (that's the whole point of the
   preview); only size is normalized now. */
.wishlist-font-pack-swatch--basic .wishlist-font-pack-swatch__sample {
  font-family: "Cinzel", serif;
}
.wishlist-font-pack-swatch--fancy .wishlist-font-pack-swatch__sample {
  font-family: "Quintessential", cursive;
}
.wishlist-font-pack-swatch--quirky .wishlist-font-pack-swatch__sample {
  font-family: "Mountains of Christmas", cursive;
  font-weight: 700;
}
.wishlist-font-pack-swatch--sketchy .wishlist-font-pack-swatch__sample {
  font-family: "Kalam", cursive;
  font-weight: 700;
}
.wishlist-font-pack-swatch--rustic .wishlist-font-pack-swatch__sample {
  font-family: "Rye", serif;
}
.wishlist-font-pack-swatch--bold .wishlist-font-pack-swatch__sample {
  font-family: "Anton", sans-serif;
}

.gc-claimants {
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0.6rem 0 0;
  border-top: 1px solid #eee6d6;
  font-size: 1.28rem;
  color: #6b6555;
}

.gc-claimants li {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  /* Space between the label and a per-claim retract button, when one
     is present — harmless with a single child (no retract control),
     which just sits flush left under space-between regardless. */
  justify-content: space-between;
}

.gc-claimants li + li {
  margin-top: 0.7rem;
}

.gc-claimants li i {
  color: #2c9e4a;
  font-size: 1.2rem;
}

.gc-claimants li.is-me {
  color: #8a5a00;
  font-weight: 600;
}

/* Comment card variant */

.gc-card--comment {
  background: #f4f1e6;
  justify-content: center;
  min-height: 160px;
}

.gc-quote-icon {
  color: #c9a51c;
  font-size: 2rem;
  margin-bottom: 0.5rem;
  opacity: 0.7;
}

.gc-comment-text {
  font-style: italic;
  font-size: 2.1rem;
  color: #2c342b;
  margin: 0;
}

/* Same red the site already uses for every other destructive hover
   state (.comment-item__delete-btn, etc.) — kept as its own variant
   rather than reusing .gc-btn--unclaim, since that class is wired to
   the claim/unclaim toggle specifically and reusing it here would make
   a "Delete" button silently behave like "Unclaim" if that handler ever
   changes. */
.gc-btn--delete {
  background: transparent;
  color: #9e2525;
  border: 1px solid rgba(158, 37, 37, 0.35);
}

.gc-btn--delete:hover {
  background: #9e2525;
  color: #fff;
}

/* Orange, deliberately distinct from .gc-btn--delete's red — red is
   reserved for the (not-yet-built) report button, moderation is its
   own, separate action and shouldn't read as the same color/urgency. */
.gc-btn--moderate {
  background: transparent;
  color: #b45f06;
  border: 1px solid rgba(180, 95, 6, 0.35);
}

.gc-btn--moderate:hover {
  background: #b45f06;
  color: #fff;
}

/* Item cards: sits in the normal footer flex row alongside the claim
   button/badge — only ever in the DOM at all when it should be visible
   (see renderGiftlistItemCard()'s own comment on why this isn't
   hidden-attribute-driven). */
.gc-moderate-item-btn,
.gc-report-item-btn {
  margin-left: auto;
}

/* Note cards have no footer row to join, so this sits as a small
   absolute corner button instead — .gc-card is already
   position:relative for this purpose. */
.gc-moderate-note-btn,
.gc-report-note-btn {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  padding: 0.3rem 0.5rem;
}

/* Item detail page header: sits alongside the back button/title inside
   .message-thread__header's flex row. Needs its own id-selector [hidden]
   override — .gc-btn sets display directly, which otherwise beats the
   browser's own [hidden] rule at equal class-selector specificity (the
   same trap documented in AGENTS.md for every other hideable .gc-btn/
   overlay element). */
.giftlist-item-moderate-btn {
  flex-shrink: 0;
}
#giftlist-item-moderate-btn[hidden],
#giftlist-item-report-btn[hidden],
#giftlist-item-restore-btn[hidden],
#giftlist-item-reject-edit-btn[hidden],
#giftlist-item-delete-now-btn[hidden] {
  display: none;
}

/* ---------- My Wish List: sections, entry forms ---------- */

/* Real button chrome for .btn-outline-dark — used on Add Note and Add
   Section (Save Draft/Discard have since moved to .btn-solid-dark, same
   as Publish, per Xavier's request). The class itself was never
   actually defined anywhere in this codebase; buttons using it fell
   back to bare .btn (transparent background, no border), which reads
   as plain uppercase text rather than a button at all — exactly
   Xavier's "just clickable text" complaint. Mirrors .btn-solid-dark's
   own color identity rather than .btn-outline-light's (that one's built
   for dark hero backgrounds and hover-fills red, which reads as an
   urgency/destructive accent that doesn't fit a neutral secondary
   action here) — same border+hover-fill shape .gc-btn--edit already
   uses elsewhere in this file for the same "neutral secondary action on
   a light panel" role. */
.btn-outline-dark {
  background-color: transparent;
  color: #2c342b;
  border: 1px solid rgba(44, 52, 43, 0.35);
  box-shadow: none;
}
.no-touch .btn-outline-dark:hover {
  background-color: #2c342b;
  color: #ffffff;
}

/* .btn-small--add — Xavier wanted the Add Item/Add Note/Add Section row
   visually distinct from the Publish/Discard/Close row above it (both
   were using .btn-solid-dark/.btn-outline-dark, so they read as one
   undifferentiated group). Went through a full-size solid gold, then a
   full-size hollow/outline gold (.btn-outline-gold, now removed) —
   neither read right: at full .btn size the row was competing with
   Publish/Discard/Close for visual weight instead of reading as a
   lighter, more frequent action, and gold-on-white contrast wasn't
   strong enough at that scale either. Xavier's own instinct — make
   these *smaller*, not just differently colored — is what actually
   fixes both problems at once: reuses the site's existing small-button
   pattern (.btn-small, already established for lower-stakes actions
   like comment Post/Cancel), same solid-gold fill and colors as
   .btn-small--save (which already sits on this exact white panel today
   as the Add Note/Add Section *submit* buttons and reads fine there) —
   proven at this size in this exact spot, just given its own modifier
   name since "save" doesn't fit a button that opens a form rather than
   submitting one. */
.btn-small--add {
  background: #c9a51c;
  color: #2c342b;
}
.btn-small--add:hover {
  background: #b3910c;
}

/* The read view's Edit List button — the one and only action in its
   row, so it reads as a real command for the page rather than one
   option among several. .btn is inline-block by default (sized to its
   own text); this overrides that specifically where requested rather
   than touching .btn itself, which plenty of other multi-button rows
   rely on staying content-sized. */
.wishlist-btn-full {
  display: block;
  width: 100%;
  text-align: center;
}

/* .wishlist-mode-actions (flex, no explicit width) is otherwise sized
   to fit its own content rather than filling its parent — fine, even
   preferable, for edit view's 3-button row (a chip that hugs its
   buttons rather than stretching with empty space after them), but it
   means .wishlist-btn-full's own width:100% above has nothing stable
   to resolve against: a flex item's percentage width can't reliably
   size against a container whose own width is itself still being
   determined by content. Scoped to just the read view, the one place
   the button is actually meant to span the full panel width — deliberately
   not changed on the shared class, which would also stretch edit
   view's row and leave dead space after its three buttons. */
#wishlist-read-view .wishlist-mode-actions {
  width: 100%;
}

.wishlist-header__actions {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}

/* Content-actions row (Add Item/Note/Section) — centered per Xavier's
   request, matching the centered edit-view mode-actions row below it
   rather than sitting left-aligned like most other panels on the page. */
.wishlist-content-actions {
  justify-content: center;
}

/* The panel wrapping this row (.wishlist-actions-block) inherits
   .inner--centered .inner__block.panel's own padding (3rem/3.5rem at
   >=1200px, site.css) — sized for a normal content panel, way more
   than a single row of small buttons needs, and Xavier flagged it as
   excess whitespace once the row itself shrank this round. Cut
   roughly in half. Needs the same 3-class specificity as the rule it's
   overriding (.wishlist-actions-block alone would lose to .panel on
   the same element); placed later in this file so it also wins the
   resulting tie. */
.inner--centered .inner__block.wishlist-actions-block {
  padding: 1.5rem 0;
}
@media (min-width: 1200px) {
  .inner--centered .inner__block.wishlist-actions-block {
    padding: 1.75rem 0;
  }
}

/* Sits inline right after the headline text now (the Gift List detail
   page's own subtitle pattern, .section__subtitle, but with an edit
   affordance since this is your own list) — a compact inline row, not
   a full-width box, so the pencil sits right next to the words instead
   of pinned to the far edge of the whole title panel. */
.wishlist-headline-display {
  display: inline-flex;
  align-items: center;
  gap: 1.5rem;
}

.wishlist-headline-edit-btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.8rem;
  height: 2.8rem;
  border-radius: 50%;
  border: 1px solid rgba(44, 52, 43, 0.2);
  background: none;
  color: #6b6555;
  font-size: 1.1rem;
  cursor: pointer;
}

.no-touch .wishlist-headline-edit-btn:hover {
  color: #2c342b;
  border-color: #2c342b;
}

/* Edit view's session-controls row only — not applied to the shared
   class itself since the read view's single Edit List button already
   fills the row via .wishlist-btn-full.
   Publish/Discard/Close all sit in one row now (Close last), all three
   full-size .btn — no size override on Close anymore, same on desktop
   and mobile. `flex-wrap: wrap` on `__primary` lets them wrap onto
   their own centered lines if three full-size buttons don't fit side
   by side at some width, rather than being squeezed narrower than
   their own caption text (that squeeze is what caused an earlier
   overflow bug with just two buttons in a no-wrap row).
   `.wishlist-mode-actions` itself also needs `justify-content: center`
   — it's a full-width block (a plain div inside `.inner__container`),
   so without it the single `__primary` flex item just sits at
   flex-start (left) inside that full width, and `__primary`'s own
   `justify-content: center` only centers its buttons within
   `__primary`'s own shrink-to-fit box, not within the wider row. Centering
   both is what actually centers the group in the panel, on one line or
   wrapped across several. */
.wishlist-grid-view .wishlist-mode-actions {
  justify-content: center;
}
.wishlist-grid-view .wishlist-mode-actions .wishlist-mode-actions__primary {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75rem;
}
/* Publish/Discard/Close have different caption lengths ("Publish" vs
   "Discard" vs "Close"), so even centered, their differing widths made
   the stack look slightly off/uneven whenever they wrapped onto
   separate lines (each line's shorter button not lining up under the
   longer ones above it). Same fixed width on all three fixes that
   regardless of layout — widened from an earlier, too-tight 19rem: at
   19rem the only-102px content area (19rem minus .btn's own 2×4.4rem
   padding) left barely enough room for "Discard"'s icon+caption, and
   in some renders it lost that margin and wrapped to a second line
   that then overflowed below the button's fixed height. 23rem leaves
   real breathing room instead of a razor-thin fit.
   Also zero out base .btn's own `margin-right` (1rem, or 1.6rem at
   ≥576px, with a `:last-of-type` rule that clears it only on the
   final button) — `gap` on this flex row already handles spacing
   between buttons, so that margin was pure redundant width tacked onto
   Publish/Discard's right side but not Close's. When wrapped one-per-
   line, centering an item with an asymmetric trailing margin shifts
   the visible button off true-center — Publish/Discard (with the
   margin) rendered ~8px left of true center, Close (margin-free via
   :last-of-type) sat at true center, so Close visibly didn't line up
   with the other two. */
.wishlist-grid-view .wishlist-mode-actions .wishlist-mode-actions__primary .btn {
  width: 23rem;
  margin-right: 0;
}

/* Session-level controls (Edit List / Save Draft / Publish / Discard)
   — just spacing now, no chip background/border (removed per Xavier's
   request). Present (and harmless as a single-button row) in the read
   view too, where it's just Edit List. */
.wishlist-mode-actions {
  padding: 0.85rem 1rem;
  margin-bottom: 1.1rem;
  border-radius: 10px;
  flex-wrap: wrap;
}

/* Went through a gold gradient (too garish), a faded neutral gradient,
   a plain solid brown, and a 50/50 brown-cream blend (rgb(184, 164,
   141)) before this — Xavier still read the 50/50 blend as barely
   different from the full-strength brown. This is the same brown
   (122, 89, 54) and the panel's own cream background (#f5efe3, see
   main.css) mixed 25% brown / 75% cream instead of 50/50 — noticeably
   lighter, still a flat opaque color, no transparency/gradient. */
.wishlist-mode-actions-divider {
  height: 1px;
  margin-top: 1.5rem;
  background: rgb(214, 202, 184);
}

/* Now living inside the same panel as the section title (Xavier's
   call — previously its own separate panel below, with that panel's
   own top padding doing this same job for free). Scoped to only when
   it directly follows the title's own .section-header, so the class's
   own margin-bottom above still works unmodified wherever else this
   chip appears. */
.section-header + .wishlist-mode-actions {
  margin-top: 1.5rem;
}

/* This panel's own generic content-panel padding (.inner--centered
   .inner__block.panel, 3rem/3.5rem top+bottom — a 3-class selector,
   needs matching specificity to actually override, same as every
   other "panel padding" fix this session) left way more space below
   the button row than above it. First pass zeroed the bottom entirely;
   Xavier preferred a bit of breathing room over none, so both sides
   are now the same modest value instead — applies to both the read
   view's Edit List button and edit mode's Publish/Discard/Close row,
   which share this class. */
.inner--centered .inner__block.wishlist-mode-panel {
  padding-top: 1.25rem;
  padding-bottom: 1.25rem;
}

/* The row itself also carries its own 1.1rem margin-bottom (see
   .wishlist-mode-actions above — meant generically for spacing before
   whatever follows), which was stacking on top of the panel's own
   1.25rem padding-bottom above: 23.5px below the row vs. 12.5px above
   it, not actually centered despite the panel's own padding being
   symmetric. The row is always the last thing in this panel in both
   places it's used, so that margin was pure redundant space here —
   zeroing it scoped to this panel leaves the panel's own matching
   padding as the only source of space on both sides. */
/* margin-top matches the panel's own 1.25rem padding-bottom, for real
   symmetry around the row — margin-bottom stays zeroed per the earlier
   fix above. The pre-existing ".section-header + .wishlist-mode-
   actions { margin-top: 1.5rem; }" rule (further up this file) never
   actually applied here: it's an adjacent-sibling selector, but
   .wishlist-mode-actions-divider sits between the header and this row
   in the DOM, breaking the direct adjacency the "+" combinator
   requires. The row had been sitting flush against the divider with
   zero gap above it this whole time — not a regression from anything
   changed this round, just never worked. */
.wishlist-mode-panel .wishlist-mode-actions {
  margin-top: 1.25rem;
  margin-bottom: 0;
}

/* Draft last-edited / last-published status line, sitting below the
   Publish/Discard/Close row — small and muted, matching .wishlist-
   section-empty's own quiet-caption treatment rather than competing
   with the buttons above it for attention. This panel's background is
   the light cream .background-mute (not dark), so the muted color here
   needs to be dark, same tone .wishlist-entry-form__qty-label already
   uses for secondary text on this same light background. */
.wishlist-draft-status {
  flex-basis: 100%;
  text-align: center;
  font-size: 1.2rem;
  font-style: italic;
  color: #6b6555;
  margin: 0.75rem 0 0;
}
.wishlist-draft-status:empty {
  display: none;
}

.wishlist-entry-form {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

.wishlist-entry-form__name,
.wishlist-entry-form__description,
.wishlist-entry-form__quantity,
.wishlist-entry-form__layout,
/* Same visual style as the fields above, but its own distinct class
   rather than reusing .wishlist-entry-form__name — sharing that class
   already caused a real bug once this session: a broad
   `.find(".wishlist-entry-form__name")` reset elsewhere in
   openWishlistItemEditor() would match both the real Name field and
   this one, overwriting whatever this was just reset to with the
   item's own name every time the editor opened. */
.wishlist-item-editor-reason-other {
  width: 100%;
  padding: 0.9rem 1.2rem;
  border-radius: 8px;
  border: 1px solid rgba(44, 52, 43, 0.2);
  background: #fff;
  color: #2c342b;
  font-size: 1.36rem;
  font-family: "Mulish", sans-serif;
}

/* Without this, main.css's own `form input, form textarea { display:
   block; }` reset (this field lives inside <form id="wishlist-item-
   editor-form">) silently overrides the `hidden` attribute — that
   compound element selector has LOWER specificity than `[hidden]`
   alone, but author-stylesheet rules always beat the browser's own
   built-in `[hidden] { display: none }` regardless of specificity
   (origin is compared before specificity in the cascade), so an
   explicit author-stylesheet re-assertion is the only way to actually
   win here. Same root cause, different rule, as the restore-form fix
   earlier this session. */
.wishlist-item-editor-reason-other[hidden] {
  display: none;
}

/* Fixed height, no resize handle — Xavier didn't want notes resizable
   at all. Only used for note text (add/edit, global and per-section) —
   never item descriptions, which are a separate contenteditable
   rich-text field. Sized for exactly 3 full lines before the scrollbar
   kicks in: this field's line-height computes to 21.76px, so content
   area needs 3 * 21.76 = 65.28px. Height is border-box (site-wide
   reset), so the declared value also has to cover the 1.8rem (18px)
   top+bottom padding AND the 2px top+bottom border on top of that —
   65.28 + 18 + 2 = 85.28px, rounded up (not down, or the 3rd line would
   clip) to 8.6rem. */
.wishlist-entry-form__description {
  resize: none;
  height: 8.6rem;
}

.wishlist-entry-form__qty-label {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: 1.28rem;
  color: #6b6555;
}

.wishlist-entry-form__qty-label .wishlist-entry-form__quantity,
.wishlist-entry-form__qty-label .wishlist-entry-form__layout {
  width: auto;
  flex: 0 0 auto;
}

.wishlist-entry-form__field-label {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

/* Without this, .wishlist-entry-form__field-label's own `display: flex`
   above silently overrides the `hidden` attribute on
   .wishlist-item-editor-reason (add mode hides this field — see
   openWishlistItemEditor()) — same root cause as every other `[hidden]`
   override this session: a normal author-stylesheet rule always beats
   the browser's built-in `[hidden] { display: none }` regardless of
   specificity, so only an explicit author-stylesheet re-assertion
   actually wins. */
.wishlist-item-editor-reason[hidden] {
  display: none;
}

/* Shared by every small field-group heading in the item editor (Name,
   Quantity, Links, Images) — bigger and bolder than the old muted
   caption style, specifically so it reads clearly as a heading next to
   the smaller "Primary" badge on the Links field, which the old,
   barely-bigger style didn't. */
.wishlist-field-label {
  font-size: 1.55rem;
  font-weight: 700;
  color: #2c342b;
}

/* Bigger/more visible than the shared .wishlist-field-label base above —
   Xavier's own request, this label sits alone as its own section rather
   than next to a form field the way Name/Quantity/Links do, so it reads
   more like a small heading than an inline field caption. Also a
   dedicated class rather than resizing .wishlist-field-label itself,
   since that base rule is shared by every other field label in the item
   editor and isn't meant to move with this one. */
.wishlist-font-pack-label {
  display: block;
  margin-bottom: 0.8rem;
  font-size: 2rem;
}

.wishlist-quantity-field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.wishlist-quantity-field__row {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.wishlist-quantity-field__row .wishlist-entry-form__quantity {
  width: auto;
  flex: 0 0 auto;
}

.wishlist-quantity-unlimited {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 1.5rem;
  color: #6b6555;
  cursor: pointer;
}

/* main.css has a blanket "form input { height: 4.4rem; margin: 0 0
   2rem; }" meant for text inputs — it was also stretching this
   checkbox to 44px tall with a 20px bottom margin, which is why the
   row looked vertically off against the small icon/text next to it.
   Not really an icon-alignment problem — the checkbox itself was the
   wrong size. Reset back to its natural small checkbox size. */
.wishlist-entry-form__unlimited-check {
  display: inline-block;
  width: auto;
  height: auto;
  margin: 0;
  padding: 0;
}

.wishlist-entry-form__quantity:disabled {
  background: #f4f1e6;
  color: #9a9384;
  cursor: not-allowed;
}

.wishlist-entry-form__actions {
  display: flex;
  gap: 0.75rem;
}

/* Tacked onto the very bottom of #wishlist-add-section-panel, outside
   the form's own .inner__container padding — a sibling of it, not a
   descendant. .inner__container's own default margin-bottom (50px,
   used elsewhere to space stacked containers apart) is zeroed here so
   it doesn't stack on top of the panel's own 30px bottom padding —
   that panel padding alone is the gap above the preview (matching a
   real section's own Edit-form-to-body-panel gap, Xavier's reference),
   and the preview cancels that same padding with a negative bottom
   margin so it still bleeds flush to the panel's real left/right/
   bottom edges instead of floating indented inside it. */
#wishlist-add-section-panel > .inner__container {
  margin-bottom: 0;
}
/* Scoped to the parent ID, not just the class alone — .inner--centered
   .panel (this page's own generic "centered content panel" rule,
   site.css above) also matches this element (it carries a plain
   .panel class too) and was winning the specificity fight against a
   bare .wishlist-add-section-preview selector, silently overriding
   both width and this margin. */
#wishlist-add-section-panel > .wishlist-add-section-preview {
  width: 100%;
  /* Top margin is the actual gap above the preview (matches the 30px
     a real section shows between its Edit form and its body panel);
     bottom margin cancels the panel's own 30px bottom padding so the
     preview still bleeds flush to the panel's true bottom edge rather
     than floating short of it — two different jobs, not one. */
  margin: 30px 0 -30px;
}

/* Sits below the edit form as its own panel (see the HTML comment in
   index.html on why it can't just be a second <form> nested inside
   that one) — same field rhythm as .wishlist-entry-form via the shared
   gap, just not itself a <form>. */
.wishlist-remove-panel {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
  margin-top: 1.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid rgba(44, 52, 43, 0.12);
}

/* Same reasoning as .comment-form[hidden] elsewhere in this file — a
   plain class selector's display:flex above wins a same-specificity
   tie against (rather than losing to) the browser's own
   [hidden]{display:none} by cascade order, so toggling the hidden
   attribute alone wouldn't actually hide this without this override. */
.wishlist-remove-panel[hidden] {
  display: none;
}

.wishlist-remove-panel__intro {
  font-size: 1.3rem;
  color: #6b6555;
  margin: 0;
}

/* One row per Hidden item — name/removal details on the left, Restore
   pinned to the right, same left-content/right-action shape as a
   message-list row. */
.wishlist-hidden-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  padding: 1rem 0;
  border-bottom: 1px solid rgba(44, 52, 43, 0.1);
}

/* flex-basis:100% forces this onto its own line below name/Restore —
   relies on the row's own flex-wrap above, only actually visible (and
   therefore only actually wrapping) once Restore is clicked. */
.wishlist-hidden-item__restore-form {
  flex: 1 1 100%;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.75rem;
}

/* Without this, the form was visible for every row regardless of its
   `hidden` attribute — a class selector and an attribute selector both
   have the same specificity, and author styles win ties over the
   browser's own `[hidden] { display: none }` rule, so the plain
   `display: flex` above silently overrode `hidden` entirely. This
   selector (class + attribute) has one more simple selector than
   either, so it wins for real. */
.wishlist-hidden-item__restore-form[hidden] {
  display: none;
}

.wishlist-hidden-item__restore-reason-select {
  flex: 1 1 200px;
}

/* Own styling rather than reusing .wishlist-entry-form__name — see the
   near-identical .wishlist-item-editor-reason-other rule above for why
   that pattern is worth avoiding here even though nothing in this
   particular row currently resets that shared class. flex-basis:100%
   (relying on the row's own flex-wrap) puts this on its own line
   below the dropdown always, rather than sitting beside it whenever
   there happens to be room. */
.wishlist-hidden-item__restore-reason-other {
  flex: 1 1 100%;
  padding: 0.6rem 1rem;
  border-radius: 8px;
  border: 1px solid rgba(44, 52, 43, 0.2);
  background: #fff;
  color: #2c342b;
  font-size: 1.3rem;
  font-family: "Mulish", sans-serif;
}

.wishlist-hidden-item__restore-form .wishlist-entry-form__actions {
  flex: 0 0 auto;
}

.wishlist-hidden-item__restore-form .comment-form__error {
  flex: 1 1 100%;
}

.wishlist-hidden-item:last-child {
  border-bottom: none;
}

.wishlist-hidden-item__body {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  min-width: 0;
}

.wishlist-hidden-item__name {
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 1.5rem;
  color: #2c342b;
}

/* Only the live Hidden accordion's row links to the item view — the
   Pending Removal list (Edit Mode drafts, unpublished) reuses this same
   class as a plain, non-interactive label, so the pointer/hover only
   applies to the anchor tag, never the plain span. Also covers every
   other "row name links out to the item view" anchor built on top of
   .wishlist-hidden-item (the admin-facing per-list Moderated/Reported
   accordions, and the cross-user Moderation page's own Moderated rows)
   — these were missing this same affordance despite being real links,
   now folded into one shared rule instead of each getting its own copy. */
a.wishlist-hidden-item__name-link,
a.giftlist-moderated-item__name-link,
a.giftlist-reported-item__name-link,
a.moderation-moderated-item__name-link {
  cursor: pointer;
}
.no-touch a.wishlist-hidden-item__name-link:hover,
.no-touch a.giftlist-moderated-item__name-link:hover,
.no-touch a.giftlist-reported-item__name-link:hover,
.no-touch a.moderation-moderated-item__name-link:hover {
  text-decoration: underline;
}

.wishlist-hidden-item__meta {
  font-size: 1.2rem;
  font-style: italic;
  color: #9a9384;
}

/* Per-row counterpart to the Moderation tile's blue "edited" badge
   (see .hub__tile-badge--edited) — same color, small pill instead of a
   corner dot since this sits inline in a row rather than on a tile
   corner. Only ever rendered inside renderModerationModeratedRow(). */
.moderation-edited-flag {
  display: inline-block;
  width: fit-content;
  padding: 0.15rem 0.7rem;
  border-radius: 999px;
  background: #3a7ca5;
  color: #f5efe3;
  font-family: "Mulish", sans-serif;
  font-size: 1.1rem;
  font-weight: 700;
}

.moderation-edited-flag[hidden] {
  display: none;
}

/* Wraps Restore + the conditional Reject Edit button together so the
   row's own flex-wrap (.wishlist-hidden-item) still treats them as one
   unit rather than wrapping between them independently. */
.moderation-row-actions {
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
}

/* ---------- Item editor: full-screen view ---------- */

/* Bigger than the shared .message-thread__title default (1.36rem) —
   scoped to this one id rather than changing that class itself, so
   Messages' own thread header stays exactly as it was. This is a
   full-screen title, not an inbox thread subject line, and read as
   too small at the same size as that. */
#wishlist-item-editor-title,
#wishlist-item-preview-title,
#activities-detail-title {
  font-size: 1.8rem;
}

/* Bumped further than the shared full-screen-title size above — Xavier's
   own call after the first pass still read as too small next to a real
   section heading (like "Editing My Wish List"'s own h2) even once
   already bigger than the plain message-thread default. */
#activities-editor-title {
  font-size: 2.4rem;
}

.wishlist-preview-disclaimer {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 1.25rem 0;
  padding: 0.9rem 1.2rem;
  border-radius: 8px;
  background: #f4f1e6;
  color: #6b6555;
  font-size: 1.2rem;
}

/* Deliberately distinct from the claim badge's own gold/cream palette
   (.gc-badge--*) — this isn't a claim-progress state, it's a warning
   that the item itself is gone, and needs to read as unmissable rather
   than blend in as just another badge color.

   Two severities, not one, matching the two different messages this
   can show (see renderGiftlistItemDetail()): orange for "hidden" (the
   owner could still Restore it, not final yet), red for "archived" via
   the --final modifier (the grace period already expired with this
   claim still on it — there's no path back, only cleanup left once the
   claim itself goes). */
.wishlist-removed-banner {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 0 0 1.25rem;
  padding: 0.9rem 1.2rem;
  border-radius: 8px;
  background: #fbe8c6;
  color: #8a5a00;
  font-weight: 600;
  font-size: 1.3rem;
}

.wishlist-moderated-editor-banner {
  margin: 0 0 1.5rem;
  padding: 0.9rem 1.2rem;
  border-radius: 8px;
  background: #f7e2e2;
  color: #9e2525;
  font-weight: 600;
  font-size: 1.28rem;
  line-height: 1.5;
}

.wishlist-removed-banner--final {
  background: #f7e2e2;
  color: #9e2525;
  /* The explanatory note right below sits close underneath, not with
     the same full gap the banner leaves for whatever normally follows
     it (the photo, when there's no note) — pulling that gap up here
     instead of down on the note keeps the note's own spacing simple
     regardless of what comes after it. */
  margin-bottom: 0.5rem;
}

/* Matches .gc-btn--moderate's orange — distinct from the base banner's
   more muted amber (an owner's own removal) and --final's red (gone for
   good) — moderation is its own, separate state with its own color
   language now, same reasoning as the gavel/flag icon split. */
.wishlist-removed-banner--moderated {
  background: #fbe3c8;
  color: #b45f06;
  margin-bottom: 0.5rem;
}

/* Same blue as .moderation-edited-flag/.hub__tile-badge--edited — one
   consistent color for "an owner edited this since it was moderated"
   everywhere that state shows up. */
.wishlist-removed-banner--edited {
  background: #dbeaf2;
  color: #3a7ca5;
  margin-bottom: 0.5rem;
}

.wishlist-removed-banner-note {
  color: #6b6555;
  font-size: 1.15rem;
  font-style: italic;
  margin: 0 0 1.25rem;
}

.wishlist-preview-desc {
  font-size: 1.36rem;
  color: #2c342b;
  margin: 0.5rem 0;
}

/* main.css has a global, unscoped `p { color: rgba(255,255,255,0.8) }`
   rule (built for the dark hero/countdown sections) — a rule that
   directly matches an element always wins over an inherited value
   regardless of specificity, so any description whose rich text got
   wrapped in a real <p> (the editor's default, not just plain text)
   rendered as near-invisible near-white text on this box's cream/white
   background instead of inheriting the dark color above. */
.wishlist-preview-desc p {
  color: inherit;
}

/* Same reset-collision as the real editor's own rich-text output
   (.wishlist-richtext-editor) — plugins.css's `font: inherit`/
   `list-style: none` strips the browser defaults for b/i/lists, so
   this needs the identical restoration wherever rich description HTML
   renders outside the editor itself. */
.wishlist-preview-desc b,
.wishlist-preview-desc strong {
  font-weight: 700;
}

.wishlist-preview-desc i,
.wishlist-preview-desc em {
  font-style: italic;
}

.wishlist-preview-desc ul,
.wishlist-preview-desc ol {
  margin: 0;
  padding-left: 1.5rem;
}

.wishlist-preview-desc ul {
  list-style: disc;
}

.wishlist-preview-desc ol {
  list-style: decimal;
}

/* This is the eventual item-inspection page, not the compact grid
   tile — a single column, full width, room for a real image gallery
   and every link, not just a cover photo and a primary link. */
.wishlist-preview-detail {
  max-width: 640px;
  margin: 0 auto;
}

.wishlist-preview-gallery__main {
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: 10px;
  overflow: hidden;
  background: #f4f1e6;
  margin-bottom: 0.75rem;
}

.wishlist-preview-gallery__main img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.wishlist-preview-gallery__thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-bottom: 1.5rem;
}

.wishlist-preview-gallery__thumbs img {
  width: 64px;
  height: 64px;
  object-fit: cover;
  border-radius: 8px;
  cursor: pointer;
  opacity: 0.7;
  outline: 2px solid transparent;
  outline-offset: 2px;
  transition: opacity 0.15s ease, outline-color 0.15s ease;
}

.wishlist-preview-gallery__thumbs img:hover {
  opacity: 0.9;
}

.wishlist-preview-gallery__thumbs img.is-active {
  opacity: 1;
  outline-color: #c9a51c;
}

.wishlist-preview-detail__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  /* Now sits below the gallery (moved back per Xavier's call) rather
     than leading the page — needs its own top spacing since the
     gallery has no bottom margin of its own to provide it. Harmless
     when there's no gallery (add mode, pre-first-photo) too, since
     it just adds a bit of breathing room under the panel's own
     padding instead. */
  margin: 1rem 0 0.5rem;
}

/* Groups the qty badge with the report button (relocated here by
   renderGiftlistItemDetail — see its own comment) as one right-aligned
   unit, so .wishlist-preview-detail__top's own space-between still
   reads as two things (name, everything else) rather than spreading
   three items apart unevenly whenever the report button is present. */
.wishlist-preview-detail__top-right {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.wishlist-preview-detail__name {
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 2.1rem;
  color: #2c342b;
  margin: 0;
  /* Same fix as .gc-title above, same reason — this is a literal <h3>
     (site.js), inheriting main.css's sitewide h3 { text-transform:
     uppercase }. The list view's card title got this fix already; the
     item detail view's own title uses a completely different element
     and was missed on the first pass. */
  text-transform: none;
}

/* The bare .wishlist-field-label span just before this has no margin
   of its own (its other uses all sit inside a flex parent that
   handles spacing via gap) — scoped here so it doesn't crowd whatever
   came before it in this plain block-flow context. */
.wishlist-preview-detail .wishlist-field-label {
  display: block;
  margin-top: 1.25rem;
}

.wishlist-preview-links {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 0.5rem 0 1rem;
}

.wishlist-preview-link {
  color: #2c342b;
  text-decoration: none;
  font-size: 1.28rem;
}

.wishlist-preview-link:hover {
  text-decoration: underline;
}

.wishlist-preview-link--primary {
  font-weight: 700;
  color: #8a5a00;
}

/* Its own card, not just another stacked block — this is the one part
   of the page someone's actually here to act on (claim), sitting right
   after the gallery and ahead of the description/links so a long
   description never stands between "I opened this" and "I can claim
   it." Bigger badge/button than the base .gc-badge/.gc-btn (which stay
   their normal compact size everywhere else — the #works mockup, and
   eventually the real Gift List viewer) since this is meant to read as
   a clearly separate, easy-to-hit action area. */
.wishlist-preview-claims {
  background: #f4f1e6;
  border-radius: 10px;
  padding: 1.25rem 1.5rem;
  margin: 1.25rem 0;
}

.wishlist-preview-claims .gc-footer {
  margin-top: 0;
}

.wishlist-preview-claims .gc-badge {
  font-size: 1.5rem;
  padding: 0.55rem 1.1rem;
}

.wishlist-preview-claims .gc-btn {
  font-size: 1.5rem;
  padding: 0.75rem 1.5rem;
}

/* Claim disables at the quantity cap — same dimmed/no-cursor treatment
   used for other disabled controls on this page, scoped here rather
   than site-wide since no other .gc-btn use sets the disabled
   attribute yet. */
.wishlist-preview-claims .gc-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Shared toggle for both the item-level (detail page body) and
   list-level (detail-view header) Watch buttons — same pill shape as
   .message-thread__archive-btn, its own class rather than reusing that
   one since it's wired to a different pair of endpoints per level
   (watch/unwatch item vs. watch/unwatch list) and shouldn't silently
   inherit Archive's own click handler if that ever changes. */
.giftlist-watch-btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: none;
  border: 1px solid rgba(44, 52, 43, 0.2);
  border-radius: 20px;
  padding: 0.4rem 1rem;
  font-size: 1.15rem;
  font-weight: 600;
  color: #6b6555;
  cursor: pointer;
  margin-left: auto;
}

.giftlist-watch-btn:hover {
  color: #2c342b;
  border-color: #2c342b;
}

.giftlist-watch-btn.is-watching {
  color: #2c342b;
  border-color: #c9a51c;
  background: #c9a51c;
}

.giftlist-watch-btn.is-watching:hover {
  background: #b3910c;
  border-color: #b3910c;
}

/* The item-level button sits in the detail body, not the header — it
   has no header flex-row to be pushed right by, so the shared class's
   margin-left: auto would just collapse to nothing here; unset it and
   give it its own breathing room under the name/qty row instead. */
.giftlist-watch-item-btn {
  margin-left: 0;
  margin-top: 0.75rem;
}

.wishlist-preview-claims .gc-btn--claim:disabled:hover {
  background: #c9a51c;
}

/* Its own background distinct from the muted card around it, rather
   than the plain top-border separator .gc-claimants uses elsewhere —
   a real sub-panel for "who's already claimed this" rather than a
   quiet trailing list. */
.wishlist-preview-claims .gc-claimants {
  background: #fff;
  border-top: none;
  border-radius: 8px;
  margin: 1rem 0 0;
  padding: 0.9rem 1.1rem;
}

/* Shared between the Preview simulation and the real Gift List cards —
   both need the exact same "claim anonymously" checkbox, per-claim
   label, and per-claim retract button. Named generically (gc-*, not
   wishlist-preview-*) since Preview was only ever the first of two real
   consumers, not the only one. The label's icon and text are wrapped
   in their own flex span (rather than left as direct li children) so
   li's own gap/justify-content only ever has two items — the label and
   the button — to space apart, keeping the icon-to-text gap fixed
   regardless of that. */
.gc-claimant-label {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.gc-claim-remove-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  border: 1px solid #9e2525;
  background: transparent;
  color: #9e2525;
  cursor: pointer;
  font-size: 1.1rem;
  font-weight: 600;
  line-height: 1;
  padding: 0.4rem 0.8rem;
  border-radius: 20px;
  white-space: nowrap;
}

.gc-claim-remove-btn:hover {
  background: #9e2525;
  color: #fff;
}

/* Same flex/gap/color shape as .message-compose__anonymous — a plain
   checkbox + label row read the same way in both places. */
.gc-claim-anon {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: 0.9rem;
  font-size: 1.2rem;
  color: #6b6555;
}

/* Same reset as .message-compose__anonymous-check — a lone class
   already beats the blanket "form input" rule on specificity, this
   just resets the properties that rule would otherwise set. */
.gc-claim-anon-check {
  display: inline-block;
  width: auto;
  height: auto;
  margin: 0;
}

/* Claims History — the full log of every claim/unclaim event ever, not
   just current state, so it deliberately reads differently from Active
   Claims above it: a plain trailing list (no muted card background),
   since this is reference material to scroll past, not the thing
   someone's here to act on. Item detail page only. */
.wishlist-claims-history {
  margin-top: 1.5rem;
}

.wishlist-claims-history-list {
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0;
  font-size: 1.28rem;
  color: #6b6555;
}

.wishlist-claims-history-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 0;
  border-top: 1px solid #eee6d6;
}

.wishlist-claims-history-item:first-child {
  border-top: none;
}

.wishlist-claims-history-item i {
  color: #9a9384;
  font-size: 1.2rem;
}

.wishlist-claims-history-empty {
  font-style: italic;
  color: #9a9384;
}

.wishlist-claims-history-show-more {
  padding-top: 0.9rem;
  text-align: center;
}

/* Same label-above-field shape as .wishlist-quantity-field — extra
   margin-top for breathing room after Images, same value used there. */
.wishlist-description-field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-top: 0.75rem;
}

.wishlist-richtext-field {
  display: flex;
  flex-direction: column;
  border: 1px solid rgba(44, 52, 43, 0.2);
  border-radius: 8px;
  overflow: hidden;
  background: #fff;
}

.wishlist-richtext-toolbar {
  display: flex;
  gap: 0.25rem;
  padding: 0.5rem;
  background: #f4f1e6;
  border-bottom: 1px solid rgba(44, 52, 43, 0.2);
}

.wishlist-richtext-btn {
  width: 32px;
  height: 32px;
  border-radius: 6px;
  border: none;
  background: transparent;
  color: #6b6555;
  cursor: pointer;
  font-size: 0.95rem;
}

.wishlist-richtext-btn:hover {
  background: rgba(44, 52, 43, 0.1);
  color: #2c342b;
}

/* Touch selection (iOS/Android) pops up a native copy/cut/paste bubble
   right next to wherever you've selected text — it's OS-level UI, not
   part of the page, so no z-index here can win against it. The only
   real fix is keeping the toolbar out of the space directly above the
   text, which is exactly where that bubble wants to render. Below this
   breakpoint (or on any coarse/touch pointer, regardless of screen
   width — covers a touchscreen at desktop width, or a phone driving a
   desktop-size viewport through remote-control software) the toolbar
   moves below the text box instead of above it, via flex `order`
   (higher than the editor's default 0, not equal — equal order falls
   back to DOM order, which is toolbar-first), and grows to an actual
   touch target size. */
@media (max-width: 768px), (pointer: coarse) {
  .wishlist-richtext-toolbar {
    order: 1;
    border-bottom: none;
    border-top: 1px solid rgba(44, 52, 43, 0.2);
    gap: 0.5rem;
    padding: 0.6rem;
  }

  .wishlist-richtext-btn {
    width: 44px;
    height: 44px;
    font-size: 1.2rem;
  }
}

.wishlist-richtext-editor {
  min-height: 8rem;
  padding: 0.9rem 1.2rem;
  font-size: 1.36rem;
  font-family: "Mulish", sans-serif;
  color: #2c342b;
  outline: none;
}

/* Activities are article-length (rules, budgets, whatever Xavier's
   writing up), not the short item-note this shared box was originally
   sized for — much taller starting height, plus resize:vertical so it
   can grow further for a genuinely long post. */
#activities-editor-description {
  min-height: 24rem;
  resize: vertical;
  overflow-y: auto;
}

.wishlist-richtext-editor:empty::before {
  content: attr(data-placeholder);
  color: #9a9384;
}

/* Same reset-collision as the b/i rule below — plugins.css sets
   `ol, ul { list-style: none; }` site-wide, so insertUnorderedList/
   insertOrderedList were building correct <ul>/<ol><li> structure with
   no visible bullets/numbers to show for it. Restored explicitly per
   list type rather than a shared rule, since ul and ol need different
   markers. */
.wishlist-richtext-editor ul,
.wishlist-richtext-editor ol {
  margin: 0;
  padding-left: 1.5rem;
}

.wishlist-richtext-editor ul {
  list-style: disc;
}

.wishlist-richtext-editor ol {
  list-style: decimal;
}

/* The site's CSS reset (plugins.css, Eric Meyer's) sets `font: inherit`
   on b/i/strong/em/u alike — that shorthand wipes out the browser's
   default bold/italic styling but leaves text-decoration untouched,
   which is why underline "worked" and bold/italic silently didn't.
   Restoring the two here explicitly rather than relying on the UA
   default the reset already removed. */
.wishlist-richtext-editor b,
.wishlist-richtext-editor strong {
  font-weight: 700;
}

.wishlist-richtext-editor i,
.wishlist-richtext-editor em {
  font-style: italic;
}

/* ---------- Item editor: images (act immediately) ---------- */

/* Extra breathing room above Images specifically — the form's own
   uniform gap between all fields reads fine everywhere else, but felt
   too tight right after the links list (which already has its own
   "+ Add Link" row immediately before this one). */
.wishlist-item-editor-images {
  margin-top: 0.75rem;
}

.wishlist-image-thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.wishlist-image-thumb {
  position: relative;
  width: 80px;
  height: 80px;
  border-radius: 8px;
  overflow: hidden;
  background: #f4f1e6;
}

/* Cover photo indicator — outline rather than a text badge, which
   wouldn't have room to sit legibly on an 80px thumbnail. Same gold
   accent used for the primary link badge elsewhere in this editor.
   outline-offset keeps it from touching the image itself. */
.wishlist-image-thumb--primary {
  outline: 2px solid #c9a51c;
  outline-offset: 2px;
}

.wishlist-image-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.wishlist-image-thumb__delete {
  position: absolute;
  top: 3px;
  right: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: none;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 0.9rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.wishlist-image-thumb__move {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  background: rgba(0, 0, 0, 0.55);
}

.wishlist-image-thumb__move button {
  flex: 1;
  border: none;
  background: none;
  color: #fff;
  padding: 0.25rem 0;
  cursor: pointer;
  font-size: 0.85rem;
}

.wishlist-image-thumb__move button:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

.wishlist-image-upload-btn {
  position: relative;
  width: 80px;
  height: 80px;
  border-radius: 8px;
  border: 2px dashed rgba(44, 52, 43, 0.25);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #9a9384;
  cursor: pointer;
  font-size: 1.5rem;
  transition: border-color 0.2s ease, color 0.2s ease;
}

.wishlist-image-upload-btn:hover {
  border-color: #c9a51c;
  color: #c9a51c;
}

/* Absolutely positioned rather than stacked in flow below the icon —
   that way the + icon stays exactly centered in the box regardless of
   the label's presence, and the text just pins to the bottom edge
   without needing to shrink or reflow anything else. color: inherit
   is what keeps it matching the button's own color in both states
   (default grey, gold on hover) with no separate hover rule needed. */
.wishlist-image-upload-btn__text {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 6px;
  text-align: center;
  font-size: 1.05rem;
  font-family: "Mulish", sans-serif;
  line-height: 1;
  white-space: nowrap;
  color: inherit;
}

/* Without this, the [hidden] attribute on the real file input has no
   effect — main.css has a blanket "form input, form textarea { display:
   block; }" rule, and an author stylesheet rule always beats the
   browser's own built-in [hidden]{display:none} regardless of
   specificity (origin precedence, not a specificity contest — [hidden]
   is a user-agent-stylesheet rule, not an authored one). Same root
   cause as the other [hidden] overrides already in this file, just
   triggered by a different conflicting rule. Without this, the native
   "Choose File" button renders right next to the + icon and spills out
   of the dashed box. The item editor no longer has its own inline
   upload input at all (photos come from the picker screen now) — this
   now guards the picker's own upload input instead. */
.wishlist-image-picker-upload-input[hidden] {
  display: none;
}

/* ---------- Item editor: links (part of Save) ---------- */

.wishlist-link-rows-field {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  align-items: flex-start;
  width: 100%;
}

.wishlist-link-rows {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: 100%;
}

.wishlist-link-row {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}

/* Fixed width regardless of how many of the 3 buttons inside are
   actually showing — otherwise hiding one via display:none frees up
   space that the flex: 2 / flex: 1 url/label fields immediately
   absorb, visibly resizing those fields row to row. 3 buttons × 28px +
   2 gaps × 0.5rem holds all three at once; justify-content: flex-start
   is what makes fewer buttons slide left within this fixed box rather
   than staying pinned to the right edge. */
.wishlist-link-row__actions {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 0.5rem;
  flex-shrink: 0;
  width: 94px;
}

/* Move Up on the first row / Move Down on the last are dead clicks, and
   the primary row's Remove is never clickable at all — all three use
   display:none (not visibility) so the remaining buttons in that row
   collapse together and slide left to fill the gap, rather than
   leaving isolated buttons floating with dead space around them.
   Covers the single-link case for free, since that row is
   simultaneously --primary and --last. Only the badge below still uses
   the visibility trick, since the URL/label columns starting the row
   are what actually benefit from staying aligned — the trailing
   buttons don't need to. */
.wishlist-link-row--primary .wishlist-link-row__move-up,
.wishlist-link-row--last .wishlist-link-row__move-down,
.wishlist-link-row--primary .wishlist-link-row__remove {
  display: none;
}

/* The primary badge always renders (even on non-primary rows, just
   hidden via visibility not display) so every row's URL/label inputs
   stay column-aligned regardless of which row currently holds it. */
.wishlist-link-row__primary-badge {
  flex-shrink: 0;
  font-size: 0.95rem;
  font-weight: 600;
  color: #c9a51c;
  white-space: nowrap;
  visibility: hidden;
}

.wishlist-link-row--primary .wishlist-link-row__primary-badge {
  visibility: visible;
}

/* With only one link, there's nothing for "Primary" to distinguish it
   from yet — stays hidden even though that lone row is still
   --primary underneath (Remove still needs to stay disabled on it).
   3-class selector so this reliably wins over the rule above
   regardless of source order, rather than relying on which one
   happens to come later in the file. */
.wishlist-link-rows--single .wishlist-link-row--primary .wishlist-link-row__primary-badge {
  visibility: hidden;
}

.wishlist-link-row__url {
  flex: 2;
  min-width: 0;
  padding: 0.6rem 0.9rem;
  border-radius: 8px;
  border: 1px solid rgba(44, 52, 43, 0.2);
  font-size: 1.2rem;
  font-family: "Mulish", sans-serif;
}

.wishlist-link-row__label {
  flex: 1;
  min-width: 0;
  padding: 0.6rem 0.9rem;
  border-radius: 8px;
  border: 1px solid rgba(44, 52, 43, 0.2);
  font-size: 1.2rem;
  font-family: "Mulish", sans-serif;
}

.wishlist-link-row__move-up,
.wishlist-link-row__move-down,
.wishlist-link-row__remove {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1px solid rgba(44, 52, 43, 0.2);
  background: #fff;
  color: #6b6555;
  cursor: pointer;
  font-size: 0.9rem;
}

.wishlist-link-row__remove:hover {
  border-color: #9e2525;
  color: #9e2525;
}

/* Same understated look as .btn-small--cancel, deliberately its own
   class rather than reusing that one — see the comment where this
   class is applied in site.js for why. */
.wishlist-add-link-row-btn {
  background: transparent;
  color: #6b6555;
  text-decoration: underline;
}

.wishlist-add-link-row-btn:hover {
  color: #2c342b;
}

/* Section accent colors — a small curated palette (tokens, not a raw
   hex picker; the backend's own validation, edit_wishlist_draft_
   section.php, already only accepts `^[a-z0-9-]{1,20}$`), coordinated
   with the site's existing gold/dark-green/cream look rather than
   generic primary colors. First pass applied this as a border-left
   stripe on the whole section; Xavier preferred the title bar itself
   (background + text) carry the color instead — this replaces that.
   Targets the header panel specifically (`:first-child`, not the
   entries panel below it) via a 3-class selector to beat
   `.background-mute`'s own single-class background-color. Title text,
   plus the drag-handle/edit-pencil icons (previously muted gray,
   #9a9384 — too low-contrast against a saturated background), switch
   to a light cream rather than pure white, matching the site's own
   off-white rather than a stark white that would clash with it. */
/* A section with no color chosen still gets a real, distinct header
   look — not the theme's plain .background-mute cream, which reads as
   barely different from the equally-pale .background-light body right
   below it (Xavier's own observation). Pale and warm, deliberately
   close in tone to the cream body rather than a bold saturated color
   like the real palette options below — just different enough to
   read as its own header rather than blending into it. Light enough
   that it needs dark text for contrast, same as gold below (the only
   other light-background color option) — .wishlist-section-title's
   own plain, unscoped default is already that same dark color, so
   there's nothing to override here for text, only the background.
   Lower specificity than each color-scoped rule below (plain
   .wishlist-section, not .wishlist-section--color-X), so a section
   that does have a color keeps overriding this exactly like before —
   this only ever shows through when none of those match. */
.wishlist-section .inner__block.background-mute.panel:first-child {
  background-color: #c4b399;
}
.wishlist-section--color-gold .inner__block.background-mute.panel:first-child {
  background-color: #c9a51c;
}
.wishlist-section--color-evergreen .inner__block.background-mute.panel:first-child {
  background-color: #3f5d3f;
}
.wishlist-section--color-cranberry .inner__block.background-mute.panel:first-child {
  background-color: #a13d42;
}
.wishlist-section--color-copper .inner__block.background-mute.panel:first-child {
  background-color: #b5651d;
}
.wishlist-section--color-slate .inner__block.background-mute.panel:first-child {
  background-color: #5b6b73;
}
.wishlist-section--color-plum .inner__block.background-mute.panel:first-child {
  background-color: #6b4a6b;
}
.wishlist-section--color-blueberry .inner__block.background-mute.panel:first-child {
  background-color: #5678a0;
}
.wishlist-section--color-mauve .inner__block.background-mute.panel:first-child {
  background-color: #b98a92;
}
.wishlist-section--color-teal .inner__block.background-mute.panel:first-child {
  background-color: #327d70;
}
.wishlist-section--color-evergreen .wishlist-section-title,
.wishlist-section--color-cranberry .wishlist-section-title,
.wishlist-section--color-copper .wishlist-section-title,
.wishlist-section--color-slate .wishlist-section-title,
.wishlist-section--color-plum .wishlist-section-title,
.wishlist-section--color-blueberry .wishlist-section-title,
.wishlist-section--color-teal .wishlist-section-title {
  color: #f5efe3;
}
.wishlist-section--color-evergreen .wishlist-section-drag-handle,
.wishlist-section--color-cranberry .wishlist-section-drag-handle,
.wishlist-section--color-copper .wishlist-section-drag-handle,
.wishlist-section--color-slate .wishlist-section-drag-handle,
.wishlist-section--color-plum .wishlist-section-drag-handle,
.wishlist-section--color-blueberry .wishlist-section-drag-handle,
.wishlist-section--color-teal .wishlist-section-drag-handle,
.wishlist-section--color-evergreen .wishlist-section-move-btn,
.wishlist-section--color-cranberry .wishlist-section-move-btn,
.wishlist-section--color-copper .wishlist-section-move-btn,
.wishlist-section--color-slate .wishlist-section-move-btn,
.wishlist-section--color-plum .wishlist-section-move-btn,
.wishlist-section--color-blueberry .wishlist-section-move-btn,
.wishlist-section--color-teal .wishlist-section-move-btn,
.wishlist-section--color-evergreen .wishlist-section-edit-btn,
.wishlist-section--color-cranberry .wishlist-section-edit-btn,
.wishlist-section--color-copper .wishlist-section-edit-btn,
.wishlist-section--color-slate .wishlist-section-edit-btn,
.wishlist-section--color-plum .wishlist-section-edit-btn,
.wishlist-section--color-blueberry .wishlist-section-edit-btn,
.wishlist-section--color-teal .wishlist-section-edit-btn {
  color: rgba(245, 239, 227, 0.75);
}
.wishlist-section--color-evergreen .wishlist-section-drag-handle:hover,
.wishlist-section--color-cranberry .wishlist-section-drag-handle:hover,
.wishlist-section--color-copper .wishlist-section-drag-handle:hover,
.wishlist-section--color-slate .wishlist-section-drag-handle:hover,
.wishlist-section--color-plum .wishlist-section-drag-handle:hover,
.wishlist-section--color-blueberry .wishlist-section-drag-handle:hover,
.wishlist-section--color-teal .wishlist-section-drag-handle:hover,
.wishlist-section--color-evergreen .wishlist-section-move-btn:not(:disabled):hover,
.wishlist-section--color-cranberry .wishlist-section-move-btn:not(:disabled):hover,
.wishlist-section--color-copper .wishlist-section-move-btn:not(:disabled):hover,
.wishlist-section--color-slate .wishlist-section-move-btn:not(:disabled):hover,
.wishlist-section--color-plum .wishlist-section-move-btn:not(:disabled):hover,
.wishlist-section--color-blueberry .wishlist-section-move-btn:not(:disabled):hover,
.wishlist-section--color-teal .wishlist-section-move-btn:not(:disabled):hover,
.wishlist-section--color-evergreen .wishlist-section-edit-btn:hover,
.wishlist-section--color-cranberry .wishlist-section-edit-btn:hover,
.wishlist-section--color-copper .wishlist-section-edit-btn:hover,
.wishlist-section--color-slate .wishlist-section-edit-btn:hover,
.wishlist-section--color-plum .wishlist-section-edit-btn:hover,
.wishlist-section--color-blueberry .wishlist-section-edit-btn:hover,
.wishlist-section--color-teal .wishlist-section-edit-btn:hover {
  color: #f5efe3;
}

/* Gold is noticeably lighter than the other five (a mid-tone yellow,
   not a deep jewel tone), so cream/off-white text on it reads as
   low-contrast the way it doesn't on the darker colors above — dark
   green instead, same pairing the site already uses for gold elsewhere
   (buttons, badges). */
.wishlist-section--color-gold .wishlist-section-title,
.wishlist-section--color-mauve .wishlist-section-title {
  color: #2c342b;
}
.wishlist-section--color-gold .wishlist-section-drag-handle,
.wishlist-section--color-gold .wishlist-section-move-btn,
.wishlist-section--color-gold .wishlist-section-edit-btn,
.wishlist-section--color-mauve .wishlist-section-drag-handle,
.wishlist-section--color-mauve .wishlist-section-move-btn,
.wishlist-section--color-mauve .wishlist-section-edit-btn {
  color: rgba(44, 52, 43, 0.65);
}
.wishlist-section--color-gold .wishlist-section-drag-handle:hover,
.wishlist-section--color-gold .wishlist-section-move-btn:not(:disabled):hover,
.wishlist-section--color-gold .wishlist-section-edit-btn:hover,
.wishlist-section--color-mauve .wishlist-section-drag-handle:hover,
.wishlist-section--color-mauve .wishlist-section-move-btn:not(:disabled):hover,
.wishlist-section--color-mauve .wishlist-section-edit-btn:hover {
  color: #2c342b;
}

/* The Edit Section form replaces the title bar in place (same colored
   panel — see the .wishlist-section-edit-btn click handler in site.js)
   — its own "Layout"/"Color" labels and Cancel
   button default to muted grays (#6b6555/#2c342b) tuned for the cream
   background they normally sit on, which clash once that background is
   a saturated accent color instead. Same cream/dark-green split as the
   title bar itself; Delete Section gets a lighter red
   (`#ff8a80`) rather than switching off red entirely — losing the
   "danger" color coding here would be worse than a contrast tweak, and
   this exact red-on-dark shade is already the site's own established
   pairing (see the auth form's own dark-background error text). Text
   inputs/selects are unaffected — they carry their own white box
   background regardless of the panel behind them, so their default
   dark text was never a contrast problem to begin with. */
.wishlist-section--color-evergreen .wishlist-section-edit-form .wishlist-entry-form__qty-label,
.wishlist-section--color-cranberry .wishlist-section-edit-form .wishlist-entry-form__qty-label,
.wishlist-section--color-copper .wishlist-section-edit-form .wishlist-entry-form__qty-label,
.wishlist-section--color-slate .wishlist-section-edit-form .wishlist-entry-form__qty-label,
.wishlist-section--color-plum .wishlist-section-edit-form .wishlist-entry-form__qty-label,
.wishlist-section--color-blueberry .wishlist-section-edit-form .wishlist-entry-form__qty-label,
.wishlist-section--color-teal .wishlist-section-edit-form .wishlist-entry-form__qty-label,
.wishlist-section--color-evergreen .wishlist-section-edit-form .wishlist-field-label,
.wishlist-section--color-cranberry .wishlist-section-edit-form .wishlist-field-label,
.wishlist-section--color-copper .wishlist-section-edit-form .wishlist-field-label,
.wishlist-section--color-slate .wishlist-section-edit-form .wishlist-field-label,
.wishlist-section--color-plum .wishlist-section-edit-form .wishlist-field-label,
.wishlist-section--color-blueberry .wishlist-section-edit-form .wishlist-field-label,
.wishlist-section--color-teal .wishlist-section-edit-form .wishlist-field-label,
.wishlist-section--color-evergreen .wishlist-section-edit-form .wishlist-layout-radio,
.wishlist-section--color-cranberry .wishlist-section-edit-form .wishlist-layout-radio,
.wishlist-section--color-copper .wishlist-section-edit-form .wishlist-layout-radio,
.wishlist-section--color-slate .wishlist-section-edit-form .wishlist-layout-radio,
.wishlist-section--color-plum .wishlist-section-edit-form .wishlist-layout-radio,
.wishlist-section--color-blueberry .wishlist-section-edit-form .wishlist-layout-radio,
.wishlist-section--color-teal .wishlist-section-edit-form .wishlist-layout-radio {
  color: #f5efe3;
}
.wishlist-section--color-gold .wishlist-section-edit-form .wishlist-entry-form__qty-label,
.wishlist-section--color-gold .wishlist-section-edit-form .wishlist-field-label,
.wishlist-section--color-gold .wishlist-section-edit-form .wishlist-layout-radio,
.wishlist-section--color-mauve .wishlist-section-edit-form .wishlist-entry-form__qty-label,
.wishlist-section--color-mauve .wishlist-section-edit-form .wishlist-field-label,
.wishlist-section--color-mauve .wishlist-section-edit-form .wishlist-layout-radio {
  color: #2c342b;
}

/* Cancel/Delete Section used to just be underlined text — reads fine
   with a mouse, but on a touch screen an underlined word is a much
   smaller, harder-to-aim target than a real button, and doesn't look
   tappable at a glance. Both get actual button shape now (border +
   a faint tint of their own color, not a solid fill — that's reserved
   for Save, so it stays the one "committing" action here), scoped to
   just these two forms via #wishlist-add-section-form/
   .wishlist-section-edit-form so nothing outside the section forms
   (comment Cancel, item editor Delete, etc.) is affected. Default
   (dark-on-light) styling here covers Tan/Gold/Mauve — the cream-on-
   dark override for the other seven colors follows below. */
#wishlist-add-section-form .btn-small--cancel,
.wishlist-section-edit-form .btn-small--cancel {
  background: rgba(44, 52, 43, 0.08);
  color: #2c342b;
  border: 1px solid rgba(44, 52, 43, 0.3);
  text-decoration: none;
}
#wishlist-add-section-form .btn-small--cancel:hover,
.wishlist-section-edit-form .btn-small--cancel:hover {
  background: rgba(44, 52, 43, 0.16);
}
.wishlist-section-edit-form .btn-small--delete {
  background: rgba(158, 37, 37, 0.08);
  color: #9e2525;
  border: 1px solid rgba(158, 37, 37, 0.35);
  text-decoration: none;
}
.wishlist-section-edit-form .btn-small--delete:hover {
  background: rgba(158, 37, 37, 0.16);
}
.wishlist-section--color-evergreen .wishlist-section-edit-form .btn-small--cancel,
.wishlist-section--color-cranberry .wishlist-section-edit-form .btn-small--cancel,
.wishlist-section--color-copper .wishlist-section-edit-form .btn-small--cancel,
.wishlist-section--color-slate .wishlist-section-edit-form .btn-small--cancel,
.wishlist-section--color-plum .wishlist-section-edit-form .btn-small--cancel,
.wishlist-section--color-blueberry .wishlist-section-edit-form .btn-small--cancel,
.wishlist-section--color-teal .wishlist-section-edit-form .btn-small--cancel {
  background: rgba(245, 239, 227, 0.12);
  color: #f5efe3;
  border-color: rgba(245, 239, 227, 0.4);
}
.wishlist-section--color-evergreen .wishlist-section-edit-form .btn-small--cancel:hover,
.wishlist-section--color-cranberry .wishlist-section-edit-form .btn-small--cancel:hover,
.wishlist-section--color-copper .wishlist-section-edit-form .btn-small--cancel:hover,
.wishlist-section--color-slate .wishlist-section-edit-form .btn-small--cancel:hover,
.wishlist-section--color-plum .wishlist-section-edit-form .btn-small--cancel:hover,
.wishlist-section--color-blueberry .wishlist-section-edit-form .btn-small--cancel:hover,
.wishlist-section--color-teal .wishlist-section-edit-form .btn-small--cancel:hover {
  background: rgba(245, 239, 227, 0.2);
}
.wishlist-section--color-evergreen .wishlist-section-edit-form .btn-small--delete,
.wishlist-section--color-cranberry .wishlist-section-edit-form .btn-small--delete,
.wishlist-section--color-copper .wishlist-section-edit-form .btn-small--delete,
.wishlist-section--color-slate .wishlist-section-edit-form .btn-small--delete,
.wishlist-section--color-plum .wishlist-section-edit-form .btn-small--delete,
.wishlist-section--color-blueberry .wishlist-section-edit-form .btn-small--delete,
.wishlist-section--color-teal .wishlist-section-edit-form .btn-small--delete,
.wishlist-section--color-gold .wishlist-section-edit-form .btn-small--delete,
.wishlist-section--color-mauve .wishlist-section-edit-form .btn-small--delete {
  background: rgba(255, 138, 128, 0.14);
  color: #ff8a80;
  border-color: rgba(255, 138, 128, 0.45);
}
.wishlist-section--color-evergreen .wishlist-section-edit-form .btn-small--delete:hover,
.wishlist-section--color-cranberry .wishlist-section-edit-form .btn-small--delete:hover,
.wishlist-section--color-copper .wishlist-section-edit-form .btn-small--delete:hover,
.wishlist-section--color-slate .wishlist-section-edit-form .btn-small--delete:hover,
.wishlist-section--color-plum .wishlist-section-edit-form .btn-small--delete:hover,
.wishlist-section--color-blueberry .wishlist-section-edit-form .btn-small--delete:hover,
.wishlist-section--color-teal .wishlist-section-edit-form .btn-small--delete:hover,
.wishlist-section--color-gold .wishlist-section-edit-form .btn-small--delete:hover,
.wishlist-section--color-mauve .wishlist-section-edit-form .btn-small--delete:hover {
  background: rgba(255, 138, 128, 0.22);
}

/* Add Section form — same live-preview idea as the Edit form above,
   but there's no rendered .wishlist-section to swap classes onto yet,
   so applyWishlistAddSectionColorPreview() (site.js) puts the
   .wishlist-section--color-X class directly on the panel itself. Same
   default-brown-first-then-per-color-override cascade as the real
   section header (see .wishlist-section .inner__block.background-mute
   .panel:first-child above) — "Default" isn't a blank/uncolored state
   here either. */
#wishlist-add-section-panel {
  background-color: #c4b399;
  /* Pinned to a known 30px rather than left at .inner__block's own
     (responsive, 9-10rem) default — 30px is what a real section's
     Edit form actually shows between its action row and its body
     panel below (that gap comes entirely from the header panel's own
     bottom padding there), so this matches it exactly on purpose. */
  padding: 30px 0;
}
#wishlist-add-section-panel.wishlist-section--color-gold {
  background-color: #c9a51c;
}
#wishlist-add-section-panel.wishlist-section--color-evergreen {
  background-color: #3f5d3f;
}
#wishlist-add-section-panel.wishlist-section--color-cranberry {
  background-color: #a13d42;
}
#wishlist-add-section-panel.wishlist-section--color-copper {
  background-color: #b5651d;
}
#wishlist-add-section-panel.wishlist-section--color-slate {
  background-color: #5b6b73;
}
#wishlist-add-section-panel.wishlist-section--color-plum {
  background-color: #6b4a6b;
}
#wishlist-add-section-panel.wishlist-section--color-blueberry {
  background-color: #5678a0;
}
#wishlist-add-section-panel.wishlist-section--color-mauve {
  background-color: #b98a92;
}
#wishlist-add-section-panel.wishlist-section--color-teal {
  background-color: #327d70;
}
#wishlist-add-section-panel .wishlist-field-label,
#wishlist-add-section-panel .wishlist-layout-radio {
  color: #2c342b;
}
#wishlist-add-section-panel.wishlist-section--color-evergreen .wishlist-field-label,
#wishlist-add-section-panel.wishlist-section--color-cranberry .wishlist-field-label,
#wishlist-add-section-panel.wishlist-section--color-copper .wishlist-field-label,
#wishlist-add-section-panel.wishlist-section--color-slate .wishlist-field-label,
#wishlist-add-section-panel.wishlist-section--color-plum .wishlist-field-label,
#wishlist-add-section-panel.wishlist-section--color-blueberry .wishlist-field-label,
#wishlist-add-section-panel.wishlist-section--color-teal .wishlist-field-label,
#wishlist-add-section-panel.wishlist-section--color-evergreen .wishlist-layout-radio,
#wishlist-add-section-panel.wishlist-section--color-cranberry .wishlist-layout-radio,
#wishlist-add-section-panel.wishlist-section--color-copper .wishlist-layout-radio,
#wishlist-add-section-panel.wishlist-section--color-slate .wishlist-layout-radio,
#wishlist-add-section-panel.wishlist-section--color-plum .wishlist-layout-radio,
#wishlist-add-section-panel.wishlist-section--color-blueberry .wishlist-layout-radio,
#wishlist-add-section-panel.wishlist-section--color-teal .wishlist-layout-radio {
  color: #f5efe3;
}
#wishlist-add-section-panel.wishlist-section--color-evergreen .btn-small--cancel,
#wishlist-add-section-panel.wishlist-section--color-cranberry .btn-small--cancel,
#wishlist-add-section-panel.wishlist-section--color-copper .btn-small--cancel,
#wishlist-add-section-panel.wishlist-section--color-slate .btn-small--cancel,
#wishlist-add-section-panel.wishlist-section--color-plum .btn-small--cancel,
#wishlist-add-section-panel.wishlist-section--color-blueberry .btn-small--cancel,
#wishlist-add-section-panel.wishlist-section--color-teal .btn-small--cancel {
  background: rgba(245, 239, 227, 0.12);
  color: #f5efe3;
  border-color: rgba(245, 239, 227, 0.4);
}
#wishlist-add-section-panel.wishlist-section--color-evergreen .btn-small--cancel:hover,
#wishlist-add-section-panel.wishlist-section--color-cranberry .btn-small--cancel:hover,
#wishlist-add-section-panel.wishlist-section--color-copper .btn-small--cancel:hover,
#wishlist-add-section-panel.wishlist-section--color-slate .btn-small--cancel:hover,
#wishlist-add-section-panel.wishlist-section--color-plum .btn-small--cancel:hover,
#wishlist-add-section-panel.wishlist-section--color-blueberry .btn-small--cancel:hover,
#wishlist-add-section-panel.wishlist-section--color-teal .btn-small--cancel:hover {
  background: rgba(245, 239, 227, 0.2);
}

/* Per-section "+ Add Item"/"+ Add Note" buttons (and the note-add
   form's own "Add Note" submit, same .btn-small--save/--add classes)
   match the section's own accent color instead of the fixed gold —
   Xavier's call, tying the whole section together visually. Scoped to
   .btn-small--save/--add *inside* a colored .wishlist-section, which
   naturally excludes the page-header's own global Add Item/Note row
   (not nested inside any .wishlist-section) and every other
   .btn-small--save usage on the site (comment Post, image picker,
   etc.) without needing extra exclusion rules — the Add Section
   form's own submit button is the one deliberate exception, since
   #wishlist-add-section-panel now carries this same color class too.
   Same cream/dark-green text pairing as the title bar. */
.wishlist-section--color-evergreen .btn-small--save,
.wishlist-section--color-evergreen .btn-small--add,
.wishlist-section--color-cranberry .btn-small--save,
.wishlist-section--color-cranberry .btn-small--add,
.wishlist-section--color-copper .btn-small--save,
.wishlist-section--color-copper .btn-small--add,
.wishlist-section--color-slate .btn-small--save,
.wishlist-section--color-slate .btn-small--add,
.wishlist-section--color-plum .btn-small--save,
.wishlist-section--color-plum .btn-small--add,
.wishlist-section--color-blueberry .btn-small--save,
.wishlist-section--color-blueberry .btn-small--add,
.wishlist-section--color-teal .btn-small--save,
.wishlist-section--color-teal .btn-small--add {
  color: #f5efe3;
}
.wishlist-section--color-gold .btn-small--save,
.wishlist-section--color-gold .btn-small--add,
.wishlist-section--color-mauve .btn-small--save,
.wishlist-section--color-mauve .btn-small--add {
  color: #2c342b;
}
.wishlist-section--color-gold .btn-small--save,
.wishlist-section--color-gold .btn-small--add {
  background: #c9a51c;
}
.wishlist-section--color-evergreen .btn-small--save,
.wishlist-section--color-evergreen .btn-small--add {
  background: #3f5d3f;
}
.wishlist-section--color-cranberry .btn-small--save,
.wishlist-section--color-cranberry .btn-small--add {
  background: #a13d42;
}
.wishlist-section--color-copper .btn-small--save,
.wishlist-section--color-copper .btn-small--add {
  background: #b5651d;
}
.wishlist-section--color-slate .btn-small--save,
.wishlist-section--color-slate .btn-small--add {
  background: #5b6b73;
}
.wishlist-section--color-plum .btn-small--save,
.wishlist-section--color-plum .btn-small--add {
  background: #6b4a6b;
}
.wishlist-section--color-blueberry .btn-small--save,
.wishlist-section--color-blueberry .btn-small--add {
  background: #5678a0;
}
.wishlist-section--color-mauve .btn-small--save,
.wishlist-section--color-mauve .btn-small--add {
  background: #b98a92;
}
.wishlist-section--color-teal .btn-small--save,
.wishlist-section--color-teal .btn-small--add {
  background: #327d70;
}

/* Save (Edit form) and Add Section (Add form) are the one place the
   rules just above actually broke instead of working as intended —
   every color's --save/--add background is the exact same hex as
   that color's own header panel (see .inner__block.background-mute
   .panel:first-child above; both numbers always match on purpose, so
   the real "+Add Item"/"+Add Note" buttons blend correctly into their
   section's identity). The Edit/Add-Section form panel *is* that same
   header background, so Save/Add Section rendered as a same-color
   fill on a same-color panel — a real button with zero edge contrast
   against its own background, invisible until :hover's fixed gold
   swap (unrelated to the panel color) suddenly broke the match and
   made it visible. Fixed by inverting Save/Add Section's fill
   relative to its own panel instead of matching it — dark panels
   (the seven saturated colors) get a solid cream pill, light panels
   (Tan/Gold/Mauve) get a solid dark pill — guaranteed contrast in
   every case, scoped to just these two forms so the correctly-working
   +Add Item/+Add Note treatment elsewhere is untouched. */
#wishlist-add-section-form .btn-small--save,
.wishlist-section-edit-form .btn-small--save {
  background: #2c342b;
  color: #f5efe3;
}
#wishlist-add-section-form .btn-small--save:hover,
.wishlist-section-edit-form .btn-small--save:hover {
  background: #1c221b;
}
.wishlist-section--color-evergreen .wishlist-section-edit-form .btn-small--save,
.wishlist-section--color-cranberry .wishlist-section-edit-form .btn-small--save,
.wishlist-section--color-copper .wishlist-section-edit-form .btn-small--save,
.wishlist-section--color-slate .wishlist-section-edit-form .btn-small--save,
.wishlist-section--color-plum .wishlist-section-edit-form .btn-small--save,
.wishlist-section--color-blueberry .wishlist-section-edit-form .btn-small--save,
.wishlist-section--color-teal .wishlist-section-edit-form .btn-small--save,
#wishlist-add-section-panel.wishlist-section--color-evergreen .btn-small--save,
#wishlist-add-section-panel.wishlist-section--color-cranberry .btn-small--save,
#wishlist-add-section-panel.wishlist-section--color-copper .btn-small--save,
#wishlist-add-section-panel.wishlist-section--color-slate .btn-small--save,
#wishlist-add-section-panel.wishlist-section--color-plum .btn-small--save,
#wishlist-add-section-panel.wishlist-section--color-blueberry .btn-small--save,
#wishlist-add-section-panel.wishlist-section--color-teal .btn-small--save {
  background: #f5efe3;
  color: #2c342b;
}
.wishlist-section--color-evergreen .wishlist-section-edit-form .btn-small--save:hover,
.wishlist-section--color-cranberry .wishlist-section-edit-form .btn-small--save:hover,
.wishlist-section--color-copper .wishlist-section-edit-form .btn-small--save:hover,
.wishlist-section--color-slate .wishlist-section-edit-form .btn-small--save:hover,
.wishlist-section--color-plum .wishlist-section-edit-form .btn-small--save:hover,
.wishlist-section--color-blueberry .wishlist-section-edit-form .btn-small--save:hover,
.wishlist-section--color-teal .wishlist-section-edit-form .btn-small--save:hover,
#wishlist-add-section-panel.wishlist-section--color-evergreen .btn-small--save:hover,
#wishlist-add-section-panel.wishlist-section--color-cranberry .btn-small--save:hover,
#wishlist-add-section-panel.wishlist-section--color-copper .btn-small--save:hover,
#wishlist-add-section-panel.wishlist-section--color-slate .btn-small--save:hover,
#wishlist-add-section-panel.wishlist-section--color-plum .btn-small--save:hover,
#wishlist-add-section-panel.wishlist-section--color-blueberry .btn-small--save:hover,
#wishlist-add-section-panel.wishlist-section--color-teal .btn-small--save:hover {
  background: #ffffff;
}

/* Content panel background — a subtle diagonal "candy cane" stripe,
   Xavier's own idea, refined to stay in the site's understated register
   rather than literal red/white candy-cane colors (which would only
   make sense for a red section and clash on gold/evergreen/etc). Each
   stripe pair is a pale tint of that section's own accent color
   (~25% accent mixed into the site's cream, #f5efe3) alternating with
   the plain cream itself — close enough in value that it reads as a
   soft woven texture, not a bold pattern. 135deg is a genuine diagonal
   (not a near-vertical/horizontal angle that would read as barely
   different from plain stripes) — Xavier's preferred direction, the
   mirror of the initial 45deg pass. 20px bands are wide enough to stay
   gentle at normal viewing distance rather than busy/noisy. */
/* A colorless section picking the Stripes pattern explicitly still
   needs a real stripe to show — same tint as the default header/
   pattern fallback (see --section-tint below and its own #e4dac9
   fallback). Positioned BEFORE the six color-scoped rules below, not
   after — this and every rule below it are equal specificity (both
   sides are a single class ancestor + 3-class descendant), so with
   equal specificity source order is what decides the winner; this has
   to come first so a section that does have a color still overrides
   it, the same ordering the header's own default-vs-color rules above
   already use correctly. (This one was ordered last for a while,
   which silently meant the six color rules below never actually won —
   every colored section's stripe rendered in this plain default tint
   regardless of its real color, until this got caught and reordered.) */
.wishlist-section .inner__block.background-light.panel {
  background-image: repeating-linear-gradient(135deg, #e4dac9 0, #e4dac9 20px, #f5efe3 20px, #f5efe3 40px);
}
.wishlist-section--color-gold .inner__block.background-light.panel {
  /* Was #ede2b1 — that only worked out to an ~18% mix of the gold
     accent into cream (the other five below land closer to 25-35%),
     landing too close to the plain cream body to actually read against
     it. Recomputed at ~30% for real contrast. */
  background-image: repeating-linear-gradient(135deg, #e8d9a7 0, #e8d9a7 20px, #f5efe3 20px, #f5efe3 40px);
}
/* Evergreen's tint doesn't follow the ~25%-accent ratio the other five
   use — the first pass at that ratio computed the mix wrong (used the
   accent's own R value in place of cream's B channel), landing on a
   near-equal-RGB gray that read as blueish rather than green. Recomputed
   correctly at a higher ~35% accent share (evergreen's R/G/B are close
   together to begin with, so it needs more weight than the other five
   to read as a clear hue rather than washing out to neutral). */
.wishlist-section--color-evergreen .inner__block.background-light.panel {
  background-image: repeating-linear-gradient(135deg, #b5bcaa 0, #b5bcaa 20px, #f5efe3 20px, #f5efe3 40px);
}
.wishlist-section--color-cranberry .inner__block.background-light.panel {
  background-image: repeating-linear-gradient(135deg, #e0c3bb 0, #e0c3bb 20px, #f5efe3 20px, #f5efe3 40px);
}
.wishlist-section--color-copper .inner__block.background-light.panel {
  background-image: repeating-linear-gradient(135deg, #e5cdb2 0, #e5cdb2 20px, #f5efe3 20px, #f5efe3 40px);
}
.wishlist-section--color-slate .inner__block.background-light.panel {
  background-image: repeating-linear-gradient(135deg, #cfcec7 0, #cfcec7 20px, #f5efe3 20px, #f5efe3 40px);
}
.wishlist-section--color-plum .inner__block.background-light.panel {
  background-image: repeating-linear-gradient(135deg, #d3c6d3 0, #d3c6d3 20px, #f5efe3 20px, #f5efe3 40px);
}
.wishlist-section--color-blueberry .inner__block.background-light.panel {
  background-image: repeating-linear-gradient(135deg, #c5cbcf 0, #c5cbcf 20px, #f5efe3 20px, #f5efe3 40px);
}
.wishlist-section--color-mauve .inner__block.background-light.panel {
  background-image: repeating-linear-gradient(135deg, #e0ccc7 0, #e0ccc7 20px, #f5efe3 20px, #f5efe3 40px);
}
.wishlist-section--color-teal .inner__block.background-light.panel {
  background-image: repeating-linear-gradient(135deg, #c4d3c6 0, #c4d3c6 20px, #f5efe3 20px, #f5efe3 40px);
}

/* The item detail page's own backdrop (#works itself, not any one
   panel) for an item in a non-default section — Xavier's actual ask:
   the plain cream that shows both around the whole card stack and in
   the gap between the header panel and the content panel below it is
   one and the same background (#works's own, per the shared .inner
   rule in main.css — .inner__content and #giftlist-item-view are both
   transparent, so it shows straight through wherever a panel doesn't
   cover it). Replacing it once here handles both spots at once, and
   leaves every panel's own fill (header, content, comments) completely
   untouched — that's the whole point Xavier corrected this to. Its own
   class, .giftlist-item-backdrop--X, rather than reusing
   .wishlist-section--color-X: that class is exactly what the header/
   body panel color rules above key off of (plain descendant selectors,
   no immediate-parent restriction), so the first pass at this — which
   did reuse it, on #works, a real ancestor of those panels — silently
   re-triggered those same rules and recolored the panels anyway, the
   opposite of what was asked. Scoped to #works specifically, not .inner
   generally — every other full-screen section keeps the plain cream
   backdrop. The class itself only ever lands on #works while the item
   page is actually showing (see
   renderGiftlistItemDetail/showGiftlistView/clearGiftlistItemBackdrop),
   so it can't bleed into the Gift Lists picker or list views that share
   this same #works. */
/* --backdrop-tint carries each color's tint to the pattern rules below
   (Dots, and every mask-based icon pattern), same split as
   .wishlist-section--color-X's own --section-tint just below this
   block — color and pattern are independent choices there and stay
   independent here too. The plain background-image here is the default
   pattern (diagonal stripe, unchanged from today's only look) — a
   .giftlist-item-backdrop-pattern--X class, when also present, wins
   over it further down in this file (equal specificity, later source
   order), same ordering rule the in-list section rules already use. */
/* A real (non-default) section with no color chosen — same tan/stripe
   fallback every in-list section already uses when colorless (see
   .wishlist-section .inner__block.background-mute.panel:first-child's
   own #c4b399 and the plain-stripe rule's own #e4dac9 tint further up
   this file), not the Unsorted section's own "untouched" look. Missing
   this was the actual bug Xavier found: a colorless real section
   ("My Section") was falling through to the same null-color check as
   Unsorted and getting no backdrop at all instead of this. */
#works.giftlist-item-backdrop--neutral {
  --backdrop-tint: #e4dac9;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #e4dac9 0, #e4dac9 20px, #f5efe3 20px, #f5efe3 40px);
}
#works.giftlist-item-backdrop--gold {
  --backdrop-tint: #e8d9a7;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #e8d9a7 0, #e8d9a7 20px, #f5efe3 20px, #f5efe3 40px);
}
#works.giftlist-item-backdrop--evergreen {
  --backdrop-tint: #b5bcaa;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #b5bcaa 0, #b5bcaa 20px, #f5efe3 20px, #f5efe3 40px);
}
#works.giftlist-item-backdrop--cranberry {
  --backdrop-tint: #e0c3bb;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #e0c3bb 0, #e0c3bb 20px, #f5efe3 20px, #f5efe3 40px);
}
#works.giftlist-item-backdrop--copper {
  --backdrop-tint: #e5cdb2;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #e5cdb2 0, #e5cdb2 20px, #f5efe3 20px, #f5efe3 40px);
}
#works.giftlist-item-backdrop--slate {
  --backdrop-tint: #cfcec7;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #cfcec7 0, #cfcec7 20px, #f5efe3 20px, #f5efe3 40px);
}
#works.giftlist-item-backdrop--plum {
  --backdrop-tint: #d3c6d3;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #d3c6d3 0, #d3c6d3 20px, #f5efe3 20px, #f5efe3 40px);
}
#works.giftlist-item-backdrop--blueberry {
  --backdrop-tint: #c5cbcf;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #c5cbcf 0, #c5cbcf 20px, #f5efe3 20px, #f5efe3 40px);
}
#works.giftlist-item-backdrop--mauve {
  --backdrop-tint: #e0ccc7;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #e0ccc7 0, #e0ccc7 20px, #f5efe3 20px, #f5efe3 40px);
}
#works.giftlist-item-backdrop--teal {
  --backdrop-tint: #c4d3c6;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #c4d3c6 0, #c4d3c6 20px, #f5efe3 20px, #f5efe3 40px);
}

/* Dots — same technique as .wishlist-section--pattern-dots, just
   targeting #works directly instead of a panel; no ::before overlay
   needed since a plain background-image doesn't touch #works's real
   children the way a mask would. */
#works.giftlist-item-backdrop-pattern--dots {
  background-color: #f5efe3;
  background-image: radial-gradient(circle, var(--backdrop-tint, #e4dac9) 8px, transparent 8.1px), radial-gradient(circle, var(--backdrop-tint, #e4dac9) 8px, transparent 8.1px);
  background-size: 42px 42px, 42px 42px;
  background-position: 0 0, 21px 21px;
}

/* Every other pattern (real shapes, not a plain gradient) — same
   ::before-overlay technique as the in-list icon patterns further down
   this file, and the same reason: mask-image tracks only alpha, so it
   lets one neutral SVG take on --backdrop-tint without a pre-colored
   copy per color, but applying a mask straight to #works would also
   mask its real children (every panel on this page) down to the
   pattern's shape — hence the overlay instead, sitting behind #works's
   actual content with #works's own inset:0 background still intact
   underneath it. #works is already position:absolute (see .inner in
   main.css), so it's already a valid positioning context for this
   without needing its own explicit `position` here — and unlike the
   panel version, this deliberately skips `overflow: hidden`, since
   #works relies on its own overflow-y:auto to let the page scroll;
   inset:0 already confines the overlay to #works's own fixed-height box
   regardless. */
#works.giftlist-item-backdrop-pattern--wavy-stripes,
#works.giftlist-item-backdrop-pattern--zigzag-stripes,
#works.giftlist-item-backdrop-pattern--stars,
#works.giftlist-item-backdrop-pattern--diamond-tiled,
#works.giftlist-item-backdrop-pattern--snowflakes,
#works.giftlist-item-backdrop-pattern--pine-trees,
#works.giftlist-item-backdrop-pattern--gift,
#works.giftlist-item-backdrop-pattern--deer {
  background-image: none;
  background-color: #f5efe3;
}
#works.giftlist-item-backdrop-pattern--wavy-stripes::before,
#works.giftlist-item-backdrop-pattern--zigzag-stripes::before,
#works.giftlist-item-backdrop-pattern--stars::before,
#works.giftlist-item-backdrop-pattern--diamond-tiled::before,
#works.giftlist-item-backdrop-pattern--snowflakes::before,
#works.giftlist-item-backdrop-pattern--pine-trees::before,
#works.giftlist-item-backdrop-pattern--gift::before,
#works.giftlist-item-backdrop-pattern--deer::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: var(--backdrop-tint, #e4dac9);
  pointer-events: none;
}
#works.giftlist-item-backdrop-pattern--wavy-stripes::before {
  -webkit-mask-image: url('../img/icons/wavy-stripes.svg');
  mask-image: url('../img/icons/wavy-stripes.svg');
  -webkit-mask-size: 52px 52px;
  mask-size: 52px 52px;
  -webkit-mask-repeat: repeat;
  mask-repeat: repeat;
}
#works.giftlist-item-backdrop-pattern--zigzag-stripes::before {
  -webkit-mask-image: url('../img/icons/zigzag-stripes.svg');
  mask-image: url('../img/icons/zigzag-stripes.svg');
  -webkit-mask-size: 52px 52px;
  mask-size: 52px 52px;
  -webkit-mask-repeat: repeat;
  mask-repeat: repeat;
}
#works.giftlist-item-backdrop-pattern--stars::before {
  -webkit-mask-image: url('../img/icons/stars-tile.svg'), url('../img/icons/stars-tile.svg');
  mask-image: url('../img/icons/stars-tile.svg'), url('../img/icons/stars-tile.svg');
  -webkit-mask-size: 60px 60px, 60px 60px;
  mask-size: 60px 60px, 60px 60px;
  -webkit-mask-position: 0 0, 30px 30px;
  mask-position: 0 0, 30px 30px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}
#works.giftlist-item-backdrop-pattern--diamond-tiled::before {
  -webkit-mask-image: url('../img/icons/diamond-tiled.svg'), url('../img/icons/diamond-tiled.svg');
  mask-image: url('../img/icons/diamond-tiled.svg'), url('../img/icons/diamond-tiled.svg');
  -webkit-mask-size: 42px 42px, 42px 42px;
  mask-size: 42px 42px, 42px 42px;
  -webkit-mask-position: 0 0, 21px 21px;
  mask-position: 0 0, 21px 21px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}
#works.giftlist-item-backdrop-pattern--snowflakes::before {
  -webkit-mask-image: url('../img/icons/snowflakes.svg'), url('../img/icons/snowflakes.svg');
  mask-image: url('../img/icons/snowflakes.svg'), url('../img/icons/snowflakes.svg');
  -webkit-mask-size: 52px 52px, 52px 52px;
  mask-size: 52px 52px, 52px 52px;
  -webkit-mask-position: 0 0, 26px 26px;
  mask-position: 0 0, 26px 26px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}
#works.giftlist-item-backdrop-pattern--pine-trees::before {
  -webkit-mask-image: url('../img/icons/pine-tree-tile.svg'), url('../img/icons/pine-tree-tile.svg');
  mask-image: url('../img/icons/pine-tree-tile.svg'), url('../img/icons/pine-tree-tile.svg');
  -webkit-mask-size: 79px 79px, 79px 79px;
  mask-size: 79px 79px, 79px 79px;
  -webkit-mask-position: 0 0, 39.5px 39.5px;
  mask-position: 0 0, 39.5px 39.5px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}
#works.giftlist-item-backdrop-pattern--gift::before {
  -webkit-mask-image: url('../img/icons/gift-tile.svg?v=3'), url('../img/icons/gift-tile.svg?v=3');
  mask-image: url('../img/icons/gift-tile.svg?v=3'), url('../img/icons/gift-tile.svg?v=3');
  -webkit-mask-size: 78px 78px, 78px 78px;
  mask-size: 78px 78px, 78px 78px;
  -webkit-mask-position: 0 0, 39px 39px;
  mask-position: 0 0, 39px 39px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}
#works.giftlist-item-backdrop-pattern--deer::before {
  -webkit-mask-image: url('../img/icons/deer-tile.svg'), url('../img/icons/deer-tile.svg');
  mask-image: url('../img/icons/deer-tile.svg'), url('../img/icons/deer-tile.svg');
  -webkit-mask-size: 90px 90px, 90px 90px;
  mask-size: 90px 90px, 90px 90px;
  -webkit-mask-position: 0 0, 45px 45px;
  mask-position: 0 0, 45px 45px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}

/* Xavier's own follow-up after seeing the backdrop-only version: the
   title panel should get the same solid-fill header treatment a real
   in-list section already has (see
   .wishlist-section--color-gold .inner__block.background-mute.panel
   :first-child above) after all — just with its own text/icon colors
   adjusted to match, which the in-list version never had to solve (its
   header only ever holds a bare title, this one also holds a back
   arrow and a report flag). Scoped to #giftlist-item-view's own header
   specifically (not reusing .wishlist-section--color-X, which would
   catch the content panel too — see the backdrop comment above for why
   that's the one thing this can't do), keyed off the same
   #works.giftlist-item-backdrop--X the backdrop already carries so no
   extra JS is needed. Same 9 fills as the in-list header, and the same
   7-of-9 dark colors get white text/icons — gold and mauve stay on the
   default dark text, exactly like their in-list counterparts, since
   both are light/mid-tone enough for it to stay readable. */
#works.giftlist-item-backdrop--neutral #giftlist-item-view .inner__block.background-mute.panel:first-child {
  background-color: #c4b399;
}
#works.giftlist-item-backdrop--gold #giftlist-item-view .inner__block.background-mute.panel:first-child {
  background-color: #c9a51c;
}
#works.giftlist-item-backdrop--evergreen #giftlist-item-view .inner__block.background-mute.panel:first-child {
  background-color: #3f5d3f;
}
#works.giftlist-item-backdrop--cranberry #giftlist-item-view .inner__block.background-mute.panel:first-child {
  background-color: #a13d42;
}
#works.giftlist-item-backdrop--copper #giftlist-item-view .inner__block.background-mute.panel:first-child {
  background-color: #b5651d;
}
#works.giftlist-item-backdrop--slate #giftlist-item-view .inner__block.background-mute.panel:first-child {
  background-color: #5b6b73;
}
#works.giftlist-item-backdrop--plum #giftlist-item-view .inner__block.background-mute.panel:first-child {
  background-color: #6b4a6b;
}
#works.giftlist-item-backdrop--blueberry #giftlist-item-view .inner__block.background-mute.panel:first-child {
  background-color: #5678a0;
}
#works.giftlist-item-backdrop--mauve #giftlist-item-view .inner__block.background-mute.panel:first-child {
  background-color: #b98a92;
}
#works.giftlist-item-backdrop--teal #giftlist-item-view .inner__block.background-mute.panel:first-child {
  background-color: #327d70;
}
#works.giftlist-item-backdrop--evergreen #giftlist-item-title,
#works.giftlist-item-backdrop--evergreen .giftlist-item-back,
#works.giftlist-item-backdrop--cranberry #giftlist-item-title,
#works.giftlist-item-backdrop--cranberry .giftlist-item-back,
#works.giftlist-item-backdrop--copper #giftlist-item-title,
#works.giftlist-item-backdrop--copper .giftlist-item-back,
#works.giftlist-item-backdrop--slate #giftlist-item-title,
#works.giftlist-item-backdrop--slate .giftlist-item-back,
#works.giftlist-item-backdrop--plum #giftlist-item-title,
#works.giftlist-item-backdrop--plum .giftlist-item-back,
#works.giftlist-item-backdrop--blueberry #giftlist-item-title,
#works.giftlist-item-backdrop--blueberry .giftlist-item-back,
#works.giftlist-item-backdrop--teal #giftlist-item-title,
#works.giftlist-item-backdrop--teal .giftlist-item-back {
  color: #f5efe3;
}

/* Same whole treatment as #works.giftlist-item-backdrop--gold above
   (backdrop pattern + solid-fill colored header + adapted text/icon
   color), just for the owner's own Wish List item pages instead of the
   browsing (Gift List) one — Xavier's call once he saw the Gift List
   version working: "might as well make it appear on the preview and
   wishlist item view pages" too. #wishlist is the shared ancestor for
   both (same reasoning as #works above: #wishlist-item-view and
   #wishlist-item-preview-view are sibling views of #wishlist-read-view,
   not descendants of it — applyWishlistFontPack()'s own comment on
   #wishlist already established this same "class has to sit high
   enough to cover every sibling view" pattern for the font pack).
   Editor/Preview's own Add/Edit forms and the read/grid views are
   deliberately untouched — only the item and preview pages get this. */
/* A real (non-default) section with no color chosen — same tan/stripe
   fallback as #works.giftlist-item-backdrop--neutral above (see that
   comment for the bug this fixes: a colorless real section, "My
   Section," was rendering with no backdrop at all instead of this). */
#wishlist.wishlist-item-backdrop--neutral {
  --backdrop-tint: #e4dac9;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #e4dac9 0, #e4dac9 20px, #f5efe3 20px, #f5efe3 40px);
}
#wishlist.wishlist-item-backdrop--gold {
  --backdrop-tint: #e8d9a7;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #e8d9a7 0, #e8d9a7 20px, #f5efe3 20px, #f5efe3 40px);
}
#wishlist.wishlist-item-backdrop--evergreen {
  --backdrop-tint: #b5bcaa;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #b5bcaa 0, #b5bcaa 20px, #f5efe3 20px, #f5efe3 40px);
}
#wishlist.wishlist-item-backdrop--cranberry {
  --backdrop-tint: #e0c3bb;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #e0c3bb 0, #e0c3bb 20px, #f5efe3 20px, #f5efe3 40px);
}
#wishlist.wishlist-item-backdrop--copper {
  --backdrop-tint: #e5cdb2;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #e5cdb2 0, #e5cdb2 20px, #f5efe3 20px, #f5efe3 40px);
}
#wishlist.wishlist-item-backdrop--slate {
  --backdrop-tint: #cfcec7;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #cfcec7 0, #cfcec7 20px, #f5efe3 20px, #f5efe3 40px);
}
#wishlist.wishlist-item-backdrop--plum {
  --backdrop-tint: #d3c6d3;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #d3c6d3 0, #d3c6d3 20px, #f5efe3 20px, #f5efe3 40px);
}
#wishlist.wishlist-item-backdrop--blueberry {
  --backdrop-tint: #c5cbcf;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #c5cbcf 0, #c5cbcf 20px, #f5efe3 20px, #f5efe3 40px);
}
#wishlist.wishlist-item-backdrop--mauve {
  --backdrop-tint: #e0ccc7;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #e0ccc7 0, #e0ccc7 20px, #f5efe3 20px, #f5efe3 40px);
}
#wishlist.wishlist-item-backdrop--teal {
  --backdrop-tint: #c4d3c6;
  background-color: transparent;
  background-image: repeating-linear-gradient(135deg, #c4d3c6 0, #c4d3c6 20px, #f5efe3 20px, #f5efe3 40px);
}

#wishlist.wishlist-item-backdrop-pattern--dots {
  background-color: #f5efe3;
  background-image: radial-gradient(circle, var(--backdrop-tint, #e4dac9) 8px, transparent 8.1px), radial-gradient(circle, var(--backdrop-tint, #e4dac9) 8px, transparent 8.1px);
  background-size: 42px 42px, 42px 42px;
  background-position: 0 0, 21px 21px;
}
#wishlist.wishlist-item-backdrop-pattern--wavy-stripes,
#wishlist.wishlist-item-backdrop-pattern--zigzag-stripes,
#wishlist.wishlist-item-backdrop-pattern--stars,
#wishlist.wishlist-item-backdrop-pattern--diamond-tiled,
#wishlist.wishlist-item-backdrop-pattern--snowflakes,
#wishlist.wishlist-item-backdrop-pattern--pine-trees,
#wishlist.wishlist-item-backdrop-pattern--gift,
#wishlist.wishlist-item-backdrop-pattern--deer {
  background-image: none;
  background-color: #f5efe3;
}
#wishlist.wishlist-item-backdrop-pattern--wavy-stripes::before,
#wishlist.wishlist-item-backdrop-pattern--zigzag-stripes::before,
#wishlist.wishlist-item-backdrop-pattern--stars::before,
#wishlist.wishlist-item-backdrop-pattern--diamond-tiled::before,
#wishlist.wishlist-item-backdrop-pattern--snowflakes::before,
#wishlist.wishlist-item-backdrop-pattern--pine-trees::before,
#wishlist.wishlist-item-backdrop-pattern--gift::before,
#wishlist.wishlist-item-backdrop-pattern--deer::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: var(--backdrop-tint, #e4dac9);
  pointer-events: none;
}
#wishlist.wishlist-item-backdrop-pattern--wavy-stripes::before {
  -webkit-mask-image: url('../img/icons/wavy-stripes.svg');
  mask-image: url('../img/icons/wavy-stripes.svg');
  -webkit-mask-size: 52px 52px;
  mask-size: 52px 52px;
  -webkit-mask-repeat: repeat;
  mask-repeat: repeat;
}
#wishlist.wishlist-item-backdrop-pattern--zigzag-stripes::before {
  -webkit-mask-image: url('../img/icons/zigzag-stripes.svg');
  mask-image: url('../img/icons/zigzag-stripes.svg');
  -webkit-mask-size: 52px 52px;
  mask-size: 52px 52px;
  -webkit-mask-repeat: repeat;
  mask-repeat: repeat;
}
#wishlist.wishlist-item-backdrop-pattern--stars::before {
  -webkit-mask-image: url('../img/icons/stars-tile.svg'), url('../img/icons/stars-tile.svg');
  mask-image: url('../img/icons/stars-tile.svg'), url('../img/icons/stars-tile.svg');
  -webkit-mask-size: 60px 60px, 60px 60px;
  mask-size: 60px 60px, 60px 60px;
  -webkit-mask-position: 0 0, 30px 30px;
  mask-position: 0 0, 30px 30px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}
#wishlist.wishlist-item-backdrop-pattern--diamond-tiled::before {
  -webkit-mask-image: url('../img/icons/diamond-tiled.svg'), url('../img/icons/diamond-tiled.svg');
  mask-image: url('../img/icons/diamond-tiled.svg'), url('../img/icons/diamond-tiled.svg');
  -webkit-mask-size: 42px 42px, 42px 42px;
  mask-size: 42px 42px, 42px 42px;
  -webkit-mask-position: 0 0, 21px 21px;
  mask-position: 0 0, 21px 21px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}
#wishlist.wishlist-item-backdrop-pattern--snowflakes::before {
  -webkit-mask-image: url('../img/icons/snowflakes.svg'), url('../img/icons/snowflakes.svg');
  mask-image: url('../img/icons/snowflakes.svg'), url('../img/icons/snowflakes.svg');
  -webkit-mask-size: 52px 52px, 52px 52px;
  mask-size: 52px 52px, 52px 52px;
  -webkit-mask-position: 0 0, 26px 26px;
  mask-position: 0 0, 26px 26px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}
#wishlist.wishlist-item-backdrop-pattern--pine-trees::before {
  -webkit-mask-image: url('../img/icons/pine-tree-tile.svg'), url('../img/icons/pine-tree-tile.svg');
  mask-image: url('../img/icons/pine-tree-tile.svg'), url('../img/icons/pine-tree-tile.svg');
  -webkit-mask-size: 79px 79px, 79px 79px;
  mask-size: 79px 79px, 79px 79px;
  -webkit-mask-position: 0 0, 39.5px 39.5px;
  mask-position: 0 0, 39.5px 39.5px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}
#wishlist.wishlist-item-backdrop-pattern--gift::before {
  -webkit-mask-image: url('../img/icons/gift-tile.svg?v=3'), url('../img/icons/gift-tile.svg?v=3');
  mask-image: url('../img/icons/gift-tile.svg?v=3'), url('../img/icons/gift-tile.svg?v=3');
  -webkit-mask-size: 78px 78px, 78px 78px;
  mask-size: 78px 78px, 78px 78px;
  -webkit-mask-position: 0 0, 39px 39px;
  mask-position: 0 0, 39px 39px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}
#wishlist.wishlist-item-backdrop-pattern--deer::before {
  -webkit-mask-image: url('../img/icons/deer-tile.svg'), url('../img/icons/deer-tile.svg');
  mask-image: url('../img/icons/deer-tile.svg'), url('../img/icons/deer-tile.svg');
  -webkit-mask-size: 90px 90px, 90px 90px;
  mask-size: 90px 90px, 90px 90px;
  -webkit-mask-position: 0 0, 45px 45px;
  mask-position: 0 0, 45px 45px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}

#wishlist.wishlist-item-backdrop--neutral #wishlist-item-view .inner__block.background-mute.panel:first-child,
#wishlist.wishlist-item-backdrop--neutral #wishlist-item-preview-view .inner__block.background-mute.panel:first-child {
  background-color: #c4b399;
}
#wishlist.wishlist-item-backdrop--gold #wishlist-item-view .inner__block.background-mute.panel:first-child,
#wishlist.wishlist-item-backdrop--gold #wishlist-item-preview-view .inner__block.background-mute.panel:first-child {
  background-color: #c9a51c;
}
#wishlist.wishlist-item-backdrop--evergreen #wishlist-item-view .inner__block.background-mute.panel:first-child,
#wishlist.wishlist-item-backdrop--evergreen #wishlist-item-preview-view .inner__block.background-mute.panel:first-child {
  background-color: #3f5d3f;
}
#wishlist.wishlist-item-backdrop--cranberry #wishlist-item-view .inner__block.background-mute.panel:first-child,
#wishlist.wishlist-item-backdrop--cranberry #wishlist-item-preview-view .inner__block.background-mute.panel:first-child {
  background-color: #a13d42;
}
#wishlist.wishlist-item-backdrop--copper #wishlist-item-view .inner__block.background-mute.panel:first-child,
#wishlist.wishlist-item-backdrop--copper #wishlist-item-preview-view .inner__block.background-mute.panel:first-child {
  background-color: #b5651d;
}
#wishlist.wishlist-item-backdrop--slate #wishlist-item-view .inner__block.background-mute.panel:first-child,
#wishlist.wishlist-item-backdrop--slate #wishlist-item-preview-view .inner__block.background-mute.panel:first-child {
  background-color: #5b6b73;
}
#wishlist.wishlist-item-backdrop--plum #wishlist-item-view .inner__block.background-mute.panel:first-child,
#wishlist.wishlist-item-backdrop--plum #wishlist-item-preview-view .inner__block.background-mute.panel:first-child {
  background-color: #6b4a6b;
}
#wishlist.wishlist-item-backdrop--blueberry #wishlist-item-view .inner__block.background-mute.panel:first-child,
#wishlist.wishlist-item-backdrop--blueberry #wishlist-item-preview-view .inner__block.background-mute.panel:first-child {
  background-color: #5678a0;
}
#wishlist.wishlist-item-backdrop--mauve #wishlist-item-view .inner__block.background-mute.panel:first-child,
#wishlist.wishlist-item-backdrop--mauve #wishlist-item-preview-view .inner__block.background-mute.panel:first-child {
  background-color: #b98a92;
}
#wishlist.wishlist-item-backdrop--teal #wishlist-item-view .inner__block.background-mute.panel:first-child,
#wishlist.wishlist-item-backdrop--teal #wishlist-item-preview-view .inner__block.background-mute.panel:first-child {
  background-color: #327d70;
}
#wishlist.wishlist-item-backdrop--evergreen #wishlist-item-view-title,
#wishlist.wishlist-item-backdrop--evergreen #wishlist-item-preview-title,
#wishlist.wishlist-item-backdrop--evergreen .wishlist-item-view-back,
#wishlist.wishlist-item-backdrop--evergreen .wishlist-item-preview-back,
#wishlist.wishlist-item-backdrop--cranberry #wishlist-item-view-title,
#wishlist.wishlist-item-backdrop--cranberry #wishlist-item-preview-title,
#wishlist.wishlist-item-backdrop--cranberry .wishlist-item-view-back,
#wishlist.wishlist-item-backdrop--cranberry .wishlist-item-preview-back,
#wishlist.wishlist-item-backdrop--copper #wishlist-item-view-title,
#wishlist.wishlist-item-backdrop--copper #wishlist-item-preview-title,
#wishlist.wishlist-item-backdrop--copper .wishlist-item-view-back,
#wishlist.wishlist-item-backdrop--copper .wishlist-item-preview-back,
#wishlist.wishlist-item-backdrop--slate #wishlist-item-view-title,
#wishlist.wishlist-item-backdrop--slate #wishlist-item-preview-title,
#wishlist.wishlist-item-backdrop--slate .wishlist-item-view-back,
#wishlist.wishlist-item-backdrop--slate .wishlist-item-preview-back,
#wishlist.wishlist-item-backdrop--plum #wishlist-item-view-title,
#wishlist.wishlist-item-backdrop--plum #wishlist-item-preview-title,
#wishlist.wishlist-item-backdrop--plum .wishlist-item-view-back,
#wishlist.wishlist-item-backdrop--plum .wishlist-item-preview-back,
#wishlist.wishlist-item-backdrop--blueberry #wishlist-item-view-title,
#wishlist.wishlist-item-backdrop--blueberry #wishlist-item-preview-title,
#wishlist.wishlist-item-backdrop--blueberry .wishlist-item-view-back,
#wishlist.wishlist-item-backdrop--blueberry .wishlist-item-preview-back,
#wishlist.wishlist-item-backdrop--teal #wishlist-item-view-title,
#wishlist.wishlist-item-backdrop--teal #wishlist-item-preview-title,
#wishlist.wishlist-item-backdrop--teal .wishlist-item-view-back,
#wishlist.wishlist-item-backdrop--teal .wishlist-item-preview-back {
  color: #f5efe3;
}

/* Same six tints as the repeating-linear-gradient rules just above,
   exposed as a custom property rather than repeated in each one — the
   pattern rules further down (Dots, and every mask-based icon pattern)
   read this instead of hardcoding their own copy of the palette, so
   color and pattern stay two genuinely independent choices instead of
   a 6-color-times-9-pattern matrix of rules. Purely additive: the
   diagonal-stripe rules above never reference this var, so today's
   only pattern is completely unchanged by adding it. */
.wishlist-section--color-gold { --section-tint: #e8d9a7; }
.wishlist-section--color-evergreen { --section-tint: #b5bcaa; }
.wishlist-section--color-cranberry { --section-tint: #e0c3bb; }
.wishlist-section--color-copper { --section-tint: #e5cdb2; }
.wishlist-section--color-slate { --section-tint: #cfcec7; }
.wishlist-section--color-plum { --section-tint: #d3c6d3; }
.wishlist-section--color-blueberry { --section-tint: #c5cbcf; }
.wishlist-section--color-mauve { --section-tint: #e0ccc7; }
.wishlist-section--color-teal { --section-tint: #c4d3c6; }

/* Layout picker — Add/Edit Section forms. Real radio buttons rather
   than a <select> (showing both options at once beats making someone
   open a menu to see what the choices even are) or the button-toggle
   this replaced (a filled/unfilled background didn't read as clearly
   "this one's picked" as an actual radio dot does — Xavier's own
   call). accent-color re-themes the browser's native radio dot/ring
   in the site's gold rather than hand-drawing a custom one — modest
   size bump (1.3em, up from a native radio's usual ~1em) since this
   is a deliberate, stand-alone choice in the form, not a dense list
   of options where the default size is more appropriate. */
.wishlist-layout-radios {
  display: flex;
  gap: 1.4rem;
  margin: 0.2rem 0;
}
.wishlist-layout-radio {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  font-size: 1.3rem;
  color: #2c342b;
}
.wishlist-layout-radio-icon {
  flex-shrink: 0;
}
.wishlist-layout-radio input[type="radio"] {
  /* main.css's blanket `form input { display:block; width:100% }`
     otherwise stretches this into a full-width block box (same
     leaking-generic-rule gotcha the anonymous-checkbox alignment fix
     hit earlier this project) — needs an explicit reset, not just a
     size declared on top of it. Custom-built (appearance:none) rather
     than the native control's own accent-color styling, so its size
     matches the color/pattern swatches exactly — a plain white circle,
     gold dot on selection, no ring (an earlier white-ring version blew
     up into an oval; box-sizing/em-relative sizing on a native <input>
     wasn't as reliable as the fixed-rem sizing the swatch <button>s
     use, so this copies that instead). */
  -webkit-appearance: none;
  appearance: none;
  display: inline-block;
  box-sizing: border-box;
  width: 2.75rem;
  height: 2.75rem;
  margin: 0;
  padding: 0;
  border: none;
  border-radius: 50%;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12);
  background-color: #ffffff;
  background-repeat: no-repeat;
  cursor: pointer;
  flex-shrink: 0;
  vertical-align: middle;
}
.wishlist-layout-radio input[type="radio"]:checked {
  /* A ::after dot (the first version of this) never quite centered —
     <input> is a replaced element and browsers are inconsistent about
     generated content's box inside one. A background-image has no
     such ambiguity: it fills the element's own box exactly, so the
     dot is centered by construction rather than by position math. */
  background-image: radial-gradient(circle, #c9a51c 0, #c9a51c 40%, transparent 41%);
}

/* Swatch picker — Add/Edit Section forms. Each swatch is a small solid
   circle in its own color; the selected one gets a ring so it reads as
   chosen without needing a checkmark glyph. "None" is a dashed-outline
   circle (no fill) rather than a 7th color, so clearing the accent is
   as visually obvious a choice as picking one. */
.wishlist-color-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 0.2rem 0;
}
.wishlist-color-swatch {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  border: 2px solid transparent;
  padding: 0;
  cursor: pointer;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12);
}
.wishlist-color-swatch.is-selected {
  border-color: #ffffff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12), 0 0 0 3px rgba(255, 255, 255, 0.6);
}
/* No accent color — shown as the site's own plain cream, the real
   color an uncolored section actually renders in, rather than a
   dashed "empty" circle. Same solid-border treatment as every other
   swatch now (see .wishlist-pattern-swatch--default's own comment for
   the matching change there). */
/* Matches the real default header color (see .wishlist-section
   .inner__block.background-mute.panel:first-child above) rather than
   the plain page cream — the swatch should preview what "no color"
   actually looks like now that it has its own real, distinct look. */
.wishlist-color-swatch--none {
  background: #c4b399;
}
.wishlist-color-swatch--gold {
  background: #c9a51c;
}
.wishlist-color-swatch--evergreen {
  background: #3f5d3f;
}
.wishlist-color-swatch--cranberry {
  background: #a13d42;
}
.wishlist-color-swatch--copper {
  background: #b5651d;
}
.wishlist-color-swatch--slate {
  background: #5b6b73;
}
.wishlist-color-swatch--plum {
  background: #6b4a6b;
}
.wishlist-color-swatch--blueberry {
  background: #5678a0;
}
.wishlist-color-swatch--mauve {
  background: #b98a92;
}
.wishlist-color-swatch--teal {
  background: #327d70;
}

/* Pattern swatches — same shape/behavior as the color swatches above
   (circle, selected ring, delegated click handler in site.js), but each
   one previews the real pattern tile instead of a flat color, since the
   pattern itself is the whole point of choosing one. Always rendered in
   a fixed neutral gold regardless of whatever color is chosen alongside
   it, purely so the picker itself doesn't need to re-render every time
   color changes. */
.wishlist-pattern-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin: 0.2rem 0;
}
.wishlist-pattern-swatch {
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  border: 2px solid transparent;
  padding: 0;
  cursor: pointer;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12);
  /* White, not the page's cream — the gold icon/shape needs real
     contrast to actually read at this size, and white gives it that
     without competing with the icon's own color the way cream (closer
     in tone to gold) did. */
  background-color: #ffffff;
}
.wishlist-pattern-swatch.is-selected {
  border-color: #ffffff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12), 0 0 0 3px rgba(255, 255, 255, 0.6);
}
/* Stripes/Wavy Stripes/Zigzag Stripes ("Line" patterns) — these three
   draw their preview from a real inline <svg> child (see
   renderWishlistPatternSwatchesHtml() in site.js) rather than a CSS
   mask-image like every other swatch below still uses. CSS masking
   turned out to render inconsistently across real browsers in a way
   this session couldn't reproduce or resolve through encoding changes
   alone (percent-encoded, then base64 — same result either way),
   while plain inline SVG is unambiguous, ordinary markup with no such
   history. Centering is just flex, no mask-position math needed. */
.wishlist-pattern-swatch--default,
.wishlist-pattern-swatch--wavy-stripes,
.wishlist-pattern-swatch--zigzag-stripes {
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.wishlist-pattern-swatch--default {
  background-color: #ffffff;
}
.wishlist-pattern-swatch--default svg,
.wishlist-pattern-swatch--wavy-stripes svg,
.wishlist-pattern-swatch--zigzag-stripes svg {
  display: block;
  pointer-events: none;
}
/* A line pattern reads right going edge-to-edge across the whole
   circle (Xavier's call) rather than sitting small and centered like
   a bounded icon, so these three size noticeably larger than the icon
   swatches below — close to the swatch's own diameter, since a
   diagonal chord through the center of a circle is exactly as long as
   the diameter, same as a horizontal or vertical one. */
.wishlist-pattern-swatch--default svg {
  width: 46px;
  height: 46px;
}
.wishlist-pattern-swatch--wavy-stripes svg,
.wishlist-pattern-swatch--zigzag-stripes svg {
  width: 44px;
  height: 44px;
}
/* Same mask-image + background-color technique as the real pattern
   rendering further down (not a CSS `filter` guess at turning black
   into gold — that produced a muddy brown that never actually matched
   the site's real #c9a51c, clashing with the true-color swatches right
   next to these). A mask only cares about the source SVG's alpha, so
   this is a pixel-exact match to the site's own gold, guaranteed.
   Applied to a ::before overlay, not the button itself — masking the
   button directly also masked away its own white circular background
   outside the icon shape, so on a colored surface (the edit form for a
   section that already has an accent color, which themes its own
   background) the section's own color showed through the gaps instead
   of white, making the swatch look tinted by whatever's behind it
   rather than a clean icon-on-white preview. */
.wishlist-pattern-swatch--stars,
.wishlist-pattern-swatch--diamond-tiled,
.wishlist-pattern-swatch--snowflakes,
.wishlist-pattern-swatch--pine-trees,
.wishlist-pattern-swatch--gift,
.wishlist-pattern-swatch--deer {
  position: relative;
  overflow: hidden;
}
.wishlist-pattern-swatch--stars::before,
.wishlist-pattern-swatch--diamond-tiled::before,
.wishlist-pattern-swatch--snowflakes::before,
.wishlist-pattern-swatch--pine-trees::before,
.wishlist-pattern-swatch--gift::before,
.wishlist-pattern-swatch--deer::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: #c9a51c;
  pointer-events: none;
  /* One crisp centered icon per swatch, not the tiled repeat the real
     pattern rendering uses — mask-repeat's initial value is `repeat`,
     same as background-repeat, so this needs to be explicit. */
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
}
/* One centered dot, not the tiled offset pair the real Dots pattern
   uses — same "single instance of the shape" treatment as every other
   swatch here. No background-size/repeat needed: an un-sized
   radial-gradient already covers the whole element as one gradient. */
.wishlist-pattern-swatch--dots {
  background-image: radial-gradient(circle, #c9a51c 7px, transparent 7.1px);
}
.wishlist-pattern-swatch--stars::before {
  -webkit-mask-image: url('../img/icons/stars.svg?v=2');
  mask-image: url('../img/icons/stars.svg?v=2');
  /* Bigger box than the others here on purpose — stars.svg's actual
     star shape only fills about a third of its own viewBox (real
     margin baked in for the tiled pattern's spacing), so matching the
     other swatches' mask-size left the visible star reading noticeably
     smaller than they do. */
  -webkit-mask-size: 62px 62px;
  mask-size: 62px 62px;
}
.wishlist-pattern-swatch--diamond-tiled::before {
  -webkit-mask-image: url('../img/icons/diamond-tiled.svg');
  mask-image: url('../img/icons/diamond-tiled.svg');
  -webkit-mask-size: 26px 26px;
  mask-size: 26px 26px;
}
.wishlist-pattern-swatch--snowflakes::before {
  -webkit-mask-image: url('../img/icons/snowflakes.svg');
  mask-image: url('../img/icons/snowflakes.svg');
  -webkit-mask-size: 30px 30px;
  mask-size: 30px 30px;
}
.wishlist-pattern-swatch--pine-trees::before {
  -webkit-mask-image: url('../img/icons/pine-tree.svg');
  mask-image: url('../img/icons/pine-tree.svg');
  /* Public-domain replacement (svgrepo.com) for the earlier hand-drawn
     pine-trees.svg — already well-centered in its own viewBox on both
     axes, so no custom mask-position nudge needed like the old one
     required. Bigger than the plain-shape swatches (Dots/Diamond) for
     the same reason as Stars: its own artwork still only fills roughly
     half its viewBox, so matching their mask-size would read small. */
  -webkit-mask-size: 38px 38px;
  mask-size: 38px 38px;
}
.wishlist-pattern-swatch--gift::before {
  -webkit-mask-image: url('../img/icons/gift.svg');
  mask-image: url('../img/icons/gift.svg');
  -webkit-mask-size: 20px 20px;
  mask-size: 20px 20px;
}
.wishlist-pattern-swatch--deer::before {
  -webkit-mask-image: url('../img/icons/deer.svg');
  mask-image: url('../img/icons/deer.svg');
  -webkit-mask-size: 38px 38px;
  mask-size: 38px 38px;
}

/* ---------- Section background patterns ----------
   Dots is a plain background-image, same technique as the diagonal-
   stripe rules above it, just a radial-gradient instead of a linear
   one — it composites directly over the panel's own background-color,
   which needs to be set explicitly here (see the icon patterns' own
   comment further down for why): .background-light's default is plain
   white, and unlike the diagonal stripe's own gradient, this one
   leaves real gaps between dots that would otherwise show that raw
   white instead of the site's usual cream. */
.wishlist-section--pattern-dots .inner__block.background-light.panel {
  background-color: #f5efe3;
  background-image: radial-gradient(circle, var(--section-tint, #e4dac9) 8px, transparent 8.1px), radial-gradient(circle, var(--section-tint, #e4dac9) 8px, transparent 8.1px);
  background-size: 42px 42px, 42px 42px;
  background-position: 0 0, 21px 21px;
}

/* Every other new pattern is a real shape (a wavy/zigzag band, or a
   tiled icon) rather than something a CSS gradient alone can draw, so
   it's built from the plain black SVGs in img/icons/ via a CSS mask —
   mask-image tracks only a source image's alpha, never its own color,
   which is what actually lets one neutral asset take on whichever
   color a section's own --section-tint resolves to, without needing a
   pre-colored copy per palette color (color and pattern staying two
   independent choices, not a color-times-pattern matrix of image
   files). Applying the mask straight to the panel itself would also
   mask its real children (the item/note cards inside it) down to the
   pattern's shape, which is why this is a ::before overlay instead: a
   plain absolutely-positioned layer carrying the color+mask, sitting
   behind the panel's actual content, with the panel's own cream
   background still fully intact underneath it. */
.wishlist-section--pattern-wavy-stripes .inner__block.background-light.panel,
.wishlist-section--pattern-zigzag-stripes .inner__block.background-light.panel,
.wishlist-section--pattern-stars .inner__block.background-light.panel,
.wishlist-section--pattern-diamond-tiled .inner__block.background-light.panel,
.wishlist-section--pattern-snowflakes .inner__block.background-light.panel,
.wishlist-section--pattern-pine-trees .inner__block.background-light.panel,
.wishlist-section--pattern-gift .inner__block.background-light.panel,
.wishlist-section--pattern-deer .inner__block.background-light.panel {
  position: relative;
  overflow: hidden;
  /* Clears whatever the color-only diagonal-stripe rule further up put
     here — same specificity, same element, but this comes later in the
     file, so it wins. Without it, the panel's own plain-stripe
     background-image stayed underneath the ::before overlay below,
     showing both patterns stacked on top of each other at once. */
  background-image: none;
  /* .background-light's own background-color is plain white (main.css)
     — the diagonal-stripe pattern never exposed that, since its
     gradient fully covers the panel with its own cream "gap" color on
     top of it. These icon patterns leave real gaps between shapes, so
     without this they showed raw white through those gaps instead of
     the site's usual cream. */
  background-color: #f5efe3;
}
.wishlist-section--pattern-wavy-stripes .inner__block.background-light.panel::before,
.wishlist-section--pattern-zigzag-stripes .inner__block.background-light.panel::before,
.wishlist-section--pattern-stars .inner__block.background-light.panel::before,
.wishlist-section--pattern-diamond-tiled .inner__block.background-light.panel::before,
.wishlist-section--pattern-snowflakes .inner__block.background-light.panel::before,
.wishlist-section--pattern-pine-trees .inner__block.background-light.panel::before,
.wishlist-section--pattern-gift .inner__block.background-light.panel::before,
.wishlist-section--pattern-deer .inner__block.background-light.panel::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: var(--section-tint, #e4dac9);
  pointer-events: none;
}
.wishlist-section--pattern-wavy-stripes .inner__block.background-light.panel::before {
  -webkit-mask-image: url('../img/icons/wavy-stripes.svg');
  mask-image: url('../img/icons/wavy-stripes.svg');
  -webkit-mask-size: 52px 52px;
  mask-size: 52px 52px;
  -webkit-mask-repeat: repeat;
  mask-repeat: repeat;
}
.wishlist-section--pattern-zigzag-stripes .inner__block.background-light.panel::before {
  -webkit-mask-image: url('../img/icons/zigzag-stripes.svg');
  mask-image: url('../img/icons/zigzag-stripes.svg');
  -webkit-mask-size: 52px 52px;
  mask-size: 52px 52px;
  -webkit-mask-repeat: repeat;
  mask-repeat: repeat;
}
/* Stars/Diamond Tiled/Snowflakes/Pine Trees all use the same two-layer
   offset-tile trick — the second mask layer is the identical image,
   just shifted half a tile in both directions, which is what gives
   these their brick/staggered look instead of a plain grid. Tile sizes
   here are ~1.3x the icons' own natural size (see img/icons/*.svg) —
   scaling the whole tile uniformly like this, rather than just the
   icon within a fixed tile, is what keeps the icon-to-gap ratio (the
   "relative spacing") the same as the icon grows, instead of making it
   relatively more cramped. */
.wishlist-section--pattern-stars .inner__block.background-light.panel::before {
  /* Uses the tighter-cropped stars-tile.svg, not the swatch icon's own
     stars.svg — same star, just a smaller viewBox around it (24px vs
     32px) so the tile pitch (and the gap between copies) shrinks while
     the star itself stays the same rendered size, same margin-
     adjustment idea as snowflake-tile.svg/gift-tile.svg, just going
     the other direction (less padding instead of more). */
  -webkit-mask-image: url('../img/icons/stars-tile.svg'), url('../img/icons/stars-tile.svg');
  mask-image: url('../img/icons/stars-tile.svg'), url('../img/icons/stars-tile.svg');
  -webkit-mask-size: 60px 60px, 60px 60px;
  mask-size: 60px 60px, 60px 60px;
  -webkit-mask-position: 0 0, 30px 30px;
  mask-position: 0 0, 30px 30px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}
.wishlist-section--pattern-diamond-tiled .inner__block.background-light.panel::before {
  -webkit-mask-image: url('../img/icons/diamond-tiled.svg'), url('../img/icons/diamond-tiled.svg');
  mask-image: url('../img/icons/diamond-tiled.svg'), url('../img/icons/diamond-tiled.svg');
  -webkit-mask-size: 42px 42px, 42px 42px;
  mask-size: 42px 42px, 42px 42px;
  -webkit-mask-position: 0 0, 21px 21px;
  mask-position: 0 0, 21px 21px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}
.wishlist-section--pattern-snowflakes .inner__block.background-light.panel::before {
  -webkit-mask-image: url('../img/icons/snowflakes.svg'), url('../img/icons/snowflakes.svg');
  mask-image: url('../img/icons/snowflakes.svg'), url('../img/icons/snowflakes.svg');
  -webkit-mask-size: 52px 52px, 52px 52px;
  mask-size: 52px 52px, 52px 52px;
  -webkit-mask-position: 0 0, 26px 26px;
  mask-position: 0 0, 26px 26px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}
.wishlist-section--pattern-pine-trees .inner__block.background-light.panel::before {
  /* Uses the padded pine-tree-tile.svg, not the swatch icon's own
     pine-tree.svg — same tree, wrapped (via a nested <svg>, so its own
     viewBox didn't need recomputing) in a larger canvas with real
     margin baked in, same technique as deer-tile.svg. */
  -webkit-mask-image: url('../img/icons/pine-tree-tile.svg'), url('../img/icons/pine-tree-tile.svg');
  mask-image: url('../img/icons/pine-tree-tile.svg'), url('../img/icons/pine-tree-tile.svg');
  -webkit-mask-size: 79px 79px, 79px 79px;
  mask-size: 79px 79px, 79px 79px;
  -webkit-mask-position: 0 0, 39.5px 39.5px;
  mask-position: 0 0, 39.5px 39.5px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}
/* Same two-layer offset trick as Stars/Diamond Tiled/Snowflakes/Pine
   Trees above — a single-layer tile here rendered as a rigid, perfectly
   aligned grid instead of the staggered brick look every other icon
   pattern has, which read as visually inconsistent with the rest of
   the set. deer.svg's own viewBox already has generous built-in
   padding (the raw traced shape originally filled almost its entire
   viewBox with no margin at all, which is why even this offset needs a
   noticeably larger tile than the other patterns to keep the two
   copies from touching). */
.wishlist-section--pattern-gift .inner__block.background-light.panel::before {
  /* gift.svg's artwork touches all four edges of its own viewBox, same
     issue the new snowflake ran into — tiling it directly overlapped
     no matter the mask-size, since shrinking that shrinks the icon and
     the tile pitch together. gift-tile.svg wraps the same artwork in a
     larger viewBox with real margin baked in, same fix as
     snowflake-tile.svg above. */
  -webkit-mask-image: url('../img/icons/gift-tile.svg?v=3'), url('../img/icons/gift-tile.svg?v=3');
  mask-image: url('../img/icons/gift-tile.svg?v=3'), url('../img/icons/gift-tile.svg?v=3');
  -webkit-mask-size: 78px 78px, 78px 78px;
  mask-size: 78px 78px, 78px 78px;
  -webkit-mask-position: 0 0, 39px 39px;
  mask-position: 0 0, 39px 39px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}
.wishlist-section--pattern-deer .inner__block.background-light.panel::before {
  /* Uses the padded deer-tile.svg, not the swatch icon's own deer.svg
     — same reindeer, wrapped (via a nested <svg>, so its own viewBox
     didn't need recomputing) in a larger canvas with real margin baked
     in, same technique as snowflake-tile.svg/gift-tile.svg. */
  -webkit-mask-image: url('../img/icons/deer-tile.svg'), url('../img/icons/deer-tile.svg');
  mask-image: url('../img/icons/deer-tile.svg'), url('../img/icons/deer-tile.svg');
  -webkit-mask-size: 80px 80px, 80px 80px;
  mask-size: 80px 80px, 80px 80px;
  -webkit-mask-position: 0 0, 40px 40px;
  mask-position: 0 0, 40px 40px;
  -webkit-mask-repeat: repeat, repeat;
  mask-repeat: repeat, repeat;
}

.wishlist-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.wishlist-section-title {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  /* Takes the remaining space so the drag handle and edit button stay
     put at either end regardless of title length, rather than relying
     on the header's own justify-content: space-between to keep three
     children (handle/title/edit) apart — which only reads right for
     exactly two. */
  flex: 1;
  font-family: "Cinzel", serif;
  font-size: 1.8rem;
  color: #2c342b;
  margin: 0;
  /* Same fix as .gc-title/.wishlist-preview-detail__name — a literal
     <h3> (site.js) inheriting main.css's sitewide h3 { text-transform:
     uppercase }. Third and last of the three "item-level" title-role
     elements that needed this same override. */
  text-transform: none;
}

.wishlist-section-drag-handle {
  background: none;
  border: none;
  padding: 0.3rem;
  color: #9a9384;
  cursor: grab;
  font-size: 1.6rem;
  flex-shrink: 0;
}

.wishlist-section-drag-handle:hover {
  color: #2c342b;
}

.wishlist-section-drag-handle:active {
  cursor: grabbing;
}

/* Even spacing across all three buttons (Xavier's call, after the
   large tap-target sizing below made the two arrows read as having
   more room between them than either had next to Edit) — one flex gap
   governing every gap in the row, rather than each button's own
   padding creating a different amount of visual space around it. */
.wishlist-section-header-actions {
  display: flex;
  align-items: center;
  gap: 2.2rem;
  flex-shrink: 0;
}

/* A precise, one-tap alternative to the drag handle above — dragging a
   section that can run most of the height of a phone screen is
   genuinely hard to pull off on touch. Compact, matching Edit's own
   footprint (no oversized min-width/height) — the row's own gap above
   is what keeps these tappable-but-close, not padding baked into each
   button. */
.wishlist-section-move-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 0.3rem;
  color: #9a9384;
  cursor: pointer;
  font-size: 1.6rem;
}

.wishlist-section-move-btn:disabled {
  color: rgba(154, 147, 132, 0.35);
  cursor: default;
}

.wishlist-section-move-btn:not(:disabled):hover {
  color: #2c342b;
}

/* The default catch-all group — no panel background, no drag handle,
   no edit control (unlike .wishlist-section below), just a small label
   plus the entries themselves sitting above the real sections. Margin
   lives on the outer wrapper (not .wishlist-default-grid itself) since
   the label needs to sit inside that same margin, not push past it. */
.wishlist-default-group {
  margin-bottom: 2.5rem;
}

/* Deliberately quiet — a plain small caption, not a real section
   title (no Cinzel serif, no dark panel, no drag/edit controls): this
   is a label for the catch-all group, not a section a visitor created
   themselves. */
.wishlist-default-grid-label {
  font-size: 1.4rem;
  text-transform: uppercase;
  letter-spacing: 0.05rem;
  color: #9a9384;
  margin: 0 0 0.75rem;
}

/* Scoped to just the Unsorted group, not the shared .wishlist-section-
   empty class as a whole — regular (owner-created) sections' own empty
   state stays left-aligned like the rest of their content; Xavier only
   asked for this one centered. */
.wishlist-default-group .wishlist-section-empty {
  text-align: center;
}

.wishlist-section-edit-btn {
  background: none;
  border: none;
  padding: 0;
  color: #9a9384;
  cursor: pointer;
  font-size: 1.6rem;
}

.wishlist-section-edit-btn:hover {
  color: #2c342b;
}

/* List layout: single column, cards read top-to-bottom in position
   order — the default, and the one that keeps a note's "get this or
   that" meaning unambiguous relative to its neighbors. Grid is the
   opt-in alternative (.gc-grid, already defined above), chosen per
   section rather than site-wide. */
.gc-list {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* List has no column width the way .gc-grid's 2-column layout does, so
   on a wide desktop viewport a card (and its full-width, 4:3 .gc-media
   photo/placeholder) stretches edge-to-edge across the whole panel —
   comically huge compared to the same card in Grid. Capped and centered
   at the same width the item detail page already reads comfortably at
   (.wishlist-preview-detail's own 640px), rather than left-aligned with
   dead space on the right, matching that existing precedent. Desktop-
   only (Xavier's call): mobile already reads fine full-width, this is
   purely a wide-viewport problem. */
@media (min-width: 1200px) {
  .gc-list .gc-card {
    /* width, not just max-width — .gc-card is a flex item in a column-
       direction .gc-list, and margin:auto centering there overrides its
       default stretch sizing in favor of a shrink-to-fit width. Without
       an explicit width, .gc-media's own `width: 100%` (its cover photo/
       placeholder box) resolves against that ambiguous shrink-to-fit
       size instead of the intended 640px, collapsing the whole card far
       smaller than the cap. */
    width: 100%;
    max-width: 640px;
    margin-left: auto;
    margin-right: auto;
  }
}

/* ---------- Bottom account bar (Hub state only) ---------- */

.account-bar {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.55rem 1.05rem 0.55rem 0.55rem;
  border-radius: 30px;
  background: rgba(245, 239, 227, 0.08);
  border: 1px solid rgba(245, 239, 227, 0.2);
}

.account-bar__profile {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  color: #f5efe3;
  text-decoration: none;
  transition: opacity 0.2s ease;
}

.account-bar__profile:hover {
  opacity: 0.8;
  color: #f5efe3;
}

.account-bar__avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  border: 1.5px solid #c9a51c;
}

.account-bar__name {
  font-size: 1.52rem;
  font-weight: 600;
}

.account-bar__divider {
  color: rgba(245, 239, 227, 0.4);
}

.account-bar__logout {
  font-size: 1.52rem;
  color: rgba(245, 239, 227, 0.7);
  text-decoration: underline;
}

.account-bar__logout:hover {
  color: #f5efe3;
}

/* Just the "Admin Mode" label inside #admin-mode-toggle, not the
   ": On"/": Off" state after it — same admin-orange as the Hub tile
   icons/menu links, always on regardless of the toggle's current state
   (it's the label for an admin-only control, not a state indicator).
   Xavier's own follow-up call, reversing an earlier version that
   colored On/Off instead and left the label plain. */
.admin-mode-toggle__label {
  color: #b45f06;
}

/* Xavier's own follow-up, refined twice: first dropped the underline
   entirely, then asked for it back under just the On/Off word — not
   under "Admin Mode" itself. .account-bar__logout's shared base rule
   underlines the whole link (including Sign Out, which keeps that
   as-is); this turns it off for the toggle specifically and the
   .admin-mode-toggle__state span below turns it back on for only the
   state word. */
#admin-mode-toggle {
  text-decoration: none;
}
.admin-mode-toggle__state {
  text-decoration: underline;
}

/* Fixes a specific mismatch Xavier pointed out: the ": " colon (its own
   span now, was a bare text node) used to inherit
   .account-bar__logout:hover's shared brighten-on-hover, visibly
   shifting color next to the label's fixed orange, which stays put.
   Giving the colon its own explicit resting color stops it inheriting
   that hover shift, without touching hover on anything else in this
   link — the On/Off word (.admin-mode-toggle__state) deliberately keeps
   the normal hover-brighten, per Xavier's own follow-up after an
   earlier version of this fix over-corrected and killed hover on the
   whole toggle, On/Off included. */
.admin-mode-toggle__colon {
  color: rgba(245, 239, 227, 0.7);
}

/* ---------- About ---------- */

/* Card-style list rather than a settings-style row list — a bordered,
   shadowed block per guide (same visual language as the homepage's own
   Hub tiles, just single-column instead of a grid, since a 2-across
   grid leaves an orphaned last row at 5 items) is what actually reads
   as clickable on its own, no chevron arrow needed. */
.about-guide-list {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.about-guide-item {
  display: flex;
  align-items: flex-start;
  gap: 1.5rem;
  padding: 1.5rem 1.75rem;
  border-radius: 14px;
  border: 1px solid rgba(44, 52, 43, 0.12);
  background: #fff;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  text-decoration: none;
  color: inherit;
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

.no-touch .about-guide-item:hover {
  transform: translateY(-3px);
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.1);
  border-color: #c9a51c;
  color: inherit;
}

.about-guide-item__icon {
  flex-shrink: 0;
  width: 3rem;
  text-align: center;
  font-size: 2rem;
  color: #c9a51c;
  margin-top: 0.2rem;
}

.about-guide-item__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.about-guide-item__title {
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 1.7rem;
  color: #2c342b;
}

.about-guide-item__text {
  font-size: 1.32rem;
  color: #6b6555;
}

.about-article .section__text:first-child {
  margin-top: 0;
}

/* Scaffolding for turning each guide's current single block of
   paragraphs into real subsections — Xavier's own request, so he has
   something to write into from his phone (subsection headings +
   dividers) plus a visible slot for each screenshot he still needs to
   take once he's back at a computer (.about-article__image-placeholder).
   Not the same classes as .static-doc__subheading/__divider elsewhere
   in this file — those are built for the dark Terms/Privacy pages
   (light text on a dark background); this panel
   (.inner__block.background-light) is the opposite, so reusing them
   directly would render near-invisible cream text on a white panel. */
.about-article__subheading {
  margin: 2.2rem 0 0.8rem;
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 1.5rem;
  color: #2c342b;
}
.about-article__subheading:first-child {
  margin-top: 0;
}

/* Same reset bug .wishlist-richtext-editor strong already documents and
   fixes elsewhere in this file: plugins.css's `font: inherit` on
   b/i/strong/em/u strips the browser's default bold everywhere, unless
   something re-applies it. .section__text (every guide paragraph across
   all six About guides, plus anywhere else this class is used) never
   had that override, so every <strong> in guide copy has been silently
   rendering as plain text this whole time. */
.section__text strong,
.section__text b {
  font-weight: 700;
}

.about-article__divider {
  margin: 2rem 0;
  border: none;
  border-top: 1px solid rgba(44, 52, 43, 0.12);
}

/* Deliberately looks unfinished (dashed border, muted, an explicit
   "Screenshot placeholder" label) rather than like a real styled
   component — the whole point is that it should stand out as something
   still to do, not blend in as if it were done. The caption underneath
   is where the specific shot/crop to capture gets described, so future-
   Xavier (or Claude, later) knows exactly what to take without having
   to re-derive it from the surrounding prose. */
.about-article__image-placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  margin: 1.5rem 0;
  padding: 2.5rem 1.5rem;
  border: 2px dashed rgba(44, 52, 43, 0.25);
  border-radius: 12px;
  background: rgba(44, 52, 43, 0.03);
  color: #6b6555;
  text-align: center;
}
.about-article__image-placeholder i {
  font-size: 2rem;
  color: rgba(44, 52, 43, 0.3);
}
.about-article__image-placeholder__label {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 1.1rem;
  color: rgba(44, 52, 43, 0.45);
}
.about-article__image-placeholder__caption {
  font-size: 1.28rem;
  font-style: italic;
  max-width: 32rem;
}

/* Real screenshot, once one exists to replace a placeholder above with
   — same rounded-corner treatment as .activity-detail__image, this
   site's existing "real photo in a content panel" look. */
.about-article__image-wrap {
  margin: 1.5rem 0;
  text-align: center;
}
.about-article__image {
  max-width: 100%;
  border-radius: 16px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
  cursor: pointer;
  transition: transform 0.15s ease;
}
.about-article__image:hover {
  transform: scale(1.015);
}
.about-article__image-caption {
  margin: 0.6rem 0 0;
  font-size: 1.28rem;
  font-style: italic;
  color: #6b6555;
}

/* ---------- Image lightbox (About guide screenshots) ---------- */
.image-lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Deliberately never scrollable, in either state — an earlier version
     made .image-lightbox--zoomed overflow:auto and panned via native
     scroll, but position:fixed combined with a nested overflow:auto
     scroll region is a documented trigger for dynamic-toolbar rendering
     corruption on Chromium-based Android browsers (confirmed on Android
     + Brave — the page rendered with a chunk of its bottom missing,
     revealing whatever was behind it, and nothing short of a full
     browser restart cleared it). Panning is now done in JS via
     transform: translate() on the image itself instead, so this
     container never needs to scroll at all. */
  overflow: hidden;
}
.image-lightbox[hidden] {
  display: none;
}
.image-lightbox__backdrop {
  /* Fixed, not absolute — matches .image-lightbox__close (see its own
     comment). Nothing here actually scrolls any more, but fixed is
     still correct and costs nothing. */
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.85);
}
.image-lightbox__img {
  position: relative;
  /* main.css's site-wide `img { width: 100%; height: auto }` reset
     otherwise wins here — width stays pinned to 100% of this
     full-viewport container even when max-height is the constraint
     actually doing the shrinking, stretching the image instead of
     scaling both dimensions together. width: auto lets both axes scale
     proportionally against whichever max- actually binds. */
  width: auto;
  height: auto;
  /* .image-lightbox is display:flex, and flex items shrink below their
     own specified width by default (flex-shrink:1) to fit the
     container's main axis — an inline width set by the zoom handler in
     site.js (e.g. 2x natural size) was silently getting shrunk right
     back down to fit, undoing the zoom entirely with no visible error.
     flex-shrink:0 is what actually lets an explicit width win. */
  flex-shrink: 0;
  max-width: 92vw;
  max-height: 92vh;
  border-radius: 12px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
  cursor: zoom-in;
  /* Pointer Events handle both tap-to-zoom and drag-to-pan (site.js) —
     without this, a touch-drag here can also get interpreted by the
     browser itself as a native scroll/pan gesture, fighting the JS
     transform being applied at the same time. */
  touch-action: none;
}
/* Tap-to-zoom + drag-to-pan (mobile, or click-drag on desktop) — swaps
   the fit-to-screen cap for a real magnified pixel size (set inline by
   site.js) and pans via transform: translate(), tracked with Pointer
   Events. See .image-lightbox's own comment for why this isn't native
   scroll. */
.image-lightbox__img--zoomed {
  max-width: none;
  max-height: none;
  cursor: grab;
}
.image-lightbox__close {
  /* Fixed rather than absolute — costs nothing and stays pinned to the
     real viewport regardless of anything happening inside the overlay. */
  position: fixed;
  top: 1.5rem;
  right: 1.5rem;
  width: 3.2rem;
  height: 3.2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.3);
  color: #f5efe3;
  font-size: 1.6rem;
  cursor: pointer;
}
.image-lightbox__close:hover {
  background: rgba(255, 255, 255, 0.2);
}

/* Xavier's own follow-up, testing on his phone — the guide body text
   (.section__text, from main.css) has no mobile-specific size at all,
   only a bump at 1400px+ desktop, so it read small here on a screen
   people are actually expected to read a whole article on. Scoped to
   .about-article specifically rather than bumping .section__text's own
   base rule, which is shared across other pages/contexts this wasn't
   asked to touch. */
@media only screen and (max-width: 767px) {
  .about-article .section__text {
    font-size: 1.6rem;
  }
  .about-article__subheading {
    font-size: 1.7rem;
  }
  .about-article__image-placeholder__label {
    font-size: 1.2rem;
  }
  .about-article__image-placeholder__caption {
    font-size: 1.4rem;
  }
  .about-article__text-placeholder {
    font-size: 1.5rem;
  }
}

/* Content stub, not a draft — Xavier's own call: he's writing the real
   guide prose himself, this just holds the spot with what should go
   there. Deliberately NOT styled like .section__text (the real,
   finished guide copy) — italic and muted, same visual family as the
   image placeholder's own caption, so it reads unambiguously as "notes
   to fill in," not as something already written. */
.about-article__text-placeholder {
  font-size: 1.32rem;
  font-style: italic;
  color: #6b6555;
}

/* The article header now just reuses .section-header/.section-header__icon
   as-is (same treatment as every other page's own title, e.g. "Editing
   My Wish List") — Xavier's own call to match rather than maintain a
   bespoke smaller title style. The back arrow slots in as the first
   flex child, same pattern #activities-edit-view already uses. */
#about-article-view .section-header {
  gap: 1.5rem;
}

/* Smaller than the shared h2 default (4.4rem/5rem/7rem) — Xavier's own
   call: these titles can run to a full sentence ("How to write your
   wishlist."), long enough that the full section-header scale read as
   too big, even though the icon next to it is kept at that same size. */
#about-article-title {
  font-size: 2.8rem;
}
@media only screen and (min-width: 768px) {
  #about-article-title {
    font-size: 3.2rem;
  }
}
@media only screen and (min-width: 1400px) {
  #about-article-title {
    font-size: 4rem;
  }
}

/* ---------- Activities ---------- */

/* One column, stacked top to bottom — not a browsing grid like Gift
   List's own .gc-grid (Xavier's own call: Activities reads as a list of
   posts, not a tile grid to browse). The cover image inside each card
   still gets the same big, full-width treatment as an item's own photo
   (see .activity-card__image below), just one card per row instead of
   two side by side. */
.activity-list {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

/* Same problem .gc-list solved for Gift Lists' own List layout, same
   fix: a flex column with no column width the way a grid has means a
   card (and its full-width cover image) stretches edge-to-edge on a
   wide desktop viewport — comically huge. Capped and centered at the
   same 640px .gc-list already settled on, desktop-only (mobile already
   reads fine full-width). width, not just max-width, for the same
   reason .gc-list needs it: margin:auto centering on a flex column item
   overrides its default stretch sizing in favor of shrink-to-fit, and
   without an explicit width .activity-card__image's own `width: 100%`
   would resolve against that ambiguous size instead of the intended cap. */
@media (min-width: 1200px) {
  .activity-list .activity-card {
    width: 100%;
    max-width: 640px;
    margin-left: auto;
    margin-right: auto;
  }
}

.activities-empty {
  color: #9a9384;
  font-size: 1.4rem;
  text-align: center;
  padding: 1rem 0;
}

.activities-btn-full {
  display: block;
  width: 100%;
  text-align: center;
  margin-top: 1.25rem;
}

/* Same [hidden]-vs-authored-display trap as elsewhere in this codebase
   (e.g. the hamburger menu's admin-only items) — .activities-btn-full's
   own display:block above otherwise beats the native [hidden] rule. */
.activities-btn-full[hidden] {
  display: none;
}

.activity-card {
  display: flex;
  flex-direction: column;
  background: #fff;
  border-radius: 16px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.07);
  padding: 1.25rem;
}

.activity-card--clickable {
  cursor: pointer;
}

.no-touch .activity-card--clickable:hover {
  transform: translateY(-4px);
  box-shadow: 0 14px 30px rgba(0, 0, 0, 0.12);
}

/* Same size/treatment as .gc-media (the Gift List's own item photo) —
   a big top-of-card image, not a small side thumbnail. */
.activity-card__image {
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: 10px;
  overflow: hidden;
  object-fit: cover;
  margin-bottom: 1rem;
  background: #f4f1e6;
}

.activity-card__image--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #2c342b;
  color: #c9a51c;
  font-size: 2.4rem;
}

.activity-card__body {
  flex: 1;
  min-width: 0;
}

.activity-card__title {
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 1.84rem;
  color: #2c342b;
  margin: 0;
  line-height: 1.3;
}

.activity-card__registry-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 1.2rem;
  color: #b45f06;
  margin-top: 0.3rem;
}

.activity-card__edit-controls {
  display: flex;
  gap: 0.5rem;
  flex-shrink: 0;
  margin-top: 1rem;
}

.btn-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.8rem;
  height: 2.8rem;
  border-radius: 50%;
  border: 1px solid rgba(44, 52, 43, 0.2);
  background: none;
  color: #6b6555;
  font-size: 1.1rem;
  cursor: pointer;
}

.btn-icon:disabled {
  opacity: 0.35;
  cursor: default;
}

.no-touch .btn-icon:hover:not(:disabled) {
  color: #2c342b;
  border-color: #2c342b;
}

.activities-editor-image-wrap {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
}

#activities-editor-image-preview {
  width: 90px;
  height: 90px;
  border-radius: 12px;
  object-fit: cover;
}

/* main.css's own `img { display: block; }` and the form input styling
   both beat the native [hidden] rule (same trap this codebase has hit
   before — see the hamburger menu's admin-only items) — spelled out
   explicitly here so hidden actually means hidden for these two. */
#activities-editor-image-preview[hidden],
.activities-editor-image-input[hidden] {
  display: none;
}

.activity-detail__image {
  width: 100%;
  max-height: 320px;
  object-fit: cover;
  border-radius: 16px;
  margin-bottom: 0.75rem;
}

/* Same treatment as an item's own detail title
   (.wishlist-preview-detail__name) — sits under the photo, not folded
   into the header bar above it. */
.activity-detail__title {
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 2.1rem;
  color: #2c342b;
  margin: 0 0 1rem;
}

.activity-detail__description {
  font-size: 1.52rem;
  color: #444;
  line-height: 1.6;
}

.activity-registry {
  margin-top: 1.75rem;
  padding-top: 1.75rem;
  border-top: 1px solid rgba(44, 52, 43, 0.12);
}

.activity-register-btn--registered {
  background: none;
  border: 1px solid #2c342b;
  color: #2c342b;
}

.activity-registry__heading {
  font-family: "Cinzel", serif;
  font-size: 1.4rem;
  color: #2c342b;
  margin: 1.25rem 0 0.5rem;
}

.activity-registrants {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 1.4rem;
  color: #444;
}

.activity-registrants li {
  padding: 0.4rem 0;
  border-bottom: 1px solid rgba(44, 52, 43, 0.08);
}

.activity-registrants__empty {
  color: #9a9384;
  border-bottom: none !important;
}

/* ---------- Account ---------- */

.account-avatar-panel {
  display: flex;
  justify-content: center;
}

.account-avatar {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.8rem;
  max-width: 32rem;
  text-align: center;
}

.account-avatar img {
  width: 96px;
  height: 96px;
  border-radius: 50%;
  object-fit: cover;
  border: 3px solid #c9a51c;
}

.account-avatar__text {
  margin: 0;
  font-size: 1.36rem;
  color: #6b6555;
}

.account-avatar .form-message {
  margin: 0.25rem 0 0;
}

.account-avatar .btn-auth {
  min-width: 18rem;
  background: #2c342b;
  color: #ffffff;
  border: 1px solid #2c342b;
}

.account-avatar .btn-auth:hover {
  background: #565d55;
  border-color: #565d55;
}

/* ---------- My Images (Account section) ----------
   Deliberately separate classes from the wishlist item editor's own
   .wishlist-image-thumb, even though the visual treatment is
   identical — these two image systems aren't connected yet (this
   library is step one of a bigger decoupling of photos from specific
   wishlist items, still in progress), and this page's own thumbnails
   have no per-item "cover photo" or manual ordering concept to carry
   over. Consolidate into one shared set of classes once the wishlist
   editor actually starts picking from this library instead of
   uploading directly to an item. */

.user-images-count {
  margin: 0.4rem 0 0;
  font-size: 1.2rem;
  color: #6b6555;
}

.user-images-toolbar {
  margin-bottom: 1rem;
}

/* Same dimmed/no-cursor disabled treatment used elsewhere on this page
   (e.g. the preview claim buttons) — shown once the account's 200
   image cap is reached, so there's nothing left to click into. */
.user-image-upload-trigger:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.user-image-thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.user-image-thumb {
  position: relative;
  width: 80px;
  height: 80px;
  border-radius: 8px;
  overflow: hidden;
  background: #f4f1e6;
}

.user-image-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.user-image-thumb__delete,
.user-images-delete-btn {
  position: absolute;
  top: 3px;
  right: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: none;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 0.9rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Fixed 80px tiles are a sensible dense grid on desktop, but they're
   both too small to comfortably tap and too small to tell apart on a
   phone. A real 3-column grid instead of "however many 80px tiles
   happen to fit" makes each tile scale up with the screen — bigger
   photo, bigger delete target — same touch breakpoint used elsewhere
   in this file (screen width OR a direct coarse-pointer check, so a
   touchscreen at desktop width still gets it). */
@media (max-width: 768px), (pointer: coarse) {
  .user-image-thumbs {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.6rem;
  }

  .user-image-thumb {
    width: auto;
    height: auto;
    aspect-ratio: 1;
  }

  .user-image-thumb__delete,
  .user-images-delete-btn {
    width: 32px;
    height: 32px;
    top: 5px;
    right: 5px;
    font-size: 1.2rem;
  }

  /* .btn-small's default padding/font-size reads fine for the other
     small text buttons on this page, but this is the one people are
     most likely to reach for right after opening this panel on a
     phone — worth a genuine touch-sized target rather than the same
     compact sizing every other small button here uses. Two classes
     (not just .user-image-upload-trigger alone) so this reliably beats
     .btn-small's own rule regardless of which one happens to come
     later in the file — same equal-specificity/source-order gotcha
     already hit a few times elsewhere in this file. */
  .btn-small.user-image-upload-trigger {
    padding: 0.75rem 1.4rem;
    font-size: 1.3rem;
  }
}

/* Same main.css "form input { display: block }" collision as the
   wishlist item editor's own upload input — see the detailed comment
   at .wishlist-image-upload-input[hidden] for the full explanation. */
.user-image-upload-input[hidden] {
  display: none;
}

.user-images-pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin-top: 1.25rem;
}

/* Shared with the item editor's own image picker screen — same pager,
   different class names on its buttons only (so the two screens'
   delegated click handlers, both bound broadly on document, can never
   double-fire on each other's Prev/Next). */
.user-images-pager__prev,
.user-images-pager__next,
.wishlist-image-picker-pager__prev,
.wishlist-image-picker-pager__next {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid rgba(44, 52, 43, 0.25);
  background: #fff;
  color: #2c342b;
  cursor: pointer;
}

.user-images-pager__prev:hover,
.user-images-pager__next:hover,
.wishlist-image-picker-pager__prev:hover,
.wishlist-image-picker-pager__next:hover {
  border-color: #c9a51c;
  color: #c9a51c;
}

.user-images-pager__prev:disabled,
.user-images-pager__next:disabled,
.wishlist-image-picker-pager__prev:disabled,
.wishlist-image-picker-pager__next:disabled {
  opacity: 0.35;
  cursor: not-allowed;
  border-color: rgba(44, 52, 43, 0.25);
  color: #2c342b;
}

.user-images-pager__status,
.wishlist-image-picker-pager__status {
  font-size: 1.2rem;
  color: #6b6555;
}

/* The picker's own thumbnails are click-to-select, not delete targets
   — a hover cue makes that obvious even though the tile itself has no
   other interactive children the way the My Images/item editor
   versions do. */
.wishlist-image-picker-thumb {
  cursor: pointer;
  transition: opacity 0.15s ease, outline-color 0.15s ease;
  outline: 2px solid transparent;
  outline-offset: 2px;
}

.wishlist-image-picker-thumb:hover {
  opacity: 0.85;
  outline-color: #c9a51c;
}

.wishlist-image-picker-url-row {
  display: flex;
  gap: 0.75rem;
  margin-top: 0.5rem;
  align-items: center;
}

/* Not inside a <form>, so the site-wide "form input" block-stretch
   rule doesn't reach this one — but reset explicitly anyway, the same
   defensive pattern used at every other input-outside-a-form in this
   file, so it can't silently break if this ever moves inside one. */
.wishlist-image-picker-url-input {
  display: block;
  flex: 1;
  height: auto;
  margin: 0;
  padding: 0.6rem 0.9rem;
  font-size: 1.3rem;
  border: 1px solid #d8d2c0;
  border-radius: 8px;
}

.wishlist-image-picker-url-row .btn-small {
  flex-shrink: 0;
}

/* This panel is its own separate .background-light block (unlike
   .account-fields above, which sits on the section's dark opening
   panel) — dark text on purpose, not the pale account-fields color. */
.account-section-title {
  margin: 0 0 1.25rem;
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 1.6rem;
  color: #2c342b;
}

.account-settings {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

#notify-admin-alerts-row[hidden] {
  display: none;
}

.account-settings__row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: 1.36rem;
  color: #444;
  cursor: pointer;
}

/* ---------- Account: Watching list ---------- */

/* Sits inside the same Notifications panel as the two global toggles
   above, separated by a rule rather than split into its own panel —
   it's the same subject ("what emails you"), just expressed as a list
   of individual subscriptions instead of a pair of switches. */
.watching-block {
  margin-top: 2rem;
  padding-top: 1.75rem;
  border-top: 1px solid #eee6d6;
}

.watching-block__title {
  margin: 0 0 0.4rem;
  font-family: "Cinzel", serif;
  font-size: 1.5rem;
  font-weight: 600;
  color: #2c342b;
}

.watching-block__intro {
  margin: 0 0 1.25rem;
  font-size: 1.25rem;
  color: #6b6555;
}

.watching-group {
  margin-bottom: 1.5rem;
}

.watching-group:last-child {
  margin-bottom: 0;
}

.watching-group__label {
  display: block;
  margin-bottom: 0.5rem;
  font-size: 1.1rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #9a9384;
}

.watching-row {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.6rem 0;
  border-bottom: 1px solid #f4f1e6;
}

.watching-row:last-child {
  border-bottom: none;
}

.watching-row__avatar,
.watching-row__thumb {
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  object-fit: cover;
}

.watching-row__avatar {
  border-radius: 50%;
}

.watching-row__thumb {
  border-radius: 6px;
}

/* Holds the same 32px slot as a real thumbnail so every row's name
   starts at the same left edge — see the comment where this is
   rendered in site.js. */
.watching-row__thumb--empty {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #f4f1e6;
  font-size: 1.2rem;
  color: #c9bfa4;
}

/* A real button (it opens that item/list), styled as a plain link so
   it reads as the row's own label rather than competing with the X
   next to it for "this is the action here." */
.watching-row__name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  border: none;
  background: none;
  padding: 0;
  text-align: left;
  font-size: 1.3rem;
  font-weight: 600;
  color: #2c342b;
  cursor: pointer;
}

.watching-row__name:hover {
  color: #b3910c;
  text-decoration: underline;
}

.watching-row__owner {
  flex-shrink: 0;
  font-size: 1.15rem;
  font-style: italic;
  color: #9a9384;
}

.watching-row__remove {
  flex-shrink: 0;
  border: none;
  background: none;
  padding: 0.3rem 0.5rem;
  font-size: 1.3rem;
  line-height: 1;
  color: #9a9384;
  cursor: pointer;
}

.watching-row__remove:hover {
  color: #9e2525;
}

.watching-empty {
  margin: 0;
  font-size: 1.25rem;
  font-style: italic;
  color: #9a9384;
}

/* The owner suffix is the first thing worth dropping on a narrow
   screen — the item name and the X are what actually matter here, and
   which list it's on is recoverable by just opening it. */
@media only screen and (max-width: 600px) {
  .watching-row__owner {
    display: none;
  }
}

/* ---------- Invites (super-admin only) ---------- */

.invites-panel-title {
  margin: 0 0 1rem;
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 1.6rem;
  color: #2c342b;
}

.invite-send-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.invite-send-form__row {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.invite-send-form__row label {
  font-size: 1.2rem;
  font-weight: 600;
  color: #6b6555;
}

/* Matches .wishlist-item-editor-reason-other's own box treatment
   elsewhere in this file — a real bordered field, not the theme's bare
   default. margin: 0 is load-bearing, same reasoning as
   .invite-add-form input's own comment on this — main.css's blanket
   "form input { margin: 0 0 2rem 0 }" was stacking an extra 20px on top
   of .invite-send-form's own `gap`, leaving the field-to-button spacing
   noticeably larger than the spacing between the fields themselves. */
/* Checkbox row reads inline (checkbox beside its own label text), not
   stacked like the select/input rows above it — the default column
   .invite-send-form__row layout only makes sense when the label sits
   above a full-width field. */
.invite-send-form__row--checkbox label {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-weight: 400;
  cursor: pointer;
}

.invite-send-form__row--checkbox input[type="checkbox"] {
  width: 1.6rem;
  height: 1.6rem;
  flex-shrink: 0;
  accent-color: #c9a51c;
  cursor: pointer;
}

.invite-send-form__row select,
.invite-send-form__row input:not([type="checkbox"]) {
  width: 100%;
  margin: 0;
  padding: 0.9rem 1.2rem;
  border-radius: 8px;
  border: 1px solid rgba(44, 52, 43, 0.2);
  background: #fff;
  color: #2c342b;
  font-size: 1.36rem;
  font-family: "Mulish", sans-serif;
}

/* Sized up from .btn-small's base (0.3rem/0.9rem padding, ~26px tall)
   specifically here — the collapsed-error-paragraph fix above made this
   button sit right against the last field with nothing to separate it,
   and at the base size that's a small, easy-to-miss tap target on a
   phone. margin-top gives it a beat of its own instead of reading as
   just another form row, and the padding clears the 44px touch floor
   the rest of the site uses (see "Phone touch targets" at the end of
   this file) without needing a mobile-only override. */
#invite-send-submit {
  margin-top: 0.4rem;
  padding: 1.1rem 1.6rem;
  font-size: 1.3rem;
}

.invites-registry-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.invites-registry-header .invites-panel-title {
  margin-bottom: 0;
}

/* A single input+button row, not the full multi-row .invite-send-form
   layout above — this is one quick field, not a form with several. */
.invite-add-form {
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* height: 4.4rem is explicit and load-bearing on both, not just the
   input's own padding — main.css's own blanket "form input { height:
   4.4rem; }" rule already forces every <input> in a <form> to exactly
   that height regardless of padding, so leaving the button to size
   itself from padding/line-height alone (a totally different box
   model — buttons aren't matched by that "form input" rule at all) is
   what actually produced the mismatch here, not a missing align-items.
   Matching both to the same explicit height, then centering each one's
   own content within it, is the only way to guarantee they line up. */
.invite-add-form input {
  flex: 1;
  min-width: 0;
  height: 4.4rem;
  /* main.css's blanket "form input { margin: 0 0 2rem 0 }" rule was the
     actual remaining culprit here — a 20px bottom margin the button
     doesn't share shifts an align-items:center'd flex item upward by
     half that difference, which is exactly the "still a bit off" gap
     Xavier was seeing even after both boxes matched at 44px tall. */
  margin: 0;
  padding: 0 1.1rem;
  border-radius: 8px;
  border: 1px solid rgba(44, 52, 43, 0.2);
  background: #fff;
  color: #2c342b;
  font-size: 1.3rem;
  font-family: "Mulish", sans-serif;
}

.invite-add-form .btn-small {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 4.4rem;
  padding: 0 1.4rem;
  font-size: 1.3rem;
}

.invites-registry-list {
  max-height: 22rem;
  overflow-y: auto;
  /* The vertical scrollbar this triggers on longer lists otherwise sits
     flush against the status text with nothing to separate them —
     Xavier's own report: "text is getting cut off a bit on the right." */
  padding-right: 1rem;
}

/* Same shape as .watching-row above (label, a secondary status/detail,
   a remove button) — different content, same list-row language. */
.invites-registry-row {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.6rem 0;
  border-bottom: 1px solid #f4f1e6;
}

.invites-registry-row:last-child {
  border-bottom: none;
}

.invites-registry-row__label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: 1.3rem;
  font-weight: 600;
  color: #2c342b;
}

/* Codes are typed/copied, not read as prose — monospace makes it clear
   at a glance which list this is (and easier to double-check a
   character isn't 0/O or 1/I/L, even though add_invite_code.php's own
   alphabet already excludes those). */
.invites-registry-row__label--code {
  font-family: "Courier New", Courier, monospace;
  letter-spacing: 0.04em;
}

.invites-registry-row__status {
  flex-shrink: 0;
  font-size: 1.15rem;
  font-style: italic;
  color: #9a9384;
}

.invites-registry-row__status--registered {
  color: #6b8f5a;
}

.invites-registry-row__remove {
  flex-shrink: 0;
  border: none;
  background: none;
  padding: 0.3rem 0.5rem;
  font-size: 1.3rem;
  line-height: 1;
  color: #9a9384;
  cursor: pointer;
}

.invites-registry-row__remove:hover {
  color: #9e2525;
}

/* Own row shape, not .invites-registry-row above — a history entry
   carries four pieces of info (name, code, recipient, sent-by/when) vs.
   the registries' plain label+status, so it's stacked in two lines
   instead of squeezed onto one. */
.invites-history-row {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 0.7rem 0;
  border-bottom: 1px solid #f4f1e6;
}

.invites-history-row:last-child {
  border-bottom: none;
}

.invites-history-row__main {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.6rem;
}

.invites-history-row__pair {
  font-family: "Courier New", Courier, monospace;
  font-weight: 600;
  font-size: 1.25rem;
  color: #2c342b;
}

.invites-history-row__email {
  font-size: 1.2rem;
  color: #6b6555;
}

.invites-history-row__meta {
  font-size: 1.1rem;
  font-style: italic;
  color: #9a9384;
}

.account-security {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.account-security__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}

.account-security__hint {
  margin: 0;
  font-size: 1.15rem;
  color: #6b6555;
}

/* ---------- Testing (super-admin only) ---------- */

.testing-mode-description {
  margin: 0 0 1.6rem;
  font-size: 1.3rem;
  line-height: 1.6;
  color: #6b6555;
  max-width: 60ch;
}

/* ---------- My Wish List ---------- */

/* Same icon used on the matching Hub tile — repeated here so a section
   carries its own visual identity over from the tile you actually
   clicked to get here, rather than that association only existing on
   the Hub screen. Not the Hub tile's own gold, though — that's the
   site's "act on this" accent color (buttons, links, active states),
   and reused here it made a plain decorative icon look like a control.
   #6b6555 is the site's own muted secondary-text tone, already used for
   de-emphasized copy elsewhere (.message-item__preview, etc.). */
.section-header {
  display: flex;
  align-items: center;
  gap: 2.25rem;
  /* The theme keeps .inner__container's own right padding and
     .section-close's (X) `right` offset locked to the same value at
     every breakpoint (10% / 8rem / 10rem) — meaning the X's 24px-wide
     box sits flush against the content area's own right edge, eating
     into it rather than living in genuinely reserved space outside
     it. A heading that legitimately fills its box right up to that
     edge (any of this site's longer ones — "Gift Lists", "My Wish
     List", "My Messages", "My Account" — not just at phone widths, any
     width where the layout is still single-column) runs straight
     under the X. Reserving a bit more room here, only for this row,
     is what actually keeps clear of it — shrinking the heading
     wrapper alone (see the min-width: 0 rule below) still lets text
     legally reach that same shared edge, since the edge itself is the
     problem, not the wrapper's ability to shrink. */
  padding-right: 3rem;
}

/* The h2/subtitle wrapper is a flex item with no shrink floor by
   default (min-width: auto), so at narrower widths it won't shrink
   below the heading's own unbroken text width — it just overflows the
   header's box instead of wrapping. Needed alongside the padding-right
   above: without this, a long heading would rather overflow past even
   that reserved edge than wrap onto a second line. */
.section-header > div {
  flex: 1;
  min-width: 0;
}

.section-header__icon {
  flex-shrink: 0;
  /* Deliberately bigger than the h2 text box itself, not just matched to
     it — matching size 1:1 still read visually smaller than the h2 once
     rendered, since the icon glyph fills its box edge-to-edge while the
     heading's own capital letters (Cinzel) only fill a portion of theirs.
     Sized to actually dominate/anchor the header the way the reference
     showed, not just sit alongside it at the same scale. */
  font-size: 6.5rem;
  color: #6b6555;
}


@media (min-width: 768px) {
  .section-header__icon {
    font-size: 7.5rem;
  }
}

/* ---------- My Messages ---------- */

.message-list {
  display: flex;
  flex-direction: column;
}

.message-item {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem 1.5rem;
  text-decoration: none;
  color: inherit;
  border-bottom: 1px solid #eee6d6;
  transition: background 0.2s ease;
}

.message-item:last-child {
  border-bottom: none;
}

.message-item:hover {
  background: #f9f6ee;
  color: inherit;
}

.message-item__avatar {
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  object-fit: cover;
}

.message-item__avatar--anon {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #2c342b;
  color: #c9a51c;
  font-size: 1.6rem;
}

.message-item__body {
  flex: 1;
  min-width: 0;
}

.message-item__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
}

/* The subject is the row's primary title now (matching the open-thread
   header's own title/subtitle order) — .message-item__with below is the
   "Conversation with X" line, one step down in visual weight. */
.message-item__subject {
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 1.36rem;
  color: #2c342b;
}

.message-item__time {
  flex-shrink: 0;
  font-size: 1.12rem;
  color: #9a9384;
}

.message-item__with {
  display: block;
  margin: 0.15rem 0 0;
  font-size: 1.15rem;
  color: #9a9384;
}

.message-item__with,
.message-thread__with {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

.message-item__with-text,
.message-thread__with-text {
  min-width: 0;
}

.message-thread__with-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.message-anon-badge {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.22rem 0.6rem;
  border-radius: 999px;
  background: rgba(44, 52, 43, 0.08);
  color: #6b6555;
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
}

.message-anon-badge i {
  font-size: 0.9em;
}

.message-anon-badge--revealed {
  background: rgba(201, 165, 28, 0.14);
  color: #2c342b;
}

.message-item__preview {
  margin: 0.2rem 0 0;
  font-size: 1.36rem;
  color: #6b6555;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.message-item.is-unread .message-item__subject,
.message-item.is-unread .message-item__preview {
  color: #2c342b;
  font-weight: 600;
}

/* A leading dot rather than new markup — .is-unread is already toggled
   correctly per row (site.js), this just gives it a clearer "something's
   new here" marker than bold text alone was. */
.message-item.is-unread .message-item__subject::before {
  content: "";
  display: inline-block;
  width: 0.8rem;
  height: 0.8rem;
  margin-right: 0.5rem;
  border-radius: 50%;
  background: #c9a51c;
  vertical-align: middle;
}

.message-inbox__header {
  padding: 0 1.5rem 1.25rem;
}

/* No color set here inherits the site's global (light/cream) body text
   color, which is invisible on this panel's white background — this
   panel needs its own dark color like every other white-panel text
   elsewhere (.message-item__subject, .comment-item__text, etc.). */
.message-list__empty {
  margin: 0;
  padding: 0 1.5rem 1.25rem;
  font-size: 1.3rem;
  color: #6b6555;
}

.message-inbox__header .btn {
  width: 100%;
  /* Deliberately left fully square — unlike the reply box's rounded
     bottom, this button is meant to stand out from the rest of the
     rounded card, not blend into its shape. */
}

/* .btn-small's base size (0.3rem/0.9rem padding, 12px font) reads too
   small for this form's own primary/only action — bumped to the same
   size #confirm-overlay's own buttons already use, a proven "bigger but
   still part of the .btn-small family" size elsewhere on the site. */
.message-compose__form .btn-small--save {
  padding: 0.9rem 2.4rem;
  font-size: 1.5rem;
  border-radius: 24px;
}

/* The accordion pattern (see "Accordion (About section)" below) assumes
   its own horizontal padding comes from a padded parent — that's true in
   About, but this whole panel is .no-padding so .message-list's rows can
   run edge-to-edge. Restoring the padding here just on the accordion
   header/panel keeps it lined up with .message-item's own 1.5rem sides
   instead of sitting flush against the panel edge. */
.message-archive .accordion-header {
  padding: 1.25rem 1.5rem;
}

.message-archive .accordion-panel.is-open,
.message-archive.is-open .accordion-panel {
  max-height: none;
}

.message-archive .message-list--archived {
  max-height: 24rem;
  overflow-y: auto;
}

.message-archive .message-list__empty {
  padding: 0 1.5rem 1.25rem;
}

/* Same pill treatment for both, and the same icon+text combination the
   thread header's own Archive/Restore toggle uses — an icon-only button
   read as unclear on its own, and text-only here would've been
   inconsistent with that other one. */
.message-item__restore-btn,
.message-item__archive-btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: none;
  border: 1px solid rgba(44, 52, 43, 0.2);
  border-radius: 20px;
  padding: 0.4rem 1rem;
  font-size: 1.15rem;
  font-weight: 600;
  color: #6b6555;
  cursor: pointer;
}

.message-item__restore-btn:hover,
.message-item__archive-btn:hover {
  color: #2c342b;
  border-color: #2c342b;
}

/* ---------- Compose / thread header (My Messages) ---------- */

.message-thread__header {
  display: flex;
  align-items: center;
  /* Was 1rem — the back arrow's own glyph already carries a fair bit of
     built-in whitespace inside its icon box, so the actual visible gap
     to the title read tighter than the gap value itself suggested.
     Site-wide (every back-arrow header uses this one class), per
     Xavier's own request. */
  gap: 1.4rem;
  padding: 1.25rem 1.5rem;
  border-bottom: 1px solid #eee6d6;
}

/* Same root cause as .section-header's own right-edge fix above, one
   level worse here: .section-header never overrides .inner__container's
   own responsive padding (0 10% / 8rem / 10rem — the exact values
   .section-close's own `right` offset is kept in lockstep with at
   every breakpoint), so its content naturally lines up with the X.
   This header overrides that down to a flat 1.5rem for its own
   compact, close-to-the-edge look — which means its content box
   extends 10%-1.5rem (base), 6.5rem (>=1200px), or 8.5rem (>=1400px)
   further right than where the X actually sits, deep past it. A long
   title's own flex/min-width/overflow truncation (below) only clips
   against that oversized box, so plenty of real text can still render
   fully within it, right through the X, without ever engaging the
   ellipsis. Restoring the container's own real right padding here —
   plus the same +3rem buffer for the X's own width the section-header
   fix uses — is what actually keeps clear, same as there; `calc()`
   lets the base rule mix the container's percentage with the flat
   buffer in one declaration. */
.message-thread__header {
  padding-right: calc(10% + 3rem);
}
@media only screen and (min-width: 1200px) {
  .message-thread__header {
    padding-right: 11rem;
  }
}
@media only screen and (min-width: 1400px) {
  .message-thread__header {
    padding-right: 13rem;
  }
}

.message-back {
  flex-shrink: 0;
  background: none;
  border: none;
  padding: 0;
  font-size: 2.2rem;
  color: #6b6555;
  cursor: pointer;
}

.message-back:hover {
  color: #2c342b;
}

.message-thread__avatar-wrap {
  flex-shrink: 0;
}

.message-thread__avatar {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  object-fit: cover;
}

/* Same masked-identity treatment as .message-item__avatar--anon and
   .message-bubble__avatar--anon — width/height/border-radius already
   come from the shared .message-thread__avatar class above. */
.message-thread__avatar--anon {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #2c342b;
  color: #c9a51c;
  font-size: 1.6rem;
}

.message-thread__heading {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* Subject is the primary title, "Conversation with X" (.message-thread__with
   below) is the subtitle — subject is what you're actually here to read,
   who it's with is secondary context, same ordering as the inbox row's
   own .message-item__subject/__with above. */
.message-thread__title {
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 1.36rem;
  color: #2c342b;
}

/* Unlike the Messages thread header, where .message-thread__title sits
   inside .message-thread__heading (already flex: 1; min-width: 0), the
   Gift List headers use this title as a bare flex item alongside the
   back button — with no shrink floor it just grows to fit a long list
   owner name or item name, pushing the header's own box wider than its
   container and running the text underneath the theme's absolutely-
   positioned .section-close (X) button, which isn't part of this flex
   row at all and so never fights back for space. Capping the title
   itself to shrink-and-truncate keeps the header's box within its
   container no matter how long the name is, which is what actually
   keeps clear of the X — its position never depends on this box. */
#giftlist-item-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* The shared .message-thread__title size (1.36rem) is right for a
   message subject line sitting above its own "Conversation with X"
   subtitle — too small here, where it's the whole page title with
   nothing else. Matches the site's h3 treatment (main.css) instead:
   same family this whole page's headings already use, one size down
   from the list-detail page's h2. Same fix, same reason, on every other
   wishlist/gift-list screen using this same class as its own standalone
   title: the owner's own item view (#wishlist-item-view-title), and the
   Add Item/Edit Item editor screen (#wishlist-item-editor-title). */
#giftlist-item-title,
#wishlist-item-view-title,
#wishlist-item-editor-title {
  font-size: 2.8rem;
  color: #0A0A0A;
  text-transform: uppercase;
}
@media only screen and (min-width: 768px) {
  #giftlist-item-title,
  #wishlist-item-view-title,
  #wishlist-item-editor-title {
    font-size: 3.2rem;
  }
}
/* #giftlist-item-title/#wishlist-item-view-title are both set to the
   item's own actual name (item.name, site.js) — the real content this
   whole "no forced uppercase on item titles" fix is about, not a static
   label the way #wishlist-item-editor-title is ("Add Item"/"Edit Item",
   left uppercase above on purpose). This is a second, separate title
   for the same item — the page header shown above the card body — that
   got missed when .wishlist-preview-detail__name (the card's own name,
   inside the body) was fixed first. */
#giftlist-item-title,
#wishlist-item-view-title {
  text-transform: none;
}

/* "Username's List" is now a real <h2> (global h2 rule, main.css,
   already the same big Cinzel styling every other page title on the
   site uses) plus a .section__subtitle line beneath it — the same
   title+subtitle pattern as "My Wish List"/"Gift Lists", replacing the
   old compact single-line .message-thread__title treatment entirely.
   No longer truncated/ellipsis'd like #giftlist-item-title still is —
   a real heading is allowed to wrap onto a second line for an unusually
   long name, same as every other h2 on the site. Takes the remaining
   space in the header row so the back button stays pinned left. */
.giftlist-detail-heading {
  flex: 1;
  min-width: 0;
}
.giftlist-detail-heading h2 {
  margin: 0;
}

/* The list owner's own avatar, anchoring the header the way
   .section-header__icon anchors About/Gift Lists' own list-view
   headers — the title sits beside it instead of centered in the row
   now that there's something on its left to balance against. */
.giftlist-detail-avatar-wrap {
  flex-shrink: 0;
}

.giftlist-detail-avatar {
  flex-shrink: 0;
  width: 84px;
  height: 84px;
  border-radius: 50%;
  object-fit: cover;
  border: 3px solid #c9a51c;
}
@media only screen and (min-width: 768px) {
  .giftlist-detail-avatar {
    width: 104px;
    height: 104px;
  }
}

.message-thread__with {
  font-size: 1.15rem;
  color: #9a9384;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Same pill treatment as the inbox row's own Archive/Restore buttons —
   an icon-only toggle here read as unclear the same way theirs did. */
/* Its own row entirely outside the title panel, between it and the
   item grid — deliberately not .inner__container (its 0 10% padding/
   5rem margin-bottom don't fit a single centered button row). */
.giftlist-detail-actions {
  display: flex;
  justify-content: center;
  gap: 1rem;
  flex-wrap: wrap;
  /* Now a sibling of the mute-cream title panel, not nested inside it —
     the panel's own background edge is what separates them visually, no
     divider line needed here anymore. Margin on both sides: room below
     the panel, and room above the item grid that follows. */
  margin: 2rem 1.5rem;
}

/* The shared .giftlist-watch-btn class carries margin-left: auto for
   its original header context (pushed right against the title row) —
   here it's centered in its own row instead, so that has to be
   unset or it'd shove the button back to the right edge regardless
   of the row's own justify-content: center above. Sized up from the
   shared class's base too — this is now the button's own dedicated
   row with room to be a real tap target, not a compact corner icon. */
.giftlist-detail-actions .giftlist-watch-btn {
  margin-left: 0;
  padding: 0.9rem 1.8rem;
  font-size: 1.4rem;
  gap: 0.6rem;
}

/* Admin-only Suspend/Unsuspend button — a distinct red accent (vs. the
   neutral watch/message buttons) so it reads as a moderation action, not
   just another regular list control. Flips to a calmer green once the
   account is already suspended (this button becomes "Unsuspend"). */
.giftlist-suspend-btn {
  border-color: rgba(158, 37, 37, 0.4);
  color: #9e2525;
}

.giftlist-suspend-btn.is-suspended {
  border-color: rgba(58, 122, 74, 0.4);
  color: #3a7a4a;
}

/* Disabled specifically while the reason overlay it opened is still
   open — nothing has been confirmed yet, so this must read as
   pending/inert, not as already having happened. */
.giftlist-suspend-btn:disabled {
  opacity: 0.5;
  cursor: default;
}

/* Same [hidden]-vs-author-stylesheet issue as .admin-reason-overlay
   above — .giftlist-watch-btn sets display: inline-flex directly,
   which otherwise overrides [hidden]'s display: none regardless of the
   attribute being present. An id selector always wins on specificity
   regardless of source order, so this reliably wins over that class
   rule without needing !important. */
#giftlist-suspend-btn[hidden] {
  display: none;
}

/* The theme's own loader.css fades `.loader` out via opacity/visibility
   over 0.6s once `.loaded` is added, but `visibility` is a discrete
   property — the browser doesn't actually flip it to hidden until the
   *end* of that transition, not the start. For that whole 0.6s window
   the loader is invisible but still sitting at z-index:9999 on top of
   the entire page, silently swallowing any tap underneath it —
   including a real click on something like Suspend Account right after
   a page reload (the Admin Mode toggle causes exactly that reload).
   pointer-events isn't something that needs to visually transition, so
   switching it off immediately (not gradually) is correct here. */
.loader.loaded {
  pointer-events: none;
}

/* ---------- Admin History ---------- */

.admin-history-empty {
  margin: 2rem 0;
  text-align: center;
  color: rgba(44, 52, 43, 0.6);
  font: normal 400 1.4rem/1.6 "Mulish", sans-serif;
}

.moderation-owner-group {
  margin: 0 0 1.75rem;
}

.moderation-owner-group:last-child {
  margin-bottom: 0;
}

.moderation-owner-link {
  display: block;
  margin: 0 0 0.5rem;
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 1.3rem;
  color: #2c342b;
  text-decoration: underline;
}

.moderation-owner-link:hover {
  color: #9e2525;
}

.admin-history-entry {
  padding: 1.4rem 0;
  border-bottom: 1px solid rgba(44, 52, 43, 0.12);
}

.admin-history-entry:last-child {
  border-bottom: none;
}

.admin-history-entry__summary {
  margin: 0 0 0.3rem;
  font: normal 600 1.4rem/1.5 "Mulish", sans-serif;
  color: #2c342b;
}

.admin-history-target-link {
  color: #2c342b;
  text-decoration: underline;
}

.admin-history-target-link:hover {
  color: #9e2525;
}

.admin-history-entry__reason {
  margin: 0 0 0.3rem;
  font: italic 400 1.3rem/1.5 "Mulish", sans-serif;
  color: #6b6555;
}

.admin-history-entry__meta {
  margin: 0;
  font-size: 1.2rem;
  color: rgba(44, 52, 43, 0.55);
}

/* ---------- Shared admin reason overlay ---------- */

.admin-reason-overlay {
  position: fixed;
  inset: 0;
  z-index: 1500;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
}

/* The [hidden] attribute's UA-stylesheet `display: none` and a class
   selector both have the same specificity — the author stylesheet
   (this file) always wins regardless of the [hidden] attribute being
   present, so `display: flex` couldn't live on the bare .admin-reason-
   overlay rule above without permanently overriding it. This is the
   real display toggle instead: flex only once :not([hidden]). */
.admin-reason-overlay:not([hidden]) {
  display: flex;
}

.admin-reason-overlay__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
}

.admin-reason-overlay__panel {
  position: relative;
  width: 100%;
  max-width: 420px;
  background: #2c342b;
  border-radius: 14px;
  padding: 2rem;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4);
}

/* Bigger and scrollable, unlike every other .admin-reason-overlay__panel
   use (a short message plus a button or two) — this one holds up to 24
   Q&A pairs, so it needs real room and its own internal scroll rather
   than growing the whole page. */
.testing-answers-overlay__panel {
  max-width: 640px;
  max-height: 85vh;
  overflow-y: auto;
  text-align: left;
}

.testing-answers-overlay__close {
  position: absolute;
  top: 1.2rem;
  right: 1.4rem;
  width: 3.2rem;
  height: 3.2rem;
  border: none;
  border-radius: 50%;
  background: rgba(245, 239, 227, 0.1);
  color: #f5efe3;
  font-size: 2.2rem;
  line-height: 1;
  cursor: pointer;
}

.testing-answers-overlay__close:hover {
  background: rgba(245, 239, 227, 0.2);
}

.testing-answers-overlay__panel h3 {
  padding-right: 3rem;
}

.testing-answers-overlay__content {
  margin-top: 1.6rem;
}

.testing-answers-overlay__section-title {
  margin: 2rem 0 1rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid rgba(245, 239, 227, 0.2);
  font-family: "Playfair Display", serif;
  font-size: 1.6rem;
  color: #c9a51c;
}

.testing-answers-overlay__section-title:first-child {
  margin-top: 0;
}

.testing-answers-overlay__qa {
  margin: 0 0 1.4rem;
}

.testing-answers-overlay__question {
  margin: 0 0 0.4rem;
  font-family: "Mulish", sans-serif;
  font-size: 1.35rem;
  color: rgba(245, 239, 227, 0.7);
}

.testing-answers-overlay__answer {
  margin: 0;
  font-size: 1.5rem;
  line-height: 1.5;
  color: #f5efe3;
  white-space: pre-line;
}

.testing-answers-overlay__answer--empty {
  color: rgba(245, 239, 227, 0.4);
  font-style: italic;
}

.admin-reason-overlay__panel h3 {
  margin: 0 0 1.2rem;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 1.8rem;
  color: #f5efe3;
}

.admin-reason-overlay__desc {
  margin: 0 0 1.2rem;
  font-size: 1.28rem;
  line-height: 1.5;
  color: rgba(245, 239, 227, 0.8);
}

.admin-reason-overlay__desc a {
  color: #c9a51c;
  text-decoration: underline;
}

/* Bigger and a little more considered than the plain admin-reason-
   overlay/confirm-overlay uses (a bare "are you sure?" prompt) — this
   is the one overlay every tester sees, not just admins, so it earns
   its own modifier rather than inheriting the smaller, purely-
   functional sizing those share. Scoped to this panel specifically so
   every other .admin-reason-overlay__panel elsewhere is untouched. */
.testing-reminder-overlay__panel {
  max-width: 460px;
  padding: 2.6rem 2.4rem;
  text-align: center;
}

.testing-reminder-overlay__icon {
  font-size: 3rem;
  color: #c9a51c;
  margin-bottom: 1rem;
}

.testing-reminder-overlay__panel h3 {
  font-size: 2.2rem;
  margin: 0 0 1.4rem;
}

.testing-reminder-overlay__message {
  font-size: 1.6rem;
  line-height: 1.6;
  margin: 0 0 1.8rem;
}

.testing-reminder-overlay__progress {
  margin: 0 0 2rem;
}

.testing-reminder-overlay__progress-label {
  margin: 0 0 0.6rem;
  color: rgba(245, 239, 227, 0.65);
  font-family: "Mulish", sans-serif;
  font-size: 1.3rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  text-align: left;
}

.testing-reminder-overlay__progress .testing-checklist-progress-track {
  margin: 0;
}

/* Stacked, not the usual inline side-by-side pairing — the action
   button is the whole point of the overlay and gets the same big gold-
   pill treatment as .testing-questionnaire-start-btn elsewhere, so it
   needs its own row rather than being squeezed next to "Not now". */
.testing-reminder-overlay__panel .wishlist-entry-form__actions {
  flex-direction: column;
  align-items: center;
  gap: 1.2rem;
}

.testing-reminder-overlay__panel .testing-questionnaire-start-btn {
  width: 100%;
  justify-content: center;
}

/* .testing-questionnaire-secondary-btn's own colors (muted brown text,
   dark-tinted border) were tuned for the checklist page's light
   background — same problem .btn-small--cancel/--delete solve below
   for this same dark panel, just for a different button class. Real
   padding (not a bare text link) so "Not now" stays comfortably
   tappable on mobile without visually competing with the gold primary
   button above it. */
.testing-reminder-overlay__panel .testing-questionnaire-secondary-btn {
  border-color: rgba(245, 239, 227, 0.35);
  color: rgba(245, 239, 227, 0.75);
}

.testing-reminder-overlay__panel .testing-questionnaire-secondary-btn:hover {
  border-color: #f5efe3;
  color: #f5efe3;
}

/* .btn-small--cancel/--delete's own colors (#6b6555 muted brown,
   #9e2525 dark red) were tuned for this site's usual cream/white panel
   backgrounds — both read poorly on this panel's dark green, and
   --cancel's hover color (#2c342b) is literally this panel's own
   background, so the button visually disappears the moment you point
   at it. Overridden here to light, high-contrast tones instead, scoped
   to this panel so every other use of these classes elsewhere on the
   site (which really does sit on a light background) is untouched. */
.admin-reason-overlay__panel .btn-small--cancel {
  color: rgba(245, 239, 227, 0.75);
}
.admin-reason-overlay__panel .btn-small--cancel:hover {
  color: #f5efe3;
}

/* #confirm-overlay's two buttons are the entire interface here (no
   form fields, nothing else to interact with), so .btn-small's default
   look — sized for a small in-form action sitting next to other
   controls — reads as too easy to miss, and Confirm in particular
   looks like a plain underlined text link rather than a real button.
   Sized and bordered up here, same "give it a real button shape"
   treatment .testing-questionnaire-secondary-btn already got in the
   testing reminder overlay. */
#confirm-overlay .btn-small {
  padding: 0.9rem 1.8rem;
  font-size: 1.4rem;
  border-radius: 20px;
}

#confirm-overlay .btn-small--cancel {
  text-decoration: none;
  border: 1px solid rgba(245, 239, 227, 0.35);
}

#confirm-overlay .btn-small--cancel:hover {
  border-color: #f5efe3;
}
.admin-reason-overlay__panel .btn-small--delete {
  color: #e0908f;
}
.admin-reason-overlay__panel .btn-small--delete:hover {
  color: #f0b3b2;
}

.admin-reason-overlay__desc a:hover {
  color: #f5efe3;
}

/* Testing Phase menu overlay — its checkbox(es) sit directly under the
   description paragraph with no breathing room before the action row,
   and its Continue button (.btn-small--cancel, same class #confirm-overlay
   uses for its own primary action) never picked up that overlay's own
   "give it a real button shape" treatment below, since that's scoped by
   id to #confirm-overlay specifically — left as a bare underlined link
   here otherwise. Sized up a step further than #confirm-overlay's own
   buttons too, matching Xavier's own "make the buttons bigger" call. */
#testing-phase-menu-checkboxes {
  margin-top: 0.5rem;
  margin-bottom: 1.5rem;
}

/* .wishlist-quantity-unlimited's own color (#6b6555, a muted brown-gray)
   is tuned for the site's usual light/cream panel backgrounds, same
   problem .btn-small--cancel/--delete solve above for this exact panel
   — reads as low-contrast/grayed-out on this overlay's dark green even
   when the checkbox isn't actually disabled. The genuinely-disabled
   case (openTestingPhaseMenu() in site.js sets opacity: 0.5 on the
   whole label) still reads correctly relative to this lighter base. */
#testing-phase-menu-overlay .wishlist-quantity-unlimited {
  color: rgba(245, 239, 227, 0.9);
}

#testing-phase-menu-overlay .btn-small {
  padding: 1.1rem 2.2rem;
  font-size: 1.5rem;
  border-radius: 24px;
}

#testing-phase-menu-overlay .btn-small--cancel {
  text-decoration: none;
  border: 1px solid rgba(245, 239, 227, 0.35);
}

#testing-phase-menu-overlay .btn-small--cancel:hover {
  border-color: #f5efe3;
}

.admin-reason-overlay__input {
  width: 100%;
  padding: 0.85rem 1.2rem;
  border-radius: 14px;
  border: 1px solid rgba(245, 239, 227, 0.35);
  background: rgba(245, 239, 227, 0.08);
  color: #f5efe3;
  font-size: 1.44rem;
  font-family: "Mulish", sans-serif;
  resize: none;
}

.admin-reason-overlay__input::placeholder {
  color: rgba(245, 239, 227, 0.6);
}

/* .inner__container's own base 5rem margin-bottom normally collapses
   away via its :last-of-type rule since the header is genuinely the
   only .inner__container left in this panel now that
   .giftlist-detail-actions lives outside it — redundant with that rule
   today, kept explicit as a guard in case the panel gains a sibling
   container again. */
#giftlist-detail-view .message-thread__header {
  margin-bottom: 0;
  /* The shared class's own border-bottom (Messages thread header) used
     to sit right above the watch-list row when that row still lived
     inside this same panel — now that the row has moved out entirely,
     it's just a stray line at the panel's own bottom edge. */
  border-bottom: none;
}

/* Same shared .message-thread__header border-bottom, same "stray line
   at the panel's own bottom edge" problem — these four title rows each
   sit alone in their own mute-cream panel with nothing else in it, so
   the divider never separated anything to begin with. Left alone only
   on the actual Messages thread header, where a "Conversation with X"
   line still follows it in the same panel. */
#giftlist-item-view .message-thread__header,
#wishlist-item-view .message-thread__header,
#wishlist-item-editor-view .message-thread__header,
#wishlist-item-preview-view .message-thread__header {
  border-bottom: none;
}

.message-thread__archive-btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: none;
  border: 1px solid rgba(44, 52, 43, 0.2);
  border-radius: 20px;
  padding: 0.4rem 1rem;
  font-size: 1.15rem;
  font-weight: 600;
  color: #6b6555;
  cursor: pointer;
}

.message-thread__archive-btn:hover {
  color: #2c342b;
  border-color: #2c342b;
}

.message-thread__reveal-btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: none;
  border: 1px solid rgba(44, 52, 43, 0.2);
  border-radius: 20px;
  padding: 0.4rem 1rem;
  font-size: 1.15rem;
  font-weight: 600;
  color: #6b6555;
  cursor: pointer;
}

.message-thread__reveal-btn:hover {
  color: #2c342b;
  border-color: #2c342b;
}

.message-thread__reveal-btn[hidden] {
  display: none;
}

.message-thread__identity-notice {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin: 1rem 1.5rem 0;
  padding: 1rem 1.25rem;
  border: 1px solid #eee6d6;
  border-radius: 14px;
  background: #f9f6ee;
  color: #2c342b;
}

.message-thread__identity-notice[hidden] {
  display: none;
}

.message-thread__identity-notice i {
  color: #c9a51c;
  font-size: 1.1em;
}

.message-thread__identity-notice-text {
  font-size: 1.15rem;
  font-weight: 600;
}

/* Filled gold once archived (Restore) — same "this is the state that
   needs attention" treatment .btn-small--save uses elsewhere, rather
   than just a color change on an otherwise-identical outline. */
.message-thread__archive-btn.is-archived {
  color: #2c342b;
  border-color: #c9a51c;
  background: #c9a51c;
}

.message-thread__archive-btn.is-archived:hover {
  background: #b3910c;
  border-color: #b3910c;
}

/* ---------- Message bubbles ---------- */

.message-thread__list {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1.5rem;
}

/* Both wraps are sticky (not fixed) so they stay within the panel's own
   width instead of the full viewport (this panel is a centered card
   with open space on either side at desktop widths; position:fixed
   would float the buttons out in that empty space instead of over the
   card). Whichever of #messages/.inner__content actually scrolls at the
   current breakpoint (see getMessagesScrollEl() in site.js) is what
   "top"/"bottom" here stick against. pointer-events:none on the wrap
   (and auto on the button itself) keeps the empty space on either side
   of the centered button from blocking clicks on whatever's underneath. */
.message-thread__top-wrap,
.message-thread__scroll-actions {
  position: sticky;
  display: flex;
  justify-content: center;
  padding: 0 1.5rem;
  pointer-events: none;
  z-index: 2;
}

.message-thread__top-wrap {
  top: 1rem;
}

.message-thread__scroll-actions {
  bottom: 1rem;
}

.message-thread__top-btn,
.message-thread__reply-jump-btn {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  border: none;
  border-radius: 20px;
  padding: 0.6rem 1.1rem;
  font-size: 1.15rem;
  font-weight: 600;
  color: #2c342b;
  background: #c9a51c;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2);
  cursor: pointer;
}

.message-thread__top-btn:hover,
.message-thread__reply-jump-btn:hover {
  background: #b3910c;
}

.message-thread__top-btn {
  border-radius: 50%;
  padding: 0.7rem;
}

/* Same reasoning as .comment-form[hidden] elsewhere in this file — these
   buttons' own display:inline-flex is equal specificity to (and later
   than) the browser's [hidden]{display:none}, so toggling the hidden
   attribute alone wouldn't actually hide them without this override. */
.message-thread__top-btn[hidden],
.message-thread__reply-jump-btn[hidden] {
  display: none;
}

/* Full-width off-colored rows rather than left/right-aligned chat
   bubbles — with every message already carrying its own avatar/name
   (see renderMessageBubble() in site.js), a narrow aligned bubble was
   reading too much like a texting app, which isn't the tone here.
   .message-thread__list's own flex-direction:column already stretches
   each row to the container's full width by default (no explicit
   width/align-self needed). */
.message-bubble {
  padding: 1rem 1.25rem;
  border-radius: 8px;
  background: #f9f6ee;
}

.message-bubble--mine {
  background: #eee6d6;
}

.message-bubble__header {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  margin-bottom: 0.4rem;
}

.message-bubble__avatar {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  object-fit: cover;
}

.message-bubble__avatar--anon {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #2c342b;
  color: #c9a51c;
  font-size: 1.1rem;
}

.message-bubble__sender {
  font-size: 1.2rem;
  font-weight: 600;
  color: #2c342b;
}

.message-bubble__text {
  margin: 0;
  font-size: 1.36rem;
  color: #444;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}

.message-bubble--deleted .message-bubble__text {
  font-style: italic;
  color: #9a9384;
}

.message-bubble__meta {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-top: 0.3rem;
}

.message-bubble__time {
  font-size: 1.05rem;
  color: #9a9384;
}

.message-bubble__edited-tag {
  font-size: 1.05rem;
  font-style: italic;
  color: #9a9384;
}

.message-bubble__edit-btn,
.message-bubble__delete-btn {
  background: none;
  border: none;
  padding: 0;
  font-size: 1.05rem;
  font-weight: 600;
  color: #9a9384;
  cursor: pointer;
  text-decoration: underline;
}

.message-bubble__edit-btn:hover {
  color: #2c342b;
}

.message-bubble__delete-btn:hover {
  color: #9e2525;
}

.message-thread__archived-notice {
  margin: 0;
  padding: 1rem 1.5rem;
  font-size: 1.2rem;
  color: #9a9384;
  border-top: 1px solid #eee6d6;
}

.message-thread__form {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  margin: 0 1.5rem 1.5rem;
  width: calc(100% - 3rem);
  box-sizing: border-box;
  padding: 1rem 1.25rem;
  border: 1px solid #eee6d6;
  border-radius: 14px;
  background: #f9f6ee;
}

/* Same reasoning as .comment-form[hidden] elsewhere in this file — this
   rule's own display:flex is equal specificity to (and later than) the
   browser's [hidden]{display:none}, so .prop("hidden", true) alone
   doesn't actually hide the reply form on an archived thread without
   this override. */
.message-thread__form[hidden] {
  display: none;
}

.message-thread__form .message-thread__input {
  flex: 1;
  /* This textarea had no styling of its own before — it was inheriting
     nothing but the theme's generic border:none/transparent-background
     reset (main.css), so on some mobile browsers it fell back to native
     textarea chrome (square corners, its own default border) instead of
     the site's own look. Same treatment as .comment-form__input/
     .message-compose__input elsewhere, for the same reason. */
  resize: none;
  height: 8.6rem;
  min-height: 40px;
  max-height: 140px;
  padding: 0.9rem 1.2rem;
  border-radius: 8px;
  border: 1px solid rgba(44, 52, 43, 0.2);
  background: #fff;
  color: #2c342b;
  font-size: 1.36rem;
  font-family: "Mulish", sans-serif;
}

/* A gold focus ring on whatever text field currently has focus, across
   every text input/textarea/select in Messages and comments — without
   one, a textarea with nothing typed in it yet gave no visual sign you
   were actually in it, which is especially easy to lose track of on
   mobile once the keyboard has shoved the rest of the page (including
   the "back to top" button) out of view. */
.comment-form__input:focus,
.message-thread__input:focus,
.message-compose__input:focus,
.message-compose__subject:focus,
.message-compose__recipient-select:focus {
  outline: none;
  border-color: #c9a51c;
  box-shadow: 0 0 0 3px rgba(201, 165, 28, 0.25);
}

/* ---------- Compose (New Message) ---------- */

.message-compose__form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1.5rem;
}

.message-compose__field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  font-size: 1.2rem;
  font-weight: 600;
  color: #6b6555;
}

.message-compose__recipient-select,
.message-compose__subject,
.message-compose__input {
  font-family: "Mulish", sans-serif;
  font-size: 1.36rem;
  color: #2c342b;
  border: 1px solid rgba(44, 52, 43, 0.2);
  border-radius: 8px;
  padding: 0.7rem 1rem;
  background: #fff;
  /* The theme's own "form input, form textarea" rule (main.css) sets a
     2rem bottom margin meant for its own stacked contact-form fields —
     this form uses its own gap between fields instead. */
  margin: 0;
}

.message-compose__input {
  resize: none;
  height: 8.6rem;
  min-height: 40px;
  max-height: 140px;
}

.message-compose__anonymous {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 1.2rem;
  color: #6b6555;
}

/* Same theme-wide "form input" rule as above forces every <input> in any
   <form> to display:block/width:100%/height:4.4rem — fine for the text
   fields, but it stretches this checkbox to the same full-width block,
   which is what made it render as a giant rectangle instead of a normal
   checkbox. A single class already beats "form input"'s two-element
   selector on specificity, so this simply resets the properties that
   rule set. */
.message-compose__anonymous-check {
  display: inline-block;
  width: auto;
  height: auto;
  margin: 0;
}

/* Same reasoning as .comment-form[hidden] above — .message-compose__field
   sets display:flex at equal specificity to (and later in the cascade
   than) the browser's own [hidden]{display:none}, so without this the
   attribute alone doesn't actually hide whichever of
   recipient-field/recipient-fixed isn't in use. */
.message-compose__field[hidden] {
  display: none;
}

/* ---------- Comment thread (Chat now; Events/Gift Lists/items later) ----------
   One level of replies only, matching the comments table's parent_id +
   reply_to_user_id design — a reply's own Reply button is hidden below,
   there's no third level to open. */

.comment-thread {
  display: flex;
  flex-direction: column;
}

.comment-list {
  display: flex;
  flex-direction: column;
}

.comment-item {
  display: flex;
  gap: 1rem;
  padding: 1.25rem 1.5rem;
  border-bottom: 1px solid #eee6d6;
}

.comment-list > .comment-item:last-child {
  border-bottom: none;
}

.comment-item__avatar {
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  object-fit: cover;
}

.comment-item__body {
  flex: 1;
  min-width: 0;
}

.comment-item__top {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.comment-item__name {
  font-family: "Cinzel", serif;
  font-weight: 600;
  font-size: 1.36rem;
  color: #2c342b;
}

.comment-item__time {
  font-size: 1.12rem;
  color: #9a9384;
}

.comment-item__reply-to {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 1.12rem;
  color: #c9a51c;
  font-weight: 600;
}

.comment-item__text {
  margin: 0.3rem 0 0;
  font-size: 1.44rem;
  color: #444;
  line-height: 1.5;
}

/* Soft-deleted (has replies, so the row stays to keep them alive — see
   delete_comment.php) — reads as a system placeholder, not real
   content, same muted/italic treatment removed-item banners elsewhere
   on the site already use for "this used to be something." */
.comment-item__text--deleted {
  font-style: italic;
  color: #9a9384;
}

.comment-item__edited-tag {
  margin-left: 0.4rem;
  font-size: 1.1rem;
  font-style: italic;
  color: #9a9384;
}

.comment-item__actions {
  display: flex;
  gap: 1rem;
  margin-top: 0.5rem;
}

.comment-item__reply-btn,
.comment-item__edit-btn,
.comment-item__delete-btn,
.comment-item__message-btn,
.comment-item__report-btn {
  display: inline-block;
  background: none;
  border: none;
  padding: 0;
  font-size: 1.2rem;
  font-weight: 600;
  color: #9a9384;
  cursor: pointer;
  text-decoration: underline;
}

.comment-item__reply-btn:hover,
.comment-item__edit-btn:hover {
  color: #2c342b;
}

.comment-item__delete-btn:hover,
.comment-item__report-btn:hover {
  color: #9e2525;
}

.comment-item__message-btn:hover {
  color: #c9a51c;
}

/* The name doubles as a second, faster way to start a PM with that
   person (site.js) — this is what keeps it from reading as plain static
   text like it used to. Gold rather than the usual dark hover color,
   matching the "Message" action-row link's own hover color, so the two
   entry points to the same action read as connected. */
.comment-item__name--link {
  cursor: pointer;
}

.comment-item__name--link:hover {
  color: #c9a51c;
  text-decoration: underline;
}

.comment-edit-form {
  margin-top: 0.5rem;
}

/* Deliberately no separate textarea styling here — it shares
   .comment-form__input with the reply/post boxes so all three look and
   resize identically. */

.comment-edit-form__actions {
  display: flex;
  gap: 0.75rem;
  margin-top: 0.5rem;
}

.btn-small {
  border: none;
  border-radius: 20px;
  padding: 0.3rem 0.9rem;
  font-size: 1.2rem;
  font-weight: 600;
  cursor: pointer;
}

/* The base size above (0.3rem/0.9rem padding, 12px font) already gets a
   mobile-only bump for touch targets (44px min-height, see the "Phone
   touch targets" block at the end of this file) — but nothing sizes it
   up for desktop, so every .btn-small button (Save/Cancel/Preview/
   Delete in the item editor, comment Post/Cancel, image picker, etc.)
   stayed at that tiny base size on a mouse-driven screen too. Xavier
   flagged this directly while sizing down the wishlist Add Item/Note/
   Section row to this same class — not a mobile problem, a desktop one,
   so it's fixed on the shared base class rather than scoped to just
   that row. Placed directly after the base rule (equal specificity,
   later wins) rather than at the end of the file with the other
   breakpoint rules, so it can't be silently overridden by the base rule
   coming later in source order. */
@media only screen and (min-width: 768px) {
  .btn-small {
    padding: 0.5rem 1.3rem;
    font-size: 1.35rem;
  }
}

.btn-small--save {
  background: #c9a51c;
  color: #2c342b;
}

.btn-small--save:hover {
  background: #b3910c;
}

.btn-small--save:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.btn-small--cancel {
  background: transparent;
  color: #6b6555;
  text-decoration: underline;
}

.btn-small--cancel:hover {
  color: #2c342b;
}

.btn-small--delete {
  background: transparent;
  color: #9e2525;
  text-decoration: underline;
}

.btn-small--delete:hover {
  color: #7a1c1c;
}

/* Same bordered look as .gc-btn--edit — a neutral secondary action,
   visually distinct from Cancel's understated underline (dismissive)
   and Save's solid dark fill (committing), since Preview is neither. */
.btn-small--preview {
  background: transparent;
  color: #2c342b;
  border: 1px solid rgba(44, 52, 43, 0.35);
}

.btn-small--preview:hover {
  background: rgba(44, 52, 43, 0.08);
}

.wishlist-section-empty {
  grid-column: 1 / -1;
  font-size: 1.36rem;
  color: #9a9384;
  font-style: italic;
  margin: 0;
}

/* A section's title bar and its entries are one visual unit, not two
   independent panels — the site's default .panel spacing (2.5rem gap,
   rounded on all 4 corners) is right for genuinely separate groupings
   elsewhere (Account's Avatar/Notifications/Security panels), but reads
   as two disconnected boxes here where they're actually one section.
   Zeroing the gap and splitting the rounding between just the outer top
   and outer bottom corners is what makes the two panels read as a
   single continuous shape instead. */
.wishlist-section .inner__block.panel:first-child {
  margin-bottom: 0;
  border-radius: 18px 18px 0 0;
}

.wishlist-section .inner__block.panel:last-child {
  border-radius: 0 0 18px 18px;
}

/* Zeroed (edit mode only — .wishlist-section itself is shared with the
   read view, which never renders the add-row, so this can't be scoped
   to that shared class alone or it'd flatten the read view's own last
   card too). The real goal is centering the add-row's buttons in the
   space below its own divider line: relative to that line, "above" is
   just margin-top (1.25rem — margin sits outside the row's own border,
   never doubled), while "below" was padding-top + content + padding-
   bottom + THIS panel's own extra bottom padding stacked after it —
   double the space below vs. above, not centered at all despite the
   row's own top/bottom padding already being equal to each other.
   Zeroing this panel's contribution leaves the row's own matching
   1.25rem top/bottom padding as the sole source of space around the
   buttons, which actually centers them relative to the divider line. */
#wishlist-sections .wishlist-section .inner__block.panel:last-child {
  padding-bottom: 0;
}

/* justify-content: center — Xavier wanted this row centered like the
   page-header's own Add Item/Note/Section row; it had no justify-content
   at all before, so it defaulted to flex-start (left-aligned). */
/* border-top color dropped to transparent (not removed outright) —
   Xavier's call: the solid cream line looked bad against the new
   diagonal-stripe section backgrounds, but the border still reserves
   its own 1px of space, so nothing about the row's actual layout/
   spacing changes, just its visibility. */
.wishlist-section-add-row {
  display: flex;
  justify-content: center;
  gap: 0.75rem;
  margin-top: 1.25rem;
  border-top: 1px solid transparent;
}

/* This row also carries .inner__container.grid-block (see the render
   comment above, in site.js) to line its horizontal inset up with the
   grid's own cards — but that class pair's own padding shorthand
   (main.css, "0 calc(10% - 15px)") is a 2-class selector, which beats
   .wishlist-section-add-row's single-class padding-top above outright
   regardless of file order. The result: a real gap before the divider
   (margin-top) but none after it, so the buttons sat flush against the
   border instead of centered in the space below it — needs the same
   2-class specificity to actually win. */
.wishlist-section-add-row.grid-block {
  padding-top: 1.25rem;
  padding-bottom: 1.25rem;
}

/* This row is now a true sibling of the grid (see the comment in
   site.js's renderWishlistSection() for why), so the grid is no
   longer .inner__container's own :last-of-type within this panel —
   its default 5-6rem margin-bottom (main.css) would otherwise reappear
   as a large, unwanted gap above this row. Own spacing (margin-top/
   padding-top/border-top above) already does that job, tighter and on
   purpose. */
.wishlist-section .grid-block {
  margin-bottom: 0;
}

/* Same reasoning as .wishlist-section-add-row's own border-top above —
   this form replaces that row in place (same divider role, same clash
   against the striped background), so it gets the identical treatment. */
.wishlist-section-add-form {
  margin-top: 1.25rem;
  border-top: 1px solid transparent;
}

/* Same bug, same fix as .wishlist-section-add-row.grid-block above: this
   form also carries .inner__container.grid-block (for its horizontal
   inset, matching the add-row it replaces), and that pair's own padding
   shorthand (main.css, "0 calc(10% - 15px)") is a 2-class selector that
   silently beats .wishlist-section-add-form's single-class padding-top
   outright — the field had horizontal padding but none vertically.
   Needs the same 2-class specificity to actually win; padding-bottom
   added too so the form isn't flush against this panel's own bottom
   edge (that panel has 0 of its own, per the add-row centering fix
   above) the same way the add-row itself isn't. */
.wishlist-section-add-form.grid-block {
  padding-top: 1.25rem;
  padding-bottom: 1.25rem;
}

/* The top toolbar's own Add Note panel inherited the generic
   content-panel padding (.inner--centered .inner__block.panel,
   3.5rem/35px top+bottom) — way more than the per-section Add Note
   form above, which was tuned down to 1.25rem/12.5px. Matches it.
   ID selector needed to beat that 3-class rule; simpler than chasing
   matching specificity the way the grid-block fixes above had to. */
#wishlist-add-note-panel {
  padding-top: 1.25rem;
  padding-bottom: 1.25rem;
}

.comment-item__replies {
  display: flex;
  flex-direction: column;
  margin-top: 1rem;
  padding-left: 1.5rem;
  border-left: 2px solid #eee6d6;
}

.comment-item__replies .comment-item {
  padding: 0.9rem 0;
  border-bottom: none;
}

.comment-item__replies .comment-item__avatar {
  width: 36px;
  height: 36px;
}

.comment-form,
.comment-reply-form {
  display: flex;
  align-items: flex-start;
  gap: 2rem;
}

.comment-form {
  padding: 1.25rem 1.5rem;
  border-top: 1px solid #eee6d6;
  background: #f9f6ee;
}

/* Without this, the [hidden] attribute in the static HTML has no actual
   effect — .comment-form's own "display: flex" above is equal
   specificity and later in the cascade, so it silently wins over the
   browser's default [hidden]{display:none} and the form shows on page
   load regardless of the attribute. jQuery's .hide()/.show() elsewhere
   in this file still work fine afterward either way, since an inline
   style always overrides any external stylesheet rule. */
.comment-form[hidden] {
  display: none;
}

.comment-add-trigger {
  display: block;
  width: calc(100% - 3rem);
  margin: 1.25rem 1.5rem;
}

.comment-reply-form {
  margin-top: 0.75rem;
}

.comment-reply-form__input-group {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.comment-form__avatar {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  object-fit: cover;
}

.comment-form__input {
  flex: 1;
  resize: none;
  /* The rows="3" attribute in the markup is kept only for accessibility/
     semantics — actual sizing is this explicit height instead, because
     browsers size a rows-based textarea using the font's own internal
     line metrics rather than this element's CSS line-height, which for
     this font/size combo rendered at the full max-height (140px) instead
     of anything resembling 3 lines. */
  height: 8.6rem;
  min-height: 40px;
  max-height: 140px;
  padding: 0.9rem 1.2rem;
  /* Smaller than the site's usual 20px pill radius on purpose — this box
     is tall enough to scroll, and a straight-edged scrollbar sitting
     right at a heavily-rounded corner is what made it look like it was
     spilling out past the edge. A softer, smaller radius keeps some
     roundness without that clash. */
  border-radius: 8px;
  border: 1px solid rgba(44, 52, 43, 0.2);
  background: #fff;
  color: #2c342b;
  font-size: 1.36rem;
  font-family: "Mulish", sans-serif;
}

.comment-form__submit {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: none;
  background: #c9a51c;
  color: #2c342b;
  font-size: 1.36rem;
  cursor: pointer;
  transition: background 0.2s ease;
}

.comment-form__submit:hover {
  background: #b3910c;
}

.comment-form__submit:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* Shown inline under a comment/reply form when posting fails — both
   forms sit on white panels, so dark/red text (matching the site's
   existing error color elsewhere) reads fine here, unlike .form-message
   which assumes a dark background. */
.comment-form__error {
  display: block;
  width: 100%;
  margin-top: 0.5rem;
  font-size: 1.2rem;
  color: #9e2525;
}

/* Collapse to nothing while empty — otherwise an unused error slot still
   reserves a line of height + margin, throwing off spacing in forms like
   the Invites send form where the button sits right after it. */
.comment-form__error:empty {
  margin-top: 0;
  display: none;
}

/* ---------- Phone touch targets ---------- */

/* Placed at the very end of this file on purpose: several rules below
   deliberately override earlier mobile media queries at equal
   specificity (notably `.gc-grid .gc-btn`, which shrinks buttons to an
   11px font on small screens), and at equal specificity the later rule
   wins. Moving this block earlier would silently undo it.

   The problem being fixed: a fingertip covers roughly 44px, but on a
   phone the Claim button measured 60x19, the back arrow 14x18, and the
   anonymous-claim checkbox 13x13 — all well under half the tappable
   area they need, on the site's most important action. The theme was
   making controls *smaller* exactly where they switch from a mouse to a
   finger. 44px is Apple's floor (Material's is 48); this aims at 44 as
   the compromise that costs the least visual weight.

   Scoped to phones only, so desktop's more compact proportions are
   untouched. */
@media only screen and (max-width: 767px) {
  /* Both selectors needed: the bare class for detail-page buttons, and
     the .gc-grid-scoped one to beat the shrink rule above. */
  .gc-btn,
  .gc-grid .gc-btn {
    min-height: 44px;
    padding: 0.7rem 1.4rem;
    font-size: 1.3rem;
  }

  .giftlist-watch-btn {
    min-height: 44px;
    padding: 0.7rem 1.4rem;
    font-size: 1.25rem;
  }

  /* The arrow glyph stays its current size — only the hit area grows,
     via padding rather than width/height, so the icon doesn't scale up
     with it. The negative margin keeps the arrow optically where it was
     despite the bigger box, so the header doesn't visibly shift. */
  .message-back {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    margin-left: -0.8rem;
  }

  /* The whole label is the tap target, not just the 13px box — this is
     the single easiest control on the page to miss, and missing it
     silently changes whether a claim is anonymous. */
  .gc-claim-anon {
    display: flex;
    align-items: center;
    min-height: 44px;
  }

  .gc-claim-anon-check {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
  }
}

/* Continues the touch-target block above (kept separate only so the two
   passes stay legible; both must remain at the end of this file to win
   on source order). This pass covers the shared controls the first one
   missed — measured by walking every section at 375px and collecting
   anything under 44px.

   .btn-small is deliberately handled by its base class rather than per
   screen: it's the Save/Cancel/Delete control used throughout the
   wishlist editor, image picker, link rows and comment forms, so fixing
   it here covers Edit Mode too, which is otherwise awkward to audit
   without creating a draft. */
@media only screen and (max-width: 767px) {
  .btn-small {
    min-height: 44px;
    padding: 0.5rem 1.2rem;
  }

  /* Checkbox rows: the <label> is the real tap target, so it carries the
     44px floor while the box itself only grows enough to be visible.
     Both notification toggles live in this pattern. */
  .account-settings__row {
    min-height: 44px;
  }

  .account-settings__row input[type="checkbox"] {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
  }

  /* Watching list rows (Account > Notifications). The name is a real
     click-through and the X cancels the subscription, so both need to be
     hittable without zooming. */
  .watching-row__name {
    min-height: 44px;
  }

  .watching-row__remove {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Cancels the wider box's own edge so the X stays optically aligned
       with the row's right edge rather than appearing inset. */
    margin-right: -0.5rem;
  }

  .user-images-pager__prev,
  .user-images-pager__next {
    min-width: 44px;
    min-height: 44px;
  }

  .accordion-header {
    min-height: 44px;
  }
}

/* Last of the sub-44px controls found by the sweep — the sign-in
   provider buttons on the logged-out screen, 3px short. Trivial, but
   it's the very first thing a new family member taps, so it may as well
   clear the same floor as everything else. */
@media only screen and (max-width: 767px) {
  .btn-auth {
    min-height: 44px;
  }
}

/* Xavier's own follow-up, found by hand rather than the sweep above: the
   Star toggle "felt too small, especially with big fingers" on mobile.
   Only .wishlist-star-btn needs the 44px floor — .wishlist-star-badge is
   the read-only equivalent shown on other people's cards, nothing to tap
   there. Originally sized past the floor to 48px for headroom, but that
   read as visually too big sitting in the tight top corner of a gift
   card thumbnail — landed on the plain 44px floor itself as the right
   balance between "easy to tap" and "not oversized." */
@media only screen and (max-width: 767px) {
  .wishlist-star-btn {
    width: 44px;
    height: 44px;
  }
}

/* iOS Safari auto-zooms the whole page when you focus a text field whose
   font-size is under 16px, then leaves you zoomed in and panning to get
   back. Every field on this site was under that threshold (most 13.6px,
   the image-picker URL box 13px), which is almost certainly why
   index.html's viewport meta carried `maximum-scale=1` — that suppresses
   the zoom, but at the cost of disabling pinch-to-zoom entirely for
   everyone. Bad trade on a site whose users include people who need to
   zoom in to read at all, so both halves are fixed together: fields go
   to 16px here, and that meta restriction is lifted.

   16px is a literal, not a rem: it's a hard threshold iOS defines in CSS
   pixels, and this stylesheet sits on a 62.5% root (1rem = 10px), so
   expressing it relatively would be fragile if that root ever changed.
   Phones only — desktop keeps its more compact fields.

   `!important` is deliberate and load-bearing here. Every field carries
   its own component class (.comment-form__input, .message-compose__subject
   and so on) at specificity 0,1,0, which outranks a bare `textarea` or
   `select` selector at 0,0,1 — a first attempt without it left all eight
   textareas and selects still at 13.6px while the inputs happened to win
   on their longer :not() chain. Rather than enumerate every current class
   (which silently regresses the moment a new styled field is added), this
   states the rule as what it actually is: a floor that component styling
   is not permitted to drop below. */
@media only screen and (max-width: 767px) {
  input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]),
  textarea,
  select,
  [contenteditable="true"] {
    font-size: 16px !important;
  }
}

/* Two bits of secondary text that fall below comfortable reading size on
   a phone. Nudged rather than normalised to body size, so they still read
   as secondary: the group label stays a small uppercase category marker,
   and the hint stays quieter than the control it explains. Worth doing at
   all because this family includes people who find small type genuinely
   hard, which is the same reason pinch-to-zoom was restored above. */
@media only screen and (max-width: 767px) {
  .watching-group__label {
    font-size: 1.2rem;
  }

  .account-security__hint {
    font-size: 1.3rem;
  }
}

/* Section headings on phones. 4.4rem (44px) is a desktop display size;
   at 375px a single long word like "ACCOUNT" or "MESSAGES" is wider than
   the space left beside the icon, and because `.section-header > div`
   carries `min-width: 0` (needed so the header box itself stops short of
   the close X) the box shrinks while the glyphs simply overflow it. Net
   result: the heading text rendered straight through the X, overlapping
   it by 62px, even though every box-based measurement said there was
   clearance. Measuring a Range over the text rather than the element is
   what actually catches this.

   Reducing the size is the right fix rather than breaking the word:
   hyphenating a display heading mid-word ("ACCO-UNT") looks far worse
   than a slightly smaller title, and 44px was outsized on a phone anyway.
   overflow-wrap is kept only as a backstop so no future longer word can
   reintroduce the overflow. */
@media only screen and (max-width: 767px) {
  .section-header h2,
  .giftlist-detail-heading h2 {
    font-size: 2.6rem;
    overflow-wrap: break-word;
  }
}

/* Scaling the heading down above left the script subtitle at 34px,
   i.e. LARGER than the 26px title it sits under, inverting the
   hierarchy (desktop is 44 vs 34, which reads correctly). Brought down
   to preserve roughly the original ratio, which also stops "Your
   profile & photo" wrapping onto a second line and reclaims some
   vertical space at the top of every section on a phone. */
@media only screen and (max-width: 767px) {
  .section-header .section__subtitle,
  .giftlist-detail-heading .section__subtitle {
    font-size: 2rem;
  }
}

/* A further boost for My Wish List/Gift Lists specifically, on top of
   the sitewide mobile shrink just above — Xavier's call after testing
   the font packs on a real phone: 26px/20px reads as genuinely small,
   not just compact, especially for anyone who finds small type hard to
   read. Scoped to .wishlist-panel-title/.wishlist-panel-subtitle (and
   their Gift List counterparts) rather than raising the *general*
   .section-header h2/.section__subtitle rule above — that rule's
   26px ceiling is deliberately load-bearing elsewhere (a documented fix
   for a single long word like "ACCOUNT" colliding with the close
   button at 375px, see its own comment), and this account/wishlist
   title text is short/multi-word enough not to carry that same
   overflow risk at a larger size. `.section-header .wishlist-panel-title`
   (two classes) intentionally outguns the plain `.section-header h2`
   (one class + one element) it's layered on top of — CSS specificity
   counts class matches before element matches, so this wins regardless
   of which rule comes later in the file. Pack-specific headline
   overrides below (Quirky/Sketchy) get their own matching bump so nudging
   this base value doesn't leave them stuck at the old ratio. */
@media only screen and (max-width: 767px) {
  .section-header .wishlist-panel-title,
  .giftlist-detail-heading #giftlist-detail-title {
    font-size: 3.2rem;
  }
  .section-header .wishlist-panel-subtitle,
  #wishlist-headline-text,
  .giftlist-detail-heading #giftlist-detail-subtitle {
    font-size: 2.4rem;
  }
  .wishlist-font-pack--fancy #wishlist-headline-text,
  .wishlist-font-pack--fancy #giftlist-detail-subtitle,
  .wishlist-font-pack--fancy .wishlist-panel-subtitle {
    font-size: 2.76rem;
  }
  .wishlist-font-pack--quirky #wishlist-headline-text,
  .wishlist-font-pack--quirky #giftlist-detail-subtitle,
  .wishlist-font-pack--quirky .wishlist-panel-subtitle {
    font-size: 2.16rem;
  }
  .wishlist-font-pack--sketchy #wishlist-headline-text,
  .wishlist-font-pack--sketchy #giftlist-detail-subtitle,
  .wishlist-font-pack--sketchy .wishlist-panel-subtitle {
    font-size: 3.12rem;
    margin-top: -0.56rem;
  }
  .wishlist-font-pack--bold #wishlist-headline-text,
  .wishlist-font-pack--bold #giftlist-detail-subtitle,
  .wishlist-font-pack--bold .wishlist-panel-subtitle {
    font-size: 2.04rem;
  }
}

/* The section header's own drag/move/edit icons, bumped again beyond
   their already-larger-than-original base size (1.3rem to 1.6rem, see
   .wishlist-section-drag-handle etc.) — small glyphs read as genuinely
   hard to see on a phone screen specifically, not just a little
   compact the way they still read fine on desktop at the base size. */
@media only screen and (max-width: 767px) {
  .wishlist-section-drag-handle,
  .wishlist-section-move-btn,
  .wishlist-section-edit-btn {
    font-size: 2rem;
  }
}

/* The template's .section-close (X) used to draw its own two strokes
   as rotated pseudo-elements on an empty <span> (main.css) — kept
   surfacing rendering quirks at the crossing point (a visible darker
   blotch where two translucent layers overlapped; once solid, rounding
   the stroke tips introduced a different artifact at certain zoom
   levels). A pre-made icon (Font Awesome's fa-xmark, tried next) traded
   that for a different problem — its solid style draws its own strokes
   with rounded ends baked into the glyph itself, the same rounded look
   already rejected, and no font-size tweak changes that (Xavier's own
   "not a matter of scale, a matter of design" call). This is a real,
   hand-built SVG instead — two plain <line> elements, so every visual
   property (thickness, color, cap shape) is set directly here rather
   than inherited from however a rotated CSS box or someone else's icon
   happens to draw it. stroke="currentColor" on the lines (index.html)
   is what lets plain `color` below still drive it, same as the arrow
   icon and the font-based attempt before it. stroke-linecap: square
   keeps the ends flat/sharp — deliberately not "round", the one thing
   both earlier attempts kept reintroducing. viewBox is 24x24 with the
   line endpoints inset from its edges (5,5 to 19,19), sized in CSS
   against the old hand-drawn X's real 34px corner-to-corner footprint,
   not by assuming parity with the arrow's own font-size would land the
   same visual size (that assumption undersized the font-icon attempt). */
.section-close-icon {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 30px;
  height: 30px;
  color: #000000;
  /* Back to the original design's own 50% -> 100% opacity hover
     (Xavier's call) — solid color for both is what fixed the
     dark-blotch-at-the-crossing bug; that fix was about the strokes
     compositing over EACH OTHER (two translucent layers stacking
     unevenly with the strokes' own resting cream/dark background), not
     about the icon's own opacity against the page — a single solid-
     colored SVG faded uniformly as one unit has no second layer to
     stack against, so the original hover idea is safe to bring back
     now that it's a real shape instead of two separately-composited
     CSS boxes. */
  opacity: 0.5;
  transition: opacity 0.2s ease-in-out;
}
.section-close-icon line {
  stroke: currentColor;
  stroke-width: 2.5;
  stroke-linecap: square;
}
.section-close.light .section-close-icon {
  color: #ffffff;
}
.no-touch .section-close:hover .section-close-icon {
  opacity: 1;
}
