/* ------------------------------------------------------------------
   Guess My Animal

   Two screens. Home is a flat field of brand colour with a search box
   on it; picking an animal swaps to a photo card with the name across
   the bottom and the answers underneath as you scroll.

   Ground and ink come from the warm end: a bone paper and an olive
   near-black rather than white on grey, so the yellow reads as a
   decision instead of the only colour present. Green, amber and red
   are then free to mean something on the answer pills.

   Motion has one authored moment - the card arriving - and quiet
   feedback everywhere else. Nothing is hidden by default: entrances
   are animations off a visible resting state, so a failed script
   leaves a working page rather than a blank one.
   ------------------------------------------------------------------ */

/* Variable, one file, 33 KB, self-hosted so the first paint does not
   wait on a third party. */
@font-face {
  font-family: 'Onest';
  src: url('fonts/onest-latin.woff2') format('woff2');
  font-weight: 300 800;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Fixed regardless of theme: the home screen stays yellow in the
     dark, so anything sitting on yellow needs an ink that never flips. */
  --sun:      #ffce1f;
  --sun-deep: #e8b400;
  --on-sun:   #1d1e19;

  --paper:  #e9eae2;
  --card:   #f8f9f3;
  --ink:    #1d1e19;
  --muted:  #5f6255;   /* tinted from the ink, never a neutral grey */
  --line:   #d7d9cc;

  --good:    #0a6b3d;  --good-bg: #dbe9dd;
  --warn:    #8a5600;  --warn-bg: #f4e6c9;
  --bad:     #b8321a;  --bad-bg:  #f2ddd6;
  --flat-bg: #e4e6da;

  /* The splash's own ground. Light keeps the brand yellow; dark makes it
     genuinely dark and moves the yellow onto the mark instead, because a
     full screen of yellow is what people turn dark mode on to escape. */
  --home-bg:   var(--sun);
  --home-ink:  var(--on-sun);
  --home-mark: var(--on-sun);

  --r-card:  20px;
  --r-photo: 26px;
  --shadow:   0 1px 2px rgba(29,30,25,.05), 0 6px 18px -10px rgba(29,30,25,.24);
  --shadow-l: 0 2px 6px rgba(29,30,25,.07), 0 22px 44px -22px rgba(29,30,25,.45);

  /* Confident arrival, no bounce. */
  --ease: cubic-bezier(.16, 1, .3, 1);

  /* Wide screens have real empty margin around the phone-width column;
     a faint scatter of the site's own paw mark says that's a choice,
     not an unstyled gap. Ink-coloured, so it flips with the theme. */
  --paw-field: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cg transform='translate(80.8,82) scale(.6)' fill='%231d1e19' fill-opacity='.05'%3E%3Cellipse cx='32' cy='41' rx='14.5' ry='12'/%3E%3Cellipse cx='14.5' cy='27' rx='6.4' ry='8'/%3E%3Cellipse cx='26' cy='16.5' rx='6.4' ry='8.6'/%3E%3Cellipse cx='38' cy='16.5' rx='6.4' ry='8.6'/%3E%3Cellipse cx='49.5' cy='27' rx='6.4' ry='8'/%3E%3C/g%3E%3C/svg%3E");
}

/* The dark palette is applied through two selectors: the media query for
   "follow the system", guarded so an explicit light choice still wins, and
   the attribute for an explicit dark choice. CSS cannot share a declaration
   block, so this pair is duplicated deliberately - edit both together or the
   toggle and the system setting drift apart. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:  #14150f;
    --card:   #1e2019;
    --ink:    #eef0e4;
    --muted:  #989c88;
    --line:   #2c2f25;

    --good:    #58cf9b;  --good-bg: #12301f;
    --warn:    #efbc63;  --warn-bg: #33270e;
    --bad:     #ff7f63;  --bad-bg:  #3a1912;
    --flat-bg: #262920;

    --home-bg:   #14150f;
    --home-ink:  #eef0e4;
    --home-mark: #ffce1f;

    --shadow:   0 6px 18px -10px rgba(0,0,0,.8);
    --shadow-l: 0 22px 44px -22px rgba(0,0,0,.9);

    --paw-field: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cg transform='translate(80.8,82) scale(.6)' fill='%23eef0e4' fill-opacity='.05'%3E%3Cellipse cx='32' cy='41' rx='14.5' ry='12'/%3E%3Cellipse cx='14.5' cy='27' rx='6.4' ry='8'/%3E%3Cellipse cx='26' cy='16.5' rx='6.4' ry='8.6'/%3E%3Cellipse cx='38' cy='16.5' rx='6.4' ry='8.6'/%3E%3Cellipse cx='49.5' cy='27' rx='6.4' ry='8'/%3E%3C/g%3E%3C/svg%3E");
  }
}
:root[data-theme="dark"] {
    --paper:  #14150f;
    --card:   #1e2019;
    --ink:    #eef0e4;
    --muted:  #989c88;
    --line:   #2c2f25;

    --good:    #58cf9b;  --good-bg: #12301f;
    --warn:    #efbc63;  --warn-bg: #33270e;
    --bad:     #ff7f63;  --bad-bg:  #3a1912;
    --flat-bg: #262920;

    --home-bg:   #14150f;
    --home-ink:  #eef0e4;
    --home-mark: #ffce1f;

    --shadow:   0 6px 18px -10px rgba(0,0,0,.8);
    --shadow-l: 0 22px 44px -22px rgba(0,0,0,.9);

    --paw-field: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'%3E%3Cg transform='translate(80.8,82) scale(.6)' fill='%23eef0e4' fill-opacity='.05'%3E%3Cellipse cx='32' cy='41' rx='14.5' ry='12'/%3E%3Cellipse cx='14.5' cy='27' rx='6.4' ry='8'/%3E%3Cellipse cx='26' cy='16.5' rx='6.4' ry='8.6'/%3E%3Cellipse cx='38' cy='16.5' rx='6.4' ry='8.6'/%3E%3Cellipse cx='49.5' cy='27' rx='6.4' ry='8'/%3E%3C/g%3E%3C/svg%3E");
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: Onest, ui-sans-serif, system-ui, 'Segoe UI', sans-serif;
  font-size: 16px;
  line-height: 1.45;
  letter-spacing: -.011em;
  -webkit-font-smoothing: antialiased;
}
/* Colour only, not the shorthand: `background:` resets every sub-property
   it doesn't mention, including background-image, which would silently
   erase the wide-screen paw field below every time the splash is shown. */
