/* Design system v1.5 components. Every class starts with gm-. Values come from tokens.css;
   the few literal numbers left are component sizes (card shapes, art sizes) listed in DESIGN-SYSTEM.md. */
.gm-page { margin: 0; font-family: var(--gm-font); font-weight: 500; font-size: var(--gm-text-md); line-height: 1.4; color: var(--gm-cream); background: var(--gm-bg) fixed; background-color: var(--gm-deep); min-height: 100vh; }
.gm-page *, .gm-page *::before, .gm-page *::after { box-sizing: border-box; }
.gm-title { font-stretch: var(--gm-title-stretch); font-weight: 900; letter-spacing: -.03em; line-height: .9; text-transform: uppercase; margin: 0; overflow-wrap: anywhere; }
.gm-quiet { color: var(--gm-soft); font-size: var(--gm-text-sm); font-weight: 700; }

/* Text that must fit: the page sets --chars to the number of characters in the longest line
   (for a title that wraps, its longest word). Size = the smaller of the max size and what fits the width.
   The width is measured from the box it sits in (hero or result head), not the screen, so scrollbars
   and frames can't push the last letter onto its own line.
   .95 was the widest real-word letter width measured on 5 Nov; "ROOMS" measured .975 on 9 Nov (wide O and M), so the formula now allows 1.0. */
.gm-hero, .gm-result-head { container-type: inline-size; }
.gm-fit { --fit-max: var(--gm-text-hero); --fit-width: 100cqi;
  font-size: min(var(--fit-max), calc(var(--fit-width) / (var(--chars, 6) * 1)));
  max-width: var(--fit-width); }  /* a long title wraps between words before it can reach the art */
@media (min-width: 760px) { .gm-hero .gm-fit { --fit-width: min(640px, calc(100cqi - 360px)); } }  /* leave room for the art */

/* Interaction states, shared by everything you can press */
.gm-pressable { cursor: pointer; -webkit-tap-highlight-color: transparent; touch-action: manipulation; transition: transform .08s, box-shadow .08s; }
.gm-pressable:focus-visible, .gm-card:focus-visible, .gm-pill:focus-visible, .gm-shelf-tab:focus-visible, .gm-btn:focus-visible, .gm-key:focus-visible, .gm-tile:focus-visible { outline: none; box-shadow: var(--gm-focus); }
.gm-btn:active, .gm-key:active, .gm-shelf-tab:active, .gm-pill:active { transform: var(--gm-press); box-shadow: 0 0 0 var(--gm-edge); }
.gm-card:active { transform: translate(2px, 3px); box-shadow: 1px 1px 0 var(--gm-edge), inset 0 3px 0 rgba(255, 255, 255, .35); }
.gm-btn:disabled, .gm-btn[aria-disabled=true], .gm-key:disabled { opacity: .45; cursor: not-allowed; transform: none; }
@media (hover: hover) { .gm-btn:hover, .gm-shelf-tab:hover, .gm-pill:hover { filter: brightness(1.06); } .gm-card:hover { transform: translateY(-2px); } }
@media (prefers-reduced-motion: reduce) { .gm-page * { transition: none !important; animation: none !important; } .gm-card:hover { transform: none; } }

/* Masthead and navigation */
.gm-masthead { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; padding: calc(16px + var(--gm-safe-top)) var(--gm-gutter) 8px; }
.gm-logo { font-stretch: var(--gm-title-stretch); font-weight: 900; font-size: var(--gm-text-xl); letter-spacing: -.03em; line-height: .9; }
.gm-masthead .gm-meta { text-align: right; font-size: var(--gm-text-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--gm-cream); }
.gm-nav { display: none; gap: 26px; font-weight: 700; font-size: var(--gm-text-sm); }
.gm-nav a, .gm-dock a { color: inherit; text-decoration: none; border-radius: var(--gm-r-pill); }
.gm-nav a { opacity: .7; } .gm-nav a[aria-current=page] { opacity: 1; }
.gm-dock { position: sticky; bottom: calc(14px + var(--gm-safe-bottom)); margin: 30px 16px 14px; height: 58px; border-radius: var(--gm-r-pill); background: var(--gm-edge); border: 1px solid rgba(255, 240, 224, .16); display: flex; align-items: center; justify-content: space-around; font-weight: 700; font-size: var(--gm-text-sm); z-index: 5; }
.gm-dock a { opacity: .7; padding: 0 12px; display: inline-flex; align-items: center; min-height: var(--gm-touch); } .gm-dock a[aria-current=page] { opacity: 1; background: var(--gm-accent); color: var(--gm-ink); padding: 0 18px; }
.gm-nav a:focus-visible, .gm-dock a:focus-visible { outline: none; box-shadow: var(--gm-focus); }
@media (min-width: 760px) { .gm-masthead { padding-top: 26px; align-items: center; } .gm-nav { display: flex; } .gm-dock { display: none; } }

/* Pills (genre filters): real buttons with aria-pressed */
.gm-pills { display: flex; gap: var(--gm-space-2); padding: 10px var(--gm-gutter) 4px; overflow-x: auto; scrollbar-width: none; }
.gm-pill { flex: none; min-height: 36px; padding: 0 15px; border-radius: var(--gm-r-pill); border: 2px solid var(--gm-faint); background: transparent; color: var(--gm-cream); font: 700 var(--gm-text-sm) var(--gm-font); cursor: pointer; }
.gm-pill[aria-pressed=true] { background: var(--gm-accent); color: var(--gm-ink); border-color: var(--gm-edge); box-shadow: 2px 2px 0 var(--gm-edge); }

/* Raised surfaces: hero, cards, modules share outline and depth */
.gm-raised { border: var(--gm-line) solid var(--gm-edge); border-radius: var(--gm-r-card); box-shadow: var(--gm-shadow-card); position: relative; overflow: hidden; color: var(--gm-ink); }
.gm-c-yellow { background: var(--gm-yellow); } .gm-c-orange { background: var(--gm-orange); } .gm-c-pink { background: var(--gm-pink); }
.gm-c-green { background: var(--gm-green); } .gm-c-red { background: var(--gm-red); } .gm-c-purple { background: var(--gm-purple); }

