/* ===================================================================
   style.css — how the app looks.

   The style is "neobrutalist": warm cream paper, thick black outlines,
   and hard offset shadows with no blur, like stickers laid on a page.
   Buttons physically press into their own shadow when tapped.

   Every colour here comes from the theme block of whichever species is
   loaded, via the CSS variables set by js/app.js. Nothing in this file
   knows what a cat is — that is what lets a dog front door look
   completely different with no new code.
   =================================================================== */

:root {
  /* Sensible fallbacks. app.js overwrites all of these from the species
     theme the moment the page loads. */
  --accent: #FF5A3C;
  --accent-text: #FFFFFF;
  --background: #FFF4E4;
  --text: #1A1614;
  --muted: #6E635B;
  --card: #FFFFFF;
  --ink: #1A1614;
  --highlight: #FFD84D;
  --pop: #8FE3B0;
  --glow-1: #FFC2AE;
  --glow-2: #A9E3C6;

  /* The three numbers that make the whole style hang together. */
  --border-width: 3px;
  --shadow-drop: 6px;
  --shadow-drop-small: 3px;

  --radius: 16px;
  --radius-small: 12px;
  --gap: 20px;
  --max-width: 460px;

  --font-display: "Baloo 2", ui-rounded, "SF Pro Rounded", system-ui,
                  -apple-system, "Segoe UI", sans-serif;
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, sans-serif;
}

* { box-sizing: border-box; }

/* The `hidden` attribute means hidden, always.

   Browsers implement `hidden` as display:none, but ANY display rule we
   write in this file silently beats it. That bit us: .upload-prompt has
   display:flex, so "Add a photo" stayed visible on top of the chosen
   photo even though the JavaScript had hidden it. This one line makes
   `hidden` reliable for every element, present and future. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--background);
  color: var(--text);
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
}

/* The soft colour glows in the corners. Fixed, so they stay put while
   the page scrolls, and behind everything so nothing is blocked. */
.glows {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(46vmax 46vmax at 0% 0%,
      color-mix(in srgb, var(--glow-1) 62%, transparent), transparent 62%),
    radial-gradient(44vmax 44vmax at 100% 100%,
      color-mix(in srgb, var(--glow-2) 58%, transparent), transparent 62%),
    radial-gradient(38vmax 38vmax at 100% 4%,
      color-mix(in srgb, var(--highlight) 30%, transparent), transparent 60%);

  /* A slow drift and breathe, so the page is never completely dead.
     Deliberately long and small: at 34 seconds a cycle you feel it
     rather than see it. It sits behind everything at z-index -1 and
     ignores the pointer, so it can never affect what you can tap. */
  animation: glow-drift 34s ease-in-out infinite alternate;
  will-change: transform;
}

@keyframes glow-drift {
  from { transform: scale(1)    translate3d(0, 0, 0); }
  50%  { transform: scale(1.06) translate3d(1.5%, -1%, 0); }
  to   { transform: scale(1.03) translate3d(-1.5%, 1.2%, 0); }
}

/* ===================================================================
   THE ONE-SCREENFUL SHELL

   The whole app — including the footer — is exactly one screen tall and
   physically cannot scroll. Not "does not need to"; cannot.

   Three things make that true, and all three are required:

   1. height, not min-height. min-height lets content taller than the
      screen just make the box taller, so nothing is ever forced to
      shrink. A fixed height compels the flexible element to give way.

   2. 100svh, not dvh or vh. svh is the SMALL viewport: the screen as it
      is with Safari's toolbars VISIBLE. That is the worst case. dvh is
      the toolbars-hidden measurement and quietly promises room a real
      phone does not have.

   3. The footer lives INSIDE the shell. Previously it sat after the
      full-height app, which made the document one-screen-plus-a-footer
      by construction — guaranteeing a scroll no matter how well the
      app itself fitted.

   The legal pages share this stylesheet and must scroll, so all of this
   is scoped to .app-shell, which only index.html carries.
   =================================================================== */

html { height: 100%; }

body.app-shell {
  height: 100vh;
  height: 100svh;
  overflow: hidden;              /* scrolling is impossible, not merely unnecessary */
  display: flex;
  flex-direction: column;
}

body.app-shell #app {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
  width: 100%;
  max-width: var(--max-width);
  margin: 0 auto;
  padding: max(10px, env(safe-area-inset-top)) 18px 8px;
  display: flex;
  flex-direction: column;
}

/* Each screen fills the space the shell gives it and manages itself. */
#screen-landing,
#screen-result,
#screen-unlock,
#screen-extras {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.screen[hidden] { display: none; }

/* The banner shown after coming back from paying. */
.flash {
  margin: 0 0 18px;
  padding: 13px 15px;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.45;
  text-align: center;
  color: var(--ink);
  background: var(--pop);
  border: var(--border-width) solid var(--ink);
  border-radius: var(--radius-small);
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
}
.flash.is-error { background: #FFD5CE; }


/* -------------------------------------------------------------------
   The two building blocks of the whole style
   ------------------------------------------------------------------- */

/* A "slab" is anything outlined and shadowed: cards, buttons, pills. */
.slab {
  border: var(--border-width) solid var(--ink);
  box-shadow: var(--shadow-drop) var(--shadow-drop) 0 var(--ink);
}

/* Tactile press: the thing slides down into its own shadow, so tapping
   feels like physically pushing a button. */
.pressable {
  transition: transform 0.08s ease, box-shadow 0.08s ease,
              background-color 0.14s ease, border-color 0.14s ease;
}
.pressable:active:not(:disabled) {
  transform: translate(var(--shadow-drop), var(--shadow-drop));
  box-shadow: 0 0 0 var(--ink);
}

:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}