body.view-home { background-color: var(--home-bg); }

/* -- Surfaces the browser would otherwise style for us ------------- */

::selection { background: var(--sun); color: var(--on-sun); }
.view-home ::selection { background: var(--home-ink); color: var(--home-bg); }
#q { caret-color: var(--ink); }

:focus-visible { outline: 3px solid var(--ink); outline-offset: 2px; border-radius: 5px; }
body.view-home :focus-visible { outline-color: var(--home-ink); }

* { scrollbar-color: var(--line) transparent; scrollbar-width: thin; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--line); border-radius: 99px;
  border: 3px solid var(--paper);
}
::-webkit-scrollbar-thumb:hover { background: var(--muted); }

/* ==================================================================
   MOTION

   One keyframe does the work. Elements rest visible and the animation
   plays off that resting state with `backwards` fill, so nothing is
   ever stuck invisible if the script never runs.
   ================================================================== */

@keyframes rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* The authored moment: the card arrives. It lifts, settles, and
   uncovers itself from the top edge down, which is the one beat this
   product has - the answer showing up. */
@keyframes deal {
  from {
    opacity: 0;
    transform: translateY(30px) scale(.955);
    clip-path: inset(14% 7% 0 7% round var(--r-photo));
  }
  to {
    opacity: 1;
    transform: none;
    clip-path: inset(0 0 0 0 round var(--r-photo));
  }
}

.anim-rise { animation: rise .55s var(--ease) backwards; }
.anim-deal { animation: deal .72s var(--ease) backwards; }
/* Stagger is capped: this is a list arriving as a list, not every
   section on the page reinterpreted as one. */
.stagger > * {
  animation: rise .5s var(--ease) backwards;
  animation-delay: calc(var(--base, 0ms) + var(--i, 0) * 30ms);
}

@media (prefers-reduced-motion: reduce) {
  /* Reduced, not removed: the fade still says something arrived. */
  @keyframes rise { from { opacity: 0; } to { opacity: 1; } }
  @keyframes deal { from { opacity: 0; } to { opacity: 1; } }
  .anim-rise, .anim-deal, .stagger > * { animation-duration: .2s; animation-delay: 0s !important; }
  .pcard.is-loading .pcard-fallback { animation: none; opacity: .6; }
  * { transition-duration: .05s !important; }
}

/* ==================================================================
   HOME
   ================================================================== */

.home {
  min-height: 100svh;
  background: var(--home-bg);
  color: var(--home-ink);
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 30px;
  padding: 36px 20px calc(20px + env(safe-area-inset-bottom));
  text-align: center;
}
.home[hidden] { display: none; }

.home-in { width: 100%; max-width: 460px; }

.mark {
  width: 76px; height: 76px;
  display: block; margin: 0 auto 20px;
  fill: var(--home-mark);
}

.wordmark {
  margin: 0;
  font-size: clamp(40px, 12vw, 60px);
  font-weight: 700;
  line-height: .95;
  letter-spacing: -.04em;
  text-wrap: balance;
}

.tagline {
  margin: 14px 0 0;
  font-size: 16px; font-weight: 400;
  color: color-mix(in srgb, var(--home-ink) 68%, transparent);
}

/* The search box on the splash: tall and unmissable. It follows the
   theme like everything else now that the splash is no longer pinned to
   yellow in the dark. */