/* Sticker tags (genre top-left, "New" or tick top-right) */
.gm-sticker { position: absolute; z-index: 2; font-size: var(--gm-text-tag); font-weight: 800; text-transform: uppercase; letter-spacing: .05em; padding: 4px 8px; background: var(--gm-cream); color: var(--gm-ink); border: 2px solid var(--gm-edge); border-radius: var(--gm-r-chip); transform: rotate(-3deg); }
.gm-sticker.tl { left: 10px; top: 10px; } .gm-sticker.tr { right: 10px; top: 10px; transform: rotate(3deg); }
.gm-sticker.dark { background: var(--gm-ink); color: var(--gm-cream); }

/* Hero: one per page, the "today's pick". The whole hero is the link.
   Phone: title on top, then a bottom band as tall as the visible art. The caption sits left in the band
   and the art bleeds off the right, so the art never touches the title or the caption.
   Desktop: the art sits on the right; title and caption keep to the left. */
.gm-hero { display: flex; flex-direction: column; gap: 10px; margin: 16px var(--gm-gutter) 0; min-height: 280px; padding: 52px 18px 16px; text-decoration: none; }
.gm-hero .gm-title { position: relative; line-height: .88; z-index: 1; }
.gm-hero .gm-art { position: absolute; right: -50px; bottom: -40px; width: 190px; height: 190px; transform: rotate(8deg); }
.gm-hero p { display: flex; align-items: flex-end; min-height: 146px; margin: auto 0 0; max-width: calc(100% - 150px); font-weight: 700; font-size: var(--gm-text-sm); position: relative; z-index: 2; }
@media (min-width: 760px) { .gm-hero { min-height: 250px; } .gm-hero .gm-art { width: 330px; height: 330px; right: 20px; bottom: -60px; } .gm-hero p { min-height: 0; max-width: 50%; } }

/* Section: heading + count, then a scrolling row. Cards in one row share a height. */
.gm-section-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: var(--gm-section-gap) var(--gm-gutter) 12px; }
.gm-section-head h3 { margin: 0; font-stretch: var(--gm-title-stretch); font-weight: 900; font-size: var(--gm-text-lg); letter-spacing: -.02em; text-transform: uppercase; flex: 1; }
.gm-row-arrows { display: none; gap: 6px; }
.gm-row-arrows button { width: 36px; height: 36px; border-radius: 50%; border: 2px solid var(--gm-edge); background: var(--gm-cream); color: var(--gm-ink); font: 900 16px var(--gm-font); box-shadow: 2px 2px 0 var(--gm-edge); cursor: pointer; }
.gm-row-arrows button:disabled { opacity: .35; cursor: default; }
@media (hover: hover) and (min-width: 760px) { .gm-row-arrows { display: flex; } }
.gm-row { display: flex; gap: var(--gm-space-3); overflow-x: auto; scrollbar-width: none; padding: 4px var(--gm-gutter) 10px; scroll-behavior: smooth; scroll-padding: 0 var(--gm-gutter); }
.gm-row::-webkit-scrollbar { display: none; }
.gm-row > * { flex: none; }

/* Game card: a link. Shapes: wide 250x170, square 170x170, small 150x150, tall 150x230. */
.gm-card { display: block; padding: 12px; text-decoration: none; transition: transform .08s, box-shadow .08s; }
.gm-card.wide { width: 270px; height: 190px; } .gm-card.square { width: 170px; height: 190px; } .gm-card.small { width: 150px; height: 150px; } .gm-card.tall { width: 150px; height: 230px; }
.gm-card .gm-art { position: absolute; right: 8px; top: 46px; width: 112px; height: 112px; }
/* Square, small and tall cards (v1.10): the card is a column (stickers, art, text). The art takes whatever space the text leaves,
   so it can never sit under the text, however long the title or tagline. Wide cards keep the art on the right, with the text beside it. */