/* -------------------------------------------------------------------
   LANDING: badge + headline, upload, picker, go. Nothing else.
   ------------------------------------------------------------------- */

/* ---- the landing page is a single non-scrolling column ----

   The badge, headline, pills and button all take their natural height.
   The upload box then absorbs whatever room is left over, shrinking on
   a small phone and growing on a large one. That is what keeps all five
   things on screen at once without hard-coding a size per device. */
/* (sizing for #screen-landing is set with #screen-result further up) */

.headline-block {
  flex: 0 0 auto;
  margin: 0 0 clamp(10px, 1.7vh, 18px);
}

.badge {
  display: inline-block;
  transform: rotate(-3deg);
  margin: 0 0 clamp(6px, 1.1vh, 12px) 2px;
  padding: 6px 13px 4px;
  background: var(--highlight);
  color: var(--ink);
  border: var(--border-width) solid var(--ink);
  border-radius: 999px;
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(10px, 1.35vh, 12px);
  letter-spacing: 0.06em;
  line-height: 1.3;
  text-transform: uppercase;
}
.badge:empty { display: none; }
.badge-centred { transform: rotate(-2deg); margin-bottom: clamp(8px, 1.6vh, 18px); align-self: center; }

/* Sized against BOTH the width and the height of the screen, so a short
   phone gets a smaller headline rather than one that eats the page. */
#headline {
  font-family: var(--font-display);
  font-size: clamp(27px, min(8.6vw, 6vh), 46px);
  line-height: 1.0;
  font-weight: 800;
  letter-spacing: -0.015em;
  margin: 0;
  text-wrap: balance;
}
/* The one word painted in the species' accent colour. */
.headline-accent {
  color: var(--accent);
  -webkit-text-stroke: 1px var(--ink);
  paint-order: stroke fill;
}

/* Was a full-width SQUARE, which on its own was taller than half a
   phone screen and pushed the button off the bottom. It is now a short
   wide rectangle that takes whatever height is left over, between a
   floor and a ceiling. The photo preview fills the same box, so
   choosing a photo never changes the page height. */
.upload {
  display: block;
  position: relative;
  width: 100%;
  /* THE FLEXIBLE ELEMENT. Everything else on this screen takes its
     natural height; the photo box takes whatever is left and no more.

     flex-basis 0 (not auto) means the chosen photo's own dimensions
     never influence this — the box is purely "the space that remains".
     min-height 0 is what actually permits it to shrink; without it a
     flex item refuses to go below its content size, which is how the
     button ended up off screen. There is deliberately no floor: a small
     photo is fine, an unreachable button is not. */
  flex: 1 1 0;
  min-height: 0;
  max-height: min(30svh, 240px);
  border: var(--border-width) solid var(--ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow-drop) var(--shadow-drop) 0 var(--ink);
  background: var(--card);
  cursor: pointer;
  overflow: hidden;
  transition: transform 0.08s ease, box-shadow 0.08s ease, background-color 0.14s ease;
}
.upload:active {
  transform: translate(var(--shadow-drop), var(--shadow-drop));
  box-shadow: 0 0 0 var(--ink);
}
.upload.is-dragging {
  background: color-mix(in srgb, var(--pop) 40%, var(--card));
}

/* Absolutely positioned so the photo fills the box without ever adding
   to its height — belt and braces alongside the flex-basis above. */
#photo-preview {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Side by side, not stacked — a short wide box wants a row. */
.upload-prompt {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: clamp(10px, 1.6vh, 14px);
  color: var(--text);
}
.upload-icon {
  flex: 0 0 auto;
  font-size: clamp(17px, 2.5vh, 26px);
  line-height: 1;
  width: clamp(38px, 5.6vh, 56px);
  height: clamp(38px, 5.6vh, 56px);
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--highlight);
  border: var(--border-width) solid var(--ink);
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
}
.upload-text {
  font-family: var(--font-display);
  font-size: clamp(15px, 2.2vh, 20px);
  font-weight: 700;
}

/* Once a photo is in, a small chip keeps the "you can change this"
   affordance without needing any extra markup or JavaScript. */
.upload.has-photo::after {
  content: "Tap to change";
  position: absolute;
  right: 8px;
  bottom: 8px;
  padding: 5px 10px 4px;
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 700;
  color: var(--ink);
  background: var(--card);
  border: 2px solid var(--ink);
  border-radius: 999px;
  pointer-events: none;
}

.picker {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: clamp(6px, 1vh, 10px);
  margin: clamp(10px, 1.7vh, 18px) 0;
}
.picker button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex: 1 1 auto;
  min-height: clamp(38px, 5.4vh, 46px);
  padding: 6px 12px;
  font-family: var(--font-display);
  font-size: clamp(13px, 1.8vh, 15px);
  font-weight: 700;
  color: var(--ink);
  background: var(--card);
  border: var(--border-width) solid var(--ink);
  border-radius: 999px;
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease, background-color 0.14s ease;
}
.picker button .pill-emoji { font-size: clamp(14px, 1.9vh, 17px); line-height: 1; }

/* Once a photo is in, the pills tighten up. The person has already read
   them by this point, so they can afford to be smaller — and the space
   goes to the photo preview, which is what they want to look at. */
#screen-landing.has-photo .picker { gap: 6px; margin: clamp(8px, 1.3vh, 13px) 0; }
#screen-landing.has-photo .picker button {
  min-height: clamp(34px, 4.6vh, 42px);
  padding: 4px 10px;
  font-size: clamp(12px, 1.6vh, 14px);
  gap: 5px;
}
#screen-landing.has-photo .picker button .pill-emoji {
  font-size: clamp(13px, 1.7vh, 15px);
}