body.view-home .searchunit { margin: 30px auto 0; }
body.view-home .searchrow {
  border-color: transparent;
  border-radius: 18px;
  box-shadow: var(--shadow-l);
  padding: 5px 5px 5px 16px;
  min-height: 62px;
}
body.view-home #q { font-size: 17px; }

.starters { margin-top: 26px; }
.starters-label {
  margin: 0 0 11px;
  font-size: 13px; font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase;
  color: color-mix(in srgb, var(--home-ink) 72%, transparent);
}
.chips { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.chip {
  font: inherit; font-size: 15px; font-weight: 500; cursor: pointer;
  color: var(--home-ink);
  background: transparent;
  border: 1.5px solid color-mix(in srgb, var(--home-ink) 24%, transparent);
  border-radius: 999px;
  padding: 9px 15px; min-height: 44px;
  transition: background .16s var(--ease), border-color .16s var(--ease), transform .16s var(--ease);
}
.chip:hover {
  background: color-mix(in srgb, var(--home-ink) 8%, transparent);
  border-color: color-mix(in srgb, var(--home-ink) 45%, transparent);
}
.chip:active { transform: scale(.97); }

.home-foot {
  margin: 0; font-size: 13.5px; font-weight: 500;
  color: color-mix(in srgb, var(--home-ink) 72%, transparent);
}

/* ==================================================================
   SEARCH UNIT  (lives in the hero, then moves into the top bar)
   ================================================================== */

.searchunit { position: relative; width: 100%; }

.searchrow {
  display: flex; align-items: center; gap: 8px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 15px;
  padding: 4px 4px 4px 13px;
  min-height: 52px;
  transition: border-color .16s var(--ease), box-shadow .16s var(--ease);
}
.searchrow:focus-within { border-color: color-mix(in srgb, var(--ink) 40%, transparent); }
.mag { flex: none; width: 20px; height: 20px; color: var(--muted); }
.mag svg { width: 100%; height: 100%; display: block; }

#q {
  flex: 1; min-width: 0;
  font: inherit; font-size: 16px; font-weight: 500;
  color: var(--ink);
  background: transparent;
  border: 0; outline: none;
  padding: 10px 0;
  min-height: 44px;
}
#q::placeholder { color: var(--muted); font-weight: 400; }
/* The native clear button is un-themed OS blue by default - restyled as a
   mask so it recolours with --muted/--ink like every other drawn icon,
   rather than being the one cold, un-authored colour on the page. */
#q::-webkit-search-cancel-button {
  cursor: pointer; -webkit-appearance: none; appearance: none;
  width: 16px; height: 16px; margin-left: 2px;
  background-color: var(--muted);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M6 6l12 12M18 6 6 18'/%3E%3C/svg%3E") center / 70% no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round'%3E%3Cpath d='M6 6l12 12M18 6 6 18'/%3E%3C/svg%3E") center / 70% no-repeat;
}
#q::-webkit-search-cancel-button:hover { background-color: var(--ink); }

.dice {
  flex: none; width: 44px; height: 44px;
  display: grid; place-items: center; cursor: pointer;
  color: var(--on-sun);
  background: var(--sun);
  border: 0; border-radius: 12px;
  transition: background .16s var(--ease), transform .16s var(--ease);
}
.dice svg { width: 21px; height: 21px; }
.dice:hover { background: var(--sun-deep); }
.dice:active { transform: scale(.94); }

/* Tossed, not just pressed: a full turn that swells slightly at the peak
   of the arc, like the die leaving the hand before it lands back flat.
   This is the site's most-repeated interaction, so it stays quick and
   quiet rather than competing with the photo card's own arrival - the
   one moment this system spends its full authored attention on.
   Two different curves, not one repeated: --ease is a front-loaded
   "confident settle" tuned for something arriving and stopping (rise,
   deal), so using it for BOTH halves of a rotation crams nearly all the
   visible spin into the first sliver of the duration and reads as a
   flick, not a roll. The first half accelerates away like a toss
   leaving the hand; only the second half gets the settle. */
/* Hardcoded, not var(--ease): a custom property referenced inside a
   keyframe's own animation-timing-function does not reliably resolve -
   @keyframes rules sit outside the normal cascade/inheritance chain, so
   it silently fell back to the shorthand's default instead of erroring.
   Same curve as --ease, just spelled out literally. */
@keyframes diceRoll {
  0%   { transform: rotate(0deg) scale(1); animation-timing-function: cubic-bezier(.55, 0, 1, .45); }
  55%  { transform: rotate(300deg) scale(1.08); animation-timing-function: cubic-bezier(.16, 1, .3, 1); }
  100% { transform: rotate(360deg) scale(1); }
}
.dice.rolling { animation: diceRoll .48s linear; }
@media (prefers-reduced-motion: reduce) {
  @keyframes diceRoll { from { opacity: .6; } to { opacity: 1; } }
  .dice.rolling { animation-duration: .15s; }
}

