/* The public side of Local Haunts: the front door and the shared-place page.
 *
 * Deliberately not sharing a stylesheet with the two auth pages. Those are a
 * single white card centred in the viewport at an anxious moment, and they own
 * `body` layout to get it. This is a site: full-bleed bands, a real footer,
 * and a dark treatment of its own. Two pages pulling `body` in opposite
 * directions through one cascade is the bug you find three months later, so
 * these tokens are duplicated on purpose and the two files never meet.
 *
 * No build step, no framework, one font request. If Google Fonts is blocked
 * the fallbacks are real stacks and every page still reads.
 */

:root {
  --sign-red: #bb2121;
  --deep-teal: #234b4d;
  --teal-deep: #1a3a3b;
  --amber: #d9861e;
  --amber-ink: #a8720f;
  --cream: #fcf6ea;
  --paper: #ffffff;
  --ink: #2b2623;
  --ink-soft: #6e6357;
  --rule: #e0d6c4;

  /* The three marks are drawn with currentColor so one token per mark keeps
   * them legible on both grounds. Hard-coded fills went dark-on-dark. */
  --mark-vouch: #234b4d;
  --mark-crown: #a8720f;
  --mark-cache: #bb2121;
  --tint-vouch: rgba(35, 75, 77, 0.13);
  --tint-crown: rgba(217, 134, 30, 0.18);
  --tint-cache: rgba(187, 33, 33, 0.12);

  --shell: 1040px;
  --measure: 62ch;
}

@media (prefers-color-scheme: dark) {
  :root {
    --sign-red: #e05c56;
    --deep-teal: #1d4143;
    --teal-deep: #16302f;
    --amber: #e59b3d;
    --amber-ink: #e3ab5e;
    --cream: #17130f;
    --paper: #221c17;
    --ink: #f0e8da;
    --ink-soft: #b6a896;
    --rule: #3b322a;

    --mark-vouch: #7fb5a9;
    --mark-crown: #e3ab5e;
    --mark-cache: #e05c56;
    --tint-vouch: rgba(127, 181, 169, 0.16);
    --tint-crown: rgba(229, 155, 61, 0.18);
    --tint-cache: rgba(224, 92, 86, 0.16);
  }
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--cream);
  color: var(--ink);
  font-family: Archivo, "Segoe UI", Helvetica, Arial, sans-serif;
  font-size: 17px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-wrap: pretty;
}

.shell {
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 24px;
}

/* ---- Masthead ------------------------------------------------------- */

.masthead {
  background: var(--teal-deep);
  padding: 20px 0;
}

.masthead .shell {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
}

.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: "Alfa Slab One", Georgia, serif;
  font-weight: 400;
  font-size: 24px;
  line-height: 1;
  color: var(--cream);
  text-decoration: none;
  letter-spacing: -0.005em;
}

/* The header is the logo itself, not type. It is served at 176px tall and
   drawn at 88, so it stays crisp on a retina screen; the ground was keyed
   out of it, so it sits on ink without a box. The lockup is a badge rather
   than a wide strip, so the name only reads from about 80px up. */
.wordmark .logo {
  display: block;
  height: 88px;
  width: auto;
}

@media (max-width: 600px) {
  .wordmark .logo {
    height: 64px;
  }
}

.wordmark .dot {
  color: var(--sign-red);
}

.motto {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: rgba(252, 246, 234, 0.62);
  margin: 0;
}

/* ---- Place page ----------------------------------------------------- */

.sheet {
  padding: 48px 0 64px;
}

.place-name {
  font-family: "Alfa Slab One", Georgia, serif;
  font-weight: 400;
  font-size: clamp(30px, 5.4vw, 46px);
  line-height: 1.1;
  letter-spacing: -0.015em;
  margin: 0 0 10px;
  max-width: 18ch;
  text-wrap: balance;
}

.place-where {
  color: var(--ink-soft);
  margin: 0 0 22px;
  max-width: var(--measure);
}

.stats {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 22px;
  padding: 16px 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  margin-bottom: 26px;
}

.stat {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
}

.stat svg {
  width: 21px;
  height: 21px;
  display: block;
  flex: none;
}

.stat .n {
  font-variant-numeric: tabular-nums;
}

.stat .of {
  font-weight: 400;
  color: var(--ink-soft);
}

.crowns {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 26px;
}