.gm-card.square, .gm-card.small, .gm-card.tall { display: flex; flex-direction: column; padding: 34px 12px 11px; }
.gm-card.square .gm-art, .gm-card.small .gm-art, .gm-card.tall .gm-art { position: static; flex: 1 1 0; min-height: 0; width: 100%; height: auto; margin: 0 0 6px; }
.gm-card.square .gm-text, .gm-card.small .gm-text, .gm-card.tall .gm-text { position: static; }
.gm-card .gm-text { position: absolute; left: 12px; right: 10px; bottom: 11px; }
/* Card titles: 19px at most; GMFit.words (fit.js) shrinks a long title so its longest word stays on one line (v1.12). */
.gm-card h4 { margin: 0; font-stretch: 112%; font-weight: 900; font-size: var(--gm-text-lg); letter-spacing: -.02em; line-height: 1; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.gm-card small { display: block; margin-top: 3px; font-weight: 700; font-size: var(--gm-text-xs); opacity: .85; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Tagline (v1.10): a few words saying what the game is, between the title and the designer. One line (two on wide and tall).
   Small cards leave it out. On wide cards the text keeps to the left of the art (132px wide: a 10-letter word fits on one line). */
.gm-card .gm-tag { display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; margin-top: 3px; font-weight: 700; font-size: var(--gm-text-sm); line-height: 1.15; overflow-wrap: anywhere; }
.gm-card.wide .gm-tag, .gm-card.tall .gm-tag { -webkit-line-clamp: 2; }
.gm-card.small .gm-tag { display: none; }
.gm-card.wide .gm-text { right: 126px; }
.gm-art { display: block; }

/* Designers */
.gm-avatar { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%; border: var(--gm-line) solid var(--gm-edge); box-shadow: var(--gm-shadow-small); font-stretch: var(--gm-title-stretch); font-weight: 900; font-size: var(--gm-text-sm); color: var(--gm-ink); flex: none; }
.gm-avatar.lg { width: 58px; height: 58px; font-size: var(--gm-text-md); }
.gm-strip { display: flex; gap: var(--gm-space-3); padding: 4px var(--gm-gutter); overflow-x: auto; scrollbar-width: none; }
.gm-strip > a { flex: none; text-align: center; font-weight: 700; font-size: var(--gm-text-sm); color: inherit; text-decoration: none; border-radius: 12px; padding: 4px; }
.gm-strip > a:focus-visible { outline: none; box-shadow: var(--gm-focus); }
.gm-strip .gm-avatar { margin: 0 auto 6px; }

/* Buttons */
.gm-btn { display: inline-grid; place-items: center; min-height: var(--gm-touch); padding: 0 20px; white-space: nowrap; border-radius: var(--gm-r-pill); border: var(--gm-line) solid var(--gm-edge); background: var(--gm-cream); color: var(--gm-ink); font: 900 var(--gm-text-md) var(--gm-font); box-shadow: var(--gm-shadow-small); cursor: pointer; text-decoration: none; transition: transform .08s, box-shadow .08s; }
.gm-btn.primary { background: var(--gm-accent); }
.gm-btn.ghost { background: transparent; color: inherit; box-shadow: none; border-color: currentColor; }
.gm-btn.block { display: grid; width: 100%; min-height: 58px; font-size: 17px; box-shadow: 4px 4px 0 var(--gm-edge); }
.gm-btn.round { width: 46px; height: 46px; min-height: 0; padding: 0; border-radius: 50%; font-size: 18px; }

/* Game pieces: tiles and keys */
.gm-tile { display: grid; place-items: center; width: 62px; height: 62px; border-radius: var(--gm-r-tile); background: var(--gm-cream); color: var(--gm-ink); border: var(--gm-line) solid var(--gm-edge); box-shadow: var(--gm-shadow-tile); font: 900 30px var(--gm-font); font-stretch: 115%; text-transform: uppercase; position: relative; top: -3px; }
.gm-tile.start { background: var(--gm-start); } .gm-tile.hit { background: var(--gm-hit); } .gm-tile.changed { background: var(--gm-changed); }
.gm-tile.empty { background: rgba(255, 240, 224, .05); border-style: dashed; border-color: var(--gm-faint); box-shadow: none; top: 0; color: transparent; }
@media (max-width: 400px) { .gm-tile { width: 54px; height: 54px; font-size: 26px; } }
/* Keys shrink to fit any phone from 375px wide (and still fit 320); they stay 44px+ tall. */
.gm-key { display: grid; place-items: center; height: 48px; min-width: 0; padding: 0; flex: 1 1 0; max-width: 46px; border-radius: var(--gm-r-key); background: color-mix(in srgb, var(--gm-cream) 13%, var(--gm-mid)); color: var(--gm-cream); border: 2px solid var(--gm-edge); box-shadow: var(--gm-shadow-key); font: 800 15px var(--gm-font); text-transform: uppercase; cursor: pointer; transition: transform .08s, box-shadow .08s; }
.gm-key.wide { flex: 2 1 0; max-width: 70px; font-size: 11px; font-stretch: 70%; }  /* narrow letters so "enter" fits at 320px */
.gm-board { display: flex; flex-direction: column; align-items: center; gap: 12px; } .gm-board .gm-line { display: flex; gap: 10px; }
.gm-keyboard { display: flex; flex-direction: column; gap: 7px; padding: 0 6px; max-width: 540px; margin: 0 auto; } .gm-keyboard .gm-line { display: flex; gap: 4px; justify-content: center; }

/* Game screen: board fills the screen, title and buttons float over it */
.gm-game-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; padding: calc(12px + var(--gm-safe-top)) 16px 0; }
.gm-game-title { text-align: center; min-width: 0; } .gm-game-title small { display: block; font-size: var(--gm-text-xs); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--gm-soft); }
.gm-game-title b { display: block; font-stretch: var(--gm-title-stretch); font-weight: 900; font-size: var(--gm-text-xl); letter-spacing: -.03em; text-transform: uppercase; line-height: 1; overflow-wrap: anywhere; }
@media (min-width: 760px) { .gm-game-head { padding: 26px var(--gm-gutter) 0; } }

/* Short screens (v1.8): an iPhone 13 in Chrome or Safari gets ~390x660.
   Pieces shrink before they overlap. Keys and buttons keep 44px+ touch height. */
@media (max-height: 700px) { .gm-tile { width: 48px; height: 48px; font-size: 22px; } .gm-board { gap: 8px; } .gm-board .gm-line { gap: 7px; } .gm-keyboard { gap: 5px; } .gm-key { height: 44px; } .gm-game-head { padding-top: calc(8px + var(--gm-safe-top)); } .gm-game-title b { font-size: var(--gm-text-lg); } .gm-shelf-bar { padding-top: 8px; padding-bottom: calc(10px + var(--gm-safe-bottom)); } .gm-shelf-body { padding-top: 16px; padding-bottom: 16px; max-height: 60dvh; overflow-y: auto; } }
@media (max-height: 560px) { .gm-tile { width: 40px; height: 40px; font-size: 19px; border-radius: 10px; } .gm-board { gap: 6px; } .gm-board .gm-line { gap: 6px; } .gm-keyboard { gap: 4px; } .gm-shelf-body { max-height: 50dvh; } }