/* Chosen: turns mint and lifts off the page a little. */
.picker button[aria-checked="true"] {
  background: var(--pop);
  transform: translateY(-3px);
  box-shadow: 5px 8px 0 var(--ink);
}
.picker button:active {
  transform: translate(var(--shadow-drop-small), var(--shadow-drop-small));
  box-shadow: 0 0 0 var(--ink);
}

/* ---- boy / girl / surprise us ----

   Hidden until a photo is chosen: it is a question about THAT pet, and
   an empty landing page must stay at four things.

   THE HIERARCHY MATTERS. These sat directly under the personality pills
   in the same size, weight and shape, so the two rows read as one
   continuous grid of choices and people could not tell which question
   they were answering. The personality is the real decision on this
   page; this is the seasoning. Three things now say so:

     a gap and a hairline above, so it is plainly a separate question
     a small quiet label beside it, naming what is being asked
     smaller, lighter, quieter pills than the ones above

   The label is BESIDE the pills, not above. There is no spare vertical
   room on a short phone, and a line of its own would have cost exactly
   the height the smaller pills just freed up. */
.gender-block { display: none; }
#screen-landing.has-photo .gender-block {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;

  /* The separation: a clear gap above, and a hairline that stops well
     short of the edges so it reads as a divider rather than a border. */
  margin: clamp(6px, 1.1vh, 12px) auto clamp(8px, 1.3vh, 14px);
  padding-top: clamp(6px, 1.1vh, 12px);
  border-top: 1px solid color-mix(in srgb, var(--ink) 16%, transparent);
  max-width: 94%;
}

.gender-label {
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  color: color-mix(in srgb, var(--ink) 62%, transparent);
  white-space: nowrap;
}

.gender-row {
  display: flex;
  gap: 6px;
  justify-content: center;
}

/* Deliberately quieter than .picker button above: shorter, smaller
   type, a lighter weight, a thinner border and no drop shadow. */
.gender-row button {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-height: 32px;
  padding: 4px 10px;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  color: color-mix(in srgb, var(--ink) 78%, transparent);
  background: transparent;
  border: 2px solid color-mix(in srgb, var(--ink) 34%, transparent);
  border-radius: 999px;
  white-space: nowrap;      /* "Surprise us" stays on one line */
  cursor: pointer;
  transition: transform 0.08s ease, background-color 0.14s ease,
              border-color 0.14s ease, color 0.14s ease;
}

/* The chosen one steps forward without shouting. */
.gender-row button[aria-checked="true"] {
  color: var(--ink);
  background: color-mix(in srgb, var(--pop) 70%, transparent);
  border-color: var(--ink);
}
.gender-row button:active { transform: translateY(1px); }

.go {
  display: block;
  flex: 0 0 auto;
  width: 100%;
  min-height: clamp(48px, 7vh, 62px);
  padding: clamp(11px, 1.7vh, 17px);
  font-family: var(--font-display);
  font-size: clamp(17px, 2.6vh, 22px);
  font-weight: 800;
  letter-spacing: 0.01em;
  text-align: center;
  text-decoration: none;
  color: var(--accent-text);
  background: var(--accent);
  border: var(--border-width) solid var(--ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow-drop) var(--shadow-drop) 0 var(--ink);
  cursor: pointer;
  transition: transform 0.08s ease, box-shadow 0.08s ease, opacity 0.14s ease;
}
.go:active:not(:disabled) {
  transform: translate(var(--shadow-drop), var(--shadow-drop));
  box-shadow: 0 0 0 var(--ink);
}
.go:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  box-shadow: var(--shadow-drop) var(--shadow-drop) 0 color-mix(in srgb, var(--ink) 45%, transparent);
}

.error {
  margin: 18px 0 0;
  padding: 12px 14px;
  font-size: 14px;
  font-weight: 600;
  text-align: center;
  color: var(--ink);
  background: #FFD5CE;
  border: var(--border-width) solid var(--ink);
  border-radius: var(--radius-small);
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
}

/* -------------------------------------------------------------------
   LOADING
   ------------------------------------------------------------------- */

#screen-loading { padding: 20vh 0; text-align: center; }

.thinking-dots { display: flex; gap: 12px; justify-content: center; margin-bottom: 24px; }
.thinking-dots span {
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--accent);
  border: var(--border-width) solid var(--ink);
  animation: bounce 1.15s infinite ease-in-out;
}
.thinking-dots span:nth-child(2) { background: var(--highlight); animation-delay: 0.15s; }
.thinking-dots span:nth-child(3) { background: var(--pop); animation-delay: 0.3s; }
@keyframes bounce {
  0%, 70%, 100% { transform: translateY(0); }
  35%           { transform: translateY(-15px); }
}
.thinking-text {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 700;
}


/* -------------------------------------------------------------------
   RESULT
   ------------------------------------------------------------------- */

/* The result page is a column too, so the card and the two main buttons
   stay on screen without scrolling. Everything below them — voice
   options, products, the sibling line — is meant to be scrolled to. */
/* The polaroid, tilted a degree or two like it was dropped on a table.
   js/card.js redraws this same tilt into the shareable image.

   It is the flexible element here, exactly as the upload box is on the
   landing page: the caption, the buttons and the voice options take
   their natural height and the card absorbs the rest. */
.polaroid {
  flex: 1 1 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  transform: rotate(-1.6deg);
  margin: 0 4px clamp(9px, 1.5vh, 18px);
  padding: clamp(6px, 1vh, 13px) clamp(6px, 1vh, 13px) clamp(4px, 0.7vh, 9px);
  background: var(--card);
  border: 4px solid var(--ink);
  border-radius: 4px;
  box-shadow: 10px 10px 0 var(--ink);
}

/* No fixed height: the photo shrinks to whatever is left after the
   caption. On a short phone it gets smaller; the buttons stay put. */