.crown-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: rgba(217, 134, 30, 0.14);
  border: 1.5px solid var(--amber);
  border-radius: 11px;
  padding: 8px 14px;
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
}

.crown-chip svg {
  width: 18px;
  height: 18px;
  display: block;
  flex: none;
}

.crown-chip .agree {
  font-weight: 600;
  color: var(--amber-ink);
  font-variant-numeric: tabular-nums;
}

.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 30px;
}

/* Under the actions on /p: "Open In Local Haunts" is a custom-scheme link
   that does nothing when the app is not installed, so this is the way in. */
.get-app {
  margin: -18px 0 30px;
  font-size: 14px;
  color: var(--ink-soft);
}

.get-app a {
  color: var(--deep-teal);
  font-weight: 600;
}

.btn {
  display: inline-block;
  font: inherit;
  font-size: 16px;
  font-weight: 700;
  border-radius: 10px;
  padding: 14px 22px;
  text-decoration: none;
  border: 1.5px solid transparent;
}

.btn.solid {
  background: var(--sign-red);
  color: #fcf6ea;
}

.btn.outline {
  background: transparent;
  color: var(--deep-teal);
  border-color: var(--rule);
}

@media (prefers-color-scheme: dark) {
  .btn.solid {
    color: #2a0f0d;
  }

  .btn.outline {
    color: #7fb5a9;
  }
}

.btn:focus-visible,
a:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
}

.credit {
  font-size: 13px;
  color: var(--ink-soft);
  margin: 0;
}

.hidden {
  display: none;
}

/* ---- Footer --------------------------------------------------------- */

.footer {
  background: var(--teal-deep);
  color: rgba(252, 246, 234, 0.62);
  padding: 32px 0;
  font-size: 14px;
}

.footer p {
  margin: 0 0 6px;
}

.footer a {
  color: rgba(252, 246, 234, 0.85);
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation: none !important;
    transition: none !important;
  }
}

/* ==== Homepage =======================================================
 *
 * Everything below is scoped to `body.home`. The share page is a cream
 * sheet that follows the reader's colour scheme; the homepage is ink,
 * always, because the logo is drawn on ink and every storefront photo in
 * the rotation was shot after dark. Two pages, one stylesheet, no shared
 * ground: these tokens are named apart from the sheet's on purpose so
 * neither page can reach into the other.
 */

.home {
  --ground: #17130f;
  --surface: #221c17;
  --type: #f0e8da;
  --type-soft: #b6a896;
  --line: #3b322a;
  --hero-red: #bb2121;
  --hero-amber: #e59b3d;
  --hero-teal: #16302f;
  --hero-cream: #fcf6ea;
  --m-vouch: #7fb5a9;
  --m-crown: #e59b3d;
  --m-cache: #e05c56;

  background: var(--ground);
  color: var(--type);
}

/* The logo draws its cutlery as cream with a deep teal keyline. Every
 * display line on the page carries the same edge. paint-order puts the
 * stroke behind the fill so the letterform keeps its full weight; where it
 * is unsupported the stroke sits inward and the type reads a touch lighter,
 * which is a degradation, not a break. Width is in em so it tracks size. */
.home .wordmark,
.splash h1,
.stmt h2,
.railhead h2,
.col h3,
.quote blockquote {
  paint-order: stroke fill;
  -webkit-text-stroke: 0.035em #234b4d;
}

.home .eyebrow {
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--hero-amber);
  margin: 0 0 16px;
}

.home .kicker {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--type-soft);
  margin: 0;
}

/* ---- Top bar -------------------------------------------------------- */

.topbar {
  background: var(--ground);
  border-bottom: 1px solid var(--line);
}

.topbar .shell {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  min-height: 92px;
}

.home .wordmark {
  color: var(--hero-cream);
}

.topnav {
  display: flex;
  align-items: center;
  gap: 28px;
}

.topnav a {
  font-size: 14px;
  font-weight: 600;
  color: var(--type-soft);
  text-decoration: none;
}

.topnav a:hover {
  color: var(--hero-amber);
}

/* ---- Splash --------------------------------------------------------- */