/* Shelf: tabs along the bottom; a tab slides its panel up over the game */
.gm-shelf { position: relative; z-index: 8; }
.gm-shelf-bar { display: flex; gap: var(--gm-space-2); padding: 12px 14px calc(16px + var(--gm-safe-bottom)); justify-content: center; }
.gm-shelf-tab { flex: 1; max-width: 220px; min-height: var(--gm-touch); text-align: center; padding: 0 8px; border: var(--gm-line) solid var(--gm-edge); border-radius: var(--gm-r-pill); background: var(--gm-cream); color: var(--gm-ink); box-shadow: var(--gm-shadow-small); font: 800 var(--gm-text-sm) var(--gm-font); cursor: pointer; transition: transform .08s, box-shadow .08s; }
.gm-shelf-tab[aria-expanded=true] { background: var(--gm-accent); }
.gm-shelf-body { position: absolute; left: 0; right: 0; bottom: 100%; padding: 22px var(--gm-gutter); background: var(--gm-panel); border-top: var(--gm-line) solid var(--gm-edge); box-shadow: 0 -16px 40px rgba(0, 0, 0, .25); }
.gm-shelf-body[hidden] { display: none; } .gm-shelf-body > * { max-width: 620px; margin-left: auto; margin-right: auto; }

/* Result: full-screen takeover of blocks. Each block hugs its content: title, one line, one action. */
.gm-result-head { padding: 8px var(--gm-gutter) 0; }
.gm-module { margin: 16px var(--gm-gutter); padding: 16px; background: var(--gm-cream); }
.gm-modules { display: grid; gap: 0 30px; align-items: start; }  /* start: blocks never stretch to match a neighbour */
@media (min-width: 760px) { .gm-modules { grid-template-columns: 1fr 1fr; } .gm-module.tall { grid-row: span 2; } }  /* the tall block takes the right column; short ones stack on the left */
.gm-stats { display: flex; justify-content: space-around; text-align: center; } .gm-stats b { display: block; font: 900 26px var(--gm-font); } .gm-stats span { font-size: var(--gm-text-xs); font-weight: 700; text-transform: uppercase; }
.gm-row2 { display: flex; align-items: center; gap: 12px; }
.gm-actions { display: flex; flex-wrap: wrap; gap: 10px; } .gm-actions > * { flex: 1 1 auto; padding: 0 14px; }  /* side by side when they fit, else one per line */ .gm-quote { font-size: var(--gm-text-md); line-height: 1.4; font-weight: 600; margin: 12px 0; }
.gm-week { display: flex; gap: 6px; margin-top: 12px; padding: 0; list-style: none; } .gm-week li { flex: 1; height: 34px; border: 2px solid var(--gm-edge); border-radius: var(--gm-r-key); display: grid; place-items: center; font-weight: 900; font-size: var(--gm-text-sm); opacity: .55; } .gm-week .on { background: var(--gm-accent); opacity: 1; box-shadow: 0 3px 0 var(--gm-edge); }