/* The polaroid lands like a photo tossed onto a table: it arrives
   turned too far, overshoots the other way, and settles at its resting
   tilt. Runs ONCE, when the result appears — js/app.js adds the class.
   The final frame matches the resting transform exactly, so there is no
   jump when the animation finishes. */
.polaroid.is-landing {
  animation: polaroid-settle 620ms cubic-bezier(0.22, 1.2, 0.36, 1) 1 both;
}
@keyframes polaroid-settle {
  0%   { transform: rotate(-7deg)   translate3d(0, -14px, 0) scale(0.965); }
  55%  { transform: rotate(1.4deg)  translate3d(0, 2px, 0)   scale(1.008); }
  78%  { transform: rotate(-2.6deg) translate3d(0, 0, 0)     scale(0.998); }
  100% { transform: rotate(-1.6deg) translate3d(0, 0, 0)     scale(1); }
}

/* The caption types itself out a word at a time. Each word is its own
   span with a staggered delay set in js/app.js, which also CAPS the
   total so the whole caption is readable well within three seconds no
   matter how long it is. The words occupy their space from the start,
   so nothing reflows and nothing moves while it plays. */
.caption .word {
  display: inline-block;
  opacity: 0;
  animation: word-in 260ms ease-out both;
}
@keyframes word-in {
  from { opacity: 0; transform: translate3d(0, 0.32em, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

.polaroid-photo {
  position: relative;
  flex: 1 1 0;
  min-height: 0;
  border: var(--border-width) solid var(--ink);
  overflow: hidden;
  line-height: 0;
  background: #EEE;
}
.polaroid img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.polaroid figcaption { flex: 0 0 auto; padding: clamp(7px, 1.1vh, 13px) 4px 2px; }

.caption {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(13px, 2vh, 20px);
  line-height: 1.22;
  font-weight: 700;
  text-align: center;
  text-wrap: pretty;
}

/* The breed guess. Quiet on purpose — it's a garnish, not the joke. */
.breed-tag {
  margin: clamp(5px, 0.9vh, 9px) 0 0;
  font-family: var(--font-body);
  font-size: clamp(9.5px, 1.2vh, 11.5px);
  font-weight: 600;
  letter-spacing: 0.02em;
  text-align: center;
  color: var(--muted);
}
.breed-tag[hidden] { display: none; }

.watermark {
  margin: clamp(5px, 1vh, 12px) 0 0;
  font-family: var(--font-display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: center;
  color: var(--muted);
}
.watermark:empty { display: none; }

/* ---- quotation marks, while the voice is actually playing ----
   The pet is talking, not singing. Speech marks say that; musical notes
   suggested the wrong thing entirely. Same drift, same fade. */
.speech-marks {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: visible;
}
.speech-mark {
  position: absolute;
  bottom: 12%;
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 1;
  color: color-mix(in srgb, var(--ink) 55%, transparent);
  opacity: 0;
  animation: mark-float 2.4s ease-out forwards;
}
@keyframes mark-float {
  0%   { opacity: 0;    transform: translate3d(0, 0, 0) rotate(0deg) scale(0.7); }
  12%  { opacity: 0.95; }
  70%  { opacity: 0.75; }
  100% { opacity: 0;    transform: translate3d(var(--drift, 16px), -130px, 0) rotate(var(--spin, 18deg)) scale(1.15); }
}

.demo-notice {
  flex: 0 0 auto;
  margin: 0 0 10px;
  padding: 7px 11px;
  font-size: 11.5px;
  line-height: 1.45;
  text-align: center;
  color: var(--ink);
  background: var(--highlight);
  border: var(--border-width) solid var(--ink);
  border-radius: var(--radius-small);
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
}

/* Two equal columns, deliberately compact — these must survive on a
   short screen alongside everything else. */
.actions {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(7px, 1.2vh, 11px);
}
.action {
  min-height: clamp(38px, 5.4vh, 50px);
  padding: clamp(6px, 1vh, 11px) 8px;
  font-family: var(--font-display);
  font-size: clamp(13px, 2vh, 16px);
  font-weight: 700;
  color: var(--ink);
  background: var(--card);
  border: var(--border-width) solid var(--ink);
  border-radius: var(--radius-small);
  box-shadow: var(--shadow-drop) var(--shadow-drop) 0 var(--ink);
  cursor: pointer;
  transition: transform 0.08s ease, box-shadow 0.08s ease, background-color 0.14s ease;
}
.action:active:not(:disabled) {
  transform: translate(var(--shadow-drop), var(--shadow-drop));
  box-shadow: 0 0 0 var(--ink);
}
.action[disabled] { opacity: 0.4; cursor: not-allowed; }
.action.is-speaking { background: var(--pop); }

.voice-options { flex: 0 0 auto; margin: clamp(7px, 1.2vh, 13px) 0 0; }
.voice-options summary {
  cursor: pointer;
  color: var(--muted);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(12px, 1.8vh, 14px);
  padding: clamp(3px, 0.6vh, 6px) 0;
  list-style: none;
}
.voice-options summary::-webkit-details-marker { display: none; }
.voice-options summary::before { content: "▸ "; }
.voice-options[open] summary::before { content: "▾ "; }

.voice-row { display: flex; align-items: center; gap: 12px; margin: 12px 0; }
.voice-label {
  flex: 0 0 62px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
}

.segmented { display: flex; gap: 8px; }
.segmented button {
  padding: 8px 16px;
  min-height: 42px;
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
  background: var(--card);
  border: var(--border-width) solid var(--ink);
  border-radius: 999px;
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
  cursor: pointer;
  transition: transform 0.08s ease, box-shadow 0.08s ease, background-color 0.14s ease;
}
.segmented button.is-on { background: var(--pop); }
.segmented button:active {
  transform: translate(var(--shadow-drop-small), var(--shadow-drop-small));
  box-shadow: 0 0 0 var(--ink);
}


/* ---- "this page is out of date" ----
   Fixed, so it never pushes the app around or creates a scrollbar. */
.stale-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 999;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 9px 12px;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  background: var(--pop);
  border-bottom: var(--border-width) solid var(--ink);
}
.stale-banner button {
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  background: var(--card);
  border: var(--border-width) solid var(--ink);
  border-radius: 999px;
  padding: 4px 12px 3px;
  cursor: pointer;
}

/* ---- restore your access ----
   Quiet by design: someone who already pays needs to find it, but it
   must never compete with the plans for attention. */

.restore-line {
  margin: 14px 0 0;
  font-size: 13px;
  line-height: 1.4;
  text-align: center;
  color: var(--ink-soft, var(--ink));
}
.restore-link {
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  background: none;
  border: none;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.restore-panel {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 12px;
  width: 100%;
}
.restore-input {
  width: 100%;
  min-height: 48px;
  padding: 10px 14px;
  font-family: var(--font-body);
  font-size: 16px;            /* 16px or iOS zooms the page on focus */
  color: var(--ink);
  background: var(--card);
  border: var(--border-width) solid var(--ink);
  border-radius: var(--radius-small);
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
}
/* Six digits, spaced out so they are easy to check against the email. */
.restore-code-input {
  text-align: center;
  font-family: var(--font-display);
  font-size: 26px;
  font-weight: 700;
  letter-spacing: 8px;
  padding-left: 8px;   /* balances the trailing letter-space */
}

.restore-message {
  margin: 0;
  font-size: 13px;
  line-height: 1.4;
  text-align: center;
}
.restore-message.is-error { font-weight: 600; }

/* ---- premium voice menu (only shown to people who've paid) ---- */

.voice-premium-head { margin: 4px 0 12px; }
.badge-inline {
  transform: rotate(-2deg);
  margin: 0;
  font-size: 11px;
  padding: 5px 11px 4px;
}

.voice-pills { display: flex; flex-wrap: wrap; gap: 9px; }
.voice-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: 1 1 auto;
  min-height: 44px;
  padding: 9px 14px;
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
  background: var(--card);
  border: var(--border-width) solid var(--ink);
  border-radius: 999px;
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.12s ease, background-color 0.14s ease;
}
.voice-pill[aria-checked="true"] {
  background: var(--pop);
  transform: translateY(-3px);
  box-shadow: 5px 8px 0 var(--ink);
}
.voice-pill:active {
  transform: translate(var(--shadow-drop-small), var(--shadow-drop-small));
  box-shadow: 0 0 0 var(--ink);
}

/* ---- the one-line hint under the controls ---- */

.voice-hint {
  color: var(--muted);
  font-size: 13px;
  line-height: 1.5;
  margin: 14px 0 0;
}
/* When it's the upsell it gets a little more presence, but stays a
   single quiet line — it is not a second go button. */
.voice-hint.is-upgrade {
  color: var(--ink);
  font-weight: 500;
  padding: 12px 14px;
  background: color-mix(in srgb, var(--highlight) 45%, var(--card));
  border: var(--border-width) solid var(--ink);
  border-radius: var(--radius-small);
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
}
/* Offered when the voice service fails. There is no device-voice
   fallback in this app, so this button is the whole recovery path. */
.voice-retry {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
  background: var(--pop);
  border: var(--border-width) solid var(--ink);
  border-radius: 999px;
  padding: 5px 13px 4px;
  margin-left: 2px;
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
  cursor: pointer;
}
.voice-retry:active {
  transform: translate(var(--shadow-drop-small), var(--shadow-drop-small));
  box-shadow: 0 0 0 var(--ink);
}

.voice-upgrade-link {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
  background: none;
  border: none;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
  white-space: nowrap;
}

.again {
  flex: 0 0 auto;
  display: block;
  width: 100%;
  min-height: clamp(36px, 5.2vh, 48px);
  margin: clamp(7px, 1.2vh, 14px) 0 0;
  padding: clamp(6px, 1vh, 12px);
  font-family: var(--font-display);
  font-size: clamp(13px, 2vh, 17px);
  font-weight: 700;
  color: var(--ink);
  background: var(--card);
  border: var(--border-width) solid var(--ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow-drop) var(--shadow-drop) 0 var(--ink);
  cursor: pointer;
  transition: transform 0.08s ease, box-shadow 0.08s ease;
}
.again:active {
  transform: translate(var(--shadow-drop), var(--shadow-drop));
  box-shadow: 0 0 0 var(--ink);
}

.text-button {
  flex: 0 0 auto;
  display: block;
  width: 100%;
  margin: clamp(8px, 1.5vh, 18px) 0 0;
  padding: clamp(5px, 1vh, 12px);
  font-family: var(--font-display);
  font-size: clamp(12px, 1.9vh, 16px);
  font-weight: 700;
  color: var(--muted);
  background: none;
  border: none;
  cursor: pointer;
}


/* -------------------------------------------------------------------
   BELOW THE FOLD: products + sibling line
   ------------------------------------------------------------------- */

/* The below-the-fold block after a result. Sits after the full-height
   result screen, so reaching it means scrolling — by design. */
#result-extras {
  flex: 0 0 auto;
  padding-top: 18px;
}


.products-heading {
  flex: 0 0 auto;
  font-family: var(--font-display);
  font-size: clamp(15px, 2.4vh, 20px);
  font-weight: 700;
  margin: 0 0 6px;
}
.products-disclosure {
  flex: 0 0 auto;
  margin: -4px 0 10px;
  font-size: clamp(10px, 1.4vh, 12px);
  line-height: 1.45;
  color: var(--muted);
}
.products-disclosure:empty { display: none; }

.product-list {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: clamp(7px, 1.2vh, 12px);
}
.product-list a {
  display: block;
  padding: clamp(9px, 1.5vh, 15px) 14px;
  color: var(--ink);
  text-decoration: none;
  background: var(--card);
  border: var(--border-width) solid var(--ink);
  border-radius: var(--radius-small);
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
  transition: transform 0.08s ease, box-shadow 0.08s ease;
}
.product-list a:active {
  transform: translate(var(--shadow-drop-small), var(--shadow-drop-small));
  box-shadow: 0 0 0 var(--ink);
}
.product-title {
  display: block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(13px, 2vh, 16px);
}
.product-blurb { display: block; margin-top: 2px; font-size: clamp(11px, 1.6vh, 13px); line-height: 1.35; color: var(--muted); }

.sibling {
  flex: 0 0 auto;
  margin: clamp(12px, 2vh, 22px) 0 0;
  font-size: 14px;
  text-align: center;
  color: var(--muted);
}
.sibling button {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 700;
  color: var(--ink);
  background: var(--highlight);
  border: var(--border-width) solid var(--ink);
  border-radius: 999px;
  padding: 7px 14px;
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
  cursor: pointer;
}
.sibling button:active {
  transform: translate(var(--shadow-drop-small), var(--shadow-drop-small));
  box-shadow: 0 0 0 var(--ink);
}


/* -------------------------------------------------------------------
   UNLOCK
   ------------------------------------------------------------------- */

/* -------------------------------------------------------------------
   PHOTO DECLINED — no animal, or a person is identifiable.
   ------------------------------------------------------------------- */

#screen-rejected {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: clamp(12px, 2vh, 20px);
  min-height: 0;
}
#screen-rejected[hidden] { display: none; }

.rejected-emoji {
  margin: 0;
  font-size: clamp(44px, 8vh, 72px);
  line-height: 1;
}
.rejected-text {
  margin: 0;
  max-width: 22em;
  font-family: var(--font-display);
  font-size: clamp(19px, 3vh, 26px);
  font-weight: 700;
  line-height: 1.25;
  text-wrap: balance;
}
#screen-rejected .go { margin-top: clamp(4px, 1vh, 10px); }


/* ---- the two subscription plans, side by side ---- */

.plans {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(8px, 1.4vh, 13px);
  margin: clamp(12px, 2vh, 20px) 0 0;
}
.plan {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: clamp(11px, 1.9vh, 18px) 8px clamp(9px, 1.6vh, 15px);
  text-decoration: none;
  color: var(--ink);
  background: var(--card);
  border: var(--border-width) solid var(--ink);
  border-radius: var(--radius);
  box-shadow: var(--shadow-drop) var(--shadow-drop) 0 var(--ink);
  transition: transform 0.08s ease, box-shadow 0.08s ease;
}
.plan:active {
  transform: translate(var(--shadow-drop), var(--shadow-drop));
  box-shadow: 0 0 0 var(--ink);
}
/* The one we'd rather they picked. */
.plan.is-featured { background: var(--pop); }

.plan-tag {
  position: absolute;
  top: calc(-1 * clamp(9px, 1.4vh, 12px));
  transform: rotate(-3deg);
  padding: 3px 9px 2px;
  background: var(--highlight);
  border: 2.5px solid var(--ink);
  border-radius: 999px;
  font-family: var(--font-display);
  font-size: clamp(8.5px, 1.15vh, 10px);
  font-weight: 800;
  letter-spacing: 0.05em;
  white-space: nowrap;
}
.plan-label {
  font-family: var(--font-display);
  font-size: clamp(13px, 1.9vh, 16px);
  font-weight: 700;
}
.plan-price {
  font-family: var(--font-display);
  font-size: clamp(24px, 4vh, 34px);
  font-weight: 800;
  line-height: 1.05;
}
.plan-note {
  font-size: clamp(9.5px, 1.3vh, 11.5px);
  line-height: 1.3;
  color: var(--muted);
  text-wrap: balance;
}

/* Renews automatically, cancel anytime. Required, so it is not styled
   as small print that can be missed. */
.subscription-terms {
  margin: clamp(10px, 1.8vh, 16px) 0 0;
  font-size: clamp(11px, 1.5vh, 13px);
  font-weight: 600;
  color: var(--muted);
}

/* The link from the result to the products screen. */
.extras-link {
  flex: 0 0 auto;
  display: block;
  width: 100%;
  margin: clamp(5px, 1vh, 10px) 0 0;
  padding: clamp(4px, 0.8vh, 8px);
  font-family: var(--font-display);
  font-size: clamp(11.5px, 1.7vh, 14px);
  font-weight: 700;
  color: var(--muted);
  background: none;
  border: none;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.extras-link:hover { color: var(--ink); }

/* ---- the products screen ---- */

#screen-extras {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
#screen-extras .products-heading { margin-top: 0; }
#screen-extras .product-list { flex: 0 1 auto; min-height: 0; }
#screen-extras .products-disclosure { margin-bottom: clamp(6px, 1.1vh, 12px); }
#screen-extras .again { margin-top: clamp(8px, 1.5vh, 16px); }


#screen-unlock { text-align: center; display: flex; flex-direction: column; justify-content: center; }
.unlock-heading {
  font-family: var(--font-display);
  font-size: clamp(21px, 3.6vh, 34px);
  font-weight: 800;
  line-height: 1.05;
  margin: 0 0 clamp(6px, 1.1vh, 12px);
}
.unlock-text { color: var(--muted); font-size: clamp(12px, 1.9vh, 16px); line-height: 1.4; margin: 0; text-wrap: balance; }


/* -------------------------------------------------------------------
   SITE FOOTER — the joke, plus the legal links. Always below the fold.
   ------------------------------------------------------------------- */

/* One compact line inside the shell: the joke and both legal links.
   It wraps to a second line on very narrow screens rather than
   overflowing, and is measured as part of the fit. */
.site-footer {
  position: relative;
  flex: 0 0 auto;
  width: 100%;
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 5px 14px max(5px, env(safe-area-inset-bottom));
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 0 5px;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--muted);
}
.footer-joke { font-weight: 500; }
.footer-joke:empty { display: none; }
.footer-joke:empty + .footer-sep { display: none; }

.site-footer a {
  color: var(--muted);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  padding: 2px 0;
}
.site-footer a:hover { color: var(--ink); }
.footer-sep { opacity: 0.45; }


/* -------------------------------------------------------------------
   LEGAL PAGES — terms and privacy. Plain and readable, same furniture.
   ------------------------------------------------------------------- */

.legal {
  position: relative;
  max-width: 660px;
  margin: 0 auto;
  padding: max(26px, env(safe-area-inset-top)) 22px
           max(60px, env(safe-area-inset-bottom));
  font-size: 16px;
  line-height: 1.65;
}

.legal-back {
  display: inline-block;
  margin-bottom: 26px;
  padding: 8px 14px;
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
  text-decoration: none;
  background: var(--card);
  border: var(--border-width) solid var(--ink);
  border-radius: 999px;
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
}
.legal-back:active {
  transform: translate(var(--shadow-drop-small), var(--shadow-drop-small));
  box-shadow: 0 0 0 var(--ink);
}

.legal h1 {
  font-family: var(--font-display);
  font-size: clamp(32px, 8vw, 42px);
  font-weight: 800;
  line-height: 1.05;
  margin: 0 0 6px;
}
.legal h2 {
  font-family: var(--font-display);
  font-size: 21px;
  font-weight: 700;
  margin: 38px 0 10px;
}
.legal-updated {
  margin: 0 0 26px;
  font-size: 13px;
  color: var(--muted);
}
.legal-lead {
  padding: 16px 18px;
  margin: 0 0 8px;
  background: var(--card);
  border: var(--border-width) solid var(--ink);
  border-radius: var(--radius-small);
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
  font-weight: 500;
}
.legal p, .legal li { color: var(--text); }
.legal ul, .legal ol { padding-left: 22px; }
.legal li { margin: 7px 0; }
.legal a { color: var(--ink); text-underline-offset: 3px; }
.legal strong { font-weight: 700; }

.legal-table {
  width: 100%;
  border-collapse: collapse;
  margin: 16px 0;
  font-size: 14px;
  background: var(--card);
  border: var(--border-width) solid var(--ink);
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
}
.legal-table th, .legal-table td {
  padding: 10px 12px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1.5px solid color-mix(in srgb, var(--ink) 25%, transparent);
}
.legal-table th {
  font-family: var(--font-display);
  font-weight: 700;
  background: color-mix(in srgb, var(--highlight) 40%, var(--card));
}
.legal-table tr:last-child td { border-bottom: none; }

.legal-links {
  margin: 46px 0 0;
  padding-top: 22px;
  border-top: 1.5px solid color-mix(in srgb, var(--ink) 25%, transparent);
  font-size: 14px;
  font-weight: 600;
  text-align: center;
}

/* Tables are the one thing that can't shrink gracefully on a phone. */
@media (max-width: 460px) {
  .legal-table { display: block; overflow-x: auto; }
}


/* ===================================================================
   PORTRAIT LOCK

   This app is portrait-only. Sideways on a phone there simply isn't the
   height for a photo, five personality pills and a button, so rather
   than degrade the layout we ask for the phone back the right way up.

   It is deliberately CSS-only. Nothing in JavaScript listens for
   rotation, so nothing tears down or rebuilds: the photo, the caption
   and the chosen personality are all still sitting there underneath.
   Turning the phone back is instant and loses nothing.

   THE TRIGGER: landscape AND under 500px tall. The height condition is
   what keeps desktops out of it — a laptop window in landscape is far
   taller than 500px, while a phone on its side is 320-430px. (If a very
   short desktop window ever trips it, adding `and (pointer: coarse)`
   below narrows it to touchscreens only.)
   =================================================================== */

.rotate-lock { display: none; }

@media (orientation: landscape) and (max-height: 500px) {
  .rotate-lock {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 999;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    padding: 20px 28px;
    text-align: center;
    background: var(--background);
  }

  .rotate-icon {
    font-size: clamp(38px, 12vh, 62px);
    line-height: 1;
    animation: nudge 2.2s ease-in-out infinite;
  }

  .rotate-text {
    margin: 0;
    max-width: 20em;
    font-family: var(--font-display);
    font-size: clamp(17px, 5.5vh, 26px);
    font-weight: 800;
    line-height: 1.2;
    color: var(--ink);
    text-wrap: balance;
  }
}

/* A gentle tip back and forth, suggesting the motion we're asking for. */
@keyframes nudge {
  0%, 45%, 100% { transform: rotate(-82deg); }
  70%, 90%      { transform: rotate(0deg); }
}


/* -------------------------------------------------------------------
   Respect people who ask their device for less movement.
   ------------------------------------------------------------------- */



/* The card-sound toggle in the footer. Sized and coloured to match the
   legal links beside it, so it reads as one more small footer item
   rather than a control demanding attention. */
.sound-toggle {
  /* Out of the flow, in the corner. In the flow it wrapped the footer
     onto another line on a 320px phone, and that height came out of the
     photo. The footer is already position: relative. */
  position: absolute;
  right: 4px;
  bottom: max(3px, env(safe-area-inset-bottom));
  font-size: 11px;
  line-height: 1;
  padding: 1px 2px;
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  opacity: 0.75;
}
.sound-toggle:hover { opacity: 1; }

/* ---- vote for the next accent ---- */

.vote-heading {
  margin: 0 0 6px;
  font-family: var(--font-display);
  font-size: clamp(21px, 4.4vw, 27px);
  font-weight: 800;
  text-align: center;
}
.vote-intro {
  margin: 0 0 clamp(10px, 1.6vh, 16px);
  font-size: 13px;
  line-height: 1.4;
  text-align: center;
  color: var(--ink);
}
.vote-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
  margin-bottom: clamp(8px, 1.4vh, 14px);
}
.vote-option {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 40px;
  padding: 5px 12px;
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  background: var(--card);
  border: var(--border-width) solid var(--ink);
  border-radius: 999px;
  box-shadow: var(--shadow-drop-small) var(--shadow-drop-small) 0 var(--ink);
  cursor: pointer;
  transition: transform 0.08s ease, box-shadow 0.08s ease, background-color 0.14s ease;
}
.vote-option .vote-count {
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  opacity: 0.72;
}
.vote-option.is-voted { background: var(--pop); cursor: default; }
.vote-option:active {
  transform: translate(var(--shadow-drop-small), var(--shadow-drop-small));
  box-shadow: 0 0 0 var(--ink);
}
.vote-leader {
  margin: 0 0 clamp(8px, 1.4vh, 14px);
  font-size: 13px;
  font-weight: 600;
  text-align: center;
}
.vote-line {
  margin: 10px 0 0;
  font-size: 12px;
  text-align: center;
}
.vote-link {
  font-family: var(--font-display);
  font-size: 12px;
  font-weight: 700;
  color: var(--ink);
  background: none;
  border: none;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}