/* The band grows with the window. At a fixed 620px a 2560 monitor showed
 * 57% of each photo and an ultrawide 43%, which is what "zoomed in" was:
 * cover fills the width and crops whatever height is left over. Tracking
 * 40vw holds the box near the 2.50:1 the frames are cut to, so 95% of the
 * photo survives from 1300px up.
 *
 * contain: paint keeps the hero from repainting when the rail scrolls over
 * its bottom edge, which is the stretch that stuttered. */
.splash {
  position: relative;
  height: clamp(520px, 40vw, 900px);
  overflow: hidden;
  background: var(--ground);
  border-bottom: 5px solid var(--hero-amber);
  contain: paint;
}

/* Frame one, held under the rotation for the life of the page. */
.hero-base {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 42%;
}

/* The rotation is two images that trade places above the base. Images, not
 * more background divs: a background layer only composites once the browser
 * decides to promote it, and until it does, every fade frame re-rasters the
 * scrim and the stroked display type stacked on top. will-change pins both
 * to the compositor, which was too expensive at six layers and is the right
 * tool at two. */
/* Only the incoming frame animates. The outgoing one stays opaque under it
 * and snaps off once the fade is done, so the compositor blends one moving
 * layer per fade instead of two: half the fill-rate cost, which is what a
 * crossfade of two full-bleed textures actually spends. The transition
 * lives on .is-on so losing the class is instant, and 500ms is enough for
 * the eye at half the frames of 900ms. */
.hero-layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 42%;
  opacity: 0;
  will-change: opacity;
}

.hero-layer.is-on {
  opacity: 1;
  z-index: 2;
  transition: opacity 500ms linear;
}

/* The copy and the scrim sit above the fading pair, so they get their own
 * layers too and stop being repainted for every frame of a crossfade. The
 * explicit z-indexes are load-bearing: the incoming frame carries z-index 2,
 * and translateZ alone leaves these at auto, which let the first fade paint
 * a photo over the headline and the ticks. */
.splash .scrim,
.splash-copy,
.dotwrap {
  transform: translateZ(0);
}

.splash .scrim { z-index: 3; }
.splash-copy { z-index: 4; }
.dotwrap { z-index: 5; }

.splash .scrim {
  position: absolute;
  inset: 0;
  /* Eased from the original: that ramp was built for copy hung in the
   * bottom left, and with the copy centred and 95% of the photo now
   * showing, an 80%-opaque middle was hiding the thing people came for.
   * The display type carries a teal keyline, so it holds up on less. */
  background: linear-gradient(
    to top,
    rgba(23, 19, 15, 0.92) 0%,
    rgba(23, 19, 15, 0.66) 30%,
    rgba(23, 19, 15, 0.12) 70%,
    rgba(23, 19, 15, 0.3) 100%
  );
}

.splash-copy {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 56px;
  text-align: center;
}

.splash h1 {
  font-family: "Alfa Slab One", Georgia, serif;
  font-weight: 400;
  font-size: clamp(34px, 5.6vw, 64px);
  line-height: 1.04;
  letter-spacing: -0.02em;
  margin: 0 auto 18px;
  max-width: 20ch;
  color: var(--hero-cream);
  text-wrap: balance;
}

.splash .sub {
  max-width: 58ch;
  font-size: clamp(16px, 2vw, 19px);
  color: rgba(252, 246, 234, 0.84);
  margin: 0 auto 26px;
  text-wrap: balance;
}

.badges {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 14px;
}

/* Official store artwork, unmodified, from Apple's and Google's brand kits.
   Both guidelines want the badge linking to a listing that exists, which is
   why these were text stand-ins until the apps were live. Height is the one
   thing we set: Apple's minimum is 40px, and Google's badge is drawn at a
   different aspect ratio, so both are sized by height and keep their own
   width. */
.badge {
  display: block;
  height: 50px;
  border-radius: 8px;
  transition: transform 0.15s ease;
}

.badge img {
  display: block;
  height: 50px;
  width: auto;
}

.badge:hover {
  transform: translateY(-1px);
}

.status-line {
  margin: 18px 0 0;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--hero-amber);
}

.dotwrap {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 22px;
}

.dotwrap .shell {
  display: flex;
  justify-content: center;
}

.dots {
  display: flex;
  gap: 8px;
}

.dot {
  width: 28px;
  height: 3px;
  background: rgba(252, 246, 234, 0.26);
  transition: background-color 300ms linear;
}

.dot.is-on {
  background: var(--hero-amber);
}

/* ---- Screenshot rail ------------------------------------------------ */