/* Screen-reader-only text */
.gm-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* Builder (added 6 Nov, v1.3): chat on the left, live preview on the right. Phone: one at a time, switched by a segment. */
.gm-builder { display: grid; grid-template-rows: auto 1fr; grid-template-columns: minmax(0, 1fr); height: 100dvh; }
.gm-builder-bar { display: flex; align-items: center; gap: 12px; padding: calc(10px + var(--gm-safe-top)) var(--gm-gutter) 10px; min-width: 0; }
.gm-builder-bar .gm-logo { font-size: var(--gm-text-lg); text-decoration: none; color: inherit; flex: none; }
.gm-builder-bar .gm-name { min-width: 0; flex: 1; font-weight: 800; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gm-builder-main { display: grid; grid-template-columns: minmax(300px, 30fr) minmax(0, 70fr); gap: 16px; padding: 0 var(--gm-gutter) calc(16px + var(--gm-safe-bottom)); min-height: 0; }
.gm-builder-main > * { min-height: 0; }
@media (max-width: 759px) { .gm-builder-bar .gm-chip { display: none; } .gm-builder-main { grid-template-columns: minmax(0, 1fr); } .gm-builder-main[data-show=chat] .gm-preview, .gm-builder-main[data-show=preview] .gm-chat { display: none; } }

/* Chip: a small quiet label (version, spending). Not pressable. */
.gm-chip { flex: none; display: inline-flex; align-items: center; min-height: 28px; padding: 0 10px; border-radius: var(--gm-r-pill); border: 2px solid var(--gm-faint); font-size: var(--gm-text-xs); font-weight: 800; letter-spacing: .04em; text-transform: uppercase; white-space: nowrap; }
.gm-chip[hidden] { display: none; }
.gm-chip.warn { border-color: var(--gm-edge); background: var(--gm-red); color: var(--gm-ink); }

/* Segment: two or three buttons where exactly one is on (aria-pressed). Phone only in the builder. */
.gm-segment { display: flex; gap: 4px; padding: 3px; border-radius: var(--gm-r-pill); border: 2px solid var(--gm-faint); flex: none; }
.gm-segment button { min-height: 36px; padding: 0 14px; border: 0; border-radius: var(--gm-r-pill); background: transparent; color: var(--gm-cream); font: 800 var(--gm-text-sm) var(--gm-font); cursor: pointer; }
.gm-segment button[aria-pressed=true] { background: var(--gm-accent); color: var(--gm-ink); }
.gm-segment button:focus-visible { outline: none; box-shadow: var(--gm-focus); }
@media (min-width: 760px) { .gm-builder-bar .gm-segment { display: none; } }

/* Chat: a raised panel with the messages scrolling and the composer pinned at the bottom. */
.gm-chat { display: flex; flex-direction: column; background: var(--gm-panel); color: var(--gm-cream); }
.gm-messages { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 10px; }
.gm-msg { max-width: 88%; padding: 10px 14px; border-radius: 16px; white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.45; }
.gm-msg.me { align-self: flex-end; background: var(--gm-cream); color: var(--gm-ink); border: 2px solid var(--gm-edge); box-shadow: var(--gm-shadow-small); border-bottom-right-radius: 4px; }
.gm-msg.ai { align-self: flex-start; background: color-mix(in srgb, var(--gm-cream) 9%, var(--gm-panel)); border-bottom-left-radius: 4px; white-space: normal; }
.gm-msg p, .gm-msg ul, .gm-msg ol { margin: 0 0 .6em; } .gm-msg ul, .gm-msg ol { padding-left: 1.3em; } .gm-msg li + li { margin-top: .25em; }
.gm-msg > :last-child, .gm-msg > :nth-last-child(2):has(+ .gm-meta) { margin-bottom: 0; }
.gm-msg strong { font-weight: 800; } .gm-msg code { font: 600 .9em ui-monospace, monospace; padding: 1px 5px; border-radius: 5px; background: rgba(255, 240, 224, .12); }
/* Welcome line: centred in the empty chat; gone once the designer sends a message. */
.gm-welcome { margin: auto; max-width: 18em; text-align: center; font-stretch: var(--gm-title-stretch); font-weight: 900; font-size: var(--gm-text-lg); line-height: 1.15; text-transform: uppercase; letter-spacing: -.02em; text-wrap: balance; }
.gm-msg.note { align-self: center; max-width: 100%; padding: 2px 6px; font-size: var(--gm-text-sm); font-weight: 700; color: var(--gm-soft); text-align: center; }
.gm-msg.err .gm-btn { margin-top: 4px; }  /* "Fix it" on an error the game hit while the designer played */
.gm-msg.err { align-self: stretch; max-width: 100%; background: var(--gm-red); color: var(--gm-ink); border: 2px solid var(--gm-edge); font-weight: 700; }
.gm-msg .gm-meta { display: block; margin-top: 6px; font-size: var(--gm-text-xs); font-weight: 700; color: var(--gm-soft); }
.gm-composer { display: flex; gap: 8px; align-items: flex-end; padding: 12px; border-top: 2px solid var(--gm-edge); }
.gm-input { flex: 1; min-height: var(--gm-touch); max-height: 160px; resize: none; overflow-y: auto; scrollbar-width: none; padding: 11px 14px; border-radius: 22px; border: var(--gm-line) solid var(--gm-edge); background: var(--gm-cream); color: var(--gm-ink); font: 500 var(--gm-text-md)/1.35 var(--gm-font); }
.gm-input:focus-visible { outline: none; box-shadow: var(--gm-focus); }
.gm-input:disabled { opacity: .6; }

/* Preview: the sealed game frame, with a slim bar above it and a problem strip below. */
.gm-preview { display: flex; flex-direction: column; gap: 10px; }
.gm-preview-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.gm-preview-bar .gm-quiet { flex: 1; min-width: 0; }
.gm-frame { flex: 1; min-height: 0; padding: 0; background: var(--gm-deep); display: flex; flex-direction: column; overflow: hidden; }
.gm-frame-slot { flex: 1; min-height: 0; }
.gm-frame iframe { display: block; width: 100%; height: 100%; border: 0; }
.gm-stage { flex: 1; min-height: 0; position: relative; }
.gm-stage .gm-frame-slot { position: absolute; inset: 0; }
.gm-frame .gm-empty { display: grid; place-items: center; height: 100%; padding: 24px; text-align: center; color: var(--gm-soft); font-weight: 700; }
.gm-problems { margin: 0; padding: 10px 14px; font-family: var(--gm-font); background: var(--gm-red); color: var(--gm-ink); border-radius: var(--gm-r-tile); border: 2px solid var(--gm-edge); font-size: var(--gm-text-sm); font-weight: 700; white-space: pre-wrap; max-height: 120px; overflow-y: auto; }
.gm-problems[hidden] { display: none; }

/* Cover view (v1.9): the preview's second face. Shows the game's cover on the real cards, the hero and the share picture.
   It sits over the game frame (the game keeps running underneath). Same cards and hero as the site, so what the designer sees is what players get. */
.gm-cover-view { position: absolute; inset: 0; overflow-y: auto; background: var(--gm-deep); color: var(--gm-cream); padding: 16px; display: flex; flex-direction: column; gap: 22px; }
.gm-cover-view[hidden] { display: none; }
.gm-cover-view h3 { margin: 0 0 4px; font-stretch: var(--gm-title-stretch); font-weight: 900; font-size: var(--gm-text-md); letter-spacing: -.02em; text-transform: uppercase; }
.gm-cover-view section > p { margin: 0 0 10px; font-size: var(--gm-text-sm); font-weight: 700; color: var(--gm-soft); max-width: 46em; }
.gm-cover-cards { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-start; padding: 0 6px 6px 0; }
.gm-cover-view .gm-hero { margin: 0; }
.gm-cover-snap { display: block; width: min(100%, 300px); height: auto; aspect-ratio: 1; background: var(--gm-cream); border: var(--gm-line) solid var(--gm-edge); border-radius: var(--gm-r-tile); box-shadow: var(--gm-shadow-small); }

/* Builder start screen: a list of the designer's games */
.gm-list { display: grid; gap: 12px; padding: 0 var(--gm-gutter); max-width: 720px; }
.gm-list .gm-raised { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 18px; text-decoration: none; }
.gm-list b { font-stretch: 112%; font-weight: 900; font-size: var(--gm-text-lg); text-transform: uppercase; }
.gm-banner { margin: 0 var(--gm-gutter) 16px; padding: 14px 18px; max-width: 720px; background: var(--gm-cream); }
.gm-banner .gm-quiet { color: var(--gm-ink); } /* soft lilac is unreadable on cream (rule 7) */

/* Working dots (added 6 Nov, v1.4): four card-colored dots that bounce and change shape (circle, square, diamond)
   while the builder works. Sits at the bottom of the chat, above the composer. */
.gm-typing { display: flex; gap: 7px; align-items: center; padding: 6px 18px 12px; height: 34px; }
.gm-typing i { width: 11px; height: 11px; border: 2px solid var(--gm-edge); background: var(--gm-yellow); border-radius: 50%; animation: gm-morph 1.6s ease-in-out infinite; }
.gm-typing i:nth-of-type(2) { background: var(--gm-pink); animation-delay: .15s; } .gm-typing i:nth-of-type(3) { background: var(--gm-green); animation-delay: .3s; } .gm-typing i:nth-of-type(4) { background: var(--gm-purple); animation-delay: .45s; }
@keyframes gm-morph {
  0%, 100% { transform: translateY(0) rotate(0); border-radius: 50%; }
  25% { transform: translateY(-7px) rotate(0); border-radius: 50%; }
  50% { transform: translateY(0) rotate(45deg); border-radius: 2px; }
  75% { transform: translateY(-4px) rotate(90deg); border-radius: 3px; }
}
/* Waiting banner (v1.5): a honey strip across the top of the board while Flip is up, so nobody mistakes it for their game. */
.gm-wait { display: flex; align-items: baseline; gap: 4px 12px; flex-wrap: wrap; padding: 10px 16px; background: var(--gm-yellow); color: var(--gm-ink); border-bottom: 2.5px solid var(--gm-edge); }
.gm-wait b { font-stretch: var(--gm-title-stretch); font-weight: 900; font-size: var(--gm-text-lg); text-transform: uppercase; letter-spacing: -.02em; }
.gm-wait span { font-weight: 700; font-size: var(--gm-text-sm); }
/* Build status: a strip along the bottom of the board while the builder works. Whimsical words, then
   "Version N is ready" + See it, when the strip turns green so it's hard to miss. */
.gm-build { display: flex; align-items: center; gap: 8px 14px; flex-wrap: wrap; min-height: calc(var(--gm-touch) + 20px); padding: 10px 16px; border-top: 2.5px solid var(--gm-edge); background: var(--gm-panel); color: var(--gm-cream); }
.gm-build.ready { background: var(--gm-green); color: var(--gm-ink); }
.gm-build.ready .gm-quiet { color: var(--gm-ink); }
.gm-build.ready .gm-btn.primary { background: var(--gm-cream); }
@media (max-width: 759px) { .gm-wait, .gm-build { padding: 8px 14px; } .gm-wait b, .gm-build-word { font-size: var(--gm-text-md); } .gm-build { flex-wrap: nowrap; } }
.gm-build-word { font-stretch: var(--gm-title-stretch); font-weight: 900; font-size: var(--gm-text-lg); text-transform: uppercase; letter-spacing: -.02em; }
.gm-build-word.working::after { content: ""; display: inline-block; width: 1.2em; text-align: left; animation: gm-ellipsis 1.2s steps(4, end) infinite; }
@keyframes gm-ellipsis { 0% { content: ""; } 25% { content: "."; } 50% { content: ".."; } 75%, 100% { content: "..."; } }
.gm-build .gm-btn { margin-left: auto; }
@media (prefers-reduced-motion: reduce) { .gm-typing i { animation: none; } .gm-build-word.working::after { animation: none; content: "…"; } }

/* Designer portfolio (v1.16, mock-up stage). Phone: profile on top, games below. Desktop: the profile is a list on the left (sticky), the games fill the right. */
.gm-wrap { max-width: 1180px; margin: 0 auto; padding-bottom: 60px; } .gm-wrap.gm-wide { max-width: none; }  /* the portfolio uses the full width: profile list at the screen's left edge */
.gm-empty { padding: 60px var(--gm-gutter); display: grid; gap: 16px; justify-items: start; }
.gm-profile { margin: 12px var(--gm-gutter) 0; padding: 28px 0 0; container-type: inline-size; }  /* extra space under the site name separates it from the profile (a divider line was tried and dropped, 9 Nov) */
.gm-profile-top { display: flex; align-items: center; gap: 16px; }
.gm-profile-top .gm-title { --fit-width: calc(100cqi - 104px); --fit-max: 48px; flex: 1; min-width: 0; line-height: .9; }
.gm-avatar.xl { width: 88px; height: 88px; font-size: var(--gm-text-xl); box-shadow: var(--gm-shadow-card); }
.gm-bio { margin: 16px 0 0; max-width: 620px; font-size: var(--gm-text-md); line-height: 1.45; }
.gm-profile-actions { margin-top: 18px; max-width: 420px; }
.gm-links { display: flex; flex-wrap: wrap; gap: 10px; margin: 14px 0 0; padding: 0; list-style: none; } .gm-links .gm-btn { font-size: var(--gm-text-sm); padding: 0 16px; }
.gm-facts { display: flex; gap: 30px; margin-top: 20px; } .gm-facts b { display: block; font: 900 26px var(--gm-font); } .gm-facts span { font-size: var(--gm-text-xs); font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: var(--gm-soft); }
.gm-section-head .gm-h { margin: 0; font-stretch: var(--gm-title-stretch); font-weight: 900; font-size: var(--gm-text-lg); letter-spacing: -.02em; text-transform: uppercase; flex: 1; }
.gm-games { display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); gap: 16px 14px; padding: 4px var(--gm-gutter); }
.gm-games .gm-card.wide { width: 100%; }  /* cards fill their column; each one opens the game */
@media (min-width: 760px) {
  .gm-portfolio { display: grid; grid-template-columns: 380px minmax(0, 1fr); gap: 0 12px; align-items: start; }
  .gm-portfolio .gm-profile { margin-right: 36px; }
  @media (min-height: 820px) { .gm-portfolio .gm-profile { position: sticky; top: 16px; } }  /* stays in view only when the window is tall enough to show all of it */
  .gm-portfolio .gm-profile-actions, .gm-portfolio .gm-links { gap: 14px; } .gm-portfolio .gm-bio { margin-top: 20px; } .gm-portfolio .gm-links { margin-top: 20px; } .gm-portfolio .gm-facts { margin-top: 28px; }
  .gm-portfolio .gm-profile-top { flex-direction: column; align-items: flex-start; gap: 14px; }
  .gm-portfolio .gm-profile-top .gm-title { --fit-width: 100cqi; --fit-max: 44px; flex: none; }
  .gm-portfolio .gm-profile-actions { flex-direction: column; flex-wrap: nowrap; } .gm-portfolio .gm-profile-actions > * { flex: none; }
  .gm-portfolio .gm-links { flex-direction: column; flex-wrap: nowrap; } .gm-portfolio .gm-links .gm-btn { display: grid; width: 100%; justify-items: start; }
  .gm-portfolio .gm-facts { flex-direction: column; gap: 10px; border-top: 2px solid var(--gm-faint); padding-top: 16px; }
  .gm-portfolio .gm-facts > div { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; } .gm-portfolio .gm-facts b { font-size: var(--gm-text-lg); order: 2; }
  .gm-portfolio .gm-section-head { padding-top: 22px; }
}