.suggest {
  position: absolute; left: 0; right: 0; top: calc(100% + 8px); z-index: 40;
  list-style: none; margin: 0; padding: 6px;
  background: var(--card);
  border-radius: 16px;
  box-shadow: var(--shadow-l);
  max-height: 52vh; overflow-y: auto;
  text-align: left;
  animation: rise .22s var(--ease) backwards;
}
.suggest[hidden] { display: none; }
.suggest li {
  display: flex; align-items: center; gap: 11px;
  padding: 11px 12px; border-radius: 11px;
  cursor: pointer; font-weight: 500; color: var(--ink);
  min-height: 48px;
  transition: background .13s var(--ease);
}
.suggest li[aria-selected="true"], .suggest li:hover { background: var(--flat-bg); }
.s-emoji { font-size: 21px; flex: none; line-height: 1; }
.s-alias { color: var(--muted); font-size: 14px; font-weight: 400; }

.noresult {
  position: absolute; left: 0; right: 0; top: calc(100% + 8px); z-index: 40;
  margin: 0; padding: 14px 16px;
  background: var(--card);
  border-radius: 16px;
  box-shadow: var(--shadow-l);
  font-size: 14.5px; font-weight: 400; color: var(--muted);
  text-align: left;
  animation: rise .22s var(--ease) backwards;
}
.noresult[hidden] { display: none; }

/* ==================================================================
   ANIMAL VIEW
   ================================================================== */

.animalview[hidden] { display: none; }

.topbar {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; gap: 9px;
  padding: 10px 14px;
  padding-top: calc(10px + env(safe-area-inset-top));
  background: color-mix(in srgb, var(--paper) 84%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
}
.back {
  flex: none; width: 44px; height: 44px;
  display: grid; place-items: center; cursor: pointer;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line); border-radius: 13px;
  transition: background .16s var(--ease), transform .16s var(--ease);
}
.back svg { width: 21px; height: 21px; }
.back:hover { background: var(--flat-bg); }
.back:active { transform: scale(.94); }
.dock-top { flex: 1; min-width: 0; }

.feed {
  max-width: 560px;
  margin: 0 auto;
  padding: 14px 14px calc(30px + env(safe-area-inset-bottom));
}

/* -- The photo card ------------------------------------------------
   Portrait, because it is read on a phone held upright and a wide crop
   of an animal wastes the screen. The name sits on the photo rather
   than under it, so the first thing on screen is one object, not two. */

.pcard {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: var(--r-photo);
  overflow: hidden;
  background: var(--flat-bg);
  box-shadow: var(--shadow-l);
}
/* Blurred, over-scaled copy filling the card behind the real photo, so
   a landscape photo in a portrait card gets bands of its own colour
   rather than having the animal cropped out of frame. */
.pcard-bg {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  filter: blur(26px) saturate(1.5) brightness(.82);
  transform: scale(1.3);
}
.pcard-img {
  position: relative;
  display: block; width: 100%; height: 100%;
  object-fit: contain;
  /* Sat high in the card so the empty band falls at the bottom, where
     the name is going anyway, instead of being split evenly and wasted. */
  object-position: center 30%;
}
/* Sits behind the photo and shows through only while there is nothing
   to show, so a missing image is a big emoji rather than a hole. */
.pcard-fallback {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
}
.pcard-fallback-emoji { font-size: 96px; line-height: 1; }
/* Empty until both photo requests have genuinely settled with nothing
   found - see Promise.allSettled in show(). Blank while loading (the
   pulse alone carries that), so this never claims "no photo" before
   it's actually true. */
.pcard-fallback-note { margin: 0; font-size: 13.5px; font-weight: 500; color: var(--muted); }
.pcard.has-photo .pcard-fallback { display: none; }
/* Quiet, not another authored moment - just enough motion that a fetch
   in flight doesn't read as "no photo exists" for the second or so it
   takes to resolve. Stops the instant a real photo lands, or the instant
   both requests come back with nothing to show. */
@keyframes pulse { 0%, 100% { opacity: .35; } 50% { opacity: .85; } }
.pcard.is-loading .pcard-fallback { animation: pulse 1.3s ease-in-out infinite; }

.pcard-scrim {
  position: absolute; left: 0; right: 0; bottom: 0; height: 62%;
  background: linear-gradient(to bottom, transparent, rgba(10,11,7,.3) 42%, rgba(10,11,7,.9));
}
.pcard-meta {
  position: absolute; left: 20px; right: 20px; bottom: 17px;
  color: #fff; text-align: left;
}
.pcard-name {
  margin: 0;
  font-size: clamp(31px, 8.6vw, 42px);
  font-weight: 700; letter-spacing: -.04em; line-height: 1.02;
  text-shadow: 0 2px 18px rgba(0,0,0,.4);
}
.pcard-emoji { font-size: .78em; }
/* Under the name, not above it. What it is and where it lives is
   detail that follows the answer, not a label introducing it. */