.railband {
  padding: 0 0 72px;
  margin-top: -70px;
  position: relative;
  z-index: 3;
}

.railhead {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 20px;
  margin: 0 0 22px;
  padding-top: 96px;
}

.railhead h2 {
  font-family: "Alfa Slab One", Georgia, serif;
  font-weight: 400;
  font-size: clamp(24px, 3.4vw, 32px);
  line-height: 1.15;
  letter-spacing: -0.01em;
  margin: 0;
  text-wrap: balance;
}

.rail {
  display: flex;
  gap: 22px;
  overflow-x: auto;
  padding: 4px 24px 18px;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
}

/* The frame is cut to what an iPhone 16e actually shoots, 1170x2532, so a
 * real screenshot drops in without being squeezed. 300 wide over a 649 screen
 * is 0.462; it was 300 over 546, which is 0.549, and the mockups inside it
 * were drawn to that wrong shape. The 54px difference between the two heights
 * is .phonecap, which sits above the screen rather than over it. */
.phone {
  flex: 0 0 300px;
  height: 703px;
  border-radius: 26px;
  border: 1px solid var(--line);
  background: var(--hero-cream);
  overflow: hidden;
  scroll-snap-align: center;
  box-shadow: 0 8px 18px rgba(0, 0, 0, 0.5);
}

.phonecap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  background: var(--hero-teal);
  color: var(--hero-cream);
}

