/* Scale all fonts ~15% larger (16px → 18.4px root). Players can step
   that up from the settings sheet: js/text-size.js sets the attribute
   below, and because everything in the app is sized in rem, the whole
   layout grows with the text rather than the text alone. */
html {
  font-size: 18.4px;
}
html[data-text-size="large"] {
  font-size: 20.7px;
}
html[data-text-size="largest"] {
  font-size: 23px;
}
/* In a frame on a news site's page (the embed), a wide frame sits in the
   page's main column beside body text of 21px or so, where the chat's
   18.4px read small. It steps up a size there, and so do the sizes a
   player picks. A phone's frame is narrower than 600px, and keeps them.
   game/index.html marks the frame on <html>. */
@media (min-width: 600px) {
  html.in-frame { font-size: 20.7px; }
  html.in-frame[data-text-size="large"] { font-size: 23px; }
  html.in-frame[data-text-size="largest"] { font-size: 25.3px; }
}

/* The colour of the strip a phone keeps for its own gesture bar.
   Two browsers, two routes to it, and this covers the second.

   A browser that lays the page out UNDER the strip gets the colour from
   the status bar, which bleeds down through the body's safe-area padding
   (below). One that insets the viewport instead leaves the strip outside
   the page, and then what shows there is the CANVAS background — which
   CSS takes from <html>, falling back to <body> only while <html> has
   none. Naming both is what lets them differ: the page stays cream and
   the strip follows the chat. js/app.js puts the class on. */
html { background: #f5efe8; }          /* cream-100, the page's own */
html.status-bar-out { background: #e54447; }   /* clay-500, the status bar's */

/* The strip a phone keeps for its own gesture bar. The page is laid out
   underneath it, so anything at the bottom of the column — the "Let's
   go!" button on a long form, the last line of the results — sat behind
   the pill with no scroll left to reach it. Padding the body holds the
   whole column clear.

   Where there's no such strip (a desktop, an older phone) env() is 0 and
   none of this moves anything. */
body {
  padding-bottom: env(safe-area-inset-bottom);
}
/* The header does the same at the top, which only shows up once the chat
   is installed to a home screen and there's no browser chrome above it. */
body > header {
  padding-top: calc(0.75rem + env(safe-area-inset-top));
}
/* The status bar is the exception: it bleeds back down through that
   padding so its red reaches the bottom of the phone, rather than
   floating above a band of page colour. */
#status-bar {
  margin-bottom: calc(-1 * env(safe-area-inset-bottom));
  padding-bottom: calc(0.5rem + env(safe-area-inset-bottom));
}
/* Full-screen overlays sit outside the body's column, so they hold
   themselves clear. (#settings-panel already did.) */
#onboarding {
  padding-bottom: env(safe-area-inset-bottom);
}

/* Onboarding overlay */
#onboarding {
  transition: opacity 0.5s ease, transform 0.5s ease;
}

#carousel {
  transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

/* Typing indicator animation */
@keyframes typingDot {
  0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-4px); }
}

.typing-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #e54447;
  display: inline-block;
  animation: typingDot 1.4s infinite;
}
.typing-dot:nth-child(2) { animation-delay: 0.2s; }
.typing-dot:nth-child(3) { animation-delay: 0.4s; }

/* Chat message entrance */
@keyframes slideUp {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: translateY(0); }
}

.chat-message {
  animation: slideUp 0.3s ease-out;
}

/* Stats bar fill animation */
@keyframes fillBar {
  from { width: 0; }
}

.stats-bar-fill {
  animation: fillBar 0.6s ease-out;
}

/* Smooth scroll for chat, and a little room under the header before the
   first bubble. */
#chat {
  scroll-behavior: smooth;
  padding-top: 1.25rem;
}
/* On a wide screen the conversation is a column down the middle, so a
   bubble's lines stay a comfortable length to read. The header, the bar
   you type in and the progress bar keep their color the whole width, but
   what's in them lines up with the column: the logo and the section name
   over the hosts' faces, the buttons, the field's end and the progress
   over the player's bubbles. #chat itself runs the whole width too (its
   scrollbar at the window's edge); its sides take the rest. */
@media (min-width: 768px) {
  #chat, body > header, #input-bar, #status-bar { padding-inline: max(1rem, calc((100% - 36rem) / 2)); }
}