.pcard-sub {
  margin: 5px 0 0;
  font-size: 14.5px; font-weight: 500;
  color: var(--sun);
  text-shadow: 0 1px 10px rgba(0,0,0,.5);
}

.blurb {
  margin: 17px 4px 0;
  font-size: 16px; font-weight: 400; color: var(--muted);
  max-width: 62ch;
}

/* -- Section labels ------------------------------------------------ */

.label {
  margin: 30px 0 10px 4px;
  font-size: 12.5px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted);
}

/* -- Quick answers -------------------------------------------------
   A list, not a grid. The question is quiet on the left and the answer
   is a bold pill on the right, so the answers line up in one column the
   eye can run straight down. Every pill says the answer in words;
   colour is layered on top and is never the only signal. */

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: 2px 16px;
}
.row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 13px 0;
  border-bottom: 1px solid var(--line);
}
.row:last-child { border-bottom: 0; }
.row .k { font-size: 15.5px; font-weight: 400; color: var(--muted); }
/* Splits the identity/risk cluster from the behaviour cluster - eight
   identical rows read as one wall of text otherwise. Spans both columns
   in the two-up grid so it reads as one shared gap, not a stray cell. */
.ans-gap { height: 14px; grid-column: 1 / -1; }

.pill {
  flex: none;
  font-size: 15px; font-weight: 600; letter-spacing: -.015em;
  padding: 6px 13px; border-radius: 999px;
  background: var(--flat-bg); color: var(--ink);
  text-align: center;
}
.pill.good { background: var(--good-bg); color: var(--good); }
.pill.warn { background: var(--warn-bg); color: var(--warn); }
.pill.bad  { background: var(--bad-bg);  color: var(--bad); }

/* -- At a glance -----------------------------------------------------
   A trivia strip, not a second answer list. It can run to ten chips for
   a well-attributed animal, so it stays visibly lighter than Quick
   Answers - the Caption size (13.5px), not the Meta size - or it reads
   as equally important and doubles the wall of text this page is trying
   not to be. */

.glance { display: flex; flex-wrap: wrap; gap: 7px; }
/* Forces a line break in the flex-wrap, splitting appearance chips from
   behaviour chips the way .ans-gap splits Quick Answers - a beat, not a
   rule, so it still reads as one glance, just no longer one flat block. */
.glance-gap { flex-basis: 100%; height: 4px; }
.gchip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 7px 12px;
  font-size: 13.5px; font-weight: 600;
}
.gchip svg { width: 14px; height: 14px; flex: none; color: var(--muted); }
.gchip .gk { color: var(--muted); font-weight: 400; }

/* -- Fact ----------------------------------------------------------- */

/* Read-only content, not an action - a solid Sun fill here competed with
   the dice button and the primary CTA for the same "this is the one
   yellow thing on screen" attention. A tint keeps it feeling like a
   highlight rather than a second (or third) press-me surface. */
.factcard {
  background: color-mix(in srgb, var(--sun) 16%, var(--card));
  border: 1px solid color-mix(in srgb, var(--sun) 40%, var(--line));
  color: var(--ink);
  border-radius: var(--r-card);
  padding: 17px 19px;
}
.factcard p { margin: 0; font-size: 16.5px; font-weight: 500; line-height: 1.4; }

/* -- Extra photos folded into the scroll ---------------------------- */

.shot:empty { display: none; }
.shot { margin-top: 30px; }
.shot figure { margin: 0; }
.shot img {
  display: block; width: 100%; height: auto;
  aspect-ratio: 4 / 3; object-fit: cover;
  border-radius: var(--r-photo);
  background: var(--flat-bg);
  box-shadow: var(--shadow);
}
.shot figcaption {
  margin: 9px 6px 0;
  font-size: 13.5px; font-weight: 400; color: var(--muted);
}

/* -- Actions -------------------------------------------------------- */

/* A grid, not flex-wrap: four buttons of uneven label length wrapped
   raggedly with the solid CTA's margin-left:auto stranding it alone on
   its own line. A fixed 2x2 grid gives every button the same width and
   keeps the two rows aligned regardless of label length. */
.actions { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin-top: 30px; }
.share { display: flex; }
.share > .btn { width: 100%; }
.btn {
  font: inherit; font-size: 15px; font-weight: 600; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  text-decoration: none;
  padding: 12px 17px; min-height: 48px;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: var(--card); color: var(--ink);
  transition: background .16s var(--ease), transform .16s var(--ease);
}
.btn:hover { background: var(--flat-bg); }
.btn:active { transform: scale(.97); }
.btn-solid {
  background: var(--sun); color: var(--on-sun); border-color: var(--sun);
}
.btn-solid:hover { background: var(--sun-deep); border-color: var(--sun-deep); }