.phonecap b {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.phonecap span {
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(252, 246, 234, 0.6);
}

/* Real screenshots since 2026-09-09, at 600x1298, which is 2x the 300x649
 * frame and the 16e's own aspect. What sat here before was five hand-built
 * DOM mockups of the app's screens, about 220 lines of CSS that had to be
 * kept in step with an app it could not see. */
.screen {
  display: block;
  width: 100%;
  height: 649px;
  object-fit: cover;
  object-position: top;
  background: #fcf6ea;
}

/* ---- No Chains band ------------------------------------------------- */

.stmt {
  background: var(--hero-red);
  padding: 38px 0;
  text-align: center;
}

/* The line is allowed out of the 1040px shell: capped there it filled less
 * than half the band's width on a big monitor and the band read as empty. */
.stmt .shell {
  max-width: none;
}

/* Derived from the window, not capped. The string sets 16.704em in Alfa
 * Slab One once the -0.02em tracking is counted, so an exact fit would
 * divide by 16.704; 21.5 holds the line to about 78% of the band, which is
 * where it wants to sit. Re-measure before changing the string or the
 * tracking, because nowrap overflows silently. */
.stmt h2 {
  font-family: "Alfa Slab One", Georgia, serif;
  font-weight: 400;
  font-size: clamp(16px, calc((100vw - 64px) / 21.5), 200px);
  line-height: 1.1;
  letter-spacing: -0.02em;
  margin: 0;
  color: var(--hero-cream);
  white-space: nowrap;
}

/* ---- The marks, as a hairline strip --------------------------------- */

.strip { padding: 76px 0 0; }

.strip .shell { text-align: center; }

.strip .cols {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.col {
  padding: 34px 30px;
  border-left: 1px solid var(--line);
}

.col p {
  text-wrap: pretty;
}

.col:first-child {
  border-left: 0;
  padding-left: 0;
}

.col .disc {
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 18px;
}

.col .disc svg {
  width: 36px;
  height: 36px;
  display: block;
}

.col .disc.vouch { color: var(--m-vouch); }
.col .disc.crown { color: var(--m-crown); }
.col .disc.cache { color: var(--m-cache); }

.col h3 {
  font-family: "Alfa Slab One", Georgia, serif;
  font-weight: 400;
  font-size: 26.5px;
  letter-spacing: -0.01em;
  margin: 0 0 11px;
}

.col p {
  margin: 0;
  color: var(--type-soft);
  font-size: 17px;
}

/* ---- Pull quote ----------------------------------------------------- */

.quote { padding: 64px 0 84px; }

.quote .shell { text-align: center; }

.quote .eyebrow { margin-bottom: 22px; }

.quote blockquote {
  margin: 0 auto;
  font-family: "Alfa Slab One", Georgia, serif;
  font-weight: 400;
  font-size: clamp(29px, 4.43vw, 44px);
  line-height: 1.14;
  letter-spacing: -0.02em;
  color: var(--hero-cream);
  max-width: 24ch;
  text-wrap: balance;
}

.quote .after {
  margin: 28px auto 0;
  max-width: 62ch;
  font-size: 18px;
  color: var(--type-soft);
  text-wrap: pretty;
}

.quote .after strong {
  color: var(--type);
  font-weight: 700;
}

.home .footer {
  border-top: 5px solid var(--hero-amber);
}

/* ---- Narrow --------------------------------------------------------- */

@media (max-width: 860px) {
  .strip .cols { grid-template-columns: 1fr; }

  .col {
    border-left: 0;
    padding: 28px 0;
    border-top: 1px solid var(--line);
  }

  .col:first-child {
    border-top: 0;
    padding-top: 30px;
  }
}

@media (max-width: 700px) {
  .topnav { display: none; }

  .splash { height: 560px; }

  .splash-copy { bottom: 48px; }

  .railband { padding-bottom: 56px; }

  .railhead {
    padding-top: 84px;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
  }

  .stmt { padding: 40px 0; }

  /* Holding one line on a 390px screen drove this to 19px, which is not a
   * statement. Three lines at 33px is. */
  .stmt h2 {
    white-space: normal;
    font-size: clamp(27px, 8.5vw, 44px);
    max-width: 13ch;
    margin: 0 auto;
    text-wrap: balance;
  }

  .strip { padding-top: 56px; }

  .quote { padding: 48px 0 64px; }
}

/* ---- Reveal on scroll ----------------------------------------------- */

/* The Bunkhouse pattern from the reference sweep: sections arrive rather
 * than being already there.
 *
 * Driven by the scroll position itself, not by a scroll listener. The usual
 * recipe listens on scroll and calls getBoundingClientRect() per element
 * per event, which reads layout on the main thread at exactly the moment
 * the main thread is busiest. animation-timeline runs on the compositor
 * and costs nothing to the main thread.
 *
 * The base state is deliberately VISIBLE and the keyframe supplies the
 * from-state, so the global prefers-reduced-motion rule (animation: none)
 * leaves every section on screen instead of hiding the page. A browser
 * without animation-timeline gets the same: content, no motion. */
@keyframes reveal {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@supports (animation-timeline: view()) {
  .reveal {
    animation: reveal linear both;
    animation-timeline: view();
    animation-range: entry 5% cover 28%;
  }

  /* The three marks land in sequence rather than as a block. */
  .col:nth-child(2) { animation-range: entry 5% cover 34%; }
  .col:nth-child(3) { animation-range: entry 5% cover 40%; }
}

/* ---- Legal and support pages ---------------------------------------
 *
 * The privacy policy, terms, support and account-deletion pages. They ride
 * the same cream sheet as /p/, because they are read in the same frame of
 * mind: somebody wants one specific answer and wants it in one screen.
 * Store review reads them too, and a policy that is hard to read is a
 * policy that gets a second look.
 */

.legal .shell {
  max-width: 46rem;
}

.legal h2 {
  font-family: 'Alfa Slab One', Georgia, serif;
  font-weight: 400;
  font-size: 22px;
  line-height: 1.25;
  letter-spacing: -0.01em;
  margin: 34px 0 10px;
}

.legal h3 {
  font-size: 16px;
  font-weight: 700;
  margin: 22px 0 6px;
}

.legal p,
.legal li {
  max-width: var(--measure);
}

.legal ul,
.legal ol {
  padding-left: 20px;
  margin: 10px 0;
}

.legal li {
  margin-bottom: 7px;
}

.legal a {
  color: var(--deep-teal);
}

@media (prefers-color-scheme: dark) {
  .legal a {
    color: #7fb5a9;
  }
}

.footer a {
  text-decoration: none;
}

.footer a:hover {
  text-decoration: underline;
}

/* ==== Feedback form (support.html) ===================================
 *
 * The LLC-wide feedback inbox: posts to Budget GPA's submit-feedback
 * function tagged local-haunts. Tokens remap in dark mode above, so the
 * fields follow the page.
 */

.feedback {
  max-width: var(--measure);
  margin: 0 0 8px;
}

.feedback .field {
  margin-bottom: 16px;
}

.feedback label {
  display: block;
  font-size: 13px;
  font-weight: 700;
  margin-bottom: 6px;
  color: var(--ink);
}

.feedback select,
.feedback input,
.feedback textarea {
  display: block;
  width: 100%;
  font: inherit;
  font-size: 16px; /* 16 stops iOS Safari zooming on focus */
  color: var(--ink);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 9px;
  padding: 12px 13px;
}

.feedback textarea {
  min-height: 140px;
  resize: vertical;
}

.feedback select:focus-visible,
.feedback input:focus-visible,
.feedback textarea:focus-visible {
  outline: 2px solid var(--deep-teal);
  outline-offset: 1px;
  border-color: var(--deep-teal);
}

.feedback button.primary {
  width: 100%;
  font: inherit;
  font-size: 16px;
  font-weight: 700;
  color: var(--cream);
  background: var(--deep-teal);
  border: 0;
  border-radius: 9px;
  padding: 13px 16px;
  cursor: pointer;
}

.feedback button.primary:disabled {
  opacity: 0.45;
  cursor: default;
}

.feedback button.primary:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
}

.feedback .hp {
  position: absolute;
  left: -10000px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.feedback .status {
  margin-top: 12px;
  min-height: 20px;
  font-size: 14px;
}

.feedback .status.ok {
  color: var(--deep-teal);
}

.feedback .status.err {
  color: var(--sign-red);
}

@media (prefers-color-scheme: dark) {
  .feedback .status.ok {
    color: #7fb5a9;
  }
}

/* ==== The share sheet ================================================
 *
 * Scoped to `body.share`, which is only p.html. This page is where most
 * first visits land — somebody's text message, on a phone, at night — so it
 * gets the ink ground the homepage uses rather than the cream sheet the
 * legal pages ride. The tokens are re-declared rather than shared with
 * `.home` because the two pages have no layout in common and one cascade
 * pulling `body` in two directions is the bug you find three months later.
 */

.share {
  --s-ground: #17130f;
  --s-surface: #221c17;
  --s-type: #f0e8da;
  --s-type-soft: #b6a896;
  --s-line: #3b322a;

  background: var(--s-ground);
  color: var(--s-type);
}

.share .masthead {
  background: var(--s-ground);
  border-bottom: 1px solid var(--s-line);
}

.share .place-name {
  color: var(--s-type);
}

.share .place-where,
.share .credit,
.share .stat .of {
  color: var(--s-type-soft);
}

.share .stats {
  border-top-color: var(--s-line);
  border-bottom-color: var(--s-line);
}

.share .stat {
  color: var(--s-type);
}

.share .btn.solid {
  background: #bb2121;
  color: #fcf6ea;
}

.share .btn.outline {
  color: #7fb5a9;
  border-color: var(--s-line);
}

.share .crown-chip {
  background: rgba(229, 155, 61, 0.14);
  border-color: #e59b3d;
  color: var(--s-type);
}

.share .crown-chip .agree {
  color: #e3ab5e;
}

.share .footer {
  background: var(--s-ground);
  border-top: 1px solid var(--s-line);
}

/* ---- Skeletons ------------------------------------------------------
 *
 * Sized to what replaces them, so the page does not jump when the data
 * lands. The shimmer is a background-position animation on one gradient:
 * no layout, no paint of anything underneath, and it stops dead for anyone
 * who has asked for reduced motion (the rule at the top of this file kills
 * every animation, which is the intent here rather than an accident).
 */

.skel {
  background: linear-gradient(
    90deg,
    var(--s-surface) 0%,
    #2e2620 50%,
    var(--s-surface) 100%
  );
  background-size: 200% 100%;
  animation: skel-shimmer 1.4s ease-in-out infinite;
  border-radius: 8px;
}

.skel-title {
  height: 42px;
  width: min(18ch, 100%);
  margin-bottom: 14px;
  border-radius: 10px;
}

.skel-line {
  height: 18px;
  width: min(34ch, 100%);
  margin-bottom: 26px;
}

.skel-row {
  display: flex;
  gap: 12px;
  margin-bottom: 18px;
  flex-wrap: wrap;
}

.skel-chip {
  height: 34px;
  width: 122px;
  border-radius: 11px;
}

.skel-btn {
  height: 50px;
  width: 168px;
  border-radius: 10px;
}

@keyframes skel-shimmer {
  from {
    background-position: 200% 0;
  }
  to {
    background-position: -200% 0;
  }
}