/* A hero in a narrow column (the portfolio's games column) uses the phone layout, whatever the screen size (v1.17). */
@container (max-width: 700px) {
  .gm-hero .gm-fit { --fit-width: calc(100cqi - 36px); }  /* minus the hero's own side padding, so a title never wraps its last letter */
  .gm-hero .gm-art { width: 190px; height: 190px; right: -50px; bottom: -40px; }
  .gm-hero p { min-height: 146px; max-width: calc(100% - 150px); }
}

/* Portfolio games grid (v1.18): the newest game's hero is the first tile in the grid, two cards wide on wide screens, so the title and art stay close. */
.gm-games > .gm-hero { margin: 0; }
@media (min-width: 1100px) { .gm-games { grid-template-columns: repeat(auto-fill, 270px); } .gm-games > .gm-hero { grid-column: span 2; } }  /* fixed 270px tracks: the hero is exactly two cards wide (554px) */

/* Portfolio (v1.19, layout B chosen 9 Nov): two heroes side by side, then type filters and a list of games in their own colors. */
.gm-hero-row { display: flex; flex-wrap: wrap; gap: 28px; padding: 16px var(--gm-gutter) 0; } .gm-hero-row .gm-hero { margin: 0; flex: 1 1 min(100%, 420px); max-width: 554px; }  /* newest game + most played, side by side when they fit; each at most 554px wide */
.gm-main .gm-section-head { padding-top: 26px; }
/* Hero with a tagline: title on top, the tagline (larger) right under it, the designer's name alone at the bottom. */
.gm-hero.has-tag { min-height: 280px; }
.gm-hero.has-tag p { min-height: 0; margin: 0; max-width: calc(100% - 150px); }
.gm-hero-tag { flex: 1; display: flex; align-items: flex-start; padding-top: 2px; max-width: calc(100% - 170px); position: relative; z-index: 2; font-weight: 700; font-size: var(--gm-text-lg); line-height: 1.2; }
.gm-card[hidden] { display: none; }
.gm-rows { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(340px, 100%), 1fr)); gap: 12px 14px; padding: 8px var(--gm-gutter) 0; }  /* as many columns as fit: no dead space on wide screens */
.gm-rowcard { display: flex; align-items: center; gap: 14px; min-height: 84px; padding: 8px 14px 8px 8px; text-decoration: none; }  /* its color comes from the game: add gm-c-<color> */
.gm-rowcard[hidden] { display: none; }
.gm-rowcard:focus-visible { outline: none; box-shadow: var(--gm-focus); }
.gm-rowart { flex: none; width: 66px; height: 66px; padding: 8px; } .gm-rowart .gm-art { width: 100%; height: 100%; }
.gm-rowtext { flex: 1; min-width: 0; display: grid; gap: 2px; } .gm-rowtext b { font-stretch: 112%; font-weight: 900; font-size: var(--gm-text-lg); letter-spacing: -.02em; line-height: 1.05; overflow-wrap: anywhere; } .gm-rowtext span { font-size: var(--gm-text-sm); font-weight: 700; opacity: .8; }
.gm-rowtag { flex: none; font-size: var(--gm-text-tag); font-weight: 800; text-transform: uppercase; letter-spacing: .05em; padding: 4px 8px; border: 2px solid var(--gm-edge); border-radius: var(--gm-r-chip); }