.copied {
  margin: 10px 4px 0; min-height: 20px;
  font-size: 14px; font-weight: 600; color: var(--good);
}
.copied:not(:empty) { animation: rise .3s var(--ease) backwards; }

/* -- Ad slot --------------------------------------------------------
   The frame is reserved so an advert dropped in later cannot shove the
   page around, but an empty frame reads as a broken component to a real
   visitor today - so it collapses to nothing until something fills it. */

.adslot {
  min-height: 100px; margin-top: 26px;
  display: grid; place-items: center;
  border: 1px dashed var(--line); border-radius: var(--r-card);
  color: var(--muted); font-size: 13px; font-weight: 400;
}
.adslot:empty { display: none; margin-top: 0; }

.foot {
  margin-top: 26px; text-align: center;
  color: var(--muted); font-size: 13.5px; font-weight: 400;
}
.foot p { margin: 0; }
.foot a { color: var(--ink); text-underline-offset: 3px; }

/* ==================================================================
   Wider screens: the card stops being a phone card and the answers
   go two-up rather than leaving half the row empty.
   ================================================================== */

@media (min-width: 620px) {
  .feed { padding-top: 20px; }
  .pcard { aspect-ratio: 3 / 2; }
  .pcard-name { font-size: 46px; }
  .rows { display: grid; grid-template-columns: 1fr 1fr; column-gap: 26px; }
  /* In two columns the last row is the final pair, so the second-to-last
     row needs its divider removed too or it trails a line under nothing.
     Position, not parity: the .ans-gap spacer between the two answer
     clusters is a grid sibling too, so it shifts every row after it by
     one slot and an odd/even check would silently stop matching. */
  .rows > .row:nth-last-child(2) { border-bottom: 0; }
  /* Keep the back button lined up with the feed's left edge instead of
     letting the search box run the whole width of a desktop window.
     Right padding is set below, alongside the menu-clearance rule it
     has to stay in step with. */
  .topbar { padding-left: max(14px, calc((100% - 588px) / 2)); }
  /* The search box itself (.dock-top) sits in a flex row between the
     back button (aligned to the feed's left edge) and the padding
     reserved for the fixed-position menu icon (aligned to the true
     viewport edge) - two different anchors, so the row between them
     widens on a desktop window. Capping it keeps the search bar a sane
     width; margin:0 auto centering it within that leftover space was
     wrong too - on an ultra-wide window padding-left grows far more
     than padding-right stays flat, so the "leftover space" itself sits
     well right of true centre and drags the whole search+dice cluster
     away from the back button with it. Left-aligned (flex default)
     keeps it planted right next to the back button instead, matching
     the feed column below rather than floating in the middle distance. */
  .dock-top { max-width: 460px; }
  /* Four buttons fit one tidy row once there's room, rather than
     staying stacked in the mobile 2x2 grid. */
  .actions { grid-template-columns: repeat(4, 1fr); }
}

/* Past the point where the content column stops filling the window,
   real empty margin opens up either side of it. A faint scatter of the
   site's own paw mark says that space was left on purpose. Applied to
   .home too, not just body: .home paints its own opaque background on
   top of body's, so body's layer alone never reaches the splash. */
@media (min-width: 900px) {
  body, .home {
    background-image: var(--paw-field);
    background-repeat: repeat;
    background-size: 140px 140px;
  }
}

/* ==================================================================
   MENU

   Fixed to the corner so it sits in the same place on the splash and
   on an animal. On the yellow it is drawn in ink; over the feed it
   takes the card surface so it reads as a control rather than a hole.
   ================================================================== */

.menu { position: fixed; z-index: 60; right: 14px; top: calc(12px + env(safe-area-inset-top)); }

.menu-btn {
  width: 44px; height: 44px;
  display: grid; place-items: center; cursor: pointer;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 13px;
  transition: background .16s var(--ease), transform .16s var(--ease);
}
.menu-btn svg { width: 21px; height: 21px; }
.menu-btn:hover { background: var(--flat-bg); }
.menu-btn:active { transform: scale(.94); }
/* On the splash the ground is already a strong colour, so the button
   is an outline on it rather than another filled box. */
body.view-home .menu-btn {
  color: var(--home-ink);
  background: transparent;
  border-color: color-mix(in srgb, var(--home-ink) 24%, transparent);
}
body.view-home .menu-btn:hover { background: color-mix(in srgb, var(--home-ink) 9%, transparent); }

/* The corner menu's mirror image on the opposite side - same fixed
   position and outline treatment, but carrying words instead of an
   icon, since what it links to (the game itself) is worth naming
   rather than tucking behind a symbol. Splash-only: hidden by default,
   shown only when body.view-home is present. */