/* ---- very short screens ----
   A 380px-tall portrait viewport is rare but real: a small phone with
   both browser bars showing. Two things had to give — the boy/girl row
   and the seven-option wish list — so they lose their padding rather
   than the page gaining a scrollbar. Nothing here is allowed to scroll,
   ever, so this is where the last few pixels come from. */
@media (max-height: 440px) {
  /* The footer joke is owner-written and can be any length. On a short
     screen a long one wraps to a third line, and that line comes
     straight out of the photo above it — which is exactly what happened
     when the cat joke was rewritten from 49 characters to 77. Capped at
     two lines here so the joke can never again decide whether the page
     fits. */
  .site-footer { font-size: 10px; }
  .footer-joke {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  #screen-landing.has-photo .gender-block {
    gap: 6px;
    margin-top: 5px;
    margin-bottom: 5px;
    padding-top: 5px;
  }
  .gender-label { font-size: 11px; }
  .gender-row { gap: 5px; }
  .gender-row button {
    min-height: 26px;
    padding: 2px 8px;
    font-size: 11px;
    gap: 3px;
  }

  .vote-heading { font-size: 19px; margin-bottom: 3px; }
  .vote-intro { display: none; }      /* the options explain themselves */
  .vote-list { gap: 5px; margin-bottom: 6px; }
  .vote-option {
    min-height: 30px;
    padding: 3px 9px;
    font-size: 12px;
    gap: 4px;
  }
  .vote-leader { font-size: 12px; margin-bottom: 6px; }
}

