/* The Well — painted by the block.
   Deep AfriCOBRA jewel pigment (brick, marigold, garden, cobalt, grape, raspberry)
   knocked back to live on warm cream paper, anchored by soft charcoal, all of it
   floating on a Takuma navy night ground. Geometry is intentional, never confetti:
   it marks function — category, picked, rank, the funded line, a section's edge. */

@font-face { font-family: 'Ciscela';
  src: url("/assets/Ciscela-Regular-21d9af5f.ttf") format("truetype");
  font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: 'IBM Plex Sans'; src: url("/assets/IBMPlexSans-Regular-64251e64.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: 'IBM Plex Sans'; src: url("/assets/IBMPlexSans-SemiBold-99b2fa32.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: 'IBM Plex Sans'; src: url("/assets/IBMPlexSans-Bold-8e5326da.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap; }
@font-face { font-family: 'IBM Plex Mono'; src: url("/assets/IBMPlexMono-Medium-3f7bbac1.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: 'IBM Plex Mono'; src: url("/assets/IBMPlexMono-Bold-e35453d6.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap; }

:root {
  /* the night ground */
  --navy:    #0A152B;
  --navy-2:  #102545;
  --phasealert-h: 58px;   /* the phase banner's fixed height (see .phasealert) */
  --onnavy:  #F1F0EB;   /* cream text on navy */
  --slate:   #9CADC2;   /* muted text on navy */

  /* Neutral paper + charcoal; gold stays reserved for actions and emphasis. */
  --cream:   #F6F5F2;
  --cream-2: #EAE8E2;   /* recessed / picked face */
  --sand:    #D9D6CC;
  --black:   #1C1714;   /* soft charcoal — lines, type, anchor */
  --ink:     #241C14;   /* body text on paper */
  --stone:   #6E635A;   /* secondary text on paper */
  --line:    rgba(28, 23, 20, .26);
  --hairline: rgba(241, 240, 235, .18);   /* ruled lines on navy */

  /* grounded AfriCOBRA — each hue has a job */
  --violet:  #553781;   /* grape — payoff, shapes */
  --cobalt:  #245C8C;   /* civic, water */
  --teal:    #2A7268;   /* backers */
  --green:   #2E6649;   /* garden — picked, services */
  --yellow:  #DB972A;   /* marigold — events, totals, new */
  --orange:  #BE5328;   /* burnt orange — warm accent */
  --magenta: #9A3354;   /* raspberry — other, eyebrows */
  --red:     #A93F2B;   /* brick — places, the well's own color */

  /* water, light to deep — the funded layers */
  --water-1: #3E84B0; --water-2: #2A6699; --water-3: #1C4B73;
  --water-4: #163D5E; --water-5: #102E48;

  --soft:      0 14px 30px -16px rgba(22, 17, 13, .5), 0 5px 12px -8px rgba(22, 17, 13, .3);
  --soft-lift: 0 24px 46px -18px rgba(22, 17, 13, .56), 0 8px 16px -10px rgba(22, 17, 13, .34);

  --display: 'Ciscela', Georgia, 'Times New Roman', serif;
  --body: 'IBM Plex Sans', Helvetica, Arial, sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, monospace;

  /* surface-aware text: navy ground by default, flipped on paper */
  --t-text:    var(--onnavy);
  --t-muted:   var(--slate);
  --t-head:    var(--onnavy);
  --t-eyebrow: var(--yellow);
}

/* paper surfaces — anything cream flips the text register to charcoal */
.deck__face, .flatcard, .card, .budget, dialog.sheet, .desktop, .cd__flyer {
  --t-text:    var(--ink);
  --t-muted:   var(--stone);
  --t-head:    var(--black);
  --t-eyebrow: var(--magenta);
  color: var(--t-text);
}

* { box-sizing: border-box; margin: 0; padding: 0; }
/* The Well paints its own dark ground, but its form controls are light cream paper.
   Pin the UA to the LIGHT scheme so Dark Mode can't repaint autofilled fields and
   native controls near-black (a background CSS can't override) — the "autofill turns
   the code box / button black" bug. Belt to the <meta name="color-scheme"> in <head>. */
html { color-scheme: light; }
html, body { color: var(--t-text); font-family: var(--body); line-height: 1.5;
  -webkit-font-smoothing: antialiased; min-height: 100dvh;
  background:
    url("/assets/fabric-dark-fdd85efc.png") repeat,
    radial-gradient(110% 70% at -10% 108%, rgba(42, 114, 104, .26), transparent 55%),
    var(--navy);
  background-size: 220px auto, auto, auto; }
img { max-width: 100%; }
a { color: inherit; }

/* a breath of night over the tippy top — content scrolls clean off the screen
   through a slight fade instead of a hard edge */
body::after { content: ""; position: fixed; top: 0; left: 0; right: 0; z-index: 58;
  height: calc(22px + env(safe-area-inset-top)); pointer-events: none;
  background: linear-gradient(to bottom, var(--navy) calc(20% + env(safe-area-inset-top)), transparent); }

/* faint paper tooth over everything */
body::before { content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 9999; opacity: .09;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
  mix-blend-mode: multiply; }

/* ---- the app shell ----
   A phone-first product: one column, full width on mobile, centered on desktop. */
.app { max-width: 480px; margin: 0 auto;
  padding: calc(42px + env(safe-area-inset-top)) 20px 44px;
  min-height: 100dvh; display: flex; flex-direction: column; }
.app--tabbed { padding-bottom: calc(100px + env(safe-area-inset-bottom)); }

/* flash messages — a marigold stamp that rises, says its piece, and goes */
#flash { position: fixed; left: 50%; transform: translateX(-50%); z-index: 60;
  width: calc(100% - 24px); max-width: 440px; display: flex; flex-direction: column; gap: 8px;
  pointer-events: none; bottom: calc(12px + env(safe-area-inset-bottom)); }
#flash.is-docked { bottom: calc(104px + env(safe-area-inset-bottom)); }
.flash { background: var(--yellow); color: var(--black); font-size: 14px; font-weight: 700;
  padding: 12px 14px; text-align: center; pointer-events: auto; cursor: pointer;
  border: 2px solid var(--black); box-shadow: 3px 3px 0 var(--black);
  animation: flash-in .28s ease-out; transition: opacity .35s ease, transform .35s ease; }
.flash--alert { background: var(--red); color: #fff; }

/* The phase-change banner: a marigold-edged bar at the top of the app that says the
   season moved on (voting opened, the block decided). Its CTA loads the fresh screen,
   which is also how a PWA with no refresh button catches up. */
/* One fixed-height line, never a wrap: the portal below is position:fixed, so
   the banner's height must be a number the portal can move down by (see the
   body:has rules at the portal). Long text ellipsizes; the bold lead survives. */
.phasealert { position: sticky; top: 0; z-index: 59;
  display: flex; align-items: center; gap: 12px; flex-wrap: nowrap;
  height: calc(var(--phasealert-h) + env(safe-area-inset-top)); box-sizing: border-box;
  padding: env(safe-area-inset-top) 14px 0 18px;
  background: var(--navy-2); border-bottom: 2px solid var(--yellow); }
.phasealert__txt { flex: 1; min-width: 0; font-size: 14px; color: var(--onnavy);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.phasealert .btn { flex: 0 0 auto; margin: 0; padding: 8px 14px; font-size: 14px; }
.flash.is-leaving { opacity: 0; transform: translateY(10px); }
@keyframes flash-in { from { opacity: 0; transform: translateY(14px); } }

/* the bottom tab bar, pinned to the viewport */
.apptabs { position: fixed; bottom: 0; left: 0; right: 0; z-index: 50;
  border-top: 1px solid var(--hairline); background: rgba(13, 26, 51, .96);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  padding-bottom: env(safe-area-inset-bottom); }
.apptabs__inner { max-width: 480px; margin: 0 auto; display: flex; }
.apptab { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 14px 0 13px; text-decoration: none; color: var(--slate); font-size: 10px; font-weight: 700;
  letter-spacing: .04em; transition: color .12s ease; }
.apptab__icon { width: 23px; height: 23px; }
.apptab__ava { width: 23px; height: 23px; border-radius: 50%; object-fit: cover; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center; border: 1.5px solid currentColor; }
.apptab__ava--txt { font-size: 9px; font-weight: 800; letter-spacing: .02em; }
.apptab:hover { color: var(--onnavy); }
.apptab.is-on { color: var(--yellow); }

/* admin runs desktop-wide, on its own sheet of paper */
.desktop { max-width: 1020px; margin: 26px auto 40px; padding: 26px 30px 34px;
  background: var(--cream); border-radius: 2px; box-shadow: var(--soft-lift); }

/* ---- type ---- */
h1 { font-family: var(--display); font-size: 32px; line-height: 1.04; font-weight: 400;
  letter-spacing: -.01em; color: var(--t-head); margin-bottom: 10px; }
h2 { font-size: 19px; font-weight: 700; color: var(--t-head); }
h3 { font-size: 15px; font-weight: 700; color: var(--t-head); }
p  { margin-bottom: 10px; }
.eyebrow { font-size: 11px; letter-spacing: .16em; text-transform: uppercase; font-weight: 700;
  color: var(--t-eyebrow); margin-bottom: 6px; }
.muted { color: var(--t-muted); }
.small { font-size: 12px; }
.lede  { font-size: 17px; }
.big   { font-size: 22px; font-weight: 800; }

/* ---- layout helpers ---- */
.row { display: flex; align-items: center; gap: 10px; }
.between { justify-content: space-between; }
.stack > * + * { margin-top: 12px; }
.spacer { flex: 1; }
.divider { border-top: 1px solid var(--hairline); margin: 26px 0; }
.desktop .divider { border-top-color: var(--line); }
.cap { font-size: 11px; color: var(--slate); text-align: center; margin-top: 8px;
  letter-spacing: .04em; }

/* ---- buttons — the punchy control: charcoal line, hard offset shadow ---- */
.btn { display: block; width: 100%; text-align: center; border: 2px solid var(--black);
  background: var(--cream-2); color: var(--black); font-family: var(--body); font-size: 15px;
  font-weight: 700; line-height: 1.2; padding: 13px 18px; text-decoration: none; cursor: pointer;
  position: relative; -webkit-appearance: none; appearance: none; /* same height as <a>, <button>, <input> */
  box-shadow: 3px 3px 0 var(--black);
  transition: transform .08s ease, box-shadow .12s ease, background .12s ease, color .12s ease; }
.btn[hidden] { display: none; }
.btn:hover { transform: translate(-2px, -2px); box-shadow: 5px 5px 0 var(--black); }
.btn:active { transform: translate(0, 0); box-shadow: 1px 1px 0 var(--black); }
.btn--solid { background: var(--magenta); color: var(--cream); }
.btn--solid:hover { background: var(--black); }
.btn--rasp { background: var(--magenta); color: var(--cream); }
.btn--white { background: var(--cream); color: var(--black); }
.btn--gold { background: var(--yellow); color: var(--navy); }
.btn--gold:hover { background: var(--black); color: var(--yellow); }
.btn--ghost { background: transparent; border-color: var(--slate); color: var(--onnavy); box-shadow: none; }
.btn--ghost:hover { transform: none; box-shadow: none; background: rgba(241, 240, 235, .06); }
/* out of play: greyed, not tappable-looking */
.btn:disabled, .btn[disabled] { border-color: #b9b29f; color: #8f887a; background: #ece5d2;
  box-shadow: none; cursor: default; transform: none; }
.btn:focus-visible, .tap:focus-visible, .chip:focus-visible {
  outline: 2px solid var(--yellow); outline-offset: 2px; }

/* text taps — the underline draws in, raspberry */
.tap { font-family: var(--body); font-size: 14px; font-weight: 700; color: inherit;
  background: none; border: 0; cursor: pointer; position: relative; text-decoration: none;
  display: inline-block; }
/* inside running text, a tap is the same size as its sentence */
.small .tap, p .tap { font-size: inherit; }
/* the underline is always there, so a tap reads as a link at rest — not plain
   text; hover just thickens it. */
.tap::after { content: ""; position: absolute; left: 0; right: 0; bottom: -3px; height: 2px;
  background: var(--magenta); transition: height .12s ease, bottom .12s ease; }
.tap:hover::after { height: 3.5px; bottom: -4px; }

/* tap-open disclosures (the receipts behind the amount) — our caret, not the browser's */
details > summary.tap { cursor: pointer; list-style: none; }
details > summary.tap::-webkit-details-marker { display: none; }
details > summary.tap::before { content: "▸ "; }
details[open] > summary.tap::before { content: "▾ "; }
details > summary.tap::after { display: none; }
.back { display: inline-flex; align-items: center; gap: 3px; font-size: 13px; font-weight: 700; margin-bottom: 14px;
  text-decoration: none; color: var(--t-text); }
/* the chevron rides bigger than its label so the back tap reads at a glance and is easy to hit */
.back::before, .pitchbar__back::before, .introback::before {
  content: "‹"; font-size: 1.9em; font-weight: 400; line-height: 0; }
/* a chevron that scales up inside a label without dragging the line-height (e.g. the gold CTA back) */
.bigchev { font-size: 1.5em; line-height: 0; vertical-align: -.08em; }

/* ---- pills / chips — squared, charcoal line, hard offset ---- */
.pill { display: inline-flex; align-items: center; font-size: 11px; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; padding: 4px 10px;
  background: var(--sand); color: var(--black);
  border: 2px solid var(--black); }   /* a status label, not an action — no raised shadow */
.pill--miss { background: transparent; border-color: var(--slate); color: var(--slate);
  box-shadow: none; }   /* a season that's over, not an action item */
.desktop .pill--miss, .flatcard .pill--miss { border-color: var(--stone); color: var(--stone); }
.pill--ok   { background: var(--green); color: #08210f; }   /* geocoded inside the block */
.pill--now  { background: #fff; color: var(--magenta); }   /* the current cycle — white, pops */
.pill--flag { background: var(--yellow); color: var(--black); }   /* needs a human look */
.stamp { font-weight: 800; font-size: 10px; letter-spacing: .09em; color: var(--yellow); }

/* the season tag + the how-a-season-works popup */
.seasontag { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.seasontag .pill { background: var(--yellow); }
.infobtn { width: 22px; height: 22px; border: 2px solid var(--onnavy); border-radius: 50%;
  background: none; color: var(--onnavy); font: inherit; font-size: 12px; font-weight: 800;
  line-height: 1; display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; padding: 0; opacity: .75; transition: opacity .12s ease, color .12s ease,
  border-color .12s ease; }
.infobtn:hover { opacity: 1; color: var(--yellow); border-color: var(--yellow); }
dialog.sheet { border: 2.5px solid var(--black); border-radius: 2px; background: var(--cream);
  padding: 20px; width: calc(100% - 32px); max-width: 400px; box-shadow: var(--soft-lift);
  margin: auto; }  /* the global reset zeroes margins; auto restores native modal centering */
dialog.sheet::backdrop { background: rgba(10, 21, 43, .62); }
/* opening a sheet shouldn't stamp a focus ring on its first button */
dialog.sheet:focus-visible, dialog.sheet:focus { outline: none; }
/* chips ride together as one bounded, segmented switch — a control, not loose squares */
.chip { display: inline-block; font-family: var(--body); font-size: 13px; font-weight: 700;
  line-height: 1.2; padding: 8px 16px; text-decoration: none; color: var(--black);
  background: var(--cream-2); border: 0; cursor: pointer; -webkit-appearance: none; appearance: none;
  transition: background .12s ease, color .12s ease; }
.chip + .chip { border-left: 2px solid var(--black); }
.chip:hover { background: var(--magenta); color: #fff; }
.chip.is-on { background: var(--black); color: var(--cream); }
.chips { display: inline-flex; align-self: flex-start; margin-bottom: 16px;
  border: 2px solid var(--black); box-shadow: 3px 3px 0 var(--black); background: var(--cream-2); }

/* ---- forms ---- */
.field { margin-bottom: 16px; }
.field label { display: block; font-size: 12px; font-weight: 700; letter-spacing: .04em;
  margin-bottom: 6px; }
.input { width: 100%; border: 2px solid var(--black); padding: 12px 13px; font: inherit;
  font-size: 16px; background: var(--cream); color: var(--ink); border-radius: 2px; /* 16px floor so iOS doesn't zoom on focus */
  transition: box-shadow .12s ease; }
.input:focus { outline: none; box-shadow: 3px 3px 0 var(--yellow); }
/* browsers repaint an autofilled field with their own UA color (pale yellow, or a
   dark box in Dark Mode) that plain `background` can't touch — pin it back to our
   cream so a pre-filled code box never darkens. */
.input:-webkit-autofill,
.input:-webkit-autofill:hover,
.input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--ink);
  -webkit-box-shadow: 0 0 0 1000px var(--cream) inset;
  caret-color: var(--ink); }
textarea.input { resize: none; }

/* ===== THE WELL (core element) =====
   A yellow vessel holding clean blue water. Funded = water layers, deepest at the
   bottom; below the cut the vessel sits dry and empty. */

/* the home panel the well lives in — a deeper pool of night, cut from the page */
.wellpanel { position: relative; margin: 4px -20px 0; padding: 24px 20px 16px;
  background: transparent; }   /* the well sits straight on the navy night ground */
.wellpanel__what { display: flex; align-items: center; gap: 9px; margin: 18px -20px -16px;
  padding: 13px 20px; border-top: 1px solid var(--hairline);
  font-size: 13px; font-weight: 700; color: var(--onnavy); text-decoration: none;
  transition: color .12s ease; }
.wellpanel__what:hover { color: var(--yellow); }
.wellpanel__q { display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; flex: 0 0 auto; border: 2px solid currentColor; border-radius: 50%;
  font-size: 11px; font-weight: 800; color: var(--yellow); }
.wellpanel__go { margin-left: auto; font-size: 15px; color: var(--magenta); }

/* header: how much, by when — the one thing a resident needs */
.wellhead { display: flex; flex-direction: column; gap: 2px; margin-bottom: 12px; }
.wellhead__lede { font-family: var(--display); font-size: 31px; font-weight: 400;
  line-height: 1.08; letter-spacing: -.01em; margin: 0; color: var(--onnavy); }
.wellhead__lede strong { font-weight: 400; color: var(--yellow); }
.wellhead__when { font-size: 15px; color: var(--slate); margin: 10px 0 0; }
.wellhead__when strong { font-weight: 800; color: var(--onnavy); }
.wellhead__amt { font-family: var(--display); font-size: 56px; font-weight: 400; line-height: .95;
  letter-spacing: -.01em; color: var(--yellow); }
.wellhead__sub { font-size: 16px; font-weight: 700; color: var(--onnavy); }
.wellhead__sub strong { color: var(--yellow); }

.well { display: block; }
.well__section { display: flex; gap: 34px; align-items: stretch; }   /* room for the list */
.well__col { width: 62px; flex: 0 0 auto; }                          /* skinny vessel */
/* the vessel: flat water in front of the brick wall — no marigold, no outline */
.well__bar { position: relative; display: flex; flex-direction: column; overflow: hidden;
  background: var(--yellow); border: 2.5px solid var(--black); border-radius: 2px;
  box-shadow: var(--soft); }
.well__bar:not(.well__bar--dry) { background: transparent; border: 0; box-shadow: var(--soft); }
.band { position: relative; background: var(--water-1); }
.band + .band { border-top: 0; }
/* each funded idea is its own pigment, poured in front of the brick */
.band:not(.band--dry):nth-child(5n+1) { background: var(--magenta); }
.band:not(.band--dry):nth-child(5n+2) { background: var(--teal); }
.band:not(.band--dry):nth-child(5n+3) { background: var(--violet); }
.band:not(.band--dry):nth-child(5n+4) { background: var(--green); }
.band:not(.band--dry):nth-child(5n+5) { background: var(--orange); }
/* the undivided pot, before any idea claims it (pitch phase): one blue body of water */
.band.band--pot:not(.band--dry) { background: var(--water-1); }
/* below the cut: a flat grey block, not funded */
.band.band--next:not(.band--dry) { background: #565c66; }
/* TENTATIVE (still voting) — the pigment is poured in stripes; the off-stripes drop to
   low opacity so the brick shows faintly through. Confirmed/closed = solid (no mask). */
.band.band--tentative {
  -webkit-mask: repeating-linear-gradient(-45deg, #000 0 9px, rgba(0,0,0,.4) 9px 18px);
          mask: repeating-linear-gradient(-45deg, #000 0 9px, rgba(0,0,0,.4) 9px 18px); }
/* dry: the empty placeholder vessel (used by the intro's waiting state) */
.well__bar--dry { background: var(--yellow); }
.band--dry { background: transparent; }
.band--dry + .band--dry { border-top: 2px dashed rgba(28, 23, 20, .35); }

/* the well stands against a real brick wall, knocked back to B&W; the panel is cut
   a touch larger than the vessel and nudged so its left edge meets the content margin */
.wellbrick { position: relative; }
.wellbrick::before { content: ""; position: absolute; z-index: 0;
  top: -11px; left: -11px; right: -11px; bottom: -11px;
  background: url("/assets/brick-wall-964b78fd.jpg") top left / 200px auto repeat;
  filter: grayscale(1) contrast(1.05) brightness(.82); box-shadow: var(--soft); }
.wellbrick > * { position: relative; z-index: 1; }

/* the funding cut — full width, greyed: below it is hope, not failure */
.well__cut { display: flex; align-items: center; gap: 12px; margin: 14px 0;
  font-size: 11px; font-weight: 800; letter-spacing: .16em; text-transform: uppercase;
  color: var(--slate); }
.well__cut::before, .well__cut::after { content: ""; flex: 1; height: 2px;
  background: rgba(156, 173, 194, .3); }

/* the running tally that sits just above the cut */
.well__subtotal { display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; margin: 14px 0 0; font-size: 12px; color: var(--slate); }
.well__subtotal strong { font-size: 14px; font-weight: 800; color: var(--onnavy); }
.well__subtotal .of { color: var(--slate); }

/* the standings list — the vote-tab voice without the cards: Ciscela serif titles,
   a tilted charcoal price tag, the vote count, and a right chevron */
.well__labels { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.well__label { position: relative; display: flex; flex-direction: column; justify-content: center;
  min-width: 0; padding: 3px 24px 3px 0; border-bottom: 1px solid var(--hairline);
  text-decoration: none; color: var(--onnavy); }
.well__label:last-child { border-bottom: 0; }
.well__label .nm { font-family: var(--display); font-size: 19px; font-weight: 500;
  letter-spacing: 0; line-height: 1.08; color: var(--onnavy); transition: color .1s ease; }
.well__label.is-dry .nm { font-size: 15.5px; color: var(--slate); }
.well__label:hover .nm { color: var(--yellow); }
.wl__meta { display: flex; align-items: center; gap: 9px; margin-top: 7px; }
.wl__price { display: inline-block; background: var(--black); color: #fff; font-weight: 800;
  font-size: 12px; padding: 2px 8px; border: 2px solid var(--black);
  box-shadow: 2px 2px 0 var(--black); transform: rotate(-2.5deg); }
/* below the cut: the cost stays quiet — no tag, no pop */
.wl__price--next { background: transparent; color: var(--slate); border: 0; box-shadow: none;
  transform: none; padding: 0; font-weight: 700; }
.wl__votes { font-size: 12.5px; color: var(--slate); }
.wl__chev { position: absolute; right: 2px; top: calc(50% - 6px); width: 9px; height: 9px;
  border-right: 2.5px solid var(--slate); border-top: 2.5px solid var(--slate); transform: rotate(45deg); }

/* pitch: the explainer text beside the well (well on the left, like the other phases) */
.pitchexplain { flex: 1; align-self: center; }
.pitchexplain p { font-size: 21px; line-height: 1.35; color: var(--onnavy); margin-bottom: 14px; }
.pitchexplain p:last-child { margin-bottom: 0; }
.pitchexplain strong { color: var(--yellow); font-weight: 700; }
.well__label .amt { font-size: 11.5px; color: var(--slate); }
.well__label.is-dry .nm { font-weight: 600; color: var(--slate); }
.well__label.is-dry .amt { color: rgba(156, 173, 194, .7); }

/* ===== THE SHARED-LINK LANDING =====
   What a forwarded link opens for someone not yet a resident. One idea, room to
   breathe, the card on scroll, then the way in. Generous spacing is the point —
   a little scroll reads as considered, not crowded. One flex column, uniform
   across all four branches (funded / missed the cut / off the ballot / still
   live) — the .spacer beat before the identity beat needs this to hold its
   fixed height. */
.share { display: flex; flex-direction: column; }
.share__head { font-size: 30px; line-height: 1.08; margin-bottom: 10px; }
.share__sub { font-family: var(--display); font-size: 19px; font-weight: 400;
  color: var(--t-head); margin-bottom: 30px; }
.share__card { margin: 0 0 30px; }
.share__what { font-size: 16px; line-height: 1.5; color: var(--ink); margin: 4px 0 16px; }
/* the price reads as a tag stamped onto the card, not a button: a hard border
   on every edge, an offset shadow, a slight tilt — the same stamped-tag
   language as .deck__price and the well panel's own .wl__price */
.share__price { display: inline-block; font-family: var(--display); font-size: 22px; font-weight: 600;
  color: var(--navy); background: var(--yellow); padding: 5px 13px;
  border: 2px solid var(--black); box-shadow: 2px 2px 0 var(--black); transform: rotate(-2deg); }
/* real body text, not a fine-print caption — var(--t-text) because this strip
   sits straight on the navy ground (no paper ancestor here): it resolves to
   full-strength cream on this page, and would still resolve to ink if this
   ever rendered inside a paper surface. */
.share__context { font-size: 16px; line-height: 1.5; color: var(--t-text); margin-bottom: 16px; }
.share__context + .share__context { margin-bottom: 24px; }
/* the identity beat: warmer and larger than the context lines, a direct
   address that sits right above the "Count me in" button it's arguing for */
.share__identity { font-family: var(--display); font-size: 22px; font-weight: 400;
  line-height: 1.3; text-align: center; color: var(--t-head); margin: 0 0 14px; }
/* the closing microline: quieter than the context lines, but still a
   considered sign-off — the display face keeps it warm at a smaller size,
   not a demoted footer */
.share__micro { font-family: var(--display); font-size: 15px; font-weight: 400;
  text-align: center; color: var(--t-muted); margin: 14px 0 0; }

/* ===== THE PROPOSAL CARD (core element) =====
   front = story, back = cost. Cream paper, charcoal line, soft drop into the page. */
.card { background: var(--cream); border-radius: 2px; box-shadow: var(--soft); }
.card__face { padding: 20px; }
.card__seam { border-top: 1.5px dashed var(--line); }
.card__title { font-family: var(--display); font-size: 24px; font-weight: 500; line-height: 1.06;
  letter-spacing: 0; margin: 2px 0 9px; color: var(--black); }
/* the review step: name it in place on the card, highlighted as the thing to do */
/* edit-in-place fields look exactly like the card text — click to edit, no boxes,
   no highlight. A faint underline on hover hints it's editable; focus firms it. */
.card__title--edit { display: block; width: 100%; margin: 2px 0 9px; padding: 0;
  border: 0; border-bottom: 1px dashed rgba(28, 23, 20, .42); background: none; -webkit-appearance: none; cursor: text;
  resize: none; overflow: hidden; }   /* review's auto-growing <textarea> use: no handle, no scrollbar */
.cardedit { font: inherit; color: inherit; flex: 1; min-width: 0; padding: 0;
  border: 0; border-bottom: 1px dashed rgba(28, 23, 20, .42); background: none; -webkit-appearance: none; cursor: text; }
.cardedit--big { font-size: 16px; }
.deck__cover--ph { background: #ada79c; }
.cardedit--why { display: block; width: 100%; flex: none; margin: 4px 0 9px; resize: none;
  color: var(--t-muted); border: 1px dashed rgba(28, 23, 20, .34); border-radius: 4px;
  padding: 8px 10px; background: rgba(28, 23, 20, .015); }
.card__title--edit:hover, .cardedit:hover { border-bottom-color: rgba(28, 23, 20, .6); }
.card__title--edit:focus, .cardedit:focus { outline: none; border-bottom-style: solid; border-bottom-color: var(--black); }
.cardedit--why:focus { border-style: solid; border-color: var(--black); }
.card__title--edit::placeholder, .cardedit::placeholder { color: var(--stone); }
.card__story dt { font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
  font-weight: 700; color: var(--magenta); margin-top: 12px; }
.card__story dd { font-size: 14px; margin: 2px 0 0; }
.card__champion { margin-top: 16px; font-size: 12px; font-weight: 700;
  border-top: 1.5px solid var(--line); padding-top: 10px; }
/* the problem/idea story paragraphs, real ink — .muted used to carry this text
   and lighten it; pin it explicit so nothing downstream can grey it out again.
   var(--t-text) already resolves to ink here (card__body only ever lives inside
   a paper surface: .flatcard/.deck__face/.card). Size/line-height untouched —
   .muted never set either. */
.card__body { color: var(--t-text); }
/* the pencil affordance that opens a field's own wizard step (return-to-review
   mode) — small, subdued, a tap target rather than a document-editing icon.
   Sits inside .card__metaline, .card__body and .eyebrow, so it resets the
   uppercase/letter-spacing it would otherwise inherit from .eyebrow. */
.fieldedit { display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; margin-left: 2px; vertical-align: middle;
  border-radius: 50%; font-size: 14px; line-height: 1; color: var(--stone);
  text-decoration: none; text-transform: none; letter-spacing: normal; cursor: pointer;
  transition: color .12s ease, background .12s ease; }
.fieldedit:hover, .fieldedit:focus-visible { color: var(--black); background: rgba(28, 23, 20, .08); }

/* budget (lives inside the card back, and on the vote screen) */
.budget { background: var(--cream-2); border: 2px solid var(--black); border-radius: 2px;
  margin-top: 4px; overflow: hidden; }
.lineitem { display: flex; justify-content: space-between; gap: 10px; padding: 11px 14px;
  border-bottom: 1.5px solid var(--line); font-size: 14px; }
.lineitem:last-child { border-bottom: 0; }
.lineitem .amt { font-variant-numeric: tabular-nums; font-weight: 700; }
.lineitem--total { background: var(--black); color: var(--cream); border-bottom: 0; font-weight: 700; }
.lineitem--total .amt { color: var(--yellow); }
/* pitch-stage honesty under the numbers — real body text, not fine print:
   this is the one line that explains why the numbers say "Estimated," so it
   gets the same size/ink as .lineitem, not eyebrow treatment. */
.budget__note { margin: 7px 0 0; font-size: 14px; color: var(--t-text); }
/* rough numbers look rough: pencil-grey, dashed, no ink committed yet */
.budget--rough { filter: grayscale(1); background: #EDEAE2; border-style: dashed; }
.budget--rough .lineitem { border-bottom-style: dashed; }
.budget--rough .lineitem--total { background: #57514A; }

/* ---- card feed: full-screen cards, swipe up for the next (TikTok style) ----
   The feed runs full bleed to the top of the screen; the bar floats over it and
   content fades out toward the tippy top instead of clipping on a hard edge. */
.feed { height: 100dvh; margin: -42px -20px -44px; position: relative;
  display: flex; flex-direction: column; }
.feed__bar { position: absolute; top: 0; left: 0; right: 0; z-index: 6;
  display: flex; align-items: center; gap: 10px; padding: 18px 14px 8px; }
.feed__scroll { flex: 1; min-height: 0; overflow-y: auto; scroll-snap-type: y mandatory;
  scrollbar-width: none; padding: 68px 20px 0; scroll-padding-top: 68px;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 58px);
  mask-image: linear-gradient(to bottom, transparent 0, #000 58px); }
.feed__scroll::-webkit-scrollbar { display: none; }
.feed__page { height: 100%; scroll-snap-align: start; scroll-snap-stop: always;
  display: flex; flex-direction: column; padding-bottom: 54px; }   /* the cue's reserved band */
.feed__cardwrap { position: relative; flex: 1; min-height: 0; }
.feed__cardwrap .deck__card { position: absolute; inset: 0; }
/* minimize (zoom out to the list) — a bare glyph on the night, no plate */
.minmax { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  width: 38px; height: 38px; background: none; color: var(--onnavy); border: 0;
  cursor: pointer; transition: color .12s ease; }
.minmax:hover { color: var(--yellow); }
/* the feed's one way out: a standout, single-line pill — overrides minmax's
   bare-glyph sizing so the "All ideas" label can sit beside the icon */
.feed__exit { width: auto; height: auto; gap: 7px; padding: 8px 14px 8px 10px;
  background: var(--cream); color: var(--black); border: 2px solid var(--black);
  border-radius: 999px; box-shadow: 2px 2px 0 var(--black);
  transition: transform .08s ease, box-shadow .12s ease; }
.feed__exit:hover { color: var(--black); transform: translate(-1px, -1px); box-shadow: 3px 3px 0 var(--black); }
.feed__exit-label { white-space: nowrap; }
/* the scroll cue: one plain statement, chevron pointing the way down. It holds
   the foot of the screen and steps aside while you're actually scrolling. */
.feed__cue { position: absolute; left: 0; right: 0; bottom: 6px; z-index: 4;
  pointer-events: none; display: flex; flex-direction: column; align-items: center; gap: 0;
  text-align: center; font-size: 13.5px; font-weight: 700; color: var(--onnavy);
  transition: opacity .3s ease; }
.feed__cue.is-quiet { opacity: 0; }

/* the closing page under the last card — centred, quiet, one tap back to the list */
.feed__page--end { display: flex; align-items: center; justify-content: center; }
.feedend { text-align: center; padding: 24px; display: flex; flex-direction: column;
  align-items: center; gap: 16px; color: var(--onnavy); }
.feedend__line { font-size: 18px; font-weight: 700; margin: 0; }
.feed__cue-chev { color: var(--yellow); animation: cuebounce 1.4s ease-in-out infinite; }
@keyframes cuebounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }

/* first-run, any entry path: a louder one-time "see the other ideas" prompt with a
   down chevron. Steps aside into the quiet "next idea" cue after the first scroll. */
.feed__cue-first { display: none; }
.feed__cue-chev--down { display: none; }
.feed__cue.is-first { bottom: 16px; }
.feed__cue.is-first .feed__cue-next,
.feed__cue.is-first .feed__cue-chev--up { display: none; }
.feed__cue.is-first .feed__cue-chev--down { display: block;
  animation: cuebounce-down 1.4s ease-in-out infinite; }
.feed__cue.is-first .feed__cue-first { display: block; font-size: 16px; margin-bottom: 4px;
  background: rgba(0, 0, 0, .58); color: #fff; padding: 9px 18px; border-radius: 999px; }
@keyframes cuebounce-down { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(6px); } }

/* ---- your pick budget, top-right ---- */
.picks:empty { display: none; }   /* the no-votes placeholder keeps its id, not its box */
.picks { display: inline-flex; align-items: center; gap: 8px; text-decoration: none;
  color: var(--black); background: var(--cream-2); border: 2px solid var(--black);
  box-shadow: 2px 2px 0 var(--black); padding: 6px 10px; white-space: nowrap; }
.picks__dots { display: inline-flex; gap: 4px; }
.picks__dots span { width: 11px; height: 11px; border: 2px solid var(--black); border-radius: 50%; }
.picks__dots span.is-on { background: var(--magenta); border-color: var(--magenta); }
.picks__label { font-size: 12px; font-weight: 700; }
.picks.picks-over { background: var(--black); color: var(--cream); }
.picks.picks-over .picks__dots span { border-color: var(--cream); }
.picks.picks-over .picks__dots span.is-on { background: var(--magenta); border-color: var(--magenta); }

/* ---- deck (one card at a time; flip for cost) ---- */
.deck__card { position: absolute; inset: 0; perspective: 2600px; }
.deck__flip { position: absolute; inset: 0; transform-style: preserve-3d; transition: transform .45s ease; }
.deck__card.is-flipped .deck__flip { transform: rotateY(180deg); }
.deck__face { position: absolute; inset: 0; backface-visibility: hidden;
  -webkit-backface-visibility: hidden; background: var(--cream); border-radius: 2px;
  box-shadow: var(--soft); padding: 18px; display: flex; flex-direction: column; overflow: hidden; }
.deck__face--back { transform: rotateY(180deg); }
/* webkit promotes positioned children (price, rank, ✓) to their own compositing
   layers, which ignore the face's backface culling and bleed through mirrored —
   each layer needs the flag itself… */
.deck__face .deck__price, .deck__face .deck__tag, .deck__face .deck__check,
.deck__face .gcard__new, .deck__face .deck__cover, .deck__face img, .deck__face video {
  backface-visibility: hidden; -webkit-backface-visibility: hidden; }
/* …and belt-and-suspenders: the turned-away face fades out and goes invisible,
   while the arriving face fades in over the flip's second half — so nothing
   bleeds through and nothing pops in abruptly */
.deck__face { transition: opacity .24s ease .21s, visibility 0s linear .21s; }
.deck__card.is-flipped .deck__face--front,
.deck__card:not(.is-flipped) .deck__face--back { visibility: hidden; opacity: 0;
  transition: opacity .2s ease .05s, visibility 0s linear .25s; }
/* cover image + price, full-bleed at the top of the front */
/* the feed card holds a fixed shape: clamp a long pitch so the actions stay put.
   Targets .card__body, not .muted — the front card's story paragraphs read in
   real ink now (see .card__body above), .muted no longer lives here. */
.deck__face--front p.card__body { display: -webkit-box; -webkit-line-clamp: 5; -webkit-box-orient: vertical;
  overflow: hidden; }
/* the "Read more" that sits under a clamped pitch — a quiet raspberry tap */
.readmore { align-self: flex-start; display: inline-flex; align-items: center; gap: 5px;
  margin-top: 8px; padding: 4px 0; background: none; border: 0; cursor: pointer; font: inherit;
  font-size: 12px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--magenta); }
.readmore[hidden] { display: none; }  /* author display beats UA [hidden] default — restore it explicitly */
/* Read more → the fixed card becomes its own scroll: the full pitch, then who it's
   for, the budget link and the vote, all reachable. At the top/bottom edge the scroll
   hands back to the feed, so the next idea is still one swipe away. Bigger type
   (accessibility) is exactly when this matters — nothing gets cut off anymore. */
.deck__face--front.is-expanded { overflow-y: auto; -webkit-overflow-scrolling: touch;
  scrollbar-width: none; }
.deck__face--front.is-expanded::-webkit-scrollbar { display: none; }
.deck__face--front.is-expanded p.card__body { display: block; -webkit-line-clamp: unset; overflow: visible; }
.deck__face--front.is-expanded .deck__cover { height: 116px; }   /* trade some thumb for words */
.deck__face--front.is-expanded .spacer { flex: 0 0 12px; }       /* no big gap once scrolling */
.deck__cover { position: relative; margin: -18px -18px 16px; height: 156px; overflow: hidden;
  border-bottom: 2.5px solid var(--black); background: var(--sand); }
.deck__cover img { width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .5s ease; }
.flatcard:hover .deck__cover img, .deck__card:hover .deck__cover img { transform: scale(1.04); }
.deck__tag { position: absolute; top: 12px; left: 12px; z-index: 3; }
/* the cover seal — a starburst sticker (NEW, else rank) so it pops and never collides */
.seal { position: absolute; top: 10px; left: 10px; z-index: 4; width: 78px; height: 78px;
  border-radius: 50%; background: var(--black); color: var(--cream); border: 2px solid var(--black);
  box-shadow: inset 0 0 0 3px rgba(241, 240, 235, .92), 2px 4px 11px rgba(0, 0, 0, .35);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding-top: 5px; font-weight: 800; font-size: 22px; line-height: 1; letter-spacing: .02em;
  text-transform: uppercase; transform: rotate(-8deg); }
.seal small { font-size: 8.5px; font-weight: 800; letter-spacing: .08em; margin-bottom: 2px; opacity: .92; }
.seal b { color: var(--yellow); }
/* the price is a tag stamped onto the corner, not a button glued to the frame:
   inset off the edge, a hard border on every side, an offset shadow, a slight
   tilt — reads as stuck on, not merged with the card's own chrome */
.deck__price { position: absolute; right: 10px; bottom: 10px; z-index: 3; background: var(--magenta);
  color: #fff; font-weight: 800; font-size: 17px; padding: 6px 13px;
  border: 2px solid var(--black); box-shadow: 2px 2px 0 var(--black); transform: rotate(-2.5deg); }
.deck__backers { margin-top: 12px; font-weight: 700; font-size: 14px; color: var(--teal); }

/* no photo → the category is the color: one quiet field, cut by planes of its
   own light and shadow — never a second pigment */
.deck__cover--bare { height: 104px; }
.deck__cover--place   { background: var(--red); }
.deck__cover--event   { background: var(--yellow); }
.deck__cover--service { background: var(--green); }
.deck__cover--other   { background: var(--magenta); }
.deck__cover--bare::before { content: ""; position: absolute; left: -28px; top: -38px;
  width: 140px; height: 140px; background: rgba(246, 245, 242, .16); transform: rotate(-4deg);
  clip-path: polygon(0 14%, 100% 0, 88% 100%, 8% 86%); }
.deck__cover--bare::after { content: ""; position: absolute; right: -24px; bottom: -44px;
  width: 118px; height: 118px; background: rgba(28, 23, 20, .16); transform: rotate(3deg);
  clip-path: polygon(14% 4%, 100% 0, 90% 92%, 0 100%); }
.deck__cover--bare .deck__tag { top: auto; bottom: 12px; }

/* who's behind it — overlapping circles, photo or initials */
.avatarstack { display: flex; align-items: center; }
.avatar--sm { flex: 0 0 auto; width: 22px; height: 22px; border: 1.5px solid var(--black);
  border-radius: 50%; background: var(--cream-2); color: var(--black); font-size: 8px;
  font-weight: 800; line-height: 1; display: inline-flex; align-items: center; justify-content: center;
  object-fit: cover; }
.avatarstack .avatar--sm:not(:first-child) { margin-left: -7px; }
.avatar--more { background: var(--black); color: var(--cream); }
.avatar--img { object-fit: cover; }

/* a file picker that sits inside its row — the label is the control, the input hides */
.filebtn { font-size: 13px; font-weight: 700; text-decoration: underline;
  text-decoration-color: var(--magenta); text-decoration-thickness: 2px; cursor: pointer; }
.filebtn input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; }
.deck__actions { margin-top: 12px; }
.deck__share { text-align: center; margin: 12px 0 0; font-size: 13px; }
.sharebtn { display: inline-flex; align-items: center; justify-content: center; width: 42px;
  height: 42px; border: 2px solid var(--black); color: var(--black); background: var(--cream-2);
  box-shadow: 2px 2px 0 var(--black); transition: transform .08s ease, box-shadow .12s ease; }
.sharebtn:hover { transform: translate(-2px, -2px); box-shadow: 4px 4px 0 var(--black); }
button.tap { border: 0; background: none; padding: 0; cursor: pointer; }
/* the flip button says what it does — one stable spot on both faces */
.flipbtn { display: inline-flex; align-items: center; justify-content: center; flex: 1;
  gap: 8px; height: 42px; padding: 0 13px;
  border: 2px solid var(--black); color: var(--black); background: var(--cream-2);
  font-family: var(--body); font-size: 13px; font-weight: 700; cursor: pointer;
  box-shadow: 2px 2px 0 var(--black); transition: transform .08s ease, box-shadow .12s ease; }
.flipbtn:hover { transform: translate(-2px, -2px); box-shadow: 4px 4px 0 var(--black); }
.flipbtn--share { background: var(--yellow); text-decoration: none; }

/* PICKED — the recessed face, dimmed cover, and corner check mark the state */
.deck__check { position: absolute; top: 13px; right: 13px; z-index: 3; width: 30px; height: 30px;
  background: var(--green); color: #fff; font-weight: 800; display: none;
  align-items: center; justify-content: center; border: 2px solid var(--black); }
.deck__card.is-backed .deck__check { display: flex; }
.deck__card.is-backed .deck__face { background: #E5EDDC; }   /* a light green wash = voted */
.deck__card.is-backed .deck__cover img { filter: grayscale(.45); opacity: .78; }
.deck__card.is-backed .deck__backbtn { background: var(--green); color: #fff; }

/* the list — the full ranked set of cards, tappable, pickable in place */
.flatcard { display: block; position: relative; background: var(--cream); border-radius: 2px;
  padding: 18px; text-decoration: none; overflow: hidden; box-shadow: var(--soft);
  transition: transform .14s ease, box-shadow .18s ease; }
a.flatcard:hover, .gcard:hover > a.gcard__open { text-decoration: none; }
.gcard { margin-bottom: 24px; }
.gcard:hover { transform: translateY(-3px); box-shadow: var(--soft-lift); }
/* NEW rides the top-left corner as a sash — the card's overflow clips its ends */
.gcard__new { position: absolute; top: 9px; left: -42px; z-index: 4; width: 132px;
  transform: rotate(-45deg); text-align: center; padding: 4px 0;
  background: var(--magenta); color: #fff; font-weight: 800; font-size: 11px;
  letter-spacing: .1em; }
.gcard__open { display: block; text-decoration: none; color: inherit; }
.gcard__actions { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-top: 14px; }
.gcard__actions .tap { white-space: nowrap; }
/* the pick button carries a long label — keep it on one line, sized to fit the card */
.deck__backbtn { white-space: nowrap; font-size: 13.5px; padding: 11px 14px; }
/* picked in the list: the slanted shade + recessed face are the signal */
.gcard .deck__check { display: none; }
.gcard.is-backed { background: #E5EDDC; }   /* a light green wash = voted */
.gcard.is-backed .deck__cover img { filter: grayscale(.45); opacity: .78; }
.gcard.is-backed .deck__backbtn { background: var(--green); color: #fff; }

/* ---- next steps: a timeline, sharing last ---- */
.timeline { list-style: none; }
.tl { position: relative; display: flex; gap: 14px; padding-bottom: 20px; }
.tl:last-child { padding-bottom: 0; }
.tl::before { content: ""; position: absolute; left: 14px; top: 30px; bottom: -2px; width: 2px;
  background: var(--black); }
.tl:last-child::before { display: none; }
.tl__dot { flex: 0 0 auto; width: 30px; height: 30px; border: 2px solid var(--black);
  border-radius: 50%; display: flex; align-items: center; justify-content: center;
  font-weight: 800; background: var(--cream); z-index: 1; }
.tl--done .tl__dot { background: var(--green); color: #fff; }
.tl--now .tl__dot::after { content: ""; width: 11px; height: 11px; border-radius: 50%;
  background: var(--yellow); border: 2px solid var(--black); }
.tl--next .tl__dot { border-style: dashed; }
.tl__body { display: flex; flex-direction: column; gap: 2px; padding-top: 3px; }

/* Pitch tab's two-beat rail — simpler than the shared timeline: two connected dots,
   a grey connector. The current beat is a solid yellow dot; in the vote phase the
   past beat becomes a hollow yellow ring (both yellow, only "now" filled); anything
   still ahead is solid grey. */
.phaserail .tl::before { background: var(--slate); }
.phaserail .tl__dot { border: 0; background: var(--slate); }
.phaserail .tl__dot::after { display: none; }
.phaserail .tl--now .tl__dot { background: var(--yellow); }
.phaserail .tl--done .tl__dot { background: transparent; box-shadow: inset 0 0 0 3px var(--yellow); }
.phaserail .tl__body > strong { font-family: var(--display); font-weight: 400; font-size: 24px; line-height: 1.08; }
.phaserail__date { align-self: flex-start; margin-top: 7px; padding: 4px 10px;
  font-family: var(--display); font-size: 16px; line-height: 1; color: var(--cream);
  background: var(--black); border: 2px solid var(--black); }

/* Fresh Pitch tab fills the screen so the browse fallback sits at the very bottom,
   well clear of the pitch CTA up in the rail. */
.minefresh { flex: 1; display: flex; flex-direction: column; }
.minefresh__browse { margin-top: auto; align-self: center; padding-top: 20px; }

/* the fresh-pitch tab's bottom liveness band: real ideas already in, fading
   one at a time — its own dark strip below the browse link, clearly not
   another card stacked on top of it */
.pitchbanner { margin-top: 14px; padding: 14px 16px; border-top: 2px solid var(--yellow);
  background: var(--navy-2); }
.pitchbanner__label { display: block; margin-bottom: 6px; font-size: 12px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--slate); }
.pitchbanner__stage { position: relative; height: 24px; overflow: hidden; }
.pitchbanner__item { position: absolute; inset: 0; opacity: 0;
  font-family: var(--display); font-size: 17px; line-height: 24px; color: var(--onnavy);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: opacity 500ms ease; }   /* 500ms matches the rotator's fade value */
.pitchbanner__item.is-visible { opacity: 1; }

/* in the list, truncate the body so cards stay compact (full text in the feed) */
.gcard .gcard__open p.muted { display: -webkit-box; -webkit-line-clamp: 3;
  -webkit-box-orient: vertical; overflow: hidden; }

/* ---- propose: one question per screen, full height, speak or type ---- */
.qstep { display: flex; flex-direction: column; flex: 1; min-height: calc(100dvh - 64px); }
/* The whole chrome is one quiet row — Back, the amber strip, × — so the
   question and the forming ticket own the screen. No page label, no step map:
   the Pitch tab teaches the shape before anyone gets here. */
.pitchbar { display: flex; align-items: center; gap: 16px;
  padding: 2px 0 12px; margin-bottom: 8px; }
.pitchbar__back { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; line-height: 1; font-weight: 300; text-decoration: none;
  text-transform: uppercase; letter-spacing: .08em; color: var(--slate); }
.pitchbar__back:hover { color: var(--onnavy); }
.pitchbar__x { background: none; border: 0; padding: 0; cursor: pointer; line-height: 1;
  font-size: 26px; font-weight: 300; color: var(--slate); margin-left: auto; }
.pitchbar__x:hover { color: var(--onnavy); }
/* the strip: one segment per question, marigold through where you are — all
   amber, no outline states. Quiet digital chrome, not paper. */
.wz-strip { display: flex; gap: 5px; list-style: none; flex: 1; max-width: 190px;
  margin: 0 auto; }
.wz-strip__seg { flex: 1; height: 3px; border-radius: 999px;
  background: rgba(219, 151, 42, .18); }
.wz-strip__seg.is-lit { background: var(--yellow); }
.wz-strip__seg.is-now { transform-origin: left; animation: wz-fill .3s ease-out .1s backwards; }
@keyframes wz-fill { from { transform: scaleX(0); } }
/* The way forward, on every step: the reader's own floating pills, pinned over
   the paper the way Prev/Next sit over a flyer while voting. Next carries the
   marigold so the next move is the brightest thing on screen. */
.wz-pills { position: fixed; left: 50%; transform: translateX(-50%); z-index: 12;
  bottom: calc(16px + env(safe-area-inset-bottom));
  display: flex; gap: 8px; align-items: center; }
.wz-pills input, .wz-pills button {
  font: inherit; font-size: 14px; font-weight: 700; border-radius: 999px;
  padding: 12px 20px; cursor: pointer; border: 1px solid rgba(28,23,20,.1);
  background: rgba(246, 245, 242,.55); color: var(--black);
  box-shadow: 0 10px 26px rgba(0,0,0,.18);
  backdrop-filter: blur(14px) saturate(1.05); -webkit-backdrop-filter: blur(14px) saturate(1.05);
  transition: opacity .16s ease; }
.wz-pills .wz-pills__go { background: var(--yellow); border-color: rgba(28,23,20,.2); padding: 12px 26px; }
.wz-pills .is-waiting { opacity: .4; cursor: default; box-shadow: none; }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .wz-pills input, .wz-pills button { background: rgba(246, 245, 242,.94); }
}
/* room under the last thing on the page so the pills never cover it. On the
   form, not the body: the body is the flex child that stretches, so padding
   there reserves nothing when the content is short. */
.qstep { padding-bottom: calc(84px + env(safe-area-inset-bottom));
  /* the ballot's own measurements, hoisted so what sits under the paper can
     line itself up with where the paper starts breaking apart */
  --dissolve: 96px; --bite: 3px; --pitch: 10px; }
/* the flyer step's paper carries its own clearance and runs to the very bottom —
   including past the app column's own bottom padding, which would otherwise
   leave a navy strip under the sheet */
.qstep--sheet { padding-bottom: 0; }
.qstep--sheet .qstep__body { padding-bottom: 0; }
.app:has(.qstep--sheet) { padding-bottom: 0; }

/* the same row, in ink, when the step runs full-bleed on paper */
.pitchbar--paper { padding-top: calc(10px + env(safe-area-inset-top)); }
.pitchbar--paper .pitchbar__back, .pitchbar--paper .pitchbar__x { color: var(--stone); }
.pitchbar--paper .pitchbar__back:hover, .pitchbar--paper .pitchbar__x:hover { color: var(--black); }
.pitchbar--paper .wz-strip__seg { background: rgba(219, 151, 42, .28); }
.pitchbar--paper .wz-strip__seg.is-lit { background: var(--yellow); }
.qstep__dots { display: inline-flex; gap: 6px; }
.qstep__dots span { width: 12px; height: 12px; border: 2px solid var(--onnavy);
  border-radius: 50%; opacity: .55; }
.qstep__dots span.is-on { background: var(--magenta); border-color: var(--magenta); opacity: 1; }
.qstep__body { display: flex; flex-direction: column; flex: 1; margin-top: 12px;
  animation: wz-arrive .32s cubic-bezier(.22, .61, .36, 1) both; }
/* each step arrives like the next length of the strip sliding up into place —
   a small settle, not a scene change */
@keyframes wz-arrive { from { opacity: 0; transform: translateY(10px); } }
/* the reassurance line lost its .small floor when .muted went from a caption
   to real body text — a touch more room before the field it's introducing,
   now that it can run two or three lines instead of one */
.qstep__body p.muted { margin-bottom: 14px; }
.qstep__q { font-family: var(--display); font-size: 30px; font-weight: 400; line-height: 1.08;
  margin-bottom: 10px; }
/* the back half of the same question — same size, same line, marigold, so it
   reads as one ask finishing rather than a second question starting */
.qstep__q-tail { color: var(--yellow); }
.qstep__input { flex: 1; width: 100%; border: 0; margin-top: 18px; padding: 14px;
  font: inherit; font-size: 18px; resize: none; color: var(--onnavy);
  background: rgba(241, 240, 235, .05); border-radius: 3px; transition: background .12s ease; }
.qstep__input::placeholder { color: rgba(156, 173, 194, .75); }
.qstep__input:focus { outline: none; background: rgba(241, 240, 235, .12); }
/* the who-step's rotating placeholder: the field owns the 18px top margin (not
   the textarea) so the overlay's top:14px lines up with the real placeholder's
   text start with no guesswork about collapsed margins. */
.qstep__field { position: relative; margin-top: 18px; }
.qstep__field .qstep__input { margin-top: 0; }
.qstep__rotorstage { position: absolute; top: 14px; left: 14px; right: 14px;
  font-size: 18px; line-height: 1.5; color: rgba(156, 173, 194, .75);
  pointer-events: none; opacity: 0; transition: opacity .5s ease; }
.qstep__rotorstage.is-visible { opacity: 1; }
/* the instant there's real text, the textarea's own placeholder is gone (native
   :placeholder-shown), so the rotating overlay sitting on top of it goes too */
textarea.qstep__input:not(:placeholder-shown) ~ .qstep__rotorstage { display: none; }
.qstep__foot { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; }
/* Previous + the primary submit share one row — Previous stays a quiet .tap,
   the primary button keeps its full visual weight */
.qstep__foot-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.qstep__foot-row .btn { width: auto; flex: 1; }
.qstep__prev { flex: 0 0 auto; color: var(--slate); }
/* Previous is now a submit (it banks the step before navigating back), same
   native-chrome reset booth__skip already needed for the same reason. */
input.qstep__prev { background: none; border: 0; padding: 0; cursor: pointer; }

/* The review screen's primary action sticks to the foot so "Send it in" is never lost
   below the tall editable card. The gradient fades the card into the navy behind it. */
.reviewfoot { position: sticky; bottom: 0; z-index: 6; margin-top: 14px;
  padding: 12px 0 calc(14px + env(safe-area-inset-bottom));
  background: linear-gradient(to top, var(--navy) 70%, rgba(10, 21, 43, 0)); }
.reviewfoot .btn { margin: 0; }

/* the character floor's live counter, sitting just above the Next button */
.charcount { align-self: flex-end; min-height: 16px; }

/* the video / photo capture step — a big tap-target that opens the camera, swapped
   for a live preview once something's recorded or chosen */
.capture__file { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; }
.capture { margin-top: 22px; }
.capture__btn { display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 12px; width: 100%; min-height: 200px; border: 2px dashed var(--slate); background: none;
  color: var(--onnavy); font: inherit; font-weight: 700; font-size: 16px; cursor: pointer; }
.capture__icon { width: 56px; height: 56px; border-radius: 50%; border: 2px solid var(--onnavy);
  display: flex; align-items: center; justify-content: center; }
.capture__icon--video::after { content: ""; width: 22px; height: 22px; border-radius: 50%;
  background: var(--red); }
.capture__icon--photo::after { content: "+"; font-size: 30px; line-height: 1; }
.capture__preview { display: none; }
.capture--has .capture__btn { display: none; }
.capture--has .capture__preview { display: block; }
/* foot: skip (outline) until a photo's added, then the prominent continue */
.capture__continue { display: none; }
.capture--has .capture__continue { display: block; }
.capture--has .capture__skip { display: none; }
.capture__preview video, .capture__preview img { width: 100%; border: 2px solid var(--black);
  box-shadow: 3px 3px 0 var(--black); background: var(--navy-2); aspect-ratio: 1 / 1; object-fit: cover; }
.capture__redo { display: block; margin: 10px auto 0; }

/* the pitch video — a mini photo booth: live camera, one record button, playback */
.booth { position: relative; margin-top: 16px; width: 100%; aspect-ratio: 3 / 4;
  border: 2px solid var(--onnavy); background: #05060B; overflow: hidden; }
.booth__live, .booth__play { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; background: #05060B; }
.booth__play { display: none; }
.cam--done .booth__live { display: none; }
.cam--done .booth__play { display: block; }
/* Mirror the live selfie preview so recording feels like a mirror. The saved file is
   flipped server-side (VideoNormalizeJob) to match, so attached playback is NOT mirrored
   here — only a freshly-recorded local clip is, via .booth__play--mirror set in JS. */
.booth__live { transform: scaleX(-1); }
.booth__play--mirror { transform: scaleX(-1); }
.booth__timer { position: absolute; top: 12px; left: 12px; z-index: 3; opacity: 0;
  font-weight: 700; font-size: 13px; color: #fff; background: rgba(0, 0, 0, .55); padding: 3px 9px; }
.cam--recording .booth__timer { opacity: 1; }
.booth__rec { position: absolute; bottom: 18px; left: 50%; transform: translateX(-50%); z-index: 3;
  width: 66px; height: 66px; border-radius: 50%; border: 3px solid #fff; background: rgba(0, 0, 0, .15);
  cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; }
.booth__rec::after { content: ""; width: 46px; height: 46px; border-radius: 50%; background: var(--red);
  transition: width .15s ease, height .15s ease, border-radius .15s ease; }
.cam--recording .booth__rec::after { width: 26px; height: 26px; border-radius: 5px; }
.booth__rec { display: none; }                       /* hidden until the curtains open */
.cam--open .booth__rec { display: flex; }
.cam--done .booth__rec, .cam--denied .booth__rec { display: none; }
.booth__denied { position: absolute; inset: 0; z-index: 4; display: none; flex-direction: column;
  align-items: center; justify-content: center; gap: 12px; padding: 22px; text-align: center;
  color: var(--onnavy); background: var(--navy-2); }
.cam--denied .booth__denied { display: flex; }
.booth__redo { display: none; margin: 12px auto 0; }
.cam--done .booth__redo { display: block; }
.booth__continue { display: none; }
.cam--done .booth__continue { display: block; }
.cam--done .booth__skip { display: none; }
/* remove rides under the clip — only offered once there's one to take down */
.booth__remove { display: none; margin: 12px auto 0; color: var(--red); }
.cam--done .booth__remove { display: block; }
/* the booth starts closed — red velvet curtains drawn until the user opens it */
.booth__curtains { position: absolute; inset: 0; z-index: 5; display: flex; pointer-events: none; }
.booth__panel { flex: 1; height: 100%; box-shadow: inset 0 0 36px rgba(0, 0, 0, .45);
  background: repeating-linear-gradient(90deg, #6f2418 0 11px, #93331f 11px 23px, #b14025 23px 31px);
  transition: transform .55s cubic-bezier(.7, 0, .25, 1); }
.booth__panel--l { border-right: 2px solid rgba(0, 0, 0, .35); }
.cam--open .booth__panel--l { transform: translateX(-101%); }
.cam--open .booth__panel--r { transform: translateX(101%); }
.booth__open { position: absolute; z-index: 6; top: 50%; left: 50%; transform: translate(-50%, -50%);
  display: flex; align-items: center; gap: 10px; padding: 13px 20px; cursor: pointer;
  background: var(--cream); color: var(--black); border: 2px solid var(--black);
  box-shadow: 3px 3px 0 var(--black); font: inherit; font-weight: 700; font-size: 15px; }
.booth__opendot { width: 16px; height: 16px; border-radius: 50%; background: var(--red); }
.cam--open .booth__open, .cam--done .booth__open, .cam--denied .booth__open { display: none; }

/* the champion picker — multi-select of you + neighbors already in */
.peoplepick { display: flex; flex-direction: column; gap: 10px; margin-top: 22px; }
.peoplepick__opt { display: flex; align-items: center; gap: 12px; padding: 10px 14px;
  border: 2px solid var(--hairline); color: var(--onnavy); cursor: pointer;
  transition: border-color .12s ease, background .12s ease; }
.peoplepick__opt input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.peoplepick__opt:has(input:checked) { border-color: var(--yellow); background: rgba(219, 151, 42, .12); }
.peoplepick__name { font-weight: 700; }
.peoplepick__check { margin-left: auto; color: var(--yellow); font-weight: 800; opacity: 0; }
.peoplepick__opt:has(input:checked) .peoplepick__check { opacity: 1; }

/* the wizard page header + the four-step map of how a pitch gets made */
/* one bounded header section: the page label, then the step row, then a divider */
.pitchhead { margin: 0 0 22px; padding-bottom: 16px; border-bottom: 1px solid var(--hairline); }
.pitchhead__title { font-family: var(--body); font-size: 14px; font-weight: 800; letter-spacing: .11em;
  text-transform: uppercase; color: var(--onnavy); margin: 0 0 14px; }
.steps { display: flex; flex-wrap: wrap; list-style: none; gap: 7px 16px; }
.step { display: flex; align-items: center; gap: 7px; color: var(--slate); opacity: .55; }
.step__num { width: 21px; height: 21px; border-radius: 50%; border: 1.5px solid currentColor;
  display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 11px; }
.step__label { font-size: 12px; font-weight: 700; }
.step.is-done { opacity: 1; }
.step.is-on { opacity: 1; color: var(--yellow); }
.step.is-on .step__num { background: var(--yellow); color: var(--navy); border-color: var(--yellow); }

/* the three Idea questions' own thin sub-progress bar, under the step map: a
   pale-yellow track, raspberry fill for cleared questions, the current one
   marked with a yellow ring so "done" and "now" don't read the same */
.idea-subbar { display: flex; gap: 6px; list-style: none; margin-top: 12px; }
.idea-subbar__seg { flex: 1; height: 4px; border-radius: 999px; background: rgba(219, 151, 42, .22); }
.idea-subbar__seg.is-done { background: var(--magenta); }
.idea-subbar__seg.is-current { background: var(--magenta); box-shadow: 0 0 0 1.5px var(--yellow); }

/* a marigold tail on the adaptive question */
.qhl { color: var(--yellow); }

/* the "already happening" interstitial mock (pending feature — no matching data
   or AI verb yet, see docs/product/existing-efforts-screen-spec.md): a flyer on a wall
   between the problem and idea questions. Acknowledges the need via qstep__q
   (the same slot every other wizard step uses for its question), then each
   existing effort gets its own solid-color banner — a little flyer pinned
   inside the flyer, not a quiet database row — before the close hands the
   mic back to a "Pitch your idea" CTA. */
.efforts { margin-top: 16px; display: flex; flex-direction: column; gap: 10px; }
.efforts__head { display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 2px; }
.sampletag { font-size: 13px; font-weight: 700; letter-spacing: .02em; text-transform: uppercase;
  color: var(--slate); border: 1.5px solid var(--slate); border-radius: 999px; padding: 3px 10px; }
.effort { border-radius: 3px; padding: 11px 14px; box-shadow: var(--soft); }
.effort--corridor { background: var(--cobalt); }
.effort--freshfood { background: var(--teal); }
.effort__name { font-family: var(--display); font-weight: 500; font-size: 19px; line-height: 1.12;
  color: var(--cream); margin-bottom: 4px; }
.effort__desc { font-size: 13px; line-height: 1.35; color: rgba(246, 245, 242, .88); margin-bottom: 8px; }
.effort__time { display: inline-block; font-size: 13px; font-weight: 800; letter-spacing: .03em;
  text-transform: uppercase; color: var(--navy); background: var(--cream); border-radius: 999px;
  padding: 3px 9px; }
/* the close: a plain body-text line (not a headline, not fine print), then the
   primary CTA moves the pitch forward — raspberry, same weight as any other
   step's affirmative action */
.closeline { font-size: 15px; line-height: 1.4; color: var(--onnavy); margin-top: 16px; margin-bottom: 0; }

/* the budget table — one box live (yellow), the rest greyed out */
.budgettable { margin-top: 2px; }
.bt { width: 100%; border-collapse: collapse; border-top: 1px solid var(--hairline); }
.bt__row { border-bottom: 1px solid var(--hairline); }
.bt__name, .bt__amt { padding: 11px 12px; color: var(--onnavy); vertical-align: middle; }
.bt__name { border-right: 1px solid var(--hairline); }   /* the column divider — reads as a table */
.bt__amt { text-align: right; width: 36%; font-variant-numeric: tabular-nums; }
.bt__row.is-done { color: var(--slate); opacity: .5; }
.bt__total { border-bottom: 0; font-weight: 700; }
.bt__total .bt__amt { color: var(--yellow); }
/* cells holding a live input give up their padding so the input fills the whole cell */
.bt__name:has(.bt__in), .bt__amt:has(.bt__in) { padding: 0; }
.bt__in { width: 100%; display: block; font: inherit; font-size: 17px; color: var(--onnavy);
  background: none; border: 0; padding: 11px 12px; -webkit-appearance: none; }
.bt__in--amt { text-align: right; }
.bt__in::placeholder { color: rgba(156, 173, 194, .55); }
.bt__in:disabled { opacity: .3; }
.bt__in:focus { outline: none; }
/* the live box: the whole cell lights up, its own grid lines turned marigold */
.bt__name:has(.bt__in.is-active), .bt__amt:has(.bt__in.is-active) {
  background: rgba(219, 151, 42, .18); box-shadow: inset 0 0 0 2px var(--yellow); }
/* paper variant — the same table on the cream review card (dark lines + ink) */
.bt--paper { border-top-color: var(--line); }
.bt--paper .bt__row { border-bottom-color: var(--line); }
.bt--paper .bt__name { border-right-color: var(--line); }
.bt--paper .bt__name, .bt--paper .bt__amt, .bt--paper .bt__in { color: var(--ink); }
.bt--paper .bt__in::placeholder { color: var(--stone); }
.bt--paper .bt__in:focus { background: rgba(28, 23, 20, .05); }
/* the "add another" row: the way forward, sitting right under the line you just
   filled. It exists only once a line is ready (the controller unhides it), so its
   arrival is itself the cue — "this line's done, here's the next." Tapping anywhere
   on it banks the line; a one-time nudge draws the eye the first moment it appears. */
.bt__row--next { cursor: pointer; animation: bt-nudge .42s ease-out; }
.bt__row--next .bt__name, .bt__row--next .bt__amt { color: var(--yellow); font-weight: 600; }
.bt__next { letter-spacing: .01em; }
.bt__row--next .bt__amt { font-size: 18px; }            /* the ↵ glyph, sized to read as a key */
.bt__row--next:active { background: rgba(219, 151, 42, .14); }   /* press feedback on tap */
@keyframes bt-nudge { from { opacity: 0; transform: translateY(-6px); } }

/* the budget builder — one line at a time, on the navy step (no boxed panel) */
.budgetbuild { margin-top: 20px; }
.budget__row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding: 11px 0; border-bottom: 1px solid var(--hairline); color: var(--onnavy); }
.budget__row .amt { font-variant-numeric: tabular-nums; font-weight: 700; }
.budget__row--total { border-bottom: 0; color: var(--slate); font-weight: 700; }
.budget__row--total .amt { color: var(--yellow); font-size: 18px; }
.budgetbuild__prompt { margin: 22px 0 0; font-family: var(--display); font-size: 21px;
  line-height: 1.12; color: var(--onnavy); }
/* the one live field reuses the question input's single underline, full width */
.budgetbuild .qstep__input { margin-top: 8px; }

/* the page label slides in at the top once the real header scrolls away */
.pagebar { position: fixed; top: 0; left: 0; right: 0; z-index: 59; /* above the top fade-scrim (58) */
  background: var(--navy); padding-top: env(safe-area-inset-top);
  opacity: 0; transform: translateY(-100%); transition: opacity .22s ease, transform .22s ease;
  pointer-events: none; }
.pagebar.is-shown { opacity: 1; transform: none; }
.pagebar__inner { max-width: 480px; margin: 0 auto; padding: 7px 20px 6px;
  display: flex; align-items: center;
  font-family: var(--display); font-size: 20px; line-height: 1.2; color: var(--onnavy); }
.pagebar .soundtoggle { pointer-events: auto; }

/* sort: a quiet inline-pill row between the header paragraph and the sticky
   bar — reuses .chip, but smaller and lighter than the bordered .chips group
   below it, so it reads as a lens on the deck, not another control */
.sortrow { display: inline-flex; gap: 8px; margin-bottom: 14px; }
.sortrow .chip { border-radius: 999px; font-size: 12px; font-weight: 600;
  padding: 5px 13px; opacity: .68; }
.sortrow .chip + .chip { border-left: 0; }
.sortrow .chip:hover, .sortrow .chip.is-on { opacity: 1; }

/* the working controls (filters, the vote budget, sound) ride sticky flush
   beneath the slide-in label while the page scrolls. ~34px tucks it a hair
   under the pagebar's real height (7+6 padding + 24px line ≈ 37px) so the two
   navy bars overlap by a few px — no sub-pixel gap can show between them. The
   safe-area inset is added on top of both, in step. */
.stickyrow { position: sticky; top: calc(34px + env(safe-area-inset-top)); z-index: 44; display: flex; align-items: center;
  gap: 10px; margin: 0 -20px; padding: 10px 20px;
  background: var(--navy); }
.stickyrow .chips { margin-bottom: 0; }
/* At a desk the cards lie landscape and wide, so a sticky opaque strip scrolling
   over them reads as a gap splitting a card. Let it scroll with the page instead. */
@media (min-width: 900px) {
  .stickyrow { position: static; margin: 6px 0 4px; padding: 0; background: none; }
}

/* ---- utility row: profile (and picks) top-right ---- */
.utilrow { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.profile { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  width: 38px; height: 38px; border: 2px solid var(--onnavy); border-radius: 50%;
  color: var(--onnavy); }
.profile.is-on { background: var(--onnavy); color: var(--navy); }

/* settings rows on the night ground — ruled by hairlines, no paper box */
.darklist { margin-top: 6px; }
.darkrow { display: flex; justify-content: space-between; align-items: center; gap: 10px;
  padding: 13px 0; border-bottom: 1px solid var(--hairline); font-size: 14px;
  color: var(--onnavy); }
.darkrow .muted { color: var(--slate); }

/* ---- profile (account) page ---- */
.avatar { flex: 0 0 auto; width: 56px; height: 56px; border: 2px solid var(--onnavy);
  border-radius: 50%; display: inline-flex; align-items: center; justify-content: center;
  font-weight: 800; font-size: 20px; color: var(--onnavy); }
.profile__row { text-decoration: none; color: inherit; }
.profile__toggle { cursor: pointer; }
.profile__toggle input { width: 18px; height: 18px; accent-color: var(--green); }

/* "Bring a neighbor in": a <details> whose <summary class="btn"> should read as
   a plain button, not a bullet — same disclosure-marker suppression as
   details > summary.tap above, just with no ▸/▾ prefix since .btn carries the
   affordance instead. Open state swaps to a pressed look; margin-bottom on the
   summary collapses with .stack's own inline margin-top below it, so the
   revealed share tools get real breathing room without touching that markup. */
.profile__invite-reveal > summary { list-style: none; }
.profile__invite-reveal > summary::-webkit-details-marker { display: none; }
.profile__invite-reveal[open] > summary { background: var(--black); color: var(--cream);
  margin-bottom: 14px; transform: translate(2px, 2px); box-shadow: 1px 1px 0 var(--black); }

/* ---- card meta: who it's for, who's behind it ---- */
.card__meta { margin-top: 13px; display: flex; flex-direction: column; gap: 13px;
  border-top: 1.5px solid var(--line); padding-top: 12px; }
.card__metaline { font-size: 16px; display: flex; align-items: center; flex-wrap: wrap;
  gap: 2px 8px; }
.card__metakey { font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
  font-weight: 700; color: var(--magenta); }
/* the standing badge — a round seal at the card's top-left, poking slightly past it */
.rallywrap { position: relative; }
.standing { position: absolute; z-index: 6; top: 10px; left: 10px;
  width: 78px; height: 78px; border-radius: 50%; padding: 8px; text-align: center;
  background: var(--black); color: var(--cream); border: 2px solid var(--black);
  box-shadow: inset 0 0 0 3px rgba(241, 240, 235, .92), 2px 4px 11px rgba(0, 0, 0, .35);
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 800; line-height: 1.1; letter-spacing: .01em;
  text-transform: uppercase; transform: rotate(-8deg); }

/* the proposer's face rides the Pitched by line */
.metava { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  width: 22px; height: 22px; border-radius: 50%; border: 1.5px solid var(--black);
  background: var(--violet); color: #fff; font-size: 8px; font-weight: 800;
  object-fit: cover; }

/* the payoff line after you pick — grape, the earned color */
.payoff { font-size: 14px; font-weight: 700; margin-top: 11px; color: var(--violet); }
/* on the cream cards (rally, etc.) drop the teal/violet so it isn't a rainbow */
.flatcard .payoff { color: var(--ink); }
.flatcard .deck__backers { color: var(--stone); }

/* the vote gets its own full-width row — the most important thing on the card */
.cardvote { margin-top: 10px; }
.cardvote .inlineform { display: block; }
.cardvote .btn { display: block; width: 100%; }

/* button_to plumbing: keep forms inline so buttons sit in rows */
.inlineform { display: inline-block; }
.inlineform .tap { font: inherit; font-size: 13px; font-weight: 700; }
.btn--auto { width: auto; display: inline-block; padding: 13px 18px; } /* same height as a full-width btn */

/* budget rows that edit in place (the cost step) */
.lineitem--edit { gap: 8px; }
.lineitem--edit input { border: 0; background: none; font: inherit; font-size: 16px; color: var(--ink);
  min-width: 0; padding: 2px 0; }   /* 16px floor so iOS doesn't zoom on focus */
.lineitem--edit input::placeholder { color: var(--stone); }
.lineitem--edit input:focus { outline: none; border-bottom: 2px solid var(--yellow); }
.lineitem--edit input.nm { flex: 1; }
.lineitem--edit input.amt { width: 90px; text-align: right; font-weight: 700; }

/* rally: your pitch is the card, and its next steps ride inside it */
.rallycard { margin-bottom: 22px; }
/* the in-review card is a status glance, not the whole pitch — clamp the case so the
   Edit button stays in view; the full text lives behind it. Targets .card__body,
   not .muted — see the note on .deck__face--front p.card__body above. */
.mine-review p.card__body { display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
.rallycard__status { position: absolute; top: 12px; right: 12px; z-index: 4; }
.rallyblock__head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 10px; }
.rallyblock__title { font-size: 18px; flex: 1; min-width: 0; }
.rallyblock__head .pill { flex: 0 0 auto; }

/* home: the propose door — a paper invitation with a folded marigold corner */
.homecta { position: relative; background: var(--cream); color: var(--ink); border-radius: 2px;
  box-shadow: var(--soft); padding: 18px; overflow: hidden;
  --t-text: var(--ink); --t-muted: var(--stone); --t-head: var(--black); }
.homecta::after { content: ""; position: absolute; top: 0; right: 0; width: 26px; height: 26px;
  background: var(--yellow); clip-path: polygon(0 0, 100% 0, 100% 100%); }
.homecta strong { font-family: var(--display); font-size: 21px; font-weight: 500; }

/* ---- staged entrance: sections rise into place on load ---- */
.rise { animation: rise .7s cubic-bezier(.22, .8, .3, 1) both; }
.rise-2 { animation-delay: .14s; }
.rise-3 { animation-delay: .28s; }
@keyframes rise { from { opacity: 0; transform: translateY(16px); } }

/* ===== INTRO (Act 0) — a four-phase picture book in cut paper =====
   One stage per beat; its pieces are carried in and out, stop-motion, as the
   beat arrives and leaves, and the highlighter marks the word that matters.
   Desktop sets the stage beside the words; mobile stacks stage over words. */
.beat { min-height: 86dvh; display: flex; flex-direction: column; justify-content: center;
  padding: 40px 0; }
.beat:first-child { padding-top: 24px; }
.beat--cta { min-height: 80dvh; text-align: center; }
.beat--cta .small { text-align: center; }
.beat__big { font-family: var(--display); font-size: 38px; font-weight: 400; line-height: 1.02;
  letter-spacing: -.01em; color: var(--onnavy); margin-bottom: 14px; text-wrap: balance; }
.beat .lede, .act__step .lede { color: #C3CFDC; font-size: 18px; }
.beat .lede strong { color: var(--yellow); font-weight: 700; }
.beat__cue { margin-top: 22px; font-size: 11px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--slate);
  animation: cuebounce 1.6s ease-in-out infinite; }


@media (min-width: 860px) {
  /* the intro breaks out of the app's phone column and plays full and grand */
  .app--wide { max-width: none; padding-left: 0; padding-right: 0; }
  .beat { display: grid; grid-template-columns: 1fr 1fr; align-items: center;
    gap: 6vw; max-width: 1180px; margin: 0 auto; padding: 8vh 48px; }
  .beat .say { max-width: 32rem; }
  .beat__big { font-size: clamp(46px, 5vw, 70px); }
  .beat .lede, .act__step .lede { font-size: 20px; }
  .beat--cta { display: flex; max-width: 560px; }   /* the live widget reads better in one narrow column */
  .beat--cta .say, .beat--cta .well, .beat--cta .wellhead { width: 100%; }
}
@media (min-width: 1200px) { .beat { max-width: 1320px; } }

/* the text: each line rises in turn as the beat settles */
.is-armed .say > * { opacity: 0; transform: translateY(16px);
  transition: opacity .6s ease, transform .6s ease; }
.is-armed .say > *:nth-child(2) { transition-delay: .12s; }
.is-armed .say > *:nth-child(3) { transition-delay: .24s; }
.is-armed.is-seen .say > * { opacity: 1; transform: none; }

/* the highlighter: a marker swipe paints across the word as the beat lands */
.hl { background-image: linear-gradient(transparent 56%, var(--hl, rgba(219,151,42,.6)) 56%);
  background-repeat: no-repeat; background-size: 0% 100%; background-position: 0 0;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
  padding: 0 .05em; transition: background-size .55s cubic-bezier(.6, 0, .2, 1) .35s; }
.is-armed.is-seen .hl { background-size: 100% 100%; }
.hl--teal    { --hl: rgba(42, 114, 104, .62); }
.hl--green   { --hl: rgba(46, 102, 73, .6); }
.hl--magenta { --hl: rgba(154, 51, 84, .62); }

/* ---- the stage: a photographic cut-out plays here, paper shapes around it ---- */
.stage { position: relative; margin: 0 auto 26px; max-width: 380px; }
@media (min-width: 860px) { .stage { margin-bottom: 0; max-width: 100%; } }

/* the hero: a paper-edged photo cut-out that pops in and breathes, Vox-style */
.cut { display: block; width: 100%; height: auto; position: relative; z-index: 1;
  transition: opacity .5s ease, transform .55s cubic-bezier(.34, 1.45, .5, 1); will-change: transform; }
.cut--building  { rotate: -1.5deg; }
.cut--open      { rotate: 2deg; max-width: 74%; margin: 0 auto; }
.cut--neighbors { rotate: -1deg; }
.cut--seedlings { rotate: 1.5deg; }
.is-armed .cut { opacity: 0; transform: translateY(34px) scale(.9); }
.is-armed.is-seen .cut { opacity: 1; transform: none; }
.is-seen .cut { animation: cutbob 5.5s ease-in-out 1.2s infinite; }
@keyframes cutbob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-7px); } }

/* the paper accents: construction-paper shapes that pop in behind the cut-out */
.paper { position: absolute; z-index: 0;
  transition: opacity .45s ease, transform .55s cubic-bezier(.34, 1.5, .5, 1); }
.is-armed .paper { opacity: 0; transform: scale(.2); }
.is-armed.is-seen .paper { opacity: 1; transform: none; }
.is-armed.is-seen .paper--1 { transition-delay: .2s; }
.is-armed.is-seen .paper--2 { transition-delay: .34s; }
.paper.sun { width: 17%; aspect-ratio: 1; border-radius: 50%; background: var(--yellow);
  right: 3%; top: 0; border: 2.5px solid var(--black); box-shadow: 3px 3px 0 var(--black); }
.paper.tri { width: 26%; aspect-ratio: 1; background: var(--magenta); left: -3%; bottom: 6%;
  clip-path: polygon(0 100%, 0 0, 100% 100%); rotate: -4deg;
  filter: drop-shadow(3px 4px 0 rgba(28, 23, 20, .5)); }
.paper.coin { width: 15%; aspect-ratio: 1; border-radius: 50%; background: var(--yellow);
  border: 2.5px solid var(--black); box-shadow: 3px 3px 0 var(--black); }
.paper.coin::after { content: ""; position: absolute; inset: 22%; border-radius: 50%;
  border: 2px solid var(--black); }
.paper.coin--a { left: 1%; top: 12%; }
.paper.coin--b { right: 2%; bottom: 12%; rotate: 10deg; }
/* ===== the building act — a self-contained, viewport-height story. The image panel
   stays put and morphs; the text panel scrolls on its own and fades at its edges.
   State lives in [data-active] on the section; the page itself doesn't scroll. ===== */
.app--intro { height: 100dvh; max-width: none; overflow: hidden; padding: 0; }

.act { position: relative; height: 100dvh; display: grid; gap: 4vw; align-items: center;
  grid-template-columns: 1fr 1fr; max-width: 1320px; margin: 0 auto; padding: 0 48px; }
.act__stage { position: relative; height: 100%; display: flex; align-items: center; justify-content: center; }

/* the text panel: its own free scroll, gradient-faded top and bottom */
.act__scroll { height: 100%; overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(transparent 0, #000 17%, #000 83%, transparent 100%);
          mask-image: linear-gradient(transparent 0, #000 17%, #000 83%, transparent 100%); }
.act__scroll::-webkit-scrollbar { display: none; }
.act__step { min-height: 100%; display: flex; flex-direction: column;
  justify-content: center; max-width: 32rem; }

/* mobile: the same thing stacked — image on top, text scrolling beneath it */
@media (max-width: 859px) {
  .act { grid-template-columns: 1fr; grid-template-rows: 40dvh 54dvh; gap: 0; padding: 6dvh 22px 0; }
  .act__stage { height: 40dvh; }
  .act__scroll { height: 54dvh;
    -webkit-mask-image: linear-gradient(transparent 0, #000 12%, #000 82%, transparent 100%);
            mask-image: linear-gradient(transparent 0, #000 12%, #000 82%, transparent 100%); }
  .act__step { min-height: 56dvh; }
  /* shrink the well graphic so it fits inside the (shorter) image panel.
     extra .act prefix beats the base .actwell rules that come later in the file. */
  .act .actwell .well__col { width: 80px; }
  .act .actwell .well__section { gap: 18px; }
  .act .actwell .well__bar, .act .actwell .aw-proj .well__labels { height: 26dvh; }
  /* sit the graphics around the middle of the (short) image panel — the building
     reads a touch lower since its image carries a ground base. */
  .act .actwell { top: 48%; height: 26dvh; }
  .act .bldg { top: 52%; }
}

/* the stage carries the building, the coins, and the morphing Well — kept square */
.stage--act { position: relative; aspect-ratio: 1 / 1; height: 100%; max-height: 540px;
  width: auto; max-width: 100%; margin: auto; }

/* the building: empty → lit, then it slides left (making room for the coins) and goes */
.bldg { position: absolute; left: 50%; top: 50%; width: 96%; transform: translate(-50%, -50%);
  z-index: 1; transition: opacity .8s ease, transform .9s cubic-bezier(.5, 0, .25, 1); }
.bldg__img { display: block; width: 100%; height: auto; }
.bldg__after { position: absolute; inset: 0; opacity: 0; transition: opacity .9s ease; }
.bldg__before { transition: opacity .9s ease; }
.act[data-active="1"] .bldg__after { opacity: 1; }
.act[data-active="1"] .bldg { transform: translate(-80%, -50%); }   /* slide left, coins spill right */
.act[data-active="2"] .bldg, .act[data-active="3"] .bldg {
  opacity: 0; transform: translate(-94%, -50%); }

/* coins: spill out to the right of the building (step 1), then drop into the well (step 2) */
.coin { position: absolute; left: 50%; top: 34%; width: 11%; height: auto; z-index: 3;
  opacity: 0; transition: opacity .5s ease, transform .8s cubic-bezier(.4, 0, .2, 1); }
.act[data-active="1"] .coin { opacity: 1; }
.act[data-active="1"] .k1 { transform: translate(150%, -10%) rotate(-12deg); }
.act[data-active="1"] .k2 { transform: translate(250%, -70%) rotate(10deg); }
.act[data-active="1"] .k3 { transform: translate(330%, 30%) rotate(16deg); }
.act[data-active="1"] .k4 { transform: translate(210%, 70%) rotate(-6deg); }
.act[data-active="1"] .k5 { transform: translate(320%, -40%) rotate(20deg); }
.act[data-active="2"] .coin { opacity: 0;
  transition: transform .9s cubic-bezier(.4, 0, .2, 1), opacity .3s ease .6s; }
.act[data-active="2"] .k1 { transform: translate(-230%, 150%) rotate(180deg); }
.act[data-active="2"] .k2 { transform: translate(-180%, 178%) rotate(220deg); transition-delay: .08s; }
.act[data-active="2"] .k3 { transform: translate(-260%, 165%) rotate(-200deg); transition-delay: .16s; }
.act[data-active="2"] .k4 { transform: translate(-200%, 188%) rotate(240deg); transition-delay: .24s; }
.act[data-active="2"] .k5 { transform: translate(-250%, 172%) rotate(160deg); transition-delay: .32s; }

/* the Well: ONE graphic, two states crossfading in place — a big vessel + label
   column, identical in both, so nothing shifts when the spots arrive */
.actwell { position: absolute; left: 50%; top: 46%; width: 92%; height: 320px;
  transform: translate(-50%, -50%); z-index: 2; }
/* a big chunky vessel for the story (the app's is a skinny 62px) */
.actwell .well__col { width: 132px; }
.actwell .well__section { gap: 28px; }
.actwell .well__bar { height: 320px; }
.actwell .aw-proj .well__labels { height: 320px; }
/* no drop-shadows on the navy ground — they muddy up. keep the brick frame flat. */
.actwell .well__bar, .actwell .wellbrick::before { box-shadow: none; }
/* the story labels are illustrative, not links — no hover lift, no link cursor */
.actwell .well__label { cursor: default; }
.actwell .well__label:hover .nm { color: var(--onnavy); }
.aw { position: absolute; inset: 0; opacity: 0;
  transition: opacity .6s ease; pointer-events: none; }
.act[data-active="2"] .aw-simple { opacity: 1; }
.act[data-active="3"] .aw-proj   { opacity: 1; pointer-events: auto; }

/* the simple pot: a blue fill that rises in the vessel as the coins drop in */
.awfill { position: absolute; left: 0; right: 0; bottom: 0; height: 0;
  background: repeating-linear-gradient(-45deg, rgba(0, 0, 0, .28) 0 9px, transparent 9px 18px),
    linear-gradient(to top, var(--water-5), var(--water-1));
  transition: height 1.1s cubic-bezier(.3, .8, .3, 1) .15s; }
.act[data-active="2"] .awfill { height: 100%; }

/* the wordmark, top-left, fixed while the story scrolls — all caps, like the app */
.wellmark { position: fixed; top: calc(18px + env(safe-area-inset-top)); left: 24px; z-index: 50; font-family: var(--body);
  font-size: 14px; font-weight: 800; letter-spacing: .2em; text-transform: uppercase;
  color: var(--onnavy); }

/* the "THE WELL" pointer on the simple pot — an arrow pointing back at the vessel,
   not a legend. Lives in the label column and fades out with the simple state. */
.aw-simple .well__labels { justify-content: center; }
.wellptr { display: inline-flex; align-items: center; gap: 10px; }
.wellptr__arrow { position: relative; flex: 0 0 auto; width: 40px; height: 2px;
  background: var(--onnavy); }
.wellptr__arrow::before { content: ""; position: absolute; left: 0; top: 50%; width: 9px; height: 9px;
  border-left: 2px solid var(--onnavy); border-bottom: 2px solid var(--onnavy);
  transform: translateY(-50%) rotate(45deg); }
.wellptr__txt { font-family: var(--body); font-weight: 800; letter-spacing: .16em;
  text-transform: uppercase; font-size: 15px; color: var(--onnavy); }

/* a sticky way back to the app while you read the story (signed-in only) */
.introback { position: fixed; left: calc(18px + env(safe-area-inset-left)); bottom: calc(18px + env(safe-area-inset-bottom)); z-index: 50;
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--yellow); color: var(--navy); font-weight: 700; font-size: 14px; padding: 12px 22px;
  border: 2.5px solid var(--black); border-radius: 999px; box-shadow: 3px 3px 0 var(--black); text-decoration: none;
  transition: opacity .5s ease, transform .5s ease; }
.introback:hover { background: var(--black); color: var(--yellow); }
/* fades out at the last beat, where the in-beat CTA takes over */
.act[data-active="3"] .introback { opacity: 0; transform: translateY(12px); pointer-events: none; }

/* the visitor's gate — the front-runner reads clear, the rest waits */
.gate .well__label { filter: blur(6px); pointer-events: none; user-select: none; }
.gate .well__section:first-child .well__label:first-child { filter: none; pointer-events: auto; }
.gate__note { text-align: center; font-size: 13px; margin-top: 12px; }

/* ---- admin desktop, on its paper sheet ---- */
.reviewrow { border: 2px solid var(--black); border-radius: 2px; background: var(--cream-2);
  margin-top: 12px; }
.reviewrow summary { padding: 12px 14px; cursor: pointer; font-size: 14px; font-weight: 700; }
.reviewrow summary .pill { margin-left: 10px; }
.reviewrow__body { padding: 16px 14px; border-top: 2px solid var(--black); background: var(--cream); }
.adminnav { display: flex; gap: 28px; border-bottom: 2px solid var(--black);
  padding-bottom: 12px; margin: 16px 0 22px; font-size: 17px; }
.adminnav a { text-decoration: none; color: var(--stone); font-weight: 800; position: relative;
  letter-spacing: -.01em; }
.adminnav a:hover { color: var(--black); }
.adminnav a.is-on { color: var(--magenta); }
.adminnav a.is-on::after { content: ""; position: absolute; left: 0; right: 0; bottom: -14px;
  height: 4px; background: var(--magenta); }
.table { width: 100%; border-collapse: collapse; font-size: 14px; margin-top: 12px; }
.table th, .table td { border: 1.5px solid var(--line); padding: 8px 10px; text-align: left;
  vertical-align: top; }
.table th { font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
  background: var(--sand); }
.cols { display: flex; gap: 20px; align-items: flex-start; }
.cols > * { flex: 1; }

/* ---- the cycle hero: the pot as a well, the season as a timeline ---- */
.cyclehero { display: flex; gap: 26px; align-items: center; margin: 16px 0 6px;
  padding: 20px 24px; background: var(--cream); border: 2px solid var(--black);
  border-radius: 2px; box-shadow: 3px 3px 0 var(--black); }
.cyclewell { width: 104px; flex: 0 0 auto; text-align: center; }
.cyclewell__vessel { position: relative; height: 128px; display: flex; align-items: center;
  justify-content: center; background: var(--cream-2); border: 2.5px solid var(--black);
  border-radius: 2px; overflow: hidden; }
.cyclewell__fill { position: absolute; left: 0; right: 0; bottom: 0; background: var(--water-1);
  border-top: 2px solid var(--black); }
.cyclewell__amt { position: relative; z-index: 1; font-weight: 800; font-size: 15px;
  color: var(--black); background: var(--cream); border: 2px solid var(--black);
  border-radius: 2px; padding: 3px 7px; box-shadow: 2px 2px 0 var(--black); }
.cyclewell__cap { margin-top: 8px; font-size: 11px; line-height: 1.3; color: var(--stone); }

.cyclehero__tl { flex: 1; min-width: 0; }
.cyclehero__phase { font-size: 12px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--magenta); margin-bottom: 14px; }
.cycletl__track { position: relative; display: flex; height: 30px; border: 2px solid var(--black);
  border-radius: 2px; }
.cycletl__seg { display: flex; align-items: center; justify-content: center; overflow: hidden;
  font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: #fff;
  border-right: 2px solid var(--black); }
.cycletl__seg:last-child { border-right: 0; }
.cycletl__seg--pitch { background: var(--water-1); }
.cycletl__seg--vote  { background: var(--water-3); }
.cycletl__today { position: absolute; top: -7px; bottom: -7px; width: 3px; background: var(--magenta); }
.cycletl__today span { position: absolute; top: -16px; left: 50%; transform: translateX(-50%);
  font-size: 9px; font-weight: 800; letter-spacing: .08em; color: var(--cream);
  background: var(--magenta); padding: 1px 5px; border-radius: 2px; white-space: nowrap; }
.cycletl__dates { position: relative; height: 34px; margin-top: 8px; }
.cycletl__dates span { position: absolute; top: 0; font-size: 11px; line-height: 1.25;
  color: var(--stone); white-space: nowrap; transform: translateX(-50%); }
.cycletl__dates span:first-child { transform: translateX(0); text-align: left; }
.cycletl__dates span:last-child  { transform: translateX(-100%); text-align: right; }

/* a cycle card in the Cycle tab: editable pot + timeline + dates */
.cyclecard { border: 2px solid var(--black); border-radius: 2px; background: var(--cream);
  box-shadow: 3px 3px 0 var(--black); padding: 16px 18px; margin: 16px 0; }
.cyclecard--now { border-color: var(--magenta); box-shadow: 3px 3px 0 var(--magenta); }
.cyclecard__bar { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.cyclepot { display: block; }
.cyclepot__label { display: block; font-size: 11px; font-weight: 800; letter-spacing: .08em;
  text-transform: uppercase; color: var(--stone); }
.cyclepot__big { font-family: var(--display); font-size: 42px; font-weight: 400; line-height: 1;
  color: var(--black); display: inline-flex; align-items: baseline; }
.cyclepot__input { font: inherit; width: 7ch; padding: 0 0 2px; color: var(--black);
  border: 0; border-bottom: 2px solid var(--black); background: none; -webkit-appearance: none; appearance: none; }
.cyclepot__input::-webkit-inner-spin-button, .cyclepot__input::-webkit-outer-spin-button {
  -webkit-appearance: none; margin: 0; } /* drop the spinner that was eating the width */
.cyclecard__foot { display: flex; gap: 16px; align-items: flex-end; flex-wrap: wrap; margin-top: 18px; }
.cycleterm { flex: 1; min-width: 170px; font-size: 11px; font-weight: 800; letter-spacing: .05em;
  text-transform: uppercase; color: var(--stone); display: flex; flex-direction: column; gap: 5px; }
.cycledates { display: flex; justify-content: space-between; gap: 12px; margin-top: 12px; }
.cycledates label { flex: 1; font-size: 11px; font-weight: 700; letter-spacing: .03em;
  color: var(--stone); display: flex; flex-direction: column; gap: 4px; }
.cycledates input { width: 100%; font: inherit; font-size: 16px; font-weight: 700; padding: 5px 2px; /* 16px floor so iOS doesn't zoom on focus */
  color: var(--black); border: 0; border-bottom: 2px solid var(--black); border-radius: 0;
  background: none; -webkit-appearance: none; appearance: none; }
.cycledates input:focus { outline: none; border-bottom-color: var(--magenta); }
.cyclecard__close { margin-top: 16px; padding-top: 14px; border-top: 2px dashed var(--line); }
.addcycle > summary { list-style: none; cursor: pointer; }
.addcycle > summary::-webkit-details-marker { display: none; }

/* ---- the ideas pipeline: clean stacked lanes ---- */
.lane { margin-top: 24px; }
.lane__head { display: flex; align-items: baseline; gap: 9px; margin-bottom: 4px;
  border-bottom: 2px solid var(--black); padding-bottom: 6px; }
.lane__head h3 { margin: 0; font-size: 15px; }
.lane__count { font-weight: 800; font-size: 13px; color: var(--black);
  background: var(--sand); border: 1.5px solid var(--black); border-radius: 2px; padding: 0 7px; }
.lane--now .lane__head { border-bottom-color: var(--yellow); border-bottom-width: 3px; }
.lane--now .lane__count { background: var(--yellow); }
.lane__empty { padding: 12px 4px; }
.lane--muted { margin-top: 22px; }
.lane--muted > summary { cursor: pointer; list-style: none; }
.lane--muted > summary::-webkit-details-marker { display: none; }
.lane--muted[open] > summary { margin-bottom: 4px; }

.idea { display: flex; align-items: center; gap: 13px; padding: 10px 6px;
  border-bottom: 1.5px solid var(--line); }
.idea:hover { background: var(--cream-2); }
.idea__thumb { flex: 0 0 auto; position: relative; width: 60px; height: 60px; overflow: hidden;
  border: 2px solid var(--black); border-radius: 2px; background: var(--cream-2); }
.idea__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.idea__thumb--place   { background: var(--red); }
.idea__thumb--event   { background: var(--yellow); }
.idea__thumb--service { background: var(--green); }
.idea__thumb--other   { background: var(--magenta); }
/* no photo: decorative blobs like the resident card's bare cover, not a flat square */
.idea__thumb--bare::before { content: ""; position: absolute; left: -14px; top: -18px;
  width: 42px; height: 42px; border-radius: 50%; background: rgba(255, 255, 255, .17); }
.idea__thumb--bare::after { content: ""; position: absolute; right: -12px; bottom: -16px;
  width: 36px; height: 36px; border-radius: 50%; background: rgba(28, 23, 20, .14); }
.idea__rankbadge { position: absolute; left: -1px; bottom: -1px; z-index: 1; background: var(--black);
  color: var(--cream); font-size: 11px; font-weight: 800; padding: 1px 5px; }
.idea__main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px;
  text-decoration: none; color: var(--black); }
.idea__main:hover .idea__title { color: var(--magenta); }
.idea__title { font-weight: 700; font-size: 16px; line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.idea__cost { font-weight: 800; color: var(--black); }
.idea__meta { font-size: 12.5px; color: var(--stone); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.idea__tel { color: var(--magenta); text-decoration: none; }
.idea__wait { color: var(--green); font-weight: 700; }
.idea__actions { flex: 0 0 auto; display: flex; gap: 8px; align-items: center; }
.idea__actions .inlineform { display: inline; }

/* ---- the edit drawer ---- */
body.drawer-open { overflow: hidden; }
.drawer { position: fixed; inset: 0; z-index: 60; display: flex; justify-content: flex-end;
  background: rgba(28, 23, 20, .38); opacity: 0; transition: opacity .18s ease; }
.drawer.is-open { opacity: 1; }
.drawer__panel { width: min(540px, 94vw); height: 100%; overflow-y: auto;
  background: var(--yellow); border-left: 2px solid var(--black);
  box-shadow: -8px 0 0 rgba(28, 23, 20, .18); display: flex; flex-direction: column;
  transform: translateX(24px); transition: transform .18s ease; }
.drawer.is-open .drawer__panel { transform: translateX(0); }
/* yellow IS the "you're editing" signal — the card sits on it as plain paper */
.drawer__head { position: sticky; top: 0; z-index: 1; display: flex; align-items: center;
  justify-content: space-between; gap: 12px; padding: 15px 20px; background: var(--yellow);
  border-bottom: 2px solid var(--black); }
.drawer__head strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.drawer__x { flex: 0 0 auto; background: none; border: 0; cursor: pointer;
  font-size: 18px; line-height: 1; color: var(--black); }
.drawer__x:hover { opacity: .6; }
.drawer__body { padding: 18px 20px 36px; }
.drawer__body .flatcard { border: 2px solid var(--black); box-shadow: 3px 3px 0 var(--black); }
.drawer__knobs { margin: 18px 0; padding-top: 4px; }
.drawer__knobs .field label { color: var(--black); }
.drawer__stage { margin-top: 18px; }

/* the boundary map editor */
.boundarymap__canvas { height: 440px; border: 2px solid var(--black); border-radius: 2px; }
.boundarymap__canvas .leaflet-container { font-family: var(--body); }
.vtx { background: var(--magenta); border: 2px solid #fff; border-radius: 50%;
  box-shadow: 0 1px 4px rgba(28, 23, 20, .5); cursor: grab; }
.vtx:active { cursor: grabbing; }

/* WYSIWYG cover: click the band/photo to set the cover image */
.coveredit { position: absolute; inset: 0; z-index: 3; display: flex; align-items: center;
  justify-content: center; cursor: pointer; background: rgba(28, 23, 20, .28);
  opacity: 0; transition: opacity .14s ease; }
.deck__cover:hover .coveredit, .coveredit:focus-within { opacity: 1; }
.coveredit__hint { font-size: 12px; font-weight: 800; letter-spacing: .04em; color: #fff;
  background: rgba(28, 23, 20, .55); border-radius: 3px; padding: 5px 10px; }
.coveredit__file { position: absolute; inset: 0; opacity: 0; cursor: pointer; }

/* review's lower-page cover-photo edit row: a small thumb + a plain tappable
   label — the same hidden-input-over-label trick as .coveredit above, just
   sitting in the page flow instead of overlaid on a hero band */
.covereditrow { display: flex; align-items: center; gap: 12px; margin-top: 8px; }
.covereditrow__thumb { width: 64px; height: 64px; object-fit: cover; border-radius: 2px;
  border: 1.5px solid var(--line); flex: 0 0 auto; }
.covereditrow__label { position: relative; font-size: 14px; font-weight: 700; cursor: pointer;
  text-decoration: underline; text-decoration-style: dashed;
  text-decoration-color: rgba(28, 23, 20, .4); text-underline-offset: 3px; }
.covereditrow__file { position: absolute; inset: 0; opacity: 0; cursor: pointer; }

/* ---- desktop: same content, desk furniture ----
   Wide screens trade the bottom tab bar for a left sidebar ruled off by one thin
   line. Sidebar and content travel together as one 820px shell, centered:
   220px sidebar + 120px gutter + 480px content column. */
/* the feed's sidebar is desk furniture only — a phone keeps the feed immersive */
.apptabs--desk { display: none; }
@media (min-width: 900px) {
  /* the content column breathes wider at a desk: 220 sidebar + 120 gutter + 560 */
  .app:not(.app--wide) { max-width: 560px; }
  .apptabs { top: 0; right: auto; left: calc((100% - 900px) / 2); width: 220px;
    padding-bottom: 0; border-top: 0; border-right: 1px solid var(--hairline);
    background: none; -webkit-backdrop-filter: none; backdrop-filter: none; }
  .apptabs--desk { display: block; }
  .apptabs__inner { flex-direction: column; max-width: none; padding-top: 34px; }
  .apptab { flex: 0 0 auto; flex-direction: row; justify-content: flex-start; gap: 12px;
    padding: 12px 14px; margin: 0 12px 2px; font-size: 14px; }
  .apptab__icon { width: 20px; height: 20px; }
  /* where you are, said with a block of light, not just a color */
  .apptab.is-on { background: rgba(219, 151, 42, .14); }
  .app--tabbed, .app--desktabs { margin-left: calc((100% - 900px) / 2 + 340px); margin-right: 0;
    padding-bottom: 44px; }
  /* the sticky label is confined to the content column — its bar never crosses the sidebar */
  .pagebar { left: calc((100% - 900px) / 2 + 340px); }
  .pagebar__inner { max-width: 560px; margin: 0; }
  /* with no tab bar below, toasts follow the content column to the bottom edge */
  body:has(.apptabs) #flash { left: calc(50% + 170px);
    bottom: calc(12px + env(safe-area-inset-bottom)); }
}

/* ===== TWO-PHASE: the reel-in-a-card =====
   When an idea has a video, the front face IS the video — bounded by the card
   frame, not the screen. Title bar up top with the proposer's face, caption
   collapsed at the bottom, exactly the reel grammar. Flip still shows the cost:
   the video is the heart, the numbers are the proof. */
/* the whole face is raspberry underneath — the video lies over it and the red
   peeks out at the top, around the video's rounded shoulders */
.deck__face--reel { padding: 0; background: var(--magenta); }
.deck__card.is-backed .deck__face--reel { background: var(--magenta); }
.reel__top { flex: 0 0 auto; display: flex; align-items: center; gap: 10px;
  padding: 11px 14px 9px; color: #fff; }
.reel__ava { flex: 0 0 auto; width: 38px; height: 38px; border-radius: 50%;
  border: 2px solid var(--cream); background: var(--violet); color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 800; }
.reel__who { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.15; }
.reel__name { font-size: 10px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
  opacity: .8; }
.reel__title { font-family: var(--display); font-size: 20px; color: #fff;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.reel__price { flex: 0 0 auto; background: var(--black); color: var(--yellow); font-weight: 800;
  font-size: 14px; padding: 5px 11px; }
/* the video: full bleed for the rest of the card, rounded shoulders up top
   so the red shows from under */
.reel__frame { position: relative; flex: 1; min-height: 0; margin: 0;
  border-radius: 18px 18px 0 0; overflow: hidden; background: var(--black);
  transform: translateZ(0); }   /* webkit: make the radius clip the video layer */
.reel__video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.reel__bottom { position: absolute; left: 0; right: 0; bottom: 0; padding: 48px 14px 14px;
  background: linear-gradient(to top, rgba(28, 23, 20, .85), transparent); color: #fff; }
.reel__bottom .tap { color: #fff; }
.reel__caption { display: block; width: 100%; text-align: left; background: none; border: 0;
  padding: 0; color: #fff; font: inherit; cursor: pointer; }
.reel__caption p { margin: 0; font-size: 14px; line-height: 1.45;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  text-shadow: 0 1px 8px rgba(28, 23, 20, .6); }
.reel__caption.is-open p { display: block; max-height: 38dvh; overflow-y: auto; }
.reel__more { font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  opacity: .75; }
.reel__caption.is-open .reel__more::after { content: " —"; }
.reel__payoff { color: #fff; text-shadow: 0 1px 8px rgba(28, 23, 20, .7); }
/* the picked ✓ drops below the reel's title band, inside the frame */
.deck__face--reel .deck__check { top: 70px; right: 14px; }
/* a video in the gallery cover slot plays the same way, smaller — aimed at the
   face, since neighbor videos are shot tall */
.deck__cover video { width: 100%; height: 100%; object-fit: cover; display: block;
  object-position: 50% 22%; }

/* every list card: a phone stacks the cover over the words (a video earns a
   taller cover, not the whole card); a desk puts the reel-shaped thumb on the
   left edge. Crops lead from the top, where the person is. */
.gcard { padding: 0; }
.gcard__thumb { position: relative; height: 158px; overflow: hidden;
  border-bottom: 2.5px solid var(--black); background: var(--sand); }
.gcard--video .gcard__thumb { height: 266px; }
.gcard__thumb video, .gcard__thumb img { position: absolute; inset: 0; width: 100%;
  height: 100%; object-fit: cover; object-position: 50% 0; }
.gcard__side { min-width: 0; padding: 16px 18px; display: flex; flex-direction: column; }
.gcard__side .card__title { margin-top: 0; }
/* the price holds the card's own bottom-right corner */
.gcard .deck__price { font-size: 14px; padding: 5px 11px; }
.gcard .gcard__actions { padding-right: 88px; justify-content: flex-start; gap: 16px; }
.gcard.is-backed .gcard__thumb video, .gcard.is-backed .gcard__thumb img {
  filter: grayscale(.4); }
/* no photo → the category is the color, same cut-paper planes, stood upright */
.gcard__thumb--place   { background: var(--red); }
.gcard__thumb--event   { background: var(--yellow); }
.gcard__thumb--service { background: var(--green); }
.gcard__thumb--other   { background: var(--magenta); }
.gcard__thumb--noimg::before { content: ""; position: absolute; left: -28px; top: -38px;
  width: 140px; height: 140px; background: rgba(246, 245, 242, .16); transform: rotate(-4deg);
  clip-path: polygon(0 14%, 100% 0, 88% 100%, 8% 86%); }
.gcard__thumb--noimg::after { content: ""; position: absolute; right: -24px; bottom: -44px;
  width: 118px; height: 118px; background: rgba(28, 23, 20, .16); transform: rotate(3deg);
  clip-path: polygon(14% 4%, 100% 0, 90% 92%, 0 100%); }

/* the sound switch: one bare icon — the muted speaker turns into a sounding,
   marigold one when sound is on */
.soundtoggle .soundtoggle__on { display: none; }
.soundtoggle.is-on { color: var(--yellow); }
.soundtoggle.is-on .soundtoggle__on { display: block; }
.soundtoggle.is-on .soundtoggle__off { display: none; }

/* ===== TWO-PHASE: the Pitch tab ===== */
/* a full-screen ask, centered down the page like the wizard's question steps */
.lockup { display: flex; flex-direction: column; justify-content: center; flex: 1;
  min-height: calc(70dvh - 64px); }
/* the note from a person — feedback as a letter, not a system flag */
.notecard { background: var(--cream-2); border: 2px solid var(--black);
  border-left: 7px solid var(--magenta); padding: 13px 14px; font-size: 14px; }
.notecard__sig { margin: 8px 0 0; font-weight: 700; font-size: 12.5px; color: var(--stone); }
/* more than one idea: a short stack, each with its status and one next move */
.stackrow { display: flex; flex-direction: column; gap: 9px; margin-bottom: 14px; }
.stackrow__top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.stackrow__title { font-size: 16px; min-width: 0; }
.stackrow__top .pill { flex: 0 0 auto; }
.stackrow__status { margin: 0; }
.stackrow__actions { display: flex; align-items: center; gap: 16px; margin-top: 2px; }
.stackrow__remove { color: var(--slate); font-weight: 600; }
/* single-draft state: a full-width progress bar (anchored to the card) + actions */
.draftprog { margin: 4px 0 2px; }
.draftprog__head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 6px; }
.draftprog__track { height: 9px; background: var(--cream-2); border: 2px solid var(--black);
  border-radius: 2px; overflow: hidden; }
.draftprog__track span { display: block; height: 100%; background: var(--magenta);
  transition: width .2s ease; }
.draftactions { display: flex; flex-direction: column; gap: 12px; margin-top: 6px; }
.draftactions .btn { align-self: stretch; }          /* Finish: full width */
.draftactions .inlineform { align-self: center; }     /* Toss: a centered quiet link */
/* foot-of-the-deck invitation to pitch */
.pitchcta { text-align: center; margin-top: 24px; }
.pitchcta__q { display: block; font-size: 16px; margin-bottom: 4px; }
.pitchcta .btn { margin-top: 12px; }

/* ===== TWO-PHASE: the out-of-votes sheet ===== */
.ovrow { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 0; border-bottom: 1.5px solid var(--line); font-size: 14px; }
.ovrow:last-child { border-bottom: 0; }
.ovrow__name { font-weight: 700; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.ovrow.is-removed .ovrow__name { opacity: .4; }
.ovrow.is-removed .ovrow__act { font-weight: 800; }   /* "Undo" reads as the live action */

/* ===== TWO-PHASE: the Me card — literally a physical membership card =====
   CR80 proportions (85.6 × 54), rounded corners, the issuer band, an ID photo,
   dense labeled fields, a barcode strip with the member number, a faint seal. */
.idcard { position: relative; aspect-ratio: 54 / 85.6; max-width: 312px; margin: 0 auto;
  min-height: max-content;   /* the interim fine print may push past true card ratio */
  display: flex; flex-direction: column; --mx: 0.3; --my: 0.2;
  background: linear-gradient(155deg, #FAF9F6, var(--cream) 45%, #EAE8E2);
  color: var(--ink);
  border-radius: 14px; overflow: hidden; box-shadow: var(--soft-lift);
  transform: perspective(900px); transform-style: preserve-3d; will-change: transform;
  transition: transform .18s ease-out; touch-action: pan-y; cursor: pointer;
  --t-text: var(--ink); --t-muted: var(--stone); --t-head: var(--black); }
/* the faint print texture a real card carries */
.idcard::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(-38deg, transparent 0 7px, rgba(28, 23, 20, .025) 7px 8px); }
/* the laminate sheen — a pool of light that follows your finger as it tilts */
.idcard::before { content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 5;
  background: radial-gradient(60% 50% at calc(var(--mx) * 100%) calc(var(--my) * 100%),
    rgba(255, 255, 255, .42), rgba(255, 255, 255, .07) 45%, transparent 70%);
  mix-blend-mode: soft-light; }
.idcard__band { flex: 0 0 auto; display: flex; align-items: center; gap: 8px;
  padding: 8px 14px; color: #fff; border-bottom: 2px solid var(--black);
  background: linear-gradient(115deg, #6A4699, var(--violet) 55%, #3D2766); }
.idcard__mark { width: 16px; height: 16px; flex: 0 0 auto; }
.idcard__org { font-family: var(--display); font-size: 15px; letter-spacing: .04em; }
.idcard__type { font-size: 9px; font-weight: 800; letter-spacing: .13em; text-transform: uppercase;
  opacity: .92; }
.idcard__body { position: relative; flex: 1; display: flex;
  flex-direction: column; gap: 11px; padding: 14px 16px; }
.idcard__seal { position: absolute; right: 8px; top: 6px; width: 88px; height: 88px;
  color: var(--black); opacity: .06; pointer-events: none; }
.idcard__row { display: flex; gap: 14px; align-items: stretch; }
.idcard__photo { flex: 0 0 46%; aspect-ratio: 3 / 4; border: 2px solid var(--black);
  background: var(--cream-2); color: var(--black); overflow: hidden;
  display: flex; align-items: center; justify-content: center; font-size: 32px; font-weight: 800; }
.idcard__photo img { width: 100%; height: 100%; object-fit: cover; }
/* the portrait doubles as the photo uploader — a tap-target with a label baked in */
.idcard__photoform { display: contents; }
.idcard__photo--edit { position: relative; cursor: pointer; }
.idcard__photofile { position: absolute; width: 1px; height: 1px; opacity: 0; }
.idcard__photoadd { position: absolute; left: 0; right: 0; bottom: 0; text-align: center;
  font-size: 10.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  padding: 5px 4px; background: rgba(10, 21, 43, .8); color: #fff; }
.idcard__sidefields { flex: 1; min-width: 0; display: flex; flex-direction: column;
  justify-content: space-between; padding: 2px 0; }
.idcard__field { min-width: 0; }
.idcard__label { display: block; font-size: 8.5px; letter-spacing: .12em; text-transform: uppercase;
  font-weight: 700; color: var(--magenta); margin-bottom: 1px; }
.idcard__value { font-size: 12.5px; font-weight: 700; line-height: 1.25; display: block;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.idcard__name { font-family: var(--display); font-size: 26px; font-weight: 500; line-height: 1;
  margin: 0; color: var(--black); }
/* the fun numbers of your life on the block, ruled off like a ledger */
.idcard__stats { margin-top: auto; display: flex; border-top: 1.5px solid var(--line);
  padding-top: 9px; }
.idcard__stat { flex: 1; display: flex; flex-direction: column; gap: 0; }
.idcard__stat + .idcard__stat { border-left: 1.5px solid var(--line); padding-left: 12px; }
/* Neighbors in is live only during the vote phase (an <a>, not a plain div) —
   drop the link defaults, keep the same layout as its plain sibling cells,
   and hint it's tappable with an underline that only shows on this one */
a.idcard__stat { text-decoration: none; color: inherit; cursor: pointer; }
a.idcard__stat:hover .idcard__label { text-decoration: underline; text-decoration-color: var(--magenta); }
.idcard__statnum { font-family: var(--display); font-size: 24px; line-height: 1;
  color: var(--black); font-variant-numeric: tabular-nums; }
.idcard__strip { flex: 0 0 auto; display: flex; align-items: center; gap: 12px;
  padding: 7px 14px; background: var(--cream-2); border-top: 1.5px solid var(--line); }
.idcard__barcode { width: 86px; height: 20px; flex: 0 0 auto;
  background: repeating-linear-gradient(90deg,
    var(--black) 0 2px, transparent 2px 4px, var(--black) 4px 5px, transparent 5px 9px,
    var(--black) 9px 12px, transparent 12px 13px, var(--black) 13px 14px, transparent 14px 18px); }
.idcard__no { font-size: 10.5px; font-weight: 700; letter-spacing: .1em;
  font-variant-numeric: tabular-nums; }
.idcard__status { margin-left: auto; font-size: 9px; font-weight: 800; letter-spacing: .12em;
  text-transform: uppercase; color: var(--stone); }
.idcard__status.is-ok { color: var(--green); }
/* not confirmed yet: the WHOLE card wears it — an interim card, dashed edge,
   slate band, the fine print. Real once verified. */
/* pending keeps a visible not-yet-laminated edge even though the confirmed
   card is borderless now — quiet stone dashes, not the old heavy black frame */
.idcard--pending { border: 2px dashed var(--stone); }
.idcard--pending .idcard__band { background: linear-gradient(115deg, #5E6F86, #46566B 55%, #364457); }
.idcard__fineprint { margin: 7px 0 0; font-size: 8px; line-height: 1.5; letter-spacing: .05em;
  text-transform: uppercase; color: var(--stone);
  /* reserve two lines so pending and confirmed reserve identical space — the
     card is the same height either way no matter how the copy wraps */
  min-height: 24px; }
/* at a desk the card lies the other way — landscape, like it's on the table */
@media (min-width: 900px) {
  .idcard { aspect-ratio: 85.6 / 54; max-width: 470px; }
  .idcard__body { display: grid; grid-template-columns: 138px 1fr; gap: 7px 16px;
    align-content: start; }
  .idcard__row { display: contents; }
  .idcard__photo { grid-column: 1; grid-row: 1 / span 4; aspect-ratio: auto;
    align-self: stretch; }
  .idcard__sidefields { grid-column: 2; flex-direction: row; flex-wrap: wrap;
    justify-content: flex-start; gap: 6px 18px; padding: 0; }
  .idcard__body > .idcard__field, .idcard__stats, .idcard__fineprint { grid-column: 2; }
  .idcard__stats { margin-top: 2px; }
  .idcard__seal { top: auto; bottom: 6px; }
}

/* the notify control — a labelled pill that names its own state and reads as a
   button: it shows On (filled) or Off (outline), and a tap flips it. No guessing
   which way a bare switch points. */
.optpill { position: relative; display: inline-flex; flex: 0 0 auto; cursor: pointer; }
.optpill input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.optpill__face { min-width: 60px; text-align: center; padding: 9px 18px; border-radius: 999px;
  font-weight: 800; font-size: 13px; letter-spacing: .02em; transition: background .15s ease; }
.optpill__face--on  { display: none; background: var(--green); color: #08210f; }
.optpill__face--off { display: inline-block; background: transparent; color: var(--slate);
  box-shadow: inset 0 0 0 2px rgba(156, 173, 194, .4); }
.optpill input:checked ~ .optpill__face--on  { display: inline-block; }
.optpill input:checked ~ .optpill__face--off { display: none; }
.optpill input:focus-visible ~ .optpill__face { outline: 2px solid var(--yellow); outline-offset: 2px; }

/* the bottom-of-profile info: same paper as the rest of the card, held by its
   rules alone — no pool of navy (it read as a heavy dark slab on the frosted
   overlay) */
.profilepanel { margin: 26px -20px 0; padding: 20px;
  border-top: 2px solid var(--black); border-bottom: 2px solid var(--black); }

/* ---- the member overlay: frosted glass growing from the pill's MJ circle ----
   Lives here, not in profile/show.html.erb's own <style>, because the overlay is
   fetched piecemeal into a turbo-frame when opened from another screen
   (shared/_pill.html.erb) — only the frame's own markup travels on that fetch, so
   a page-local <style> tag would never reach it. One shared stylesheet works
   identically whether /profile loads standalone or the pill grows this overlay in
   place over the board/pitch-tab/rally underneath. */
.pov {
  position: fixed; inset: 0; z-index: 40; overflow-y: auto; -webkit-overflow-scrolling: touch;
  background:
    linear-gradient(180deg, rgba(246, 245, 242, .58), rgba(234, 232, 226, .44) 42%, rgba(234, 232, 226, .36));
  backdrop-filter: blur(22px) saturate(1.05); -webkit-backdrop-filter: blur(22px) saturate(1.05);
  animation: pov-grow .5s cubic-bezier(.32, .72, .24, 1) both;
}
/* no backdrop-filter support: swap the see-through wash for a near-opaque one, so
   whatever's actually behind the card (real page content now, not just this page's
   own background) doesn't turn into an unblurred mess underneath it */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .pov { background: rgba(246, 245, 242, .97); }
}
@keyframes pov-grow {
  from { clip-path: circle(22px at 38% 97%); }
  to   { clip-path: circle(150% at 38% 97%); }
}
.pov__in {
  max-width: 480px; margin: 0 auto;
  padding: calc(56px + env(safe-area-inset-top)) 20px calc(120px + env(safe-area-inset-bottom));
  animation: pov-rise .4s ease-out .32s both;
}
@keyframes pov-rise { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }
/* Sticky, not fixed: .pov's backdrop-filter makes it the containing block for
   fixed descendants, so "fixed" would scroll away with the pane. Sticky at the
   top of the scroll content stays put; the negative bottom margin keeps the
   card from being pushed down by the button's own row. */
.pov__x {
  position: sticky; top: calc(14px + env(safe-area-inset-top)); z-index: 41;
  margin: 0 16px -40px auto;
  width: 40px; height: 40px; display: grid; place-items: center; border-radius: 50%;
  background: rgba(28, 23, 20, .08); border: 1px solid var(--line); color: var(--black);
  font-size: 20px; line-height: 1; text-decoration: none;
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}
.pov__x:focus-visible { outline: 2px solid var(--magenta); outline-offset: 2px; }
/* the pill still floats above the wash while the overlay's open — same call-site
   consistency as every other screen it renders on, just needs to clear .pov's
   z-index to stay tappable. Scoped to :has(.pov) so it only fires then, wherever
   .pov happens to be mounted — this page's own or a frame fetched onto another. */
body:has(.pov) .tk-modes { z-index: 41; }
/* the top-of-screen breath of navy reads as a smear on this page's light wash
   (same trap as the paper board/reader — see docs/plans/tk-components.md) */
body:has(.pov)::after { display: none; }

/* the light register: everything loose on the wash flips to paper's ink, not the
   app's usual cream-on-navy (mirrors this file's own paper flip for .card/
   .deck__face — same tokens, just triggered locally on the overlay) */
.profv {
  --t-text: var(--ink); --t-muted: var(--stone);
  --t-head: var(--black); --t-eyebrow: var(--magenta);
  color: var(--t-text);
}
.profv .darkrow { color: var(--t-text); border-bottom-color: var(--line); }
.profv .darkrow .muted { color: var(--t-muted); }
.profv .divider { border-top-color: var(--line); }
.profv .optpill__face--off { color: var(--stone); box-shadow: inset 0 0 0 2px rgba(110, 99, 90, .35); }
/* the trust panel reads in the card's own light tokens now that its navy pool
   is gone — no re-flip needed */


/* gesture words track the pointer, not the window: a mouse clicks and scrolls */
.g-desk { display: none; }
@media (hover: hover) and (pointer: fine) {
  .g-touch { display: none; }
  .g-desk { display: inline; }
}

/* stillness for those who ask for it */
@media (prefers-reduced-motion: reduce) {
  .is-armed .say > *, .is-armed .cut, .is-armed .paper {
    opacity: 1 !important; transform: none !important; transition: none !important; }
  .bldg, .bldg__after, .coin, .aw, .awfill { transition: none !important; }
  .hl { background-size: 100% 100% !important; }
  .rise, .beat__cue, .cut, .bt__row--next, .pov, .pov__in { animation: none !important; }
  .btn, .chip, .gcard, .deck__cover img { transition: none !important; }
  /* the bottom-banner slideshow holds its first slide still, with zero animation */
  .pitchbanner__item, .pitchbanner__stage { transition: none !important; }
}

/* the quiet second door on the media step */
.mediaalt { margin-top: 16px; text-align: center; }
.mediaalt .capture__preview { display: none; margin-top: 10px; }
.mediaalt.capture--has .capture__preview { display: block; }
.mediaalt img { max-width: 100%; border-radius: 2px; }

/* skip demoted to a text link under Continue, with its beat of reassurance */
.booth__only { text-align: center; margin: 0; }
input.booth__skip { background: none; border: 0; align-self: center; color: var(--slate); text-decoration: underline; cursor: pointer; }

/* the budget honesty line under the total */
.potline { margin-top: 8px; }


/* --- tk design system (flyer redesign) --- */
/* Ported from the storyboard mocks (2026-08-12 "ticket stack" reroll):
   app/views/storyboard/_flyer_styles.html.erb, _flyer_wizard_styles.html.erb,
   and the flyer_*_mock.html.erb views. Those mocks stay the design source —
   this section is the reusable component port, per
   docs/plans/flyer-implementation-plan.md §3. Component reference + copy-pasteable
   markup skeletons: docs/plans/tk-components.md.

   Every selector below is prefixed tk- (board / reader / ballot / pill) or
   wz- (pitch-wizard chrome) so nothing here can collide with a class
   application.css already owns — .idea, .card, .step, .bt, etc. are
   untouched. This section only ADDS: no view reads these classes yet, so
   nothing on a live screen changes. The board/reader/wizard views land in
   later phases of the plan and will consume these classes then. */

/* ---- tokens joining the existing set ----
   Everything the mocks' own --print/--brick/--marigold/--orange/--stone/
   --cream/--navy tokens named is already in :root above (var(--black),
   var(--red), var(--yellow), var(--orange), var(--stone), var(--cream),
   var(--navy) — same values) — the rules below reuse those directly rather
   than re-declaring aliases. Only what's genuinely new joins here. */
:root {
  --serial: #D9D9D6;              /* neutral serial-number grey — a listing, never a rank */
  --paper: #F6F5F2;               /* one neutral ballot stock, folded or open */
  --paper-open: #F6F5F2;
  --perf: rgba(28, 23, 20, .28);  /* perforation dash + punched holes, on paper grounds */
  --paper-grain: url("/assets/paper-open-d9b736b5.webp");
  --paper-open-grain: url("/assets/paper-open-d9b736b5.webp");
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3CfeColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 .05 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  /* per-element paper grain — distinct from body::before's page-wide tooth
     overlay above; this one paints into one specific paper surface at a time
     (ticket, portal, receipt…) the way the mocks apply it as a background. */
}

/* Ciscela is the resident voice; Plex carries civic furniture and machine print. */
.tk-cond {
  font-family: var(--body); font-weight: 700; letter-spacing: -.025em;
}
.tk-mono {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
}
.tk-disp { font-family: var(--display); font-weight: 400; }
.tk-portal, .tk-reader { font-synthesis: none; }

/* ---- status-bar fade, scoped off tk- paper grounds ----
   application.css's own body::after (the top-of-screen breath-of-navy fade,
   defined above) is correct on navy grounds but smears across paper. This
   only fires on pages that mount a tk- paper surface, so no current screen
   is affected — it's a new selector, the old body::after rule is untouched. */
body:has(.tk-portal)::after,
body:has(.tk-reader)::after,
body:has(.tk-home)::after { display: none; }
body:has(.tk-portal)::before { display: none; }

/* ---- home, revealed behind the portal on pull-down ---- */
.tk-home {
  position: fixed; inset: 0; background: var(--navy) var(--grain);
  color: var(--cream); padding: 14px 18px 0;
}
.tk-home__bar {
  display: flex; align-items: baseline; justify-content: center;
  font-size: 13px; letter-spacing: .3em; text-transform: uppercase;
  padding: 8px 2px 20px;
}
.tk-home__ticket {
  background: var(--cream) var(--paper-grain); background-size: 512px 512px;
  color: var(--black); border-radius: 10px;
  padding: 20px 18px 26px; position: relative; margin: 0 0 14px;
  clip-path: polygon(10px 0, calc(100% - 10px) 0, 100% 10px, 100% calc(100% - 10px),
                     calc(100% - 10px) 100%, 10px 100%, 0 calc(100% - 10px), 0 10px);
  box-shadow: 0 10px 26px rgba(0,0,0,.4);
}
.tk-home__ticket--idea { color: var(--red); }
.tk-home__spine {
  position: absolute; left: 14px; top: 16px; bottom: 16px; width: 18px;
  border-right: 1.5px solid currentColor; opacity: .7;
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  font-size: 10px; line-height: 1.15;
}
.tk-home__label {
  position: absolute; left: 18px; bottom: 8px; font-size: 9px;
  letter-spacing: .3em; text-transform: uppercase; opacity: .7;
}
.tk-home__say { font-family: var(--display); font-size: 26px; line-height: 1.16; margin: 0; }

/* ---- the portal: a tall civic ticket ---- */
.tk-portal {
  position: fixed; left: 0; right: 0; top: 58px; bottom: 0; z-index: 5;
  background: var(--paper) var(--paper-grain); background-size: 512px 512px; color: var(--black);
  border-radius: 18px 18px 0 0;
  clip-path: polygon(14px 0, calc(100% - 14px) 0, 100% 14px, 100% 100%, 0 100%, 0 14px);
  box-shadow: 0 -18px 44px rgba(0,0,0,.5);
  transition: transform .5s cubic-bezier(.32,.72,.24,1),
              top .5s cubic-bezier(.32,.72,.24,1),
              border-radius .5s cubic-bezier(.32,.72,.24,1),
              clip-path .5s cubic-bezier(.32,.72,.24,1),
              box-shadow .5s;
  display: flex; flex-direction: column;
}
.tk-portal.is-down { transform: translateY(calc(100% - 132px)); }
.tk-portal__grab {
  flex: none; display: grid; place-items: center; width: 100%; padding: 9px 0 3px;
  cursor: grab; touch-action: none; border: 0; background: transparent;
}
.tk-portal__grab:active { cursor: grabbing; }
.tk-portal__grab::before {
  content: ""; width: 44px; height: 5px; border-radius: 3px; background: var(--perf);
}
.tk-portal__body { flex: 1; position: relative; min-height: 0; }
.tk-board { position: absolute; inset: 0; overflow-y: auto; overscroll-behavior: contain; }

.tk-sheet { position: relative; }

/* When the phase banner is up, the fixed portal steps down by its height —
   otherwise the banner covers the pot. The portal's own top transition makes
   a live phase flip push it down rather than jump. */
body:has(.phasealert) .tk-portal { top: calc(var(--phasealert-h) + env(safe-area-inset-top) + 6px); }

/* portal head: the season facts, printed */
.tk-head { flex: none; background: var(--paper) var(--paper-grain); background-size: 512px 512px;
  padding: 6px 16px 0; position: relative; z-index: 9; }
.tk-portal.is-immersive .tk-head { background: var(--paper-open) var(--paper-open-grain); background-size: 512px 512px; }
.tk-head__row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
/* the sound toggle's .minmax base is styled for navy grounds (--onnavy, near-
   invisible here) — the head is paper, so it gets its own quiet ink color. */
.tk-head__row .soundtoggle { align-self: center; color: var(--stone); }
.tk-head__row .soundtoggle:hover { color: var(--black); }
.tk-head__row .soundtoggle.is-on { color: var(--yellow); }
.tk-head__pot { font-size: 32px; color: var(--black); margin: 0; font-family: var(--display); }
.tk-head__pot small {
  display: block; font-size: 9px; letter-spacing: .26em; font-weight: 700;
  font-family: var(--body); margin-top: 1px;
}
.tk-head__live {
  display: flex; justify-content: space-between; font-size: 11px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; padding: 8px 1px 9px;
}
.tk-head__live .left { color: var(--red); }
.tk-perf { position: relative; height: 1px; margin: 0 -16px;
  background: repeating-linear-gradient(90deg, var(--perf) 0 3px, transparent 3px 7px); }
.tk-perf::before, .tk-perf::after {
  content: ""; position: absolute; top: -5px; width: 10px; height: 10px;
  border-radius: 50%; background: var(--navy); z-index: 4;
}
.tk-perf::before { left: -5px; } .tk-perf::after { right: -5px; }

/* ---- the ticket string ---- */
.tk-string { padding: 0 0 150px; }
.tk-stub { position: relative; }
.tk-stub + .tk-stub .tk-join { display: block; }
.tk-join { display: none; position: relative; height: 1px;
  background: repeating-linear-gradient(90deg, var(--perf) 0 3px, transparent 3px 7px); }
.tk-join::before, .tk-join::after {
  content: ""; position: absolute; top: -5px; width: 10px; height: 10px;
  border-radius: 50%; background: var(--navy); z-index: 4;
}
.tk-join::before { left: -5px; } .tk-join::after { right: -5px; }

/* folded band: filled number block full-bleed off the left edge, sentence, price, punch */
.tk-row {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 0 14px; align-items: center;
  width: 100%; text-align: left; border: 0; background: none; padding: 0 16px 0 0;
  font: inherit; color: inherit; cursor: pointer; min-height: 72px;
  position: relative;
}
.tk-row > * { position: relative; z-index: 1; }
.tk-row__no {
  align-self: stretch; display: grid; place-items: center; min-width: 46px;
  background: var(--serial); color: var(--black); font-size: 15px; font-weight: 700;
  letter-spacing: .04em; padding: 0 8px;
}
.tk-row__mid { min-width: 0; padding: 12px 0; overflow-wrap: anywhere; }
/* media rows: the idea line stops short of the peek's dot field — dark type
   over dark halftone is unreadable, so the sentence wraps before it */
.tk-row--media .tk-row__mid { padding-right: 84px; }
.tk-row__say {
  font-family: var(--display); font-weight: 400;
  font-size: 18px; line-height: 1.18; margin: 0; color: var(--black);
  /* The board is the flyer at a distance: one idea line per row, never the
     whole story (that lives in the reader). Clamped so a long-winded pitch
     can't turn the ballot into an essay. */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.tk-row__cnt { display: block; font-size: 12px; color: var(--stone); margin-top: 3px; }
.tk-row--media .tk-row__cnt { color: var(--stone); }
.tk-row__end { display: flex; flex-direction: column; align-items: flex-end; gap: 7px; padding: 12px 0; }
.tk-row__end--browse { padding: 12px 0; }   /* pitch/browse phase: price only, no ballot mark */
.tk-row__price { font-size: 13px; font-weight: 700; white-space: nowrap; }
.tk-mark {
  width: 22px; height: 22px; border: 2.5px solid var(--black);
  display: inline-block; position: relative; background: transparent;
}
.is-picked .tk-mark::before, .is-picked .tk-mark::after {
  content: ""; position: absolute; left: -3px; right: -3px; top: 50%; height: 3px;
  background: var(--red); border-radius: 2px;
}
.is-picked .tk-mark::before { transform: rotate(43deg); }
.is-picked .tk-mark::after { transform: rotate(-47deg); }
.is-picked .tk-mark::before { animation: tk-mark-a .12s ease-out both; transform-origin: center; }
.is-picked .tk-mark::after { animation: tk-mark-b .12s ease-out .08s both; transform-origin: center; }
@keyframes tk-mark-a { from { transform: rotate(43deg) scaleX(0); } to { transform: rotate(43deg) scaleX(1); } }
@keyframes tk-mark-b { from { transform: rotate(-47deg) scaleX(0); } to { transform: rotate(-47deg) scaleX(1); } }
.is-picked .tk-row__price { color: var(--red); }

/* Board media retains natural color, softened to sit with the printed paper. */
/* The peek's fade-out is a halftone screen, not an airbrush: a 140×8 SVG mask —
   It stretches to the full desktop width without SVG letterboxing.
   Solid on the right, then staggered dot columns stepping down in radius toward
   the left edge, the way a printed image actually dissolves into the paper.
   The reader's video hero deliberately does NOT use this — moving footage keeps
   a smooth scrim so the text over it stays legible. */
.tk-row__peek, .tk-row--media::after {
  -webkit-mask-image: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22140%22 height=%228%22 viewBox=%220 0 140 8%22 preserveAspectRatio=%22none%22%3E%3Cg fill=%22black%22%3E%3Ccircle cx=%2214%22 cy=%222%22 r=%220.42%22/%3E%3Ccircle cx=%2218%22 cy=%222%22 r=%220.57%22/%3E%3Ccircle cx=%2222%22 cy=%222%22 r=%220.72%22/%3E%3Ccircle cx=%2226%22 cy=%222%22 r=%220.88%22/%3E%3Ccircle cx=%2230%22 cy=%222%22 r=%221.05%22/%3E%3Ccircle cx=%2234%22 cy=%222%22 r=%221.22%22/%3E%3Ccircle cx=%2238%22 cy=%222%22 r=%221.39%22/%3E%3Ccircle cx=%2242%22 cy=%222%22 r=%221.57%22/%3E%3Ccircle cx=%2246%22 cy=%222%22 r=%221.75%22/%3E%3Ccircle cx=%2250%22 cy=%222%22 r=%221.93%22/%3E%3Ccircle cx=%2254%22 cy=%222%22 r=%222.12%22/%3E%3Ccircle cx=%2258%22 cy=%222%22 r=%222.31%22/%3E%3Ccircle cx=%2262%22 cy=%222%22 r=%222.5%22/%3E%3Ccircle cx=%2266%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2270%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2274%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2278%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2282%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2286%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2290%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2294%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2298%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22102%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22106%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22110%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22114%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22118%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22122%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22126%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22130%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22134%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22138%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2212%22 cy=%226%22 r=%220.35%22/%3E%3Ccircle cx=%2216%22 cy=%226%22 r=%220.49%22/%3E%3Ccircle cx=%2220%22 cy=%226%22 r=%220.64%22/%3E%3Ccircle cx=%2224%22 cy=%226%22 r=%220.8%22/%3E%3Ccircle cx=%2228%22 cy=%226%22 r=%220.96%22/%3E%3Ccircle cx=%2232%22 cy=%226%22 r=%221.13%22/%3E%3Ccircle cx=%2236%22 cy=%226%22 r=%221.3%22/%3E%3Ccircle cx=%2240%22 cy=%226%22 r=%221.48%22/%3E%3Ccircle cx=%2244%22 cy=%226%22 r=%221.66%22/%3E%3Ccircle cx=%2248%22 cy=%226%22 r=%221.84%22/%3E%3Ccircle cx=%2252%22 cy=%226%22 r=%222.03%22/%3E%3Ccircle cx=%2256%22 cy=%226%22 r=%222.22%22/%3E%3Ccircle cx=%2260%22 cy=%226%22 r=%222.41%22/%3E%3Ccircle cx=%2264%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2268%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2272%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2276%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2280%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2284%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2288%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2292%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2296%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22100%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22104%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22108%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22112%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22116%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22120%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22124%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22128%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22132%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22136%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22140%22 cy=%226%22 r=%222.6%22/%3E%3C/g%3E%3Crect x=%2262%22 width=%2278%22 height=%228%22 fill=%22black%22/%3E%3C/svg%3E');
  mask-image: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22140%22 height=%228%22 viewBox=%220 0 140 8%22 preserveAspectRatio=%22none%22%3E%3Cg fill=%22black%22%3E%3Ccircle cx=%2214%22 cy=%222%22 r=%220.42%22/%3E%3Ccircle cx=%2218%22 cy=%222%22 r=%220.57%22/%3E%3Ccircle cx=%2222%22 cy=%222%22 r=%220.72%22/%3E%3Ccircle cx=%2226%22 cy=%222%22 r=%220.88%22/%3E%3Ccircle cx=%2230%22 cy=%222%22 r=%221.05%22/%3E%3Ccircle cx=%2234%22 cy=%222%22 r=%221.22%22/%3E%3Ccircle cx=%2238%22 cy=%222%22 r=%221.39%22/%3E%3Ccircle cx=%2242%22 cy=%222%22 r=%221.57%22/%3E%3Ccircle cx=%2246%22 cy=%222%22 r=%221.75%22/%3E%3Ccircle cx=%2250%22 cy=%222%22 r=%221.93%22/%3E%3Ccircle cx=%2254%22 cy=%222%22 r=%222.12%22/%3E%3Ccircle cx=%2258%22 cy=%222%22 r=%222.31%22/%3E%3Ccircle cx=%2262%22 cy=%222%22 r=%222.5%22/%3E%3Ccircle cx=%2266%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2270%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2274%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2278%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2282%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2286%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2290%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2294%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2298%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22102%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22106%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22110%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22114%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22118%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22122%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22126%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22130%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22134%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%22138%22 cy=%222%22 r=%222.6%22/%3E%3Ccircle cx=%2212%22 cy=%226%22 r=%220.35%22/%3E%3Ccircle cx=%2216%22 cy=%226%22 r=%220.49%22/%3E%3Ccircle cx=%2220%22 cy=%226%22 r=%220.64%22/%3E%3Ccircle cx=%2224%22 cy=%226%22 r=%220.8%22/%3E%3Ccircle cx=%2228%22 cy=%226%22 r=%220.96%22/%3E%3Ccircle cx=%2232%22 cy=%226%22 r=%221.13%22/%3E%3Ccircle cx=%2236%22 cy=%226%22 r=%221.3%22/%3E%3Ccircle cx=%2240%22 cy=%226%22 r=%221.48%22/%3E%3Ccircle cx=%2244%22 cy=%226%22 r=%221.66%22/%3E%3Ccircle cx=%2248%22 cy=%226%22 r=%221.84%22/%3E%3Ccircle cx=%2252%22 cy=%226%22 r=%222.03%22/%3E%3Ccircle cx=%2256%22 cy=%226%22 r=%222.22%22/%3E%3Ccircle cx=%2260%22 cy=%226%22 r=%222.41%22/%3E%3Ccircle cx=%2264%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2268%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2272%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2276%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2280%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2284%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2288%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2292%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%2296%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22100%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22104%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22108%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22112%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22116%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22120%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22124%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22128%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22132%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22136%22 cy=%226%22 r=%222.6%22/%3E%3Ccircle cx=%22140%22 cy=%226%22 r=%222.6%22/%3E%3C/g%3E%3Crect x=%2262%22 width=%2278%22 height=%228%22 fill=%22black%22/%3E%3C/svg%3E');
  -webkit-mask-size: 140px 8px; mask-size: 140px 8px;
  -webkit-mask-repeat: repeat-y; mask-repeat: repeat-y;
}
.tk-row__peek {
  position: absolute; right: 0; top: 0; bottom: 0; width: 140px; z-index: 0;
  background-color: var(--navy); background-size: cover; background-position: center;
  filter: saturate(.55); pointer-events: none; opacity: .92;
}
.tk-row--media::after {
  content: ""; position: absolute; right: 0; top: 0; bottom: 0; width: 140px; z-index: 0;
  pointer-events: none;
  background: linear-gradient(to left, rgba(0,0,0,.8) 20%, transparent);
}
.tk-row--media .tk-row__price { color: var(--cream); }
.tk-row--media .tk-mark { border-color: var(--cream); }
.tk-row--media.is-picked .tk-row__price { color: var(--yellow); }

/* ---- shared flyer row: Pitch tab's "Already up" rows, Rally's pick rows ----
   Same folded serial/idea/trailing-value pattern as tk-row/tk-stub above, but a
   plain link, full-bleed inside a 22px-padded portal body — not the board's ballot
   row. One home for shared/_flyer_row.html.erb. */
.tk-flyerrow {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 0 14px; align-items: center;
  width: calc(100% + 44px); margin: 0 -22px; padding: 0 22px 0 0; text-align: left;
  border: 0; border-bottom: 2px dashed var(--perf); background: none;
  font: inherit; color: inherit; text-decoration: none; min-height: 64px;
}
.tk-flyerrow:first-of-type { border-top: 2px dashed var(--perf); }
.tk-flyerrow__no { align-self: stretch; background: var(--serial) var(--grain); font-weight: 700; padding: 0 10px; }
.tk-flyerrow__say { font-family: var(--display); font-size: 17px; line-height: 1.2; margin: 10px 0; color: var(--black); }
.tk-flyerrow__end { font-size: 13px; font-weight: 700; }
.tk-flyerrow.is-picked .tk-flyerrow__end { color: var(--red); }
.tk-flyerrow.is-quiet { opacity: .45; }

/* ---- the reader: fullscreen flyers, one continuous scroll ----
   The chrome never moves: the head is fixed above, each flyer's band pins to
   the top and its pick bar to the bottom, and both swap as you drift into the
   next ballot. Tapping the band is the way back to the board. */
.tk-reader {
  position: absolute; inset: 0;
  overflow-y: auto; overscroll-behavior: contain;
  scroll-snap-type: y mandatory;
  background: var(--paper-open) var(--paper-open-grain); background-size: 512px 512px; z-index: 8;
  will-change: clip-path;
}
.tk-reader.is-unfolding { animation: tk-unfold-settle .48s cubic-bezier(.22,.78,.24,1) both; transform-origin: top center; }
.tk-reader.is-folding { animation: tk-fold-lift .5s cubic-bezier(.32,.72,.24,1) both; transform-origin: top center; }
@keyframes tk-unfold-settle {
  0% { transform: perspective(900px) rotateX(-3deg) translateY(3px); filter: brightness(.94); }
  68% { transform: perspective(900px) rotateX(.5deg); filter: brightness(1.01); }
  100% { transform: none; filter: none; }
}
@keyframes tk-fold-lift { from { transform: none; } to { transform: perspective(900px) rotateX(-2deg) translateY(2px); } }
/* immersive: the same card slides up to cover the page — no smear, corners square off */
.tk-portal.is-immersive {
  top: 0; border-radius: 0; box-shadow: none;
  clip-path: polygon(0 0, 100% 0, 100% 0, 100% 100%, 0 100%, 0 0);
}
.tk-portal.is-immersive .tk-portal__grab { display: none; }
/* The pill used to be a DOM sibling of the portal; it's wrapped in the desk
   island now, so a sibling combinator no longer reaches it and the nav sat on
   top of the reader's own back/prev/next. Match on the page instead. */
/* Phone only: the open flyer takes the whole screen, so the pill gets out of
   the way. On a desk (>=800px, where .tk-desk-island becomes the sidebar) the
   flyer opens inside the portal and the sidebar keeps its place. */
@media (max-width: 799.98px) {
  body:has(.tk-portal.is-immersive) .tk-modes,
  body:has(.tk-portal.is-immersive) .tk-desk-island { display: none; }
}
/* the portal pulled down (home teaser showing behind) also hides the tab bar —
   the grab bar is the way back, and the teaser's own doors do the navigating */
.tk-portal.is-down ~ .tk-modes { display: none; }
/* one plate like the tab bar: icon circle left, Prev and Next as separate pills */
/* Prev/Next aren't toggles, so no island: each is its own floating translucent
   glass pill, same material as the tab bar. */
.tk-rdnav {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(16px + env(safe-area-inset-bottom)); z-index: 12;
  display: flex; gap: 8px; align-items: center;
  background: none; border: 0; padding: 0; box-shadow: none;
}
.tk-rdnav[hidden] { display: none; }
.tk-rdnav button {
  background: rgba(246, 245, 242,.55); color: var(--black); border: 1px solid rgba(28,23,20,.1);
  cursor: pointer; border-radius: 999px; padding: 11px 18px; font-size: 14px; font-weight: 700;
  box-shadow: 0 10px 26px rgba(0,0,0,.18);
  backdrop-filter: blur(14px) saturate(1.05); -webkit-backdrop-filter: blur(14px) saturate(1.05);
  display: flex; align-items: center; gap: 5px;
}
.tk-rdnav button svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .tk-rdnav button { background: rgba(246, 245, 242,.94); }
}
.tk-rdnav .tk-rdnav__back { width: 42px; height: 42px; padding: 0; font-size: 16px; display: grid; place-items: center; }
.tk-reader[hidden] { display: none; }
.tk-page {
  height: 100%; position: relative;
  scroll-snap-align: start; scroll-snap-stop: always;
  display: flex; background: var(--paper-open) var(--paper-open-grain); background-size: 512px 512px;
}
.tk-page + .tk-page { border-top: 1px solid transparent;
  border-image: repeating-linear-gradient(90deg, var(--perf) 0 3px, transparent 3px 7px) 1; }
.tk-page + .tk-page::before, .tk-page + .tk-page::after {
  content: ""; position: absolute; top: -6px; width: 10px; height: 10px;
  border-radius: 50%; background: var(--navy); z-index: 6;
}
.tk-page + .tk-page::before { left: -5px; }
.tk-page + .tk-page::after { right: -5px; }
.tk-rd { flex: 1; overflow-y: auto; display: flex; flex-direction: column; min-width: 0; }
/* flex: none here is load-bearing — this section wrapper sits inside two
   nested scroll containers (.tk-reader > .tk-page > .tk-rd); without it the
   rail and sticky stamp collapse to one viewport instead of the flyer's
   full scrollable height. */
.tk-rd__in { position: relative; min-height: 100%; display: flex; flex-direction: column; flex: none; }
.tk-rd__tab {
  position: absolute; left: 0; top: 0; bottom: 0; width: 46px; z-index: 4;
  pointer-events: none; background: var(--serial) var(--paper-grain); background-size: 512px 512px;
  background-blend-mode: multiply;
}
.tk-rd__stamp { position: sticky; top: 0; z-index: 7; height: 0; }
.tk-rd__stamp .tk-row__no {
  display: grid; place-items: center; width: 46px; min-width: 0;
  padding: 13px 0; box-sizing: border-box; font-size: 13px; letter-spacing: .06em;
  background: var(--serial) var(--paper-grain); background-size: 512px 512px;
  background-blend-mode: multiply;
}

.tk-rd__body { padding: 6px 16px 6px 62px; display: flex; flex-direction: column; flex: 1; }
.tk-rd__act {
  position: sticky; bottom: 0; margin-top: auto; padding: 14px 16px 96px 62px;
  margin-left: 0; margin-right: 0;
  background: var(--paper-open) var(--paper-open-grain); background-size: 512px 512px;
  box-shadow: 0 -10px 18px -14px rgba(0,0,0,.35);
  display: flex; align-items: center; gap: 12px;
}

/* media ballots: the video IS the background — text prints over it, lower */
.tk-rd--media .tk-rd__body { padding-top: 0; }
.tk-hero {
  position: relative; z-index: 3; margin: 0 -16px 0 -62px; min-height: 60dvh;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: 0 16px 16px 62px;
  background-color: var(--navy); background-size: 0 0;
}
/* Filter only the inherited photo layer, so text and controls keep their colors. */
.tk-hero::before {
  content: ""; position: absolute; inset: 0;
  background-image: inherit; background-size: cover; background-position: center;
  filter: saturate(.55); pointer-events: none;
}
.tk-hero::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(to bottom, rgba(0,0,0,.08), rgba(0,0,0,.78)),
    radial-gradient(rgba(28,23,20,.15) 1px, transparent 1.1px);
  background-size: auto, 4px 4px;
}
.tk-hero > * { position: relative; z-index: 1; }
.tk-hero .tk-prob { color: var(--cream); }
.tk-hero .tk-idea { color: var(--yellow); }
.tk-hero__cap {
  color: var(--cream); font-size: 9px; letter-spacing: .22em;
  text-transform: uppercase; margin-top: 12px; opacity: .85;
}
/* Video keeps the same muted natural color as photos. A neutral bottom scrim
   holds the text without tinting the footage blue. */
.tk-hero--video { overflow: hidden; background: none; isolation: isolate; }
.tk-hero--video::before, .tk-hero--video::after { content: none; }
.tk-hero > .tk-hero__video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  z-index: -2; filter: saturate(.55);
}
/* Safari composites accelerated <video> layers above positioned siblings no
   matter the paint order — every layer that must sit over the video gets its
   own compositor layer (translateZ) and an explicit z-index, and the hero
   isolates so none of this leaks into the page's stacking. */
.tk-hero > .tk-hero__tint {
  position: absolute; inset: 0; pointer-events: none; z-index: -1;
  background: transparent;
}
.tk-hero > .tk-hero__grain {
  position: absolute; inset: 0; pointer-events: none; z-index: -1;
  background:
    linear-gradient(to bottom, rgba(0,0,0,.05), rgba(0,0,0,.78)),
    radial-gradient(rgba(28,23,20,.16) 1px, transparent 1.1px);
  background-size: auto, 4px 4px;
}
/* Negative z on the video+overlay layers (inside the hero's isolated context)
   is the one ordering Safari's promoted video layer reliably honors — the
   in-flow text needs no compositor tricks of its own then. */
.tk-hero--video .tk-prob, .tk-hero--video .tk-idea, .tk-hero--video .tk-hero__cap {
  z-index: 1;
}

/* the flyer: problem first, both sentences big, elab small, receipt under */
.tk-prob { font-family: var(--display); font-weight: 400; font-size: 28px; line-height: 1.14; margin: 8px 0 0; color: var(--black); }
.tk-idea { font-family: var(--display); font-weight: 400; font-size: 28px; line-height: 1.14; margin: 12px 0 0; color: var(--red); }
.tk-elab { font-size: 14px; line-height: 1.5; color: var(--stone); margin: 14px 0 0; }

/* The photo, printed into the paper with muted natural color and halftone. */
.tk-photo {
  margin: 18px -16px 0; height: 240px; position: relative;
  background-color: var(--navy); background-size: cover; background-position: center;
  filter: saturate(.55) contrast(1.05);
}
.tk-photo--tall { height: 46dvh; min-height: 300px; margin-left: -62px; }
.tk-photo--tall .tk-photo__play { width: 68px; height: 68px; font-size: 24px; }
.tk-photo__play {
  position: absolute; inset: 0; margin: auto; width: 58px; height: 58px;
  border-radius: 50%; background: rgba(28,23,20,.55); color: var(--cream);
  display: grid; place-items: center; font-size: 20px; padding-left: 5px;
  border: 1.5px solid rgba(246, 245, 242,.7);
}
.tk-photo::before {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(transparent, rgba(0,0,0,.55));
}
.tk-photo::after {
  content: ""; position: absolute; inset: 0; opacity: .3;
  background: radial-gradient(rgba(28,23,20,.6) 1px, transparent 1.1px);
  background-size: 4px 4px;
}
.tk-photo figcaption {
  position: absolute; left: 10px; bottom: 8px; color: var(--cream);
  font-size: 9px; letter-spacing: .2em; text-transform: uppercase;
}
.tk-clip {
  display: flex; align-items: center; gap: 12px; margin: 16px 0 0;
  border: 1.5px solid var(--black); padding: 8px; cursor: pointer; background: none;
  width: 100%; text-align: left; font: inherit; color: inherit;
}
.tk-clip__thumb {
  flex: none; width: 104px; height: 68px; position: relative;
  background-color: var(--navy); background-size: cover; background-position: center;
  filter: saturate(.55);
}
.tk-clip__thumb::after {
  content: "\25B6"; position: absolute; inset: 0; margin: auto; width: 26px; height: 26px;
  border-radius: 50%; background: rgba(28,23,20,.6); color: var(--cream);
  display: grid; place-items: center; font-size: 10px; padding-left: 2px;
}
.tk-clip__cap { font-size: 12.5px; line-height: 1.45; color: var(--stone); }
.tk-clip__cap b { display: block; color: var(--black); font-size: 13px; }

/* the receipt: black print on the paper, sprocket holes down its edge */
.tk-rc { margin: 18px 0 0; border: 1.5px solid var(--black); position: relative; }
.tk-rc table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.tk-rc td { padding: 7px 10px 6px; border-bottom: 1px dashed var(--perf); }
.tk-rc td:last-child { text-align: right; white-space: nowrap; }
.tk-rc tr:last-child td {
  border: 0; background: var(--black); color: var(--cream);
  font-weight: 700; font-size: 14px; padding: 9px 10px;
}

/* who's behind it — the pitcher's face and the count in words */
.tk-behind {
  margin: 18px 0; display: flex; align-items: center; gap: 10px;
  font-size: 14px; line-height: 1.45;
}
.tk-behind b { color: var(--red); }
.tk-avatar {
  flex: none; width: 34px; height: 34px; border-radius: 50%;
  border: 1.5px solid var(--black); background-size: 480%;
  background-color: var(--sand);
}
.tk-pick {
  flex: none; display: flex; align-items: center; gap: 12px;
  border: 2.5px solid var(--black); background: var(--paper-open); color: var(--black);
  cursor: pointer; padding: 14px 16px; font-size: 17px;
  letter-spacing: .06em; text-transform: uppercase;
  box-shadow: 0 4px 0 rgba(28,23,20,.2);
}
.tk-pick .tk-mark { border-width: 2.5px; flex: none; }
.is-picked .tk-pick { border-color: var(--red); color: var(--red); }
/* .tk-pick's smaller sibling: the same stamped control (charcoal rule, paper
   face, machine caps, one flat shadow under it) at the size a secondary action
   takes — Edit, Withdraw, Toss. --quiet drops the weight for the ones you'd
   rather nobody pressed by accident; --wide is the full-column version. */
.tk-act { display: inline-flex; align-items: center; justify-content: center;
  border: 2.5px solid var(--black); background: var(--paper-open) var(--grain);
  background-size: 512px 512px; color: var(--black);
  font-family: var(--body); font-size: 13px; font-weight: 700; line-height: 1;
  letter-spacing: .06em; text-transform: uppercase; text-decoration: none;
  padding: 15px 18px; cursor: pointer;
  box-shadow: 0 4px 0 rgba(28, 23, 20, .2);
  transition: transform .08s ease, box-shadow .12s ease; }
.tk-act { gap: 9px; }
.tk-act svg { width: 16px; height: 16px; flex: none; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.tk-act:active { transform: translateY(2px); box-shadow: 0 2px 0 rgba(28, 23, 20, .2); }
.tk-act--quiet { border-width: 2px; border-color: rgba(28, 23, 20, .3);
  color: var(--stone); background: none; box-shadow: none; }
.tk-act--quiet:active { transform: none; box-shadow: none; }
.tk-act--wide { display: flex; width: 100%; }
/* The invite banner: the reason first, the tap under it. Same construction as
   the near-miss card on the Pitch tab — bordered paper, a red caption, one
   serif line — because it's the same kind of object: a small thing on the
   page telling you something worth knowing. */
.tk-bring { border: 2px solid var(--black); background: var(--paper-open) var(--grain);
  background-size: 512px 512px; padding: 16px; margin-top: 26px; }
.tk-bring__cap { font-size: 10px; letter-spacing: .28em; text-transform: uppercase;
  font-weight: 700; color: var(--red); margin: 0 0 8px; }
.tk-bring__why { font-family: var(--display); font-size: 16px; line-height: 1.25;
  color: var(--black); margin: 0 0 14px; }
.tk-bring .tk-act { width: 100%; }
.tk-rd__amt { font-size: 16px; font-weight: 700; }
.tk-share { background: none; border: 0; font-size: 21px; color: var(--black); cursor: pointer; padding: 8px; }

/* ---- the mode pill: modern, floating, dark plate so it reads on any ground ---- */
.tk-modes {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 9;
  display: flex; justify-content: center;
  padding: 10px 16px calc(16px + env(safe-area-inset-bottom));
  pointer-events: none;
}
/* Muted floating glass, not a dark island: the bar recedes, the navy chip on
   the selected verb is the one saturated thing. */
.tk-pill {
  pointer-events: auto; display: flex; align-items: center; gap: 4px;
  background: rgba(246, 245, 242,.55); border-radius: 999px; padding: 5px;
  border: 1px solid rgba(28,23,20,.1);
  box-shadow: 0 10px 26px rgba(0,0,0,.18);
  backdrop-filter: blur(14px) saturate(1.05); -webkit-backdrop-filter: blur(14px) saturate(1.05);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .tk-pill { background: rgba(246, 245, 242,.94); }
}
/* the profile circle: its own object, pinned to the left corner — not part of
   the toggle's island (vertically centered on the island's 52px height) */
.tk-pill__me {
  position: absolute; left: 16px; bottom: calc(21px + env(safe-area-inset-bottom));
  pointer-events: auto;
  width: 42px; height: 42px; border-radius: 50%; flex: none;
  background: var(--navy-2); color: var(--cream);
  display: grid; place-items: center; font-size: 12px; font-weight: 700;
  text-decoration: none; letter-spacing: .04em;
  border: 1px solid rgba(28,23,20,.1);
  box-shadow: 0 10px 26px rgba(0,0,0,.18);
}
/* phase 5: a resident's own photo fills the circle instead of initials */
.tk-pill__me img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
.tk-pill__seg {
  display: flex; align-items: center; gap: 7px; text-decoration: none;
  padding: 11px 20px; border-radius: 999px; font-size: 15px; font-weight: 700;
  color: var(--black);
}
.tk-pill__seg svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.8; }
.tk-pill__seg:hover, .tk-pill__seg:focus-visible {
  color: var(--navy); background: rgba(10,21,43,.08);
}
.tk-pill__seg--on { background: var(--navy); color: var(--cream); }
.tk-pill__seg--on:hover, .tk-pill__seg--on:focus-visible { background: var(--navy); color: var(--cream); }

/* ==============================================================   wz- : the pitch-wizard chrome
   ==============================================================   Ported from _flyer_wizard_styles.html.erb. Two objects carry the love:
   the FIELD is paper you write on (cream stock, Ciscela, a drawn mic in the
   corner — not an admin box), and the FLYER PREVIEW is the actual ballot
   stub from the board (serial block bleeding off the left edge, perforated
   edges, brick idea line — the wizard builds the same object the board
   shows). Sits inside the app's own qstep/pitchbar/pitchhead/steps/booth/bt
   chrome (all pre-existing, unchanged) — wz- classes only add the paper
   objects living inside that chrome, so every var() below is already in the
   existing :root; nothing new to declare. */

/* the app's own question screen: p.muted small above h1.qstep__q, the field
   is the app's qstep__input — this only adds the drawn mic in its corner */
.wz-fieldwrap { position: relative; }
.wz-fieldwrap .qstep__input { min-height: 132px; padding-right: 52px; }
.wz-guide { font-size: 13px; color: var(--slate); margin: 10px 0 0; }
.wz-guide--under { margin: -2px 0 0; }   /* riding the question, before the paper */
.wz-lead { font-family: var(--display); font-size: 17.5px; line-height: 1.35;
  color: var(--yellow); margin: 0 0 10px; }
.wz-mic { position: absolute; right: 10px; bottom: 10px; width: 34px; height: 34px;
  display: grid; place-items: center; border: 0; border-radius: 50%;
  background: rgba(241, 240, 235,.1); color: var(--slate); cursor: pointer; }
.wz-mic svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }

/* the flyer preview: the same stub the board prints — grain, perforation
   with punched corners, solid serial column */
.wz-stub { position: relative; }
.wz-perf { position: relative; height: 0; border-top: 2px dashed rgba(28,23,20,.35); z-index: 2; }
.wz-perf::before, .wz-perf::after { content: ""; position: absolute; top: -8px;
  width: 16px; height: 16px; border-radius: 50%; background: var(--navy); }
.wz-perf::before { left: -8px; } .wz-perf::after { right: -8px; }
.wz-flyer { position: relative; background: var(--paper-open) var(--grain);
  color: var(--ink); padding: 15px 16px 15px 60px; }
.wz-flyer__no { position: absolute; left: 0; top: 0; bottom: 0; min-width: 44px;
  display: grid; place-items: center; background: var(--serial); color: var(--ink);
  font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 15px; font-weight: 700; }
.wz-flyer p { font-family: var(--display); font-size: 19px; line-height: 1.26; margin: 0; }
.wz-flyer p + p { margin-top: 8px; }
.wz-flyer .wzidea { color: var(--red); }
/* the ghost ballot: the flyer while it's in the making — the stub's shape at
   low opacity on the navy, so it reads as forming, not printed yet. Fields
   inside stay the app's own dark inputs. It turns to solid paper only at
   "Here's your flyer." */
/* No serial rail while it's forming — a number is a place on the board, and
   this stub doesn't have one yet. What says "ballot" instead: the torn
   perforation above (punched holes where it left the strip), and a bottom
   that never hard-ends — the paper tapers out through a dot-grid fade into
   whatever's coaching below it. */
/* ONE piece of stock, one flat color, one shape. Everything that makes it a
   ballot — the perforated tear at the top, the punched holes at its ends, and
   the edge that dissolves into paper dots where the writing hasn't reached —
   is cut into a single mask and painted in a single fill. Stacking translucent
   layers to fake those features is what made it read cheap: each overlap
   showed as a different value. The mask unions the tear strip, the body, and
   the dot dissolve, then subtracts the two holes. */
.wz-ghost {
  /* Tall on purpose: a ballot is a length of paper, so it takes the screen it's
     given instead of shrink-wrapping a field. Nothing is ever written into the
     dissolve — the bottom padding reserves that whole zone for the dots. */
  position: relative; display: block; min-height: 56vh; margin: 24px 0 0;
  padding: 26px 16px calc(var(--dissolve) + 12px);
  background: #1B2538;              /* the stock — a single value, never alpha-stacked */
  /* Being written on is a property of the whole sheet, not of one line: the
     paper sits back until it's the thing you're working on, then comes up to
     full. One shape, one value change. */
  opacity: .74; cursor: text; transition: opacity .18s ease;
  /* The tear is a row of small round bites taken OUT of the edge — the edge
     line itself stays where it is; the paper is just missing at each punch.
     Teeth standing above the edge read as a coupon icon, not a perforation. */
  -webkit-mask-image:
    radial-gradient(circle var(--bite) at 50% 0, transparent var(--bite), #000 calc(var(--bite) + .6px)),
    linear-gradient(#000, #000),
    linear-gradient(to bottom, #000, transparent),
    radial-gradient(circle, #000 1.15px, transparent 1.35px);
  mask-image:
    radial-gradient(circle var(--bite) at 50% 0, transparent var(--bite), #000 calc(var(--bite) + .6px)),
    linear-gradient(#000, #000),
    linear-gradient(to bottom, #000, transparent),
    radial-gradient(circle, #000 1.15px, transparent 1.35px);
  -webkit-mask-size: var(--pitch) 100%, 100% calc(100% - var(--dissolve)), 100% var(--dissolve), 8px 8px;
  mask-size: var(--pitch) 100%, 100% calc(100% - var(--dissolve)), 100% var(--dissolve), 8px 8px;
  -webkit-mask-position: top left, top left, bottom, 0 0;
  mask-position: top left, top left, bottom, 0 0;
  -webkit-mask-repeat: repeat-x, no-repeat, no-repeat, repeat;
  mask-repeat: repeat-x, no-repeat, no-repeat, repeat;
  -webkit-mask-composite: source-in, source-over, source-in, source-over;
  mask-composite: intersect, add, intersect, add; }
.wz-ghost p { font-family: var(--display); font-size: 19px; line-height: 1.3;
  margin: 0; color: var(--onnavy); }
/* "Let's" hangs over the field's first-line indent so the clause wraps as one
   paragraph — the same shape it prints in */
.wz-ghost .wz-lets { display: block; position: relative; margin-top: 12px; }
.wz-ghost .wz-lets b { position: absolute; left: 0; top: 0;
  font-family: var(--display); font-size: 19px; font-weight: 400;
  color: var(--yellow); }
.wz-ghost .wz-lets textarea { min-height: 2.6em; margin-top: 0; text-indent: 50px; }

/* ink going down on the ticket: the answer field IS the stub's next line —
   Ciscela on the ghost paper, no admin box anywhere. Lines already banked
   from earlier steps print above it (wz-ghost__print), so every screen is
   the same object with more ink on it. */
.wz-ghost__print { margin: 0 0 12px; }
/* the second half of question one, on the same paper: its ask is a quiet line
   in the interface voice so the resident's own words stay the only serif here */
.wz-second { margin-top: 18px; padding-top: 14px;
  border-top: 1px solid rgba(246, 245, 242, .12); }
.wz-second__ask { font-family: var(--body); font-size: 13px; letter-spacing: .02em;
  color: var(--slate); margin: 0 0 6px; }
/* The ink sizes to what's written (the paper below it is what's tall), so the
   focus line lands under the words instead of floating mid-sheet. */
.wz-ink { display: block; width: 100%; border: 0; background: none; padding: 0 44px 2px 0;
  font-family: var(--display); font-size: 19px; line-height: 1.3; color: var(--onnavy);
  resize: none; min-height: 2.6em; caret-color: var(--yellow);
  field-sizing: content; }
.wz-ink::placeholder { color: rgba(156, 173, 194, .6); }
.wz-ink:focus { outline: none; }
.wz-ink--line { min-height: 66px; }        /* a one-sentence line, not a page */
/* already answered, printed here for context — not editable on this step.
   Scoped past .wz-ghost p, which would otherwise win on specificity. Works
   both as the class on a single printed line (the fuse step) and as a wrapper
   around several of them (every step after it, where the sheet is filling up). */
.wz-ghost .wz-set, .wz-ghost .wz-set p { color: rgba(241, 240, 235, .42); }
.wz-ghost .wz-set { margin-bottom: 22px; }
.wz-ghost .wz-set p + p { margin-top: 8px; }
.wz-ghost:focus-within { opacity: 1; }
/* the note under the two big lines prints small and in the body face here,
   because that's how it prints on the real ballot (.tk-elab) */
.wz-ink--note { font-family: var(--body); font-size: 15px; line-height: 1.55;
  min-height: 4.5em; }
/* the receipt is written into the same way */
.wz-receipt:focus-within { opacity: 1; }
/* Next waits until the answer is one (floor_controller) */
.btn.is-waiting, .wz-pills .is-waiting { opacity: .38; cursor: default; box-shadow: none; }

/* The receipt is the SECOND half of the same sheet, so the whole shape flips:
   it dissolves in from ABOVE (the flyer runs up out of frame) and ends on a
   real torn edge at the bottom. It stays tall whether or not there's anything
   written yet — a receipt is a length of paper, not a box that hugs a field. */
.wz-ghost.wz-receipt { min-height: 62vh; margin: 24px 0 20px;
  /* the writing starts where the paper stops dissolving */
  padding: calc(var(--dissolve) + 16px) 16px 30px;
  -webkit-mask-image:
    radial-gradient(circle var(--bite) at 50% 100%, transparent var(--bite), #000 calc(var(--bite) + .6px)),
    linear-gradient(#000, #000),
    linear-gradient(to top, #000, transparent),
    radial-gradient(circle, #000 1.15px, transparent 1.35px);
  mask-image:
    radial-gradient(circle var(--bite) at 50% 100%, transparent var(--bite), #000 calc(var(--bite) + .6px)),
    linear-gradient(#000, #000),
    linear-gradient(to top, #000, transparent),
    radial-gradient(circle, #000 1.15px, transparent 1.35px);
  -webkit-mask-size: var(--pitch) 100%, 100% calc(100% - var(--dissolve)), 100% var(--dissolve), 8px 8px;
  mask-size: var(--pitch) 100%, 100% calc(100% - var(--dissolve)), 100% var(--dissolve), 8px 8px;
  -webkit-mask-position: bottom left, bottom left, top, 0 0;
  mask-position: bottom left, bottom left, top, 0 0; }

/* the receipt end of the stub: the budget table as machine print on the same
   forming ticket — figures in mono, hairlines in the ghost's own cream, no
   grid boxes. Committed lines stay full ink (they're banked, not spent). */
.wz-receipt .bt { border-top: 0; }
.wz-receipt .bt__row { border-bottom: 1px solid rgba(246, 245, 242, .16); }
.wz-receipt .bt__name { border-right: 0; }
.wz-receipt .bt__name, .wz-receipt .bt__in { padding-left: 0; }
.wz-receipt .bt__amt, .wz-receipt .qty, .wz-receipt .bt__in--qty, .wz-receipt .bt__in--amt {
  font-family: var(--mono); font-variant-numeric: tabular-nums; }
.wz-receipt .qty { color: var(--slate); }
/* columns not yet born don't reserve room — the name gets the whole line
   until ×times and $ actually exist */
.wz-receipt .qty:empty, .wz-receipt .bt__amt:empty { width: 0; padding: 0; }
.wz-receipt .bt__row.is-done { color: var(--onnavy); opacity: 1; }
.wz-receipt .bt__total .bt__amt { color: var(--yellow); font-weight: 700; }
/* no box, no ring: the sheet's own brightening says it's live (see .wz-ghost) */
.wz-receipt .bt__name:has(.bt__in.is-active), .wz-receipt .bt__amt:has(.bt__in.is-active) {
  background: none; box-shadow: none; }
/* coaching, not resident ink — it escapes the ghost's Ciscela default */
.wz-receipt .potline { margin-top: 10px; color: var(--yellow);
  font-family: var(--body); font-size: 13px; line-height: 1.4; }
.wz-receipt__go { display: block; margin-top: 12px; }
.wz-receipt__go[hidden] { display: none; }

/* the print moment: at "Here's your flyer" the ghost turns solid paper — the
   stub settles in a beat after the step itself arrives */
.wz-stub--print { animation: wz-print .5s cubic-bezier(.22, .61, .36, 1) .1s both; }
@keyframes wz-print { from { opacity: 0; transform: translateY(8px) scale(.985); } }

/* Hints live in the same slot the neighbors' lines do — under the sheet, in
   the coaching zone, where they can be glanced at while writing. A tiny grey
   line squeezed under the question read as fine print nobody has to obey;
   these are three things worth saying, at a size that says so. */
.wz-hints { position: relative; margin: calc(28px - var(--dissolve)) 0 0; padding: 0;
  list-style: none; display: flex; flex-direction: column; gap: 10px; }
.wz-hints li { position: relative; padding-left: 20px; font-size: 15px;
  line-height: 1.35; color: rgba(241, 240, 235, .72); }
.wz-hints li::before { content: ""; position: absolute; left: 0; top: .5em;
  width: 7px; height: 7px; border-radius: 50%; background: var(--red); }

/* neighbors' lines: quiet, real, below the work */
.wz-said { position: relative; margin-top: calc(28px - var(--dissolve)); }
.wz-said p { font-family: var(--display); font-size: 15.5px; line-height: 1.35;
  color: rgba(241, 240, 235,.72); margin: 0 0 9px; }

/* ---- washes: highlighter behind the words ----
   NOT in flyer_pitch_fuse_mock.html.erb — that mock only shows the
   degraded, no-AI-key state (plainly joined, "no invented highlights" is a
   comment about THAT state, not a ban on washes generally). The redesign
   spec (docs/plans/flyer-redesign.md §6) calls for a wash behind each answer once
   it's folded into the composed sentence, so the resident can see nothing
   was invented: wash-a marks words traceable to the "who" answer, wash-b to
   the "problem" answer, wash-c is the new "Let's ___" clause being typed for
   the first time. "Highlighter behind the words, not colored text" — same
   visual idiom as the intro's .hl marker swipe elsewhere in this file,
   ported here as a static (non-animated) wash since the wizard has no
   scroll entrance to key an animation off. Wrap the words to mark in a
   plain span — <span class="wz-wash wz-wash--a">already said this part</span> —
   never <mark>, so there's no UA yellow to reset. */
.wz-wash {
  background-image: linear-gradient(transparent 58%, var(--wz-wash, rgba(219,151,42,.55)) 58%);
  background-repeat: no-repeat; background-size: 100% 100%;
  -webkit-box-decoration-break: clone; box-decoration-break: clone;
  padding: 0 .05em; color: inherit; font: inherit;
}
.wz-wash--a { --wz-wash: rgba(42,114,104,.5); }   /* teal — the "who" answer */
.wz-wash--b { --wz-wash: rgba(154,51,84,.5); }    /* raspberry — the "problem" answer */
.wz-wash--c { --wz-wash: rgba(219,151,42,.55); }  /* marigold — the new "Let's ___" clause */

/* ==============================================================   phase 3 additions: ideas#index → the board + reader
   ===================================================================== */

/* The pre-existing 4-tab bar (.apptabs) still renders this phase — it dies in
   phase 5 when the floating pill replaces it — but it's pinned at a higher
   z-index than the reader's own chrome (the pick bar, .tk-rdnav), so it would
   sit on top of both once a flyer opens fullscreen. Hide it (and the flash
   dock) only while the reader's immersive — same body:has() idiom the
   status-bar fade above uses, so no view has to know about this. */
body:has(.tk-portal.is-immersive) .apptabs,
body:has(.tk-portal.is-immersive) #flash { display: none; }

/* Out of picks: the pick bar and its mark go quiet, per docs/plans/tk-components.md's
   note that no state was shipped in the mocks for this — a link (to Rally's
   undo-able picks), not a disabled button, so it stays reachable. */
.tk-pick.is-quiet { opacity: .45; box-shadow: none; text-decoration: none; }
.tk-mark.is-quiet { opacity: .5; }

/* ==============================================================   phase 4 additions: the wizard restep (problem → who → fuse → money →
   flyer → media). Page-specific furniture (the flyer preview's .elab/.cost,
   the media step's .md-flyer/.md-quiet) stays view-scoped per
   docs/plans/tk-components.md's own precedent — only the reusable pieces land here.
   ===================================================================== */

/* The mic, listening — a slow pulse so a tap registers as "it heard me" even
   before any words land in the field. */
.wz-mic.is-listening { background: rgba(219,151,42,.28); color: var(--yellow);
  animation: wz-mic-pulse 1.4s ease-in-out infinite; }
@keyframes wz-mic-pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(219,151,42,.4); }
  50% { box-shadow: 0 0 0 6px rgba(219,151,42,0); } }

/* The ceiling (flyer-redesign.md §6): never blocks Next — a quiet aside under
   the field, an offer to shorten only when a key makes that offer honest. */
.wz-ceiling { margin-top: 10px; font-size: 12.5px; color: var(--slate); }
.wz-ceiling__shorten { margin-left: 6px; background: none; border: 0; padding: 0;
  font: inherit; color: var(--yellow); text-decoration: underline; cursor: pointer; }
.wz-ceiling__shorten:disabled { opacity: .5; cursor: default; }

/* The fuse step's compose: a read-only washed sentence with its own "tap to
   fix anything" seam into a plain textarea — the moment it's tapped, the
   words are theirs, not a trace of two answers anymore (flyer-redesign.md §9's
   divergence rule, made physical). */
.wz-fuse__edit { display: block; margin-top: 8px; background: none; border: 0; padding: 0;
  font-size: 12.5px; color: var(--slate); text-decoration: underline; cursor: pointer; }

/* The money, born progressive: the ×times column reads quiet grey until it's
   the live column (matches flyer_budget_mock's own inline rule). */
.bt td.qty { width: 54px; text-align: center; color: var(--slate); }
.bt__row.bt__total td.qty { color: inherit; }
.bt__in--qty { text-align: center; }

/* Visually-hidden label utility — the flyer step's tap-editable fields read as
   printed flyer text with no visible label, but still need one for a screen reader. */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

/* Desktop within reach (flyer-qa-plan Flow 9.4: "the card centers") — the paper
   shells cap and center; the navy ground shows around them. margin-auto
   centering (never transform: a transform would re-parent fixed/sticky
   descendants like the reader nav plate). .jn (the join card, view-scoped
   styles) is included here because inset:0 + margin:auto is how an absolutely
   positioned box centers — its own partial stays width-agnostic. */
@media (min-width: 700px) {
  .tk-portal, .jn { max-width: 480px; margin-left: auto; margin-right: auto; }
  .tk-home__bar, .tk-modes { max-width: 480px; margin-left: auto; margin-right: auto; left: 0; right: 0; }
}

/* ---- desktop ballot desk ----
   The mobile object stays intact; a wide screen gives it a civic sidebar and
   a broader, still-bounded sheet on the right. Opening a row unfolds inside
   that sheet rather than taking over the monitor. */
.tk-desk-island { display: contents; }
.tk-desk-island__brand { display: none; }
@media (min-width: 800px) {
  body:has(.tk-portal) { overflow: hidden; background: var(--navy) var(--grain); }
  .tk-home { display: none; }

  body:has(.tk-portal) .tk-desk-island {
    position: fixed; display: flex; flex-direction: column; z-index: 9;
    left: max(24px, calc(50% - 560px)); top: 24px; bottom: 24px; width: 240px;
    padding: 16px; color: var(--black); background: rgba(246, 245, 242,.46);
    border: 1px solid rgba(246, 245, 242,.34); border-radius: 28px;
    box-shadow: inset 0 1px rgba(255,255,255,.24), 0 18px 52px rgba(0,0,0,.3);
  }
  body:has(.tk-portal) .tk-desk-island__brand {
    display: block; margin: 4px 8px 0; font-size: 14px;
    letter-spacing: .28em; text-transform: uppercase;
  }

  /* The phone's exact profile + two-tab control, turned vertically. No new
     destinations, labels, icons, or interaction model at a desk. */
  body:has(.tk-portal) .tk-desk-island .tk-modes {
    position: static; display: flex; flex: 1; flex-direction: column;
    align-items: flex-start; justify-content: flex-start; gap: 14px;
    width: 100%; max-width: none; margin: 76px 0 0; padding: 0;
    pointer-events: none;
  }
  body:has(.tk-portal) .tk-desk-island .tk-pill {
    order: 1; width: 100%; flex-direction: column; align-items: stretch;
    border: 0; border-radius: 20px; padding: 0; background: transparent;
    box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none;
  }
  body:has(.tk-portal) .tk-desk-island .tk-pill__seg {
    width: 100%; justify-content: flex-start; padding: 12px 16px;
  }
  body:has(.tk-portal) .tk-desk-island .tk-pill__me {
    order: 2; position: static; width: 42px; height: 42px; margin-top: auto;
  }

  .tk-portal {
    left: max(292px, calc(50% - 272px)); right: max(24px, calc(50% - 560px));
    top: 24px; bottom: 24px; width: auto; max-width: 800px; margin: 0;
    border-radius: 6px 6px 2px 2px;
    box-shadow: 0 18px 52px rgba(0,0,0,.45);
  }
  body:has(.phasealert) .tk-portal { top: calc(var(--phasealert-h) + 16px); }
  .tk-portal.is-immersive {
    top: 24px; border-radius: 3px; box-shadow: 0 18px 52px rgba(0,0,0,.45);
  }
  .tk-head { padding: 8px 24px 0; }
  .tk-head__live { padding: 11px 1px 12px; }
  .tk-perf { margin-left: -24px; margin-right: -24px; }

  .tk-row { min-height: 82px; padding-right: 24px; gap: 0 18px; }
  .tk-row__no { min-width: 58px; }
  .tk-row__say { font-size: 20px; }
  .tk-row--media .tk-row__mid { padding-right: 150px; }
  .tk-row__peek, .tk-row--media::after { width: 210px; }
  .tk-row__peek, .tk-row--media::after {
    -webkit-mask-size: 210px 8px; mask-size: 210px 8px;
  }

  .tk-rdnav {
    position: absolute; left: 50%; bottom: 16px;
  }
  .tk-rd__act { padding: 14px 24px 76px 62px; }
  .tk-rd:not(.tk-rd--media) .tk-rd__body {
    width: 100%; max-width: 650px; align-self: center; padding: 18px 32px 8px 76px;
  }
  .tk-rd--media .tk-rd__body {
    display: grid; grid-template-columns: minmax(250px, .95fr) minmax(280px, 1.05fr);
    grid-template-rows: auto auto 1fr auto; column-gap: 26px;
    padding: 18px 24px 18px 62px;
  }
  .tk-rd--media .tk-hero {
    grid-column: 1; grid-row: 1 / 5; min-height: 100%;
    margin: -18px 0 -18px -16px; padding: 28px 24px;
  }
  .tk-rd--media .tk-elab { grid-column: 2; grid-row: 1; margin-top: 0; font-size: 15px; }
  .tk-rd--media .tk-rc { grid-column: 2; grid-row: 2; }
  .tk-rd--media .tk-behind { grid-column: 2; grid-row: 3; align-self: start; }
  .tk-rd--media .tk-rd__body > .muted { grid-column: 2; grid-row: 4; }
  .tk-rd--media .tk-prob, .tk-rd--media .tk-idea { font-size: 30px; }
}

/* ---- the standalone navy-page shell ----
   The full-bleed screens that aren't nested in the app's paper .app column —
   the landing, the shared card, the record, the join sheet — all paint the
   same fixed, scrolling night ground under their own screen-specific
   furniture. One primitive, composed via the markup (class="tk-navy-page
   xx"), so each screen's own .ld/.cd/.jn/.rc style block only carries what's
   actually its own. */
.tk-navy-page { position: fixed; inset: 0; overflow-y: auto; background: var(--navy) var(--grain); color: var(--cream); }

@media (prefers-reduced-motion: reduce) {
  .tk-portal, .tk-reader, .tk-rdnav button, .tk-pick { transition: none !important; animation: none !important; }
  .tk-reader { scroll-behavior: auto !important; }
  .is-picked .tk-mark::before, .is-picked .tk-mark::after { animation: none !important; }
  .wz-mic.is-listening { animation: none !important; }
  .qstep__body, .wz-strip__seg.is-now, .wz-stub--print { animation: none !important; }
}

/* The storybook lives behind the ballot; its scroll must stay above the folded sheet. */
.tk-home--story { display: block; position: fixed; inset: 0 0 190px; padding: 0; overflow: hidden; }
.tk-home--story .act { height: 100%; max-width: none; }
.tk-home--story .wellmark { top: 22px; }
.tk-home--story .introback { display: none; }
@media (max-width: 799.98px) {
  body:has(.tk-portal.is-down) .tk-desk-island { display: none; }
  .tk-home--story .act { grid-template-rows: minmax(130px, 45%) minmax(0, 55%); padding: 48px 22px 0; }
  .tk-home--story .act__stage, .tk-home--story .act__scroll { height: 100%; min-height: 0; }
  .tk-home--story .act__step { min-height: 100%; padding: 20px 0; }
  .tk-home--story .beat__big { font-size: clamp(23px, 6vw, 32px); }
  .tk-home--story .lede { font-size: 15px; }
  .tk-home--story .stage--act { max-height: 100%; }
  .tk-home--story .actwell .well__bar, .tk-home--story .actwell .aw-proj .well__labels { height: 150px; }
}
@media (min-width: 800px) {
  .tk-home--story { left: max(292px, calc(50% - 272px)); right: max(24px, calc(50% - 560px)); bottom: 180px; }
  .tk-home--story .act { grid-template-columns: 1fr; grid-template-rows: 45% 55%; gap: 0; padding: 52px 28px 0; }
  .tk-home--story .act__stage, .tk-home--story .act__scroll { height: 100%; min-height: 0; }
  .tk-home--story .act__step { min-height: 100%; max-width: none; padding: 20px 0; }
  .tk-home--story .beat__big { font-size: 28px; }
  .tk-home--story .lede { font-size: 16px; }
  .tk-home--story .actwell .well__bar, .tk-home--story .actwell .aw-proj .well__labels { height: 170px; }
}
.tk-home--story .act__scroll { mask-image: none; -webkit-mask-image: none; }
.tk-home--story .act__step { justify-content: flex-start; padding-top: 36px; }
.tk-home--story .actwell { top: 50%; height: 150px; }
@media (min-width: 800px) {
  .tk-home--story .wellmark { display: none; }
  .tk-home--story .actwell { height: 170px; }
}

/* Wizard controls must respect the budget phase and match link chrome. */
.budgettable [hidden] { display: none !important; }
.budgettable .small { display: flex; gap: 16px; flex-wrap: wrap; }
button.pitchbar__back { border: 0; background: none; padding: 0; font-family: inherit; cursor: pointer; }

/* Keep profile settings legible over both navy and paper pages. */
.pov { background: rgba(246, 245, 242, .94); }

.tk-board > .tk-string > .muted { color: var(--stone); }
/* Keep the closed ballot's surroundings quiet on tablets, and make the
   story readable when landscape or an on-screen keyboard shortens the view. */
body:has(.tk-portal:not(.is-down)) .tk-home--story .act__stage,
body:has(.tk-portal:not(.is-down)) .tk-home--story .act__scroll { visibility: hidden; }
.qstep input, .qstep textarea { scroll-margin-top: 80px; scroll-margin-bottom: 120px; }
@media (max-height: 600px) {
  .qstep:not(.qstep--keyboard) .wz-pills {
    position: static; transform: none; align-self: center; flex-shrink: 0;
    margin: 24px auto calc(24px + env(safe-area-inset-bottom));
  }
  .tk-home--story { bottom: 146px; }
  .tk-home--story .act { grid-template-columns: 90px minmax(0, 1fr); grid-template-rows: 1fr;
    gap: 18px; padding: 42px 20px 8px; }
  .tk-home--story .act__step { padding: 8px 0 24px; }
  .tk-home--story .beat__big { font-size: 21px; }
  .tk-home--story .lede { font-size: 13px; line-height: 1.4; }
  .tk-home--story .actwell, .tk-home--story .actwell .well__bar { height: 110px; }
  .tk-home--story .actwell .well__col { width: 54px; }
  .tk-home--story .actwell .well__labels { display: none; }
  .tk-home--story .wellmark { top: 12px; }
}

/* Keep budget navigation above native keyboards and Safari browser chrome. */
.qstep--keyboard .wz-pills {
  position: static; transform: none; align-self: center; flex-shrink: 0;
  margin: 24px auto calc(120px + env(safe-area-inset-bottom));
}
.qstep--keyboard input { scroll-margin-bottom: 140px; }

/* Status notices cannot intercept wizard controls, even as native keyboard
   or short-screen layouts move navigation into the document flow. */
body:has(.qstep) #flash { top: calc(84px + env(safe-area-inset-top)); bottom: auto; }
body:has(.qstep) .flash:not(.flash--alert) { pointer-events: none; }

/* Direct budget editing: the printed multiplier remains a real touch target. */
.budgettable [data-budget-target="guide"] { color: var(--yellow); }
.bt__quantity-edit, .budgettable .bt__edit-action { color: var(--onnavy); font: inherit; border: 0; border-radius: 8px;
  background: rgba(246, 245, 242, .12); padding: 8px 10px; min-width: 44px; min-height: 44px; cursor: pointer; }
.bt__quantity-edit:hover, .bt__quantity-edit:focus-visible,
.budgettable .bt__edit-action:hover, .budgettable .bt__edit-action:focus-visible { color: var(--yellow); background: rgba(246, 245, 242, .2); }
.bt__quantity-edit:focus-visible, .budgettable .bt__edit-action:focus-visible { outline: 2px solid var(--yellow); outline-offset: 2px; }
.wz-guide.wz-guide--problem { font-size: 18px; line-height: 1.45; }

.budgettable .bt__edit-action { margin: 0 8px 8px 0; font-size: 14px; font-weight: 600; }