.play-link {
  display: none;
  position: fixed; z-index: 60; left: 14px; top: calc(12px + env(safe-area-inset-top));
  align-items: center;
  height: 44px; padding: 0 16px;
  font: inherit; font-size: 14.5px; font-weight: 600;
  color: var(--home-ink);
  text-decoration: none;
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--home-ink) 24%, transparent);
  border-radius: 13px;
  transition: background .16s var(--ease), transform .16s var(--ease);
}
.play-link:hover { background: color-mix(in srgb, var(--home-ink) 9%, transparent); }
.play-link:active { transform: scale(.97); }
body.view-home .play-link { display: inline-flex; }

.menu-panel {
  position: absolute; right: 0; top: calc(100% + 8px);
  min-width: 210px;
  display: flex; flex-direction: column;
  padding: 6px;
  background: var(--card);
  border-radius: 16px;
  box-shadow: var(--shadow-l);
  animation: rise .2s var(--ease) backwards;
}
.menu-panel[hidden] { display: none; }

/* Both floating panels are plain elements, not native <dialog>, so nothing
   dims what's behind them the way the report dialog's ::backdrop does.
   On the splash the panel can overlap the wordmark - its position is
   viewport-height dependent, so nudging one element's margin only fixes
   one screen size. Dimming the page instead makes the overlap read as an
   intentional layer at every height, the way the dialog's backdrop already
   does for the report form. */