/* The very shortest of all — a small phone with every bar showing.
   By this point the photo has already shrunk to nothing, so the last
   pixels have to come from the controls themselves. The personality
   pills are the biggest block on the page — five of them across three
   rows — so they give up a little height before anything else does.
   They stay comfortably above the 44px tap target on any screen tall
   enough to matter; this tier only exists for a viewport under 400px
   tall, where the alternative is a scrollbar. */
@media (max-height: 400px) {
  #screen-landing.has-photo .picker { gap: 4px; margin: 5px 0; }
  #screen-landing.has-photo .picker button {
    min-height: 29px;
    padding: 2px 8px;
    font-size: 11.5px;
    gap: 4px;
  }
  #screen-landing.has-photo .picker button .pill-emoji { font-size: 12px; }

  #screen-landing.has-photo .gender-block {
    margin-top: 4px;
    margin-bottom: 4px;
    padding-top: 4px;
  }
  .gender-label { font-size: 10px; }
  .gender-row button {
    min-height: 24px;
    padding: 1px 7px;
    font-size: 10px;
  }
}

/* ===================================================================
   MOTION IS OPTIONAL

   Some people get motion sickness or migraines from movement on a
   screen, and their phone already knows: iOS calls it Reduce Motion,
   Android calls it Remove Animations. Every bit of decoration above
   switches itself off here.

   Nothing in this app DEPENDS on an animation to work. The caption
   words are visible with the animation removed, the polaroid sits at
   its resting tilt, and the voice still plays — it just does so without
   the notes. Turning all of it off costs the visitor nothing but the
   decoration.
   =================================================================== */
@media (prefers-reduced-motion: reduce) {
  .glows { animation: none; }

  .thinking-dots span { animation: none; }
  .picker button[aria-checked="true"] { transform: none; }
  /* Hold the phone icon at the angle it's asking you to correct, rather
     than freezing it mid-wiggle where it would read as upright. */
  .rotate-icon { animation: none !important; transform: rotate(-82deg); }

  .polaroid.is-landing { animation: none; }

  /* The words must still be VISIBLE, so this is opacity 1 with no
     animation rather than simply cancelling the keyframes — cancelling
     alone would leave them stuck at opacity 0 and the caption blank. */
  .caption .word {
    animation: none;
    opacity: 1;
    transform: none;
  }

  /* No notes at all, rather than motionless notes sitting on the card. */
  .speech-marks { display: none; }

  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}