/* Summary card gradient border */
.summary-card {
  background: linear-gradient(white, white) padding-box,
              linear-gradient(135deg, #e54447, #8db3d9, #e54447) border-box;
  border: 3px solid transparent;
  border-radius: 1rem;
}

/* Display type: Google Sans Flex for big moments (the landing page's
   headlines, "Thanks, Clark!") and for button labels. Body copy stays
   Public Sans, which reads better at length. */
.display {
  font-family: 'Google Sans Flex', 'Public Sans', sans-serif;
  letter-spacing: -0.025em;
}
.btn-display {
  font-family: 'Google Sans Flex', 'Public Sans', sans-serif;
  font-weight: 600;
}

/* End section (spec §11.6): a peek carousel of 4:5 share cards, the
   current card centred with its neighbours peeking in either side and
   only it at full opacity, with the thanks above and the Invite others
   button below.
   *2026-09-17:* one shape at every width. The end screen is headed for an
   iframe on someone else's page, so a wide window gets a roomier phone
   rather than a different layout: the copy, the cards and the button all
   cap at --col.
   *2026-09-20:* still one shape, but anchored left rather than centred:
   a column adrift in the middle of a desktop's white read as a page that
   had lost its layout.
   *2026-09-30:* off the full-width white. The copy, the button and the
   podcast link sit on the chat's own cream, in its margins like any
   bubble, and only the cards are on white, in a rounded box of their own
   (.share-strip); on a phone that box is a band from edge to edge. The
   card is 76% of it, and --peek is what's left, halved, so peek + slide +
   peek is exactly the box. */
.end-section {
  --col: 33rem;
  --slide: 76%;
  --peek: calc((100% - var(--slide)) / 2);
}
/* *2026-09-28:* and never taller than the chat can show with "Thanks"
   above it and the button below, so where the chat parks (16px under the
   top) all three are on screen at once: a laptop's window, a short embed,
   a small phone. #chat is a size container, so 100cqh is its height
   inside its padding, and 2rem more is the whole chat. Around the card the
   section takes about 16.75rem (--around), in rem so it follows the text
   size: the heading and blurb above, the box's own padding, the button,
   and under it the podcast link too (2026-09-30), so that's on screen as
   well. Then 28px: that 16px and a little air. The card is 4:5, so its
   width is 0.8 of the height left, and never under 8rem; the neighbours'
   peek grows to take up the rest. Where cqh isn't known, the card keeps
   to its width.
   *2026-10-16:* the end is in labeled parts now, so what's on screen
   with the card is "Thanks", the line under it, the row for your answers
   and the two labels as well as the button; the podcast link has gone
   down to Keep going. Measured, that's about 20rem, and on a phone,
   where the line and the row each take two lines, about 22.75rem. */
@supports (height: 1cqh) {
  .end-section {
    --around: 20.75rem;
    --slide: min(76%, max(8rem, (100cqh + 2rem - var(--around) - 28px) * 0.8));
  }
  /* Narrower than the desktop's column (a wide embed, a tablet), the line
     under "Thanks" takes two lines. */
  @container (width < 36rem) {
    .end-section { --around: 22.25rem; }
  }
  @container (width < 30rem) {
    .end-section { --around: 23.5rem; }
  }
  /* Narrower still (a phone's embed), a line more. */
  @container (width < 19.25rem) {
    .end-section { --around: 24.75rem; }
  }
}
/* The cards' box, white on the chat's cream, with the bubbles' shadow. The
   neighbours are cut at its edges. */
.share-strip {
  max-width: var(--col);
  margin-right: auto;
  padding-block: 1rem;
  background: #fff;
  border-radius: 1.5rem;
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  overflow: hidden;
}
/* A phone, or any chat no wider than the column: the box would fill the
   chat anyway, so it's a white band from edge to edge instead, out over
   the chat's margins (which halve under 300px), with the card at the size
   it has always had there. #chat is the container. */
@container (width <= 33rem) {
  .share-strip { max-width: none; margin-inline: -1rem; border-radius: 0; box-shadow: none; }
}
@media (width < 300px) {
  .share-strip { margin-inline: -0.5rem; }
}
/* The copy, the button and the podcast link: the same column, left. */
.end-col { max-width: var(--col); margin-right: auto; }
/* *2026-10-16:* from 768px wide the chat is itself a column down the
   middle (#chat, above), so the end fills it: the cards' box and the
   button as wide as the bubbles. */
@media (min-width: 768px) {
  .end-section { --col: none; }
}
/* *2026-09-30:* in a frame on someone else's page (the embed on
   michiganpublic.org), the column is centred in the frame instead: there
   the frame is the page's own column, with the story above and below it,
   and a block hugging its left side looked unfinished. The game marks the
   frame on <html> before its first paint (game/index.html). Only where the
   chat is wider than the column: in a narrower one (the embed on a phone)
   the column is the chat's whole width, and the cards' box is the band
   from edge to edge, which a centring margin would push a rem right. */
@container (width > 33rem) {
  html.in-frame .end-col,
  html.in-frame .share-strip { margin-left: auto; }
}
/* In an embed as wide as a news site's column (the desktop embed), the
   podcast link under Invite others doesn't have to be on screen with the
   cards: the page around it carries an ad for the show. The link is still
   there, just below, and the card takes its room (the fit, above, leaves
   out its 1.75rem). On a phone, embedded or not, the link stays in view
   with the cards. */
/* *2026-10-16:* the end is in labeled parts now (your answers, a card to
   share, where to go next), and the podcast link has moved down to the
   last of them, so a wide embed fits the same way as anywhere else. */
/* The end's parts: a small label over each, like "Also open" on the
   start screen, and rows that read as one thing each, white on the
   chat's cream like the start screen's chat cards. */
.end-label {
  margin-top: 1.25rem;
  margin-bottom: 0.5rem;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #6b7280;
  text-align: center;
}
/* The icon on the left stands level with the title's first line (as in
   the backroom's Edit live chat box); the arrow on the right sits in the
   middle of the row. */
.end-row {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  width: 100%;
  padding: 0.75rem 1rem;
  background: #fff;
  border: 1px solid var(--color-cream-200);
  border-radius: 0.875rem;
  text-align: left;
  transition: border-color 0.15s, background-color 0.15s;
}
.end-row:hover { border-color: var(--color-cotton-300); background: var(--color-cotton-50); }
.end-row-icon { font-size: 1.375rem; line-height: 1.22rem; color: var(--color-clay-500); }
.end-row-title { display: block; font-weight: 700; font-size: 0.9375rem; color: var(--color-ink); line-height: 1.3; text-wrap: balance; }
.end-row-note { display: block; font-size: 0.75rem; color: #6b7280; margin-top: 0.125rem; }
.end-row-end { align-self: center; font-size: 1.25rem; line-height: 1.22rem; color: #9ca3af; transition: transform 0.2s; }
.end-row-static:hover { border-color: transparent; background: #fff; }
.end-row[aria-expanded="true"] .end-row-end { transform: rotate(180deg); }
.end-links { display: flex; flex-direction: column; gap: 0.5rem; }
/* A row and what's under it as one white card: the answers (the results,
   then the email offer), and the episodes, listed in line with the title. */
.end-fold { background: #fff; border: 1px solid var(--color-cream-200); border-radius: 0.875rem; overflow: hidden; }
.end-fold > .end-row { border: 0; border-radius: 0; }
.end-episodes { display: flex; flex-direction: column; gap: 0.375rem; margin-top: -0.375rem; padding: 0 1rem 0.875rem 3.125rem; text-align: left; }
.review-answers { padding: 0 1rem 1rem; }
/* Inside the results card, under the rows, as far from them as the
   title and date are above, and set like the date: gray-500, 4.8:1 on
   white (the date was gray-400, 2.5:1, too faint to pass). */
.summary-note { font-size: 11px; color: #6b7280; text-align: center; margin-top: 0.75rem; text-wrap: balance; }
/* Under the results, the offer to email them, centered. The ask and
   the confirmation are balanced; the fine print only avoids a last word
   alone, so its lines run full rather than in a narrow block. */
.end-email { margin-top: 0.75rem; text-align: center; text-wrap: balance; }
.end-email .end-email-consent { text-wrap: pretty; }
/* The ask, the box and Save in a light pink box of their own, the fine
   print under it. The ask is a small Google Sans Flex heading, so it
   doesn't read as one more answer. */
.end-email-box { padding: 0.875rem 0.875rem 0.875rem; background: var(--color-clay-50); border-radius: 0.875rem; }
.end-email-ask { font-family: 'Google Sans Flex', 'Public Sans', sans-serif; font-size: 1rem; font-weight: 600; line-height: 1.3; color: var(--color-ink); margin-bottom: 0.625rem; }
.end-email-error { font-size: 0.75rem; color: #dc2626; margin-top: 0.375rem; }
.end-email-consent { font-size: 0.6875rem; font-style: italic; color: #6b7280; line-height: 1.5; margin-top: 0.625rem; }
.end-email-saved { font-size: 0.875rem; color: #6b7280; text-wrap: pretty; }
.end-email-saved strong { color: #374151; white-space: nowrap; }
.end-episodes a { font-size: 0.875rem; font-weight: 600; color: var(--color-cotton-600); text-decoration: underline; text-underline-offset: 2px; }
.share-carousel {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.share-carousel::-webkit-scrollbar { display: none; }
/* Drawn inside the scroller, where the box's edges can't cut it off. */
.share-carousel:focus-visible { outline: 2px solid #1c4bc4; outline-offset: -2px; border-radius: 1.25rem; }
/* The peeks are margins on the end slides (percentages of the strip
   itself), so peek + slide + peek is exactly the strip and the first and
   last cards snap to the middle like every other one. */
.share-slide:first-child { margin-left: var(--peek); }
.share-slide:last-child { margin-right: var(--peek); }
.share-slide {
  flex: 0 0 var(--slide);
  scroll-snap-align: center;
  scroll-snap-stop: always;
  aspect-ratio: 4 / 5;
  background: #efe9e0;
  border-radius: 1.25rem;
  overflow: hidden;
  opacity: 0.45;
  transform: scale(0.96);
  transition: opacity 0.25s ease, transform 0.25s ease;
}
.share-slide.is-active { opacity: 1; transform: none; }
.share-slide > svg {
  display: block;
  width: 100%;
  height: auto;
}
/* The arrows sit on the current card's own left and right edges, halfway
   down it — on the card's edge horizontally, centred on it vertically. */
.share-strip .share-icon {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  box-shadow: 0 2px 8px rgb(48 47 46 / 0.18);
  z-index: 1;
}
.share-strip .share-prev { left: var(--peek); }
.share-strip .share-next { left: calc(100% - var(--peek)); }
.share-strip .share-icon:hover { transform: translate(-50%, -50%) scale(1.06); }
.share-strip .share-icon:active { transform: translate(-50%, -50%) scale(0.94); }
.share-icon {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 9999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  color: #302f2e;
  border: 1px solid #ddd0c0;
  transition: transform 0.12s ease, background 0.12s ease, opacity 0.12s ease;
}
.share-icon:hover { transform: scale(1.06); background: #faf7f2; }
.share-icon:active { transform: scale(0.94); }
.share-icon:disabled { opacity: 0.3; pointer-events: none; }
.share-icon .material-symbols-rounded { font-size: 22px; }

/* Share button pulse */
@keyframes gentlePulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(229, 68, 71, 0.4); }
  50% { box-shadow: 0 0 0 8px rgba(229, 68, 71, 0); }
}

.share-btn {
  animation: gentlePulse 2s infinite;
}

/* Continue link ("Next question") — the whole link breathes while the
   arrow beckons in and out, on the same clock so they stay in sync */
@keyframes continuePulse {
  0%, 100% { opacity: 0.65; }
  50% { opacity: 1; }
}

@keyframes arrowNudge {
  0%, 100% { transform: translateX(0); }
  50% { transform: translateX(5px); }
}

.continue-link {
  animation: continuePulse 1.8s ease-in-out infinite;
}

.continue-link .material-symbols-rounded {
  animation: arrowNudge 1.8s ease-in-out infinite;
}

/* Hide scrollbar but keep scroll */
#chat::-webkit-scrollbar { width: 4px; }
#chat::-webkit-scrollbar-thumb { background: #ddd0c0; border-radius: 2px; }

/* One screen tall, and only the chat scrolls. The page is held to the
   space it's given (100% down from <html>), not sized in viewport units:
   installed to a Pixel's home screen, the column ran about 70px past the
   bottom of the window (the status bar and the gesture bar together, as if
   the units measured the whole screen), and since that was the chat's own
   end, the "Let's go!" button and the last of the chat sat below it with
   no scroll left to reach them. 100% is the window the page actually
   has, in a browser tab too, and like the units it holds still while a
   phone's keyboard is up. */
html, body { height: 100%; }

/* Prevent zoom on input focus (iOS) */
@media screen and (max-width: 768px) {
  input[type="text"], input[type="email"] {
    font-size: 16px;
  }
}

/* Stabilize layout when virtual keyboard opens */
body {
  overflow: hidden;
}

/* Prevent scroll-into-view jump on focus */
#answer-input {
  scroll-margin: 0;
}

/* Year guesser slider ─────────────────────────────────────────────
   Layout: segmented rail with visible gaps + pill-rounded end caps,
   a custom map-pin thumb, and a transparent native <input type=range>
   on top to drive the interaction (keyboard + drag + snap-to-step).
   The native track + thumb are invisible; we render our own so the
   thumb sits centered on the four segment centers rather than the
   usual 0/33/66/100% the browser lays down for min=0 max=3.
   ----------------------------------------------------------------- */
.year-track-wrap {
  position: relative;
  height: 40px;
  margin: 14px 0 4px;
  user-select: none;
  -webkit-user-select: none;
}
.year-segments {
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  gap: 6px;
  pointer-events: none;
}
.year-seg {
  flex: 1 1 0;
  height: 10px;
  border-radius: 999px;  /* pill endcaps */
  background: #ede4d9;   /* cream-200 */
  transition: background 180ms ease;
}
.year-seg.active {
  background: #1c4bc4;   /* cotton-600 — matches the selected-answer outline */
}
.year-thumb {
  position: absolute;
  top: 50%;
  width: 32px;
  height: 32px;
  transform: translate(-50%, -50%);
  pointer-events: none;
  /* Bounce-settle when the slider snaps from one segment to another. */
  transition: left 220ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.year-thumb svg {
  display: block;
  width: 100%;
  height: 100%;
}
input[type=range].year-slider {
  -webkit-appearance: none;
  appearance: none;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  background: transparent;
  outline: none;
  cursor: pointer;
  margin: 0;
}
input[type=range].year-slider::-webkit-slider-runnable-track {
  height: 100%;
  background: transparent;
  border: none;
}
input[type=range].year-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 40px;   /* wide hit target — invisible, custom thumb shows visually */
  height: 40px;
  background: transparent;
  border: none;
  cursor: grab;
}
input[type=range].year-slider::-moz-range-track {
  height: 100%;
  background: transparent;
  border: none;
}
input[type=range].year-slider::-moz-range-thumb {
  width: 40px;
  height: 40px;
  background: transparent;
  border: none;
  cursor: grab;
}
input[type=range].year-slider:active::-webkit-slider-thumb,
input[type=range].year-slider:active::-moz-range-thumb {
  cursor: grabbing;
}
input[type=range].year-slider:disabled {
  opacity: 0.6;
  cursor: default;
}
input[type=range].year-slider:disabled::-webkit-slider-thumb,
input[type=range].year-slider:disabled::-moz-range-thumb {
  cursor: default;
}

/* Rating scale: the circles are strung along a line — o—o—o—o—o — drawn
   in their own outline, 2px of cream-200, so the row reads as a scale
   rather than as loose buttons. The grid alternates circle and link
   tracks, which is what lets a link meet the circles either side of it
   with nothing in between; the column gap is 0 for that reason, and only
   the rows are spaced. layoutRatingRow (js/chat.js) writes the track
   list, because how many columns there are depends on how much room the
   bubble turned out to have — and it hides the one link that would
   straddle a row break, so no line runs off the end of a row. */
.rating-row {
  display: grid;
  grid-template-columns: minmax(0, 2.5rem);
  gap: 0.375rem 0;
  align-items: center;
}
.rating-pill {
  width: 100%;
  aspect-ratio: 1;
  padding: 0;
}
/* The links never react to hover: they're the rail the circles sit on,
   and lighting them made the hover feel like it belonged to the row
   rather than to the one circle under the finger. */
.rating-link {
  height: 2px;
  background: var(--color-cream-200);
  transition: opacity 150ms;
}
/* Answered: the links dim with the outlines they match, leaving the
   chosen circle lit on its own. */
.rating-row.is-locked .rating-link { opacity: 0.5; }

/* Odd one out grid */
.choice-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  max-width: 100%;
  overflow: hidden;
  overflow-wrap: break-word;
}
/* A card too narrow for two across (a homepage sidebar) stacks them,
   rather than cutting off the second. The card is the container. */
@container (width < 12rem) {
  .choice-grid { grid-template-columns: minmax(0, 1fr); }
}

/* The fun fact: a very light blue box inset in the white bubble, a dark
   blue "Fun fact" pill, then the fact. *2026-10-20:* it was a line of
   small grey text after a sparkle, which testers read past like fine
   print (and the sparkle read as AI). */
.fun-fact {
  margin-top: 0.75rem;
  padding: 0.625rem 0.75rem 0.75rem;
  background: var(--color-cotton-50);
  border-radius: 0.75rem;
}
.fun-fact-pill {
  display: inline-block;
  padding: 0.125rem 0.5rem;
  border-radius: 999px;
  background: var(--color-cotton-600);
  color: #fff;
  font-size: 0.625rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  line-height: 1rem;
  text-transform: uppercase;
}
.fun-fact-text {
  margin-top: 0.375rem;
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1.45;
  color: #374151;
}
/* Icons inside the fun fact (the podcast icon on its episode link) must
   never expand the line box:
   size to the surrounding text, kill the glyph's own line-height, and
   sit it slightly below center optically. Overrides the UNLAYERED
   Google-Fonts base rule (.material-symbols-rounded { font-size: 24px;
   line-height: 1 }) on specificity — Tailwind size utilities on these
   icons are already dead against that base rule; align-middle was the
   only live utility and is beaten too. Any icon inside a fact footer
   is deliberately normalized to text size. */
.fun-fact .material-symbols-rounded {
  font-size: 1em;
  line-height: 1;
  vertical-align: -0.125em;
}

/* Bottom status bar — question progress dots. The active dot stretches
   into a pill; the width morph reads as the pill sliding on advance. */
/* ── Status bar (2.0): section name left, "12 / 40" + a thin progress
   bar right. Shown when questions start, animated out when the
   summary begins — the results phase gets the full chat height back. */
/* On the brand-red status bar: a white track, filled in the dark
   blue as the player gets through the chat. */
#status-progress-track {
  width: 72px;
  height: 4px;
  border-radius: 999px;
  background: #fff;
  overflow: hidden;
}
#status-progress-fill {
  height: 100%;
  border-radius: 999px;
  background: #1c4bc4; /* cotton-600 */
  transition: width 300ms ease;
}
/* On a desktop, the bar and the words beside it a size up (game/index.html). */
@media (min-width: 768px) {
  #status-progress-track { width: 112px; height: 6px; }
}
/* Section divider between question groups — structural, renders in
   every chat-verbosity mode. */
.section-divider-line {
  background: #ddd0c0; /* cream-300 */
}
@media (prefers-reduced-motion: reduce) {
  #status-progress-fill {
    transition: none;
  }
}

/* The blank in a fill-in-the-blank clue (blankify in js/chat.js). As a
   plain inline-block it inherited the paragraph's tall line-height, so its
   bottom border — the line — hung 0.63em under the text, most of the way
   to the next line. overflow: hidden makes an inline-block's baseline its
   bottom margin edge, which pins the box's bottom to the text baseline;
   the negative margin then drops the line 0.12em under it, where an
   underline sits. */
.clue-blank {
  vertical-align: baseline;
  overflow: hidden;
  height: 1em;
  margin-bottom: -0.12em;
}

/* ── Scramble ────────────────────────────────────────────────────────
   The player spells the word by moving tiles out of the bank and into
   the answer row — no text field, so no soft keyboard.

   Two places, two looks, so it's always clear what's still in play and
   what's been set:
   · the bank: a cream panel of white tiles that pop off it, with
     Shuffle, Give up and Check inside;
   · the answer row, on the bubble's white: solid cream wells, one per
     letter of the word, and a letter that lands in one turns deep blue
     with the letter reversed out.
   A tile that goes up leaves the bank entirely (the bank closes around
   it); a copy (.scramble-ghost) flies between the two and changes
   colour on the way. Shuffle re-orders the bank with a FLIP slide. */

/* Tiles and wells share one size, worked out from whichever row is
   longer (--count, set inline) and the bank's padding, so the whole
   word fits on a line in the bubble: up to 2.25rem, never under 1.6rem
   (then a row wraps). cqi is the board's own width. */
.scramble-board {
  container-type: inline-size;
  --gap: 0.3rem;
  --pad: 0.5rem;
}
.scramble-answer,
.scramble-tiles {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap);
}
.scramble-answer { margin-bottom: 0.75rem; }
.scramble-tile,
.scramble-slot {
  --size: clamp(1.6rem, calc((100cqi - 2 * var(--pad) - (var(--count) - 1) * var(--gap)) / var(--count)), 2.25rem);
  flex: none;
  width: var(--size);
  height: var(--size);
  border-radius: 0.5rem;
  border: 2px solid transparent;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-size: calc(var(--size) * 0.48);
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

/* The bank. */
.scramble-bank {
  background: #f5efe8; /* cream-100, the chat's own cream */
  border-radius: 0.9rem;
  padding: var(--pad);
}
.scramble-tiles.is-empty { display: none; }
/* In a card too narrow for all three (a homepage sidebar), Check drops to
   its own line, still on the right, rather than running off the card. */
.scramble-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-top: var(--pad);
}
.scramble-tiles.is-empty + .scramble-controls { margin-top: 0; }
.scramble-tile {
  background: #fff;
  border-color: #ede4d9; /* cream-200 */
  color: #302f2e; /* ink — the one darkest text colour */
  box-shadow: 0 1px 2px rgba(48, 47, 46, 0.1);
  cursor: pointer;
  /* Dragging is a gesture on the tile, so the tile can't also scroll the
     page. Only while the question is live — see .scramble-locked. */
  touch-action: none;
  transition: transform 300ms cubic-bezier(0.2, 1.35, 0.4, 1), box-shadow 220ms ease;
}
.scramble-tile:hover:not(:disabled) { transform: translateY(-1px); box-shadow: 0 3px 6px rgba(48, 47, 46, 0.14); }
/* Up in the row: gone from the bank, not left behind as a gap. */
.scramble-tile.used { display: none; }
/* Mid-shuffle a tile floats above the row and casts a deeper shadow, so
   the whole card is seen lifting and sliding (JS toggles this for the
   duration of the FLIP slide). */
.scramble-tile.shuffling {
  position: relative;
  z-index: 5;
  box-shadow: 0 9px 18px rgba(0, 0, 0, 0.18);
}

/* The answer row: solid wells, then set letters. */
.scramble-slot {
  background: #ede4d9; /* cream-200 — a well, a shade darker than the bubble */
  color: #fff;
  transition: background 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
}
.scramble-slot.filled {
  background: #1c4bc4; /* cotton-600, the one blue */
  border-color: #1c4bc4;
  box-shadow: 0 1px 2px rgba(28, 75, 196, 0.3);
  cursor: pointer;
  touch-action: none;
}
.scramble-slot.filled:hover:not(:disabled) { background: #3a62d0; border-color: #3a62d0; } /* cotton-600, a step lighter */
/* While a drag hovers it. */
.scramble-slot.drop-target { box-shadow: 0 0 0 2px #8babfe; } /* cotton-300 */
/* The destination of a flying copy stays as it was until the copy lands:
   an empty well for a letter going up, an invisible gap in the bank for
   one coming down. */
.scramble-slot.landing {
  background: #ede4d9;
  border-color: transparent;
  box-shadow: none;
  color: transparent;
}
.scramble-tile.landing { visibility: hidden; }
/* A letter clicking into its well. */
.scramble-slot.just-set { animation: scrambleSet 240ms cubic-bezier(0.2, 1.4, 0.4, 1); }
@keyframes scrambleSet {
  0% { transform: scale(1.12); }
  100% { transform: scale(1); }
}
.scramble-tile.drag-origin,
.scramble-slot.drag-origin { opacity: 0.35; }

/* The flying copy. White like a bank tile, or blue like a set letter —
   and it changes from one to the other in flight. */
.scramble-ghost {
  position: fixed;
  z-index: 60;
  pointer-events: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  border: 2px solid #ede4d9;
  border-radius: 0.5rem;
  color: #302f2e;
  font-weight: 800;
  box-shadow: 0 9px 18px rgba(0, 0, 0, 0.18);
  transform-origin: top left;
  transition: transform 260ms cubic-bezier(0.2, 1.1, 0.4, 1), background 220ms ease, border-color 220ms ease, color 220ms ease;
}
.scramble-ghost.is-set {
  background: #1c4bc4;
  border-color: #1c4bc4;
  color: #fff;
}
.scramble-ghost.dragging { transition: none; }

/* Check: live only when every well is full. */
.scramble-check {
  font-family: 'Google Sans Flex', 'Public Sans', sans-serif;
  font-weight: 600;
  font-size: 0.8125rem;
  padding: 0.45rem 1.1rem;
  border-radius: 0.75rem;
  color: #fff;
  background: linear-gradient(to bottom right, #e54447, #c92e32); /* clay-500 → 600 */
  transition: transform 150ms ease, background 160ms ease, color 160ms ease;
}
.scramble-check:hover:not(:disabled) { transform: scale(1.05); }
.scramble-check:active:not(:disabled) { transform: scale(0.95); }
/* Inactive, but still plainly a button with a label: a darker cream,
   with the bank panel's own cream as the text, 3:1 either way. Hex, not
   var(--color-cream-500): Tailwind only emits theme variables that a file
   it scans uses, and this stylesheet isn't one, so the var came out empty
   and the button vanished into the panel. */
.scramble-check:disabled { background: #9c876a; color: #f5efe8; cursor: default; } /* cream-500 / cream-100 */

/* Answered or given up: the card stays as the record, and the page
   scrolls normally over it again. */
.scramble-locked .scramble-tile,
.scramble-locked .scramble-slot { touch-action: auto; cursor: default; }
.scramble-locked .scramble-tile:hover { transform: none; box-shadow: 0 1px 2px rgba(48, 47, 46, 0.1); }
.scramble-locked .scramble-slot.filled:hover { background: #1c4bc4; border-color: #1c4bc4; }

.scramble-shuffle-btn { transition: transform 150ms ease, color 150ms ease; }
.scramble-shuffle-btn:active { transform: scale(0.88); }
@keyframes shuffleSpin { from { transform: rotate(0); } to { transform: rotate(360deg); } }
.scramble-shuffle-btn.spinning .material-symbols-rounded { animation: shuffleSpin 320ms cubic-bezier(0.3, 0, 0.2, 1); }
@media (prefers-reduced-motion: reduce) {
  .scramble-tile { transition: none; }
  .scramble-slot.just-set,
  .scramble-shuffle-btn.spinning .material-symbols-rounded { animation: none; }
}

/* ── Sound it Out (emoji rebus) ──────────────────────────────────────
   A row of animated Noto emoji joined by + separators — a little
   equation the player says out loud. Items pop in with a slight
   cascade (delay driven by the --rebus-i custom property the renderer
   stamps on each item); under reduced motion the renderer swaps the
   animated webp for the still SVG and the pop-in is disabled here. */
/* The row wraps like a line of text so it can balance its lines: eight
   pieces on a phone come out 4 + 4, not 4 + 3 + 1. A + belongs to the
   piece after it (.rebus-unit), so a wrapped line starts with "+ 🔑". */
.rebus-row {
  --piece: 3.25rem;
  container-type: inline-size;
  text-wrap: balance;
  line-height: 1;
  margin: -0.25rem 0; /* the units' own margins space the lines */
}
/* Four or more pieces shrink, never below 2.25rem, until four fit on a
   line: full size on a desktop, a step down on a phone. In a row that
   narrow the + signs and letter tiles slim down too, since a wrapped line
   starts with a + and so carries four of them. */
.rebus-row-long { --piece: clamp(2.25rem, (100cqw - 5rem) / 4.5, 3.25rem); }
@container (max-width: 22rem) {
  .rebus-row-long .rebus-plus { margin: 0 0.15rem; font-size: 0.95rem; }
  .rebus-row-long .rebus-text { padding: 0 0.35rem; font-size: 1.1rem; }
}
.rebus-unit {
  display: inline-flex;
  align-items: center;
  vertical-align: middle;
  margin: 0.25rem 0;
}
/* Emoji Meanings: a string, the way people send it (👁️👄👁️), side by side
   with no + signs between. */
.rebus-row-string .rebus-unit + .rebus-unit { margin-left: 0.2rem; }
.rebus-item {
  display: inline-flex;
  animation: rebusPop 420ms cubic-bezier(0.2, 1.35, 0.4, 1) both;
  animation-delay: calc(var(--rebus-i, 0) * 150ms);
}
.rebus-emoji {
  width: var(--piece, 3.25rem);
  height: var(--piece, 3.25rem);
  object-fit: contain;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}
.rebus-native {
  font-size: calc(var(--piece, 3.25rem) * 0.77);
  line-height: var(--piece, 3.25rem);
  text-align: center;
}
/* Literal letter pieces ("H" + 👂 + "T") — tile-styled to rhyme with the
   scramble letters, sized to sit flush beside the emoji artwork. */
.rebus-text {
  min-width: calc(var(--piece, 3.25rem) * 0.77);
  height: var(--piece, 3.25rem);
  padding: 0 0.55rem;
  border-radius: 0.6rem;
  background: #fff;
  border: 2px solid #ede4d9; /* cream-200 */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-size: 1.35rem;
  letter-spacing: 0.04em;
  color: #302f2e; /* ink — the one darkest text colour */
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
  user-select: none;
}
.rebus-plus {
  margin: 0 0.4rem;
  font-weight: 800;
  font-size: 1.15rem;
  animation: rebusPop 420ms cubic-bezier(0.2, 1.35, 0.4, 1) both;
  animation-delay: calc(var(--rebus-i, 0) * 150ms - 75ms);
}
@keyframes rebusPop {
  from { opacity: 0; transform: scale(0.4) rotate(-8deg); }
  to { opacity: 1; transform: scale(1) rotate(0); }
}
@media (prefers-reduced-motion: reduce) {
  .rebus-item,
  .rebus-plus { animation: none; }
}

/* ── Settings sheet ──────────────────────────────────────────────────
   One panel, two personalities: below 640px it's a bottom sheet that
   slides up from the edge; at sm+ it becomes a centered modal that
   scales/fades in. app.js toggles `.open` on the overlay (plus the
   `hidden` class outside the transition window). */
#settings-overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
}
#settings-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(31, 41, 55, 0.4); /* gray-800/40 */
  opacity: 0;
  transition: opacity 300ms ease;
}
#settings-overlay.open #settings-backdrop {
  opacity: 1;
}
#settings-panel {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  max-height: 85dvh;
  overflow-y: auto;
  border-radius: 1.5rem 1.5rem 0 0;
  padding: 1.25rem 1.25rem calc(1.25rem + env(safe-area-inset-bottom));
  transform: translateY(100%);
  /* Eased spring-ish slide, matches the app's playful motion */
  transition: transform 360ms cubic-bezier(0.32, 0.72, 0, 1);
}
#settings-overlay.open #settings-panel {
  transform: translateY(0);
}
@media (min-width: 640px) {
  #settings-panel {
    left: 50%;
    right: auto;
    bottom: auto;
    top: 50%;
    width: 100%;
    max-width: 26rem;
    border-radius: 1.5rem;
    padding: 1.25rem;
    transform: translate(-50%, -50%) scale(0.95);
    opacity: 0;
    transition: transform 250ms ease, opacity 250ms ease;
  }
  #settings-overlay.open #settings-panel {
    transform: translate(-50%, -50%) scale(1);
    opacity: 1;
  }
}
@media (prefers-reduced-motion: reduce) {
  #settings-backdrop,
  #settings-panel {
    transition: none;
  }
}

/* Toggle switch (sound & vibration) */
.settings-switch {
  position: relative;
  width: 44px;
  height: 26px;
  border-radius: 999px;
  background: #ddd0c0; /* cream-300 */
  transition: background 200ms ease;
}
.settings-switch[aria-checked="true"] {
  background: #e54447; /* clay-500 */
}
.settings-switch-knob {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
  transition: transform 200ms ease;
}
.settings-switch[aria-checked="true"] .settings-switch-knob {
  transform: translateX(18px);
}

/* Text-size + typing-speed segmented controls — active option pops white */
.text-size-option.active,
.speed-option.active {
  background: #fff;
  color: #e54447; /* clay-500 */
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
}


/* Material Symbols brings its own rule — .material-symbols-rounded
   { font-size: 24px }, straight from the Google Fonts stylesheet — and it
   is unlayered, so it beat every Tailwind text-* class an icon carried:
   57 of the 84 icons on the chat editor were drawing at 24px whatever
   size they asked for, which is why the lock beside the slug and the one
   on the private badge looked oversized. These are unlayered too, and one
   class more specific, so an icon now gets the size it asks for. An icon
   that asks for nothing keeps the 24px it always had. */
.material-symbols-rounded.text-xs { font-size: 0.75rem; }
.material-symbols-rounded.text-sm { font-size: 0.875rem; }
.material-symbols-rounded.text-base { font-size: 1rem; }
.material-symbols-rounded.text-lg { font-size: 1.125rem; }
.material-symbols-rounded.text-xl { font-size: 1.25rem; }
.material-symbols-rounded.text-2xl { font-size: 1.5rem; }
.material-symbols-rounded.text-3xl { font-size: 1.875rem; }