body:has(#menuPanel:not([hidden]))::before,
body:has(#sharePanel:not([hidden]))::before {
  content: '';
  position: fixed; inset: 0; z-index: 45;
  background: rgba(20,21,15,.5);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.menu-item {
  font: inherit; font-size: 15px; font-weight: 500; text-align: left;
  cursor: pointer; text-decoration: none;
  color: var(--ink); background: transparent; border: 0;
  padding: 12px 13px; border-radius: 11px; min-height: 46px;
  display: flex; align-items: center;
  transition: background .13s var(--ease);
}
.menu-item:hover { background: var(--flat-bg); }

/* Room for the button so a long search box never slides under it. A flat
   68px at every width, not a centering formula: growing it with the
   viewport was leaving an ever-widening dead zone between the search
   unit and the menu on wide desktop windows, while the left edge stays
   aligned with the feed regardless. */
.topbar { padding-right: 68px; }

/* ==================================================================
   REPORT DIALOG
   ================================================================== */

.dlg {
  width: min(460px, calc(100vw - 28px));
  padding: 0; border: 0;
  border-radius: var(--r-card);
  background: var(--card);
  color: var(--ink);
  box-shadow: var(--shadow-l);
}
.dlg::backdrop { background: rgba(20,21,15,.5); backdrop-filter: blur(3px); }
.dlg[open] { animation: rise .28s var(--ease) backwards; }

.dlg-in { padding: 20px; display: flex; flex-direction: column; gap: 15px; }
.dlg-title { margin: 0; font-size: 22px; font-weight: 700; letter-spacing: -.03em; }
.dlg-sub { margin: -9px 0 0; font-size: 14.5px; color: var(--muted); }
.dlg-sub:empty { display: none; }
.dlg-note { margin: -9px 0 0; font-size: 14.5px; color: var(--muted); }

.kinds { margin: 0; padding: 0; border: 0; display: flex; flex-wrap: wrap; gap: 7px; }
.kinds legend {
  padding: 0 0 9px;
  font-size: 12.5px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
}
.kinds label {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 14px; border-radius: 999px;
  border: 1px solid var(--line);
  font-size: 14.5px; font-weight: 500; cursor: pointer;
  transition: background .14s var(--ease), border-color .14s var(--ease);
}
.kinds input { position: absolute; opacity: 0; pointer-events: none; }
.kinds label:hover { background: var(--flat-bg); }
/* Selected state carries a fill and a heavier weight, not colour alone -
   ink-toned rather than Sun, so the dialog still has exactly one solid
   Sun element: "Send report", the thing that's actually an action. */
.kinds label:has(input:checked) {
  background: color-mix(in srgb, var(--ink) 8%, var(--card));
  border-color: var(--ink);
  color: var(--ink); font-weight: 600;
}
.kinds label:has(input:focus-visible) { outline: 3px solid var(--ink); outline-offset: 2px; }

.fld { display: flex; flex-direction: column; gap: 7px; }
.fld-label { font-size: 14.5px; font-weight: 500; color: var(--muted); }
.dlg textarea {
  font: inherit; font-size: 15.5px;
  color: var(--ink); background: var(--paper);
  border: 1px solid var(--line); border-radius: 13px;
  padding: 12px 13px; resize: vertical; min-height: 96px;
  caret-color: var(--ink);
}
.dlg textarea:focus { outline: none; border-color: color-mix(in srgb, var(--ink) 45%, transparent); }

/* Off-screen rather than display:none, so a bot that reads the DOM
   still finds it and fills it in. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.dlg-status { margin: 0; min-height: 20px; font-size: 14px; font-weight: 500; color: var(--muted); }
.dlg-status.ok  { color: var(--good); }
.dlg-status.err { color: var(--bad); }

.dlg-actions { display: flex; gap: 9px; }
.dlg-actions .btn-solid { margin-left: auto; }

/* ==================================================================
   PROSE  (the about page)
   ================================================================== */

body.view-page { background: var(--paper); }

.topbar-mark {
  display: inline-flex; align-items: center; gap: 9px;
  font-size: 15.5px; font-weight: 600; letter-spacing: -.02em;
  color: var(--ink); text-decoration: none;
}
.topbar-mark svg { width: 21px; height: 21px; fill: var(--ink); }

.prose {
  max-width: 640px;
  margin: 0 auto;
  padding: 26px 18px 40px;
}
/* Measure sits inside the comfortable range for reading rather than
   running the full width of the container. */
.prose p, .prose ul { max-width: 62ch; }

.prose h1 {
  margin: 6px 0 0;
  font-size: clamp(31px, 8vw, 44px);
  font-weight: 700; line-height: 1.05; letter-spacing: -.04em;
  text-wrap: balance;
}
.prose h2 {
  margin: 38px 0 0;
  font-size: clamp(20px, 5vw, 24px);
  font-weight: 700; letter-spacing: -.03em;
}
.prose p { margin: 14px 0 0; font-size: 16.5px; line-height: 1.6; }
.prose .lede { font-size: 18.5px; line-height: 1.5; color: var(--muted); margin-top: 16px; }
.prose strong { font-weight: 600; }
.prose em { font-style: italic; }
.prose a { color: var(--ink); text-underline-offset: 3px; text-decoration-thickness: 1px; }
.prose a:hover { text-decoration-thickness: 2px; }
.prose .btn { text-decoration: none; margin-top: 4px; }
/* .prose a's ink colour would otherwise outrank .btn-solid's on-sun text
   by specificity, leaving the button unreadable once dark mode splits
   ink and on-sun apart. */
.prose a.btn-solid { color: var(--on-sun); }

.ticks { margin: 14px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 8px; }
.ticks li {
  position: relative; padding-left: 26px;
  font-size: 16.5px; line-height: 1.5;
}
/* Drawn, not a bullet glyph: a question mark reads as "ask this". */
.ticks li::before {
  content: "?";
  position: absolute; left: 0; top: 1px;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--sun); color: var(--on-sun);
  font-size: 12px; font-weight: 700;
  display: grid; place-items: center;
}

.prose-foot { padding: 0 18px calc(34px + env(safe-area-inset-bottom)); }

/* ==================================================================
   SHARE

   Native sheet on a touch device, where it reaches WhatsApp and
   everything else properly. On a desktop, where that sheet is either
   missing or drops the link, an explicit panel instead.
   ================================================================== */

.share { position: relative; display: inline-flex; }

.share-panel {
  position: absolute; left: 0; bottom: calc(100% + 8px); z-index: 50;
  min-width: 194px;
  display: flex; flex-direction: column;
  padding: 6px;
  background: var(--card);
  border-radius: 16px;
  box-shadow: var(--shadow-l);
  animation: rise .2s var(--ease) backwards;
}
.share-panel[hidden] { display: none; }

.share-item {
  display: flex; align-items: center; gap: 11px;
  font: inherit; font-size: 15px; font-weight: 500; text-align: left;
  cursor: pointer; text-decoration: none;
  color: var(--ink); background: transparent; border: 0;
  padding: 11px 12px; border-radius: 11px; min-height: 46px;
  transition: background .13s var(--ease);
}
.share-item:hover { background: var(--flat-bg); }
/* Brand marks are filled shapes, not part of the drawn stroke set. */
.share-item svg { width: 19px; height: 19px; flex: none; fill: currentColor; }

/* ==================================================================
   THEME TOGGLE

   Three states, because "follow my phone" is the right default and
   removing it would be a downgrade for anyone who has set it once.
   ================================================================== */

.theme {
  display: flex; gap: 3px;
  margin: 5px 6px 3px;
  padding: 3px;
  background: var(--flat-bg);
  border-radius: 12px;
}
.theme-btn {
  flex: 1;
  font: inherit; font-size: 13.5px; font-weight: 500;
  cursor: pointer; color: var(--muted);
  background: transparent; border: 0; border-radius: 9px;
  padding: 8px 4px; min-height: 38px;
  transition: background .14s var(--ease), color .14s var(--ease);
}
.theme-btn:hover { color: var(--ink); }
/* The active state carries a surface and a weight change, not colour
   alone, so it survives being read without colour. */
.theme-btn[aria-pressed="true"] {
  background: var(--card);
  color: var(--ink);
  font-weight: 600;
  box-shadow: 0 1px 2px rgba(29,30,25,.14);
}
.theme-label {
  margin: 9px 13px 2px;
  font-size: 11.5px; font-weight: 600;
  letter-spacing: .09em; text-transform: uppercase;
  color: var(--muted);
}
.menu-sep { height: 1px; margin: 6px 8px; background: var(--line); border: 0; }