/* Share dialog (v1.20, mock-up). */
.gm-profile-id { flex: 1; min-width: 0; }
.gm-dialog { width: min(420px, calc(100vw - 32px)); padding: 0; background: var(--gm-cream); margin: auto; }
.gm-dialog::backdrop { background: rgba(11, 6, 8, .65); }
.gm-dialog-body { padding: 18px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; }
.gm-copyrow { display: flex; gap: 8px; } .gm-copyrow .gm-input { flex: 1; min-width: 0; background: #fff; color: var(--gm-ink); border-color: var(--gm-edge); }
.gm-qr { justify-self: center; width: min(220px, 100%); padding: 4px; background: #fff; border: var(--gm-line) solid var(--gm-edge); border-radius: var(--gm-r-tile); } .gm-qr svg { display: block; width: 100%; height: auto; }
.gm-mod-title { margin: 0 0 6px; font-stretch: var(--gm-title-stretch); font-weight: 900; font-size: var(--gm-text-sm); letter-spacing: -.01em; text-transform: uppercase; }
.gm-mod-sub { margin: 0; font-size: var(--gm-text-sm); font-weight: 700; opacity: .75; }
/* Profile link buttons on a phone: tighter padding and gap, and each button shares the row, so three fit per line (375px and up). */
@media (max-width: 759px) { .gm-profile .gm-links { gap: 8px; } .gm-profile .gm-links li { flex: 1 1 auto; } .gm-profile .gm-links .gm-btn { display: grid; width: 100%; padding: 0 8px; } }

/* Form (v1.21, the designer's profile page): a cream panel with labelled fields, a row of color swatches and a list of link rows.
   Fields sit inside a .gm-banner (cream), so labels are ink. A textarea uses .gm-input with .gm-textarea. */
.gm-masthead-right { display: flex; align-items: center; gap: 12px; }
.gm-form { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }  /* minmax(0,…): a long line never stretches the column past the card */
.gm-field { display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; } .gm-field > label, .gm-field > .gm-label { font-weight: 800; font-size: var(--gm-text-sm); }
.gm-field .gm-hint { font-size: var(--gm-text-sm); font-weight: 700; opacity: .75; }  /* one line: keep hints short; on phones up to 400px they drop to text-xs */
.gm-field .gm-input { width: 100%; min-width: 0; }
.gm-input.gm-textarea { min-height: 96px; max-height: 240px; border-radius: var(--gm-r-tile); resize: vertical; }
.gm-field .gm-input[readonly] { background: color-mix(in srgb, var(--gm-ink) 8%, var(--gm-cream)); }
.gm-swatches { display: flex; flex-wrap: wrap; gap: 10px; }
.gm-swatch { width: var(--gm-touch); height: var(--gm-touch); border-radius: 50%; border: var(--gm-line) solid var(--gm-edge); cursor: pointer; padding: 0; }
.gm-swatch[aria-pressed=true] { box-shadow: 0 0 0 3px var(--gm-cream), 0 0 0 6px var(--gm-edge); }
.gm-swatch:focus-visible { outline: none; box-shadow: var(--gm-focus); }
.gm-linkrow { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) auto; gap: 8px; align-items: center; }
@media (max-width: 559px) { .gm-linkrow { grid-template-columns: minmax(0, 1fr) auto; } .gm-linkrow .gm-input:nth-child(2) { grid-column: 1 / -1; grid-row: 2; } }

/* Marquee (v1.22): the backdrop for welcome pages. Slanted rows of cover tiles, still (a drifting version was tried and dropped as
   distracting). Fixed behind the page and dimmed towards the edges so the content on top stays readable. Built by marquee.js; decoration only. */
.gm-marquee { position: fixed; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
.gm-marquee::after { content: ""; position: absolute; inset: 0; background: radial-gradient(ellipse at center, rgba(20, 12, 60, .15) 0%, rgba(20, 12, 60, .72) 100%); }
.gm-marq-field { position: absolute; left: -15%; right: -15%; top: -15%; bottom: -15%; display: flex; flex-direction: column; justify-content: center; gap: var(--gap, 18px); transform: rotate(-9deg); }
.gm-marq-row { display: flex; gap: var(--gap, 18px); width: max-content; }
.gm-marq-tile { flex: none; width: 128px; height: 128px; position: relative; border-radius: var(--gm-r-card, 22px); border: var(--gm-line) solid var(--gm-edge); box-shadow: var(--gm-shadow-small); overflow: hidden; }
.gm-marq-tile .gm-art { position: absolute; inset: 14%; width: 72%; height: 72%; }
@media (max-width: 559px) { .gm-marquee { --gap: 14px; } .gm-marq-tile { width: 96px; height: 96px; } }

/* Address field (v1.23): the site's address is printed inside the box, in front of what the designer types, so they see exactly
   where their handle lands ("freshbatchgames.com/" + mara). One pill; the input inside has no border of its own. Locked once chosen. */
.gm-addr { display: flex; align-items: center; min-height: var(--gm-touch); padding-left: 14px; border-radius: 22px; border: var(--gm-line) solid var(--gm-edge); background: var(--gm-cream); }
.gm-addr:focus-within { box-shadow: var(--gm-focus); }
.gm-addr .gm-addr-pre { flex: none; font-weight: 800; opacity: .55; white-space: nowrap; }
.gm-addr .gm-input, .gm-field .gm-addr .gm-input { flex: 1; width: auto; min-width: 0; border: 0; border-radius: 0; background: transparent; box-shadow: none; padding-left: 0; }
.gm-addr .gm-input:focus-visible { box-shadow: none; }
.gm-addr.locked { background: color-mix(in srgb, var(--gm-ink) 8%, var(--gm-cream)); }
/* Narrow phones: the address sits in small type above what the designer types, still inside the one box, so a long handle has room. */
@media (max-width: 400px) {
  .gm-addr { flex-direction: column; align-items: stretch; gap: 0; padding: 8px 14px 0; }
  .gm-addr .gm-addr-pre { font-size: var(--gm-text-xs); }
  .gm-addr .gm-input, .gm-field .gm-addr .gm-input { width: 100%; min-height: var(--gm-touch); padding: 0; margin-top: -6px; }  /* a full-height tap target, tucked up under the address */
}

/* Stage (v1.24): the centred-card layout for welcome-style pages (Welcome, profile, login, invite). A cream card sits in the middle
   of the screen over the marquee backdrop. Use: <div id="backdrop"></div><main class="gm-stage"><div class="gm-raised gm-banner gm-stage-card">
   <p class="gm-brand">…</p><h1 class="gm-title">…</h1><p class="gm-lede">…</p> … </div></main>. Add .one-line to a lede that must never wrap. */
.gm-stage { position: relative; z-index: 1; min-height: 100vh; min-height: 100svh; display: grid; place-items: center; padding: calc(24px + var(--gm-safe-top, 0px)) var(--gm-gutter) 40px; }
.gm-stage-card { width: min(640px, 100%); margin: 0; max-width: none; }
.gm-stage-card.narrow { width: min(480px, 100%); }
.gm-brand { margin: 0 0 4px; font-stretch: var(--gm-title-stretch); font-weight: 900; font-size: var(--gm-text-sm); text-transform: uppercase; letter-spacing: .08em; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.gm-brand a { color: inherit; font-size: var(--gm-text-xs); }
.gm-stage-card h1.gm-title { margin: 6px 0 6px; font-size: clamp(30px, 7vw, 44px); line-height: .95; }
.gm-lede { margin: 0 0 18px; font-weight: 700; opacity: .8; overflow-wrap: anywhere; }  /* a long email address breaks instead of running out of the card */
.gm-lede.one-line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 400px) { .gm-lede.one-line { font-size: var(--gm-text-sm); } }  /* on the narrowest phones a one-line lede drops a size so it still fits */
.gm-stage-card .gm-form + .gm-quiet { margin: 12px 0 0; }
