/* ==========================================================================
   Climate Heroes components. Requires tokens.css first.
   Every class is prefixed ch- so it can sit beside the shell's own styles.
   Lifted from the approved Reviews dashboard; specimen.html shows each one in use.
   Every colour is a token; if you need a colour that is not in tokens.css, ask first.
   ========================================================================== */

/* Respect people who ask their device for less motion. Kept here as well as in
   tokens.css so copying only this file still brings it along. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ---- Page frame ---------------------------------------------------------- */
/* Same numbers the shell's main.shell-main uses, so a standalone page and a shell page line up.
   Inside the shell, use <main class="shell-main"> instead and skip .ch-page. */
.ch-page   { max-width: 1080px; margin: 0 auto; padding: 28px 24px 56px; }
.ch-page.tv { max-width: 1700px; }                      /* TV displays: wider, then scaled to fit the screen (chFitScreen) */
@media (max-width: 640px) { .ch-page { padding: 20px 16px 40px; } }
.ch-title, .shell .ch-title { font-size: 26px; font-weight: 700; letter-spacing: -.01em; margin: 0 0 4px; text-align: center; }   /* optional page title under the logo (the shell's own `.shell h1` would otherwise win) */

/* ---- Logo header: the logo with its transparent margins cropped off ------ */
/* The 2000x1358 logo file has empty margins. This 200x115 window crops them and leaves
   the whole badge intact (phones: 150x86). Use logo.webp or logo.png here, NOT logo-tight.png
   (that one is already cropped: just give it a width). Keep these numbers: they are measured. */
.ch-brand { display: flex; justify-content: center; margin-bottom: 24px; }
.ch-logo  { width: 200px; height: 115px; overflow: hidden; position: relative; flex: none; }
.ch-logo img { position: absolute; width: 255px; height: auto; left: -27px; top: -29px;
               animation: ch-pop .7s var(--ease-pop) both; }
@media (max-width: 640px) {
  .ch-logo { width: 150px; height: 86px; }
  .ch-logo img { width: 191px; left: -20px; top: -22px; }
}

/* ---- Panel: the basic white card ----------------------------------------- */
.ch-panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--sp-5);
  animation: ch-rise var(--t-rise) var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 110ms);          /* set style="--i:0,1,2..." to stagger entrances */
}
.ch-panel.tint { background: linear-gradient(135deg, var(--accent-soft), var(--surface)); }
.ch-panel.tint :is(.ch-note, .ch-label, .ch-figs span, .ch-meter-scale, .ch-who .when) { color: var(--ink-2); }   /* ink-3 is for plain white only */
@media (max-width: 640px) { .ch-panel { padding: 20px; } }
.ch-split { display: grid; grid-template-columns: 1fr 1.25fr; gap: 16px; }     /* two panels side by side; stacks below 860px */
@media (max-width: 860px) { .ch-split { grid-template-columns: 1fr; } }
.ch-stack > * + * { margin-top: 16px; }                                         /* 16px between panels */

/* ---- Text roles ---------------------------------------------------------- */
/* LABEL: small uppercase text ABOVE a number.  CAPTION: plain small text BELOW or beside it.
   Use a <div> or <span>, not a <label> element (inside the shell, `.shell label` restyles it). */
.ch-label  { color: var(--ink-3); font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .08em; }
.ch-note   { font-size: 12px; color: var(--ink-3); }                            /* caption */
.ch-hero-num { font-size: 64px; font-weight: 800; line-height: 1.05; letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.ch-hero-num small { font-size: 40px; font-weight: 800; color: var(--gold-text); letter-spacing: -.01em; }  /* the GOAL: gold, bold, 40px or more on desktop (23px on phones) */
.ch-pct    { font-size: 30px; font-weight: 800; color: var(--accent); font-variant-numeric: tabular-nums; }
.ch-section-head { display: flex; align-items: baseline; justify-content: space-between; margin: 0 0 14px; }
.ch-section-head h2 { margin: 0; }
@media (max-width: 640px) {
  .ch-hero-num { font-size: 38px; white-space: nowrap; } .ch-hero-num small { font-size: 23px; } .ch-pct { font-size: 22px; }
}

/* ---- Headline row: label + hero number on the left, the percentage on the right ----------- */
.ch-headline { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 20px; }   /* leaves the gap above the meter */

/* ---- Figures: a row of big numbers with captions ------------------------- */
.ch-figs { display: grid; grid-template-columns: repeat(3, auto); justify-content: space-between; gap: 18px; width: 100%; }
.ch-figs b    { display: block; font-size: 38px; line-height: 1.1; font-weight: 800; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.ch-figs span { font-size: 12px; color: var(--ink-3); }                         /* caption under each figure: plain, not uppercase */
@media (max-width: 640px) { .ch-figs b { font-size: 34px; } }

/* ---- Rating: big number + big gold stars --------------------------------- */
.ch-rate { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 20px; }
.ch-rate .big  { font-size: 64px; font-weight: 800; line-height: 1; letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
.ch-stars      { color: var(--star); letter-spacing: 3px; font-size: 22px; white-space: nowrap; }
.ch-stars .off { color: var(--line); }
.ch-rate .ch-stars { font-size: 48px; line-height: 1; letter-spacing: 4px; animation: ch-twinkle 3s ease-in-out infinite; }
@media (max-width: 640px) { .ch-rate .big { font-size: 52px; } .ch-rate .ch-stars { font-size: 34px; letter-spacing: 2px; } }

/* ---- Chip ---------------------------------------------------------------- */
.ch-chip { display: inline-block; font-size: 12px; font-weight: 600; padding: 3px 10px; border-radius: var(--radius-pill);
           color: var(--ok); background: var(--ok-soft); }
.ch-chip.warn { color: var(--warn); background: var(--warn-soft); }
.ch-chip.bad  { color: var(--bad);  background: var(--bad-soft); }
.ch-chip.idle { color: var(--ink-2); background: var(--idle-soft); }      /* neutral: ink-2 text, because the idle grey alone is too faint at 12px */

/* ---- Notice -------------------------------------------------------------- */
.ch-notice { padding: 10px 14px; border-radius: var(--radius-sm); margin: 0 0 16px; font-size: 14px; background: var(--accent-soft); color: var(--ink); }
.ch-notice.err { background: var(--bad-soft); color: var(--bad); }
.ch-notice.ok  { background: var(--ok-soft);  color: var(--ok); }

/* ---- Review card --------------------------------------------------------- */
.ch-grid  { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 14px; }
@media (max-width: 640px) { .ch-grid { grid-template-columns: 1fr; } }
.ch-review {
  position: relative; display: flex; flex-direction: column; gap: 12px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 20px 20px 18px;
  transition: transform .2s, border-color .2s, box-shadow .2s;
  animation: ch-rise .55s var(--ease-out) both; animation-delay: calc(var(--i, 0) * 90ms + 500ms);
}
.ch-review:hover { transform: translateY(-3px); border-color: var(--accent); box-shadow: var(--shadow-hover); }
.ch-review::before { content: "\201C"; position: absolute; top: 6px; right: 16px; font: 700 64px/1 Georgia, serif; color: var(--accent-soft); }  /* decorative quote mark: the one serif glyph */
.ch-review .ch-stars { font-size: 16px; letter-spacing: 2px; }
.ch-review .ch-stars span.s { display: inline-block; animation: ch-star .5s cubic-bezier(.2, 1.6, .4, 1) both;      /* stars pop in one by one: chStars(n, true) */
                              animation-delay: calc(var(--i, 0) * 90ms + 650ms + var(--k) * 70ms); }
.ch-review p { margin: 0; white-space: pre-wrap; color: var(--ink); position: relative; }
.ch-review p.none { color: var(--ink-3); font-style: italic; }
.ch-who { display: flex; align-items: center; gap: 10px; margin-top: auto; }
.ch-who .name { font-weight: 600; line-height: 1.2; }
.ch-who .when { font-size: 12px; color: var(--ink-3); }
.ch-av { width: 36px; height: 36px; border-radius: 50%; display: grid; place-items: center; flex: none; color: #fff; font-weight: 700; font-size: 14px; }  /* colour: chAvatarColor(name) */
.ch-empty { text-align: center; color: var(--ink-3); padding: 40px 16px; border: 1px dashed var(--line); border-radius: var(--radius-lg); }

/* ---- Meter: bright red liquid with a flowing stripe, shine and bubbles ---- */
.ch-meter { position: relative; height: 36px; border-radius: var(--radius-pill); background: var(--surface-2); border: 1px solid var(--line); }
.ch-meter .fill {
  position: relative; height: 100%; width: 0; min-width: 36px; border-radius: var(--radius-pill); overflow: hidden;
  background: linear-gradient(90deg, var(--meter-red-a), var(--meter-red-b));
  transition: width 1.4s var(--ease-out);                /* fillMeter(fill, percent) animates it */
  box-shadow: 0 0 14px rgba(255, 40, 30, .45); animation: ch-glow 2.4s ease-in-out infinite alternate;
}
.ch-meter .fill::before { content: ""; position: absolute; inset: 0; background-size: 28.3px 28.3px; animation: ch-flow 1.1s linear infinite;
  background-image: repeating-linear-gradient(-45deg, rgba(255,255,255,.08) 0 10px, transparent 10px 20px); }
.ch-meter .fill::after  { content: ""; position: absolute; left: 0; right: 0; top: 0; height: 48%; border-radius: 999px 999px 0 0;
  background: linear-gradient(rgba(255,255,255,.32), rgba(255,255,255,0)); }
.ch-bubble { position: absolute; bottom: -10px; left: var(--x); width: var(--s); height: var(--s); border-radius: 50%; z-index: 2;
  background: radial-gradient(circle at 30% 30%, rgba(255,255,255,.75), rgba(255,255,255,.22) 60%); border: 1.5px solid rgba(255,255,255,.95);
  animation: ch-bub var(--d) ease-in infinite; animation-delay: var(--dl); }        /* makeBubbles(fill) creates these; always show them */
.ch-tick { position: absolute; bottom: 0; background: var(--line); z-index: 1; border-radius: 1px; }   /* divider marks across the meter: buildMeterScale() */
.ch-meter-scale { position: relative; height: 26px; margin-top: 6px; font-size: 12px; color: var(--ink-3); font-variant-numeric: tabular-nums; line-height: 1.1; }
.ch-meter-scale span { position: absolute; transform: translateX(-50%); }
.ch-meter-scale span:first-child { transform: none; }
.ch-meter-scale span:last-child  { transform: translateX(-100%); }

/* ---- Highlight cell: gold border, red edge, pulsing glow (+ confetti) ----- */
/* One of the two exceptions to "cards are plain white" (the other is .ch-panel.tint): use it once per view, for the single thing to notice. */
.ch-latest {
  position: relative; overflow: hidden; display: grid; grid-template-columns: 1fr auto; gap: 8px 26px; align-items: center;
  padding: 18px 26px; border: 2px solid var(--gold-fill); border-left: 8px solid var(--alert-red); border-radius: var(--radius-lg);
  background: linear-gradient(90deg, var(--wash-gold), var(--surface) 55%, var(--accent-soft)); box-shadow: var(--shadow-glow);
  animation: ch-rise var(--t-rise) var(--ease-out) both, ch-ping 2.6s ease-in-out .8s infinite;
}
.ch-latest > *:not(.ch-confetti) { position: relative; z-index: 1; }
.ch-latest .lbl  { font-size: 13px; font-weight: 800; text-transform: uppercase; letter-spacing: .12em; color: var(--label-red); }   /* the one red label */
.ch-latest p     { margin: 4px 0 0; font-size: 24px; line-height: 1.35; font-weight: 700; color: var(--ink);
                   display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.ch-latest p.none{ font-style: italic; color: var(--ink-2); font-weight: 600; }
.ch-latest .who  { display: flex; align-items: center; justify-content: flex-end; gap: 16px; text-align: right; min-width: 0; }
.ch-latest .big  { font-size: 30px; font-weight: 800; letter-spacing: -.01em; line-height: 1.1; }
.ch-latest .sub, .shell .ch-latest .sub { margin: 4px 0 0; font-size: 14px; color: var(--ink-2); }   /* `.shell .sub` has its own margins */
/* A person's headshot, or (no photo on file) their initials in a circle the same size */
.ch-latest .photo, .ch-latest .av { width: 92px; height: 92px; border-radius: 50%; flex: none; border: 4px solid var(--gold-ring); box-shadow: 0 6px 18px rgba(11,61,107,.3); }
.ch-latest .photo { object-fit: cover; }
.ch-latest .av { display: grid; place-items: center; color: #fff; font-weight: 800; font-size: 34px; }
.ch-confetti { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
.ch-confetti i { position: absolute; top: -14px; opacity: 0; animation: ch-confetti linear infinite; }   /* startConfetti(box) */
@media (max-width: 640px) {
  .ch-latest { grid-template-columns: 1fr; } .ch-latest .who { justify-content: flex-start; text-align: left; }
  .ch-latest p { font-size: 17px; } .ch-latest .big { font-size: 22px; }
  .ch-latest .photo, .ch-latest .av { width: 64px; height: 64px; font-size: 24px; }
}

/* ---- Ranking columns: grow upward; a circle (photo or count) sits on each tip */
/* Build with chRankColumns(el, items, {height}); it sizes the circles, names and bars to fit. */
.ch-cols { --ball: 56px; --cname: 20px; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(54px, 1fr); align-items: end; gap: 6px;
           height: 340px; border-bottom: 3px solid var(--line); overflow-x: auto; overflow-y: hidden; padding-top: 4px; }
.ch-col  { height: 100%; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; min-width: 0; }
.ch-stem { position: relative; width: min(46%, 64px); min-width: 22px; height: 0; border-radius: 14px 14px 0 0;
           background: linear-gradient(0deg, var(--accent), var(--accent-bright));
           transition: height 1.2s var(--ease-out); transition-delay: calc(var(--i, 0) * 80ms + 200ms); }   /* set style.height after render to animate */
.ch-stem::after { content: ""; position: absolute; inset: 0; border-radius: inherit; background: linear-gradient(90deg, rgba(255,255,255,.28), rgba(255,255,255,0) 55%); }
.ch-ball { position: absolute; left: 50%; top: 0; transform: translate(-50%, -50%); width: var(--ball); height: var(--ball); border-radius: 50%;
           display: grid; place-items: center; z-index: 2; background: var(--accent); color: #fff; font-weight: 800; font-size: calc(var(--ball) * .46);
           font-variant-numeric: tabular-nums; border: 3px solid #fff; box-shadow: 0 4px 14px rgba(11,61,107,.35); }
/* Top three: only the FIRST place bar turns gold; places 2 and 3 keep blue bars with a silver / bronze circle. */
.ch-col.r1 .ch-stem { background: linear-gradient(0deg, var(--rank1-stem), var(--gold-fill)); }
.ch-col.r1 .ch-ball { background: linear-gradient(135deg, var(--rank1-a), var(--gold-ring)); color: var(--rank1-ink); }
.ch-col.r2 .ch-ball { background: linear-gradient(135deg, var(--rank2-a), var(--silver)); color: var(--rank2-ink); }
.ch-col.r3 .ch-ball { background: linear-gradient(135deg, var(--rank3-a), var(--bronze)); color: var(--rank3-ink); }
.ch-ball.photo { background: #fff; border: calc(var(--ball) * .045 + 2px) solid var(--accent); box-shadow: 0 8px 24px rgba(11,61,107,.4); }
.ch-ball.photo img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; display: block; }
.ch-col.r1 .ch-ball.photo { border-color: var(--gold-ring); } .ch-col.r2 .ch-ball.photo { border-color: var(--silver); } .ch-col.r3 .ch-ball.photo { border-color: var(--bronze); }
.ch-badge { position: absolute; right: calc(var(--ball) * -.08); bottom: calc(var(--ball) * -.08); min-width: calc(var(--ball) * .4); height: calc(var(--ball) * .4);
            padding: 0 calc(var(--ball) * .08); border-radius: 99px; display: grid; place-items: center; background: var(--ink); color: #fff; font-weight: 800;
            font-size: calc(var(--ball) * .26); font-variant-numeric: tabular-nums; border: 2.5px solid #fff; box-shadow: 0 2px 8px rgba(11,61,107,.4); }   /* count on a photo circle */
.ch-cname { height: var(--cnameh, 36px); display: flex; align-items: center; justify-content: center; padding-top: 8px; font-weight: 800; font-size: var(--cname);
            color: var(--ink); text-align: center; line-height: 1.1; max-width: 100%; overflow-wrap: anywhere; }
.ch-col.tilt .ch-cname { writing-mode: vertical-rl; transform: rotate(180deg); align-items: flex-start; white-space: nowrap; }   /* names too wide: stand on end */
@media (max-width: 640px) { .ch-cols { grid-auto-columns: minmax(44px, 1fr); } }                  /* many columns on a phone: scroll sideways inside the panel */

/* ---- Mascot ("the Hero"): bobs gently; one per view ----------------------- */
.ch-mascot { height: 100px; width: auto; transform-origin: 50% 100%;
             animation: ch-bob 2.6s ease-in-out infinite alternate, ch-pop .8s var(--ease-pop) .35s backwards; }   /* `backwards`, not `both`: a held pop would freeze the bob */
/* Standing in a corner of a panel: give the panel `.has-mascot` and the image `.corner` (keep him 56-130px: set --mh).
   Add bottom padding to the panel so he never covers text or numbers. */
.ch-panel.has-mascot { position: relative; padding-bottom: calc(var(--mh, 100px) + 8px); }
.ch-mascot.corner { position: absolute; right: 2%; bottom: 0; height: var(--mh, 100px); }
@media (max-width: 640px) { .ch-panel.has-mascot { --mh: 72px; } }

/* ---- TV deck: slides that swap sideways on a timer ------------------------ */
/* chDeck({...}) drives these. The first slide rests on the left, the second on the right. */
.ch-deck  { position: relative; display: grid; overflow-x: clip; padding: 4px 4px 14px; margin: -4px -4px 0; }
.ch-slide { grid-area: 1 / 1; min-width: 0; visibility: visible; transition: transform var(--t-swap) var(--ease-swap), opacity var(--t-swap) ease; }
.ch-slide.off-left, .ch-slide.off-right { opacity: 0; visibility: hidden; pointer-events: none;
  transition: transform var(--t-swap) var(--ease-swap), opacity var(--t-swap) ease, visibility 0s linear var(--t-swap); }
.ch-slide.off-left  { transform: translateX(-104%); }
.ch-slide.off-right { transform: translateX(104%); }
.ch-dots  { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 8px; }
.ch-dots button { border: 1px solid var(--line); background: var(--surface); color: var(--ink-3); font: 600 12px var(--font); padding: 5px 14px; border-radius: var(--radius-pill); cursor: pointer; }
.ch-dots button[aria-current="true"] { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.ch-timer { display: block; width: 140px; height: 4px; border-radius: 99px; background: var(--surface-2); overflow: hidden; }
.ch-timer i { display: block; height: 100%; width: 0; background: var(--alert-red); border-radius: 99px; }

/* ---- Pop-up: one item fades in from the top and out again ----------------- */
/* chPopups({...}) shows one every 30s for 9s; never over the headline number. */
.ch-popup { position: fixed; top: 68px; left: 50%; width: min(380px, calc(100vw - 32px)); z-index: 50; opacity: 0; transform: translate(-50%, -40px); pointer-events: none;
            transition: opacity .7s ease, transform .8s cubic-bezier(.2, .9, .3, 1.1); }
.ch-popup.show { opacity: 1; transform: translate(-50%, 0); pointer-events: auto; }
.ch-popup .ch-review { box-shadow: var(--shadow-pop); border-color: var(--accent); transition: none; animation: none; }
.ch-popup .ch-review:hover { transform: none; }
.ch-popup .ch-stars { font-size: 18px; }
.ch-popup .x { position: absolute; top: 6px; right: 8px; z-index: 3; border: 0; background: none; color: var(--ink-3); font-size: 20px; line-height: 1; cursor: pointer; padding: 4px 8px; }
.ch-popup .x:hover { color: var(--ink); }

/* ---- Buttons and tables (standalone pages; inside the shell its own .btn and tables apply) */
.ch-btn { font: inherit; font-size: 14px; font-weight: 600; padding: 8px 14px; border-radius: var(--radius-sm); border: 1px solid var(--line); background: var(--surface); color: var(--ink); cursor: pointer; }
.ch-btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.ch-btn:disabled { opacity: .55; cursor: default; }
.ch-table-wrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); }
.ch-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.ch-table th { text-align: left; font-weight: 600; color: var(--ink-2); background: var(--surface-2); padding: 9px 12px; border-bottom: 1px solid var(--line); white-space: nowrap; }
.ch-table td { padding: 9px 12px; border-bottom: 1px solid var(--line); vertical-align: top; }
.ch-table tr:last-child td { border-bottom: none; }
.ch-table td.num, .ch-table th.num { text-align: right; font-variant-numeric: tabular-nums; }

/* ---- Motion library (the only keyframes dashboards should use) ----------- */
@keyframes ch-rise    { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@keyframes ch-pop     { from { opacity: 0; transform: scale(.7); }        to { opacity: 1; transform: scale(1); } }
@keyframes ch-star    { from { opacity: 0; transform: scale(0) rotate(-40deg); } to { opacity: 1; transform: none; } }
@keyframes ch-bob     { from { transform: translateY(0) rotate(-1.5deg); } to { transform: translateY(-8px) rotate(1.5deg); } }
@keyframes ch-twinkle { 0%, 100% { filter: none; } 50% { filter: drop-shadow(0 0 6px rgba(245,166,35,.8)); } }
@keyframes ch-flow    { from { background-position: 0 0; } to { background-position: 28.3px 0; } }
@keyframes ch-glow    { from { box-shadow: 0 0 8px rgba(255,40,30,.3); } to { box-shadow: 0 0 18px rgba(255,40,30,.6); } }
@keyframes ch-bub     { 0% { transform: translate(0,0) scale(.5); opacity: 0; } 15% { opacity: 1; } 85% { opacity: 1; } 100% { transform: translate(var(--dx), -50px) scale(1.1); opacity: 0; } }
@keyframes ch-ping    { 0%,100% { box-shadow: 0 0 0 0 rgba(255,201,60,0), 0 12px 34px rgba(255,59,47,.14); } 50% { box-shadow: 0 0 0 7px rgba(255,201,60,.3), 0 12px 34px rgba(255,59,47,.22); } }
@keyframes ch-confetti{ 0% { transform: translate(0,0) rotate(0); opacity: 0; } 8% { opacity: .9; } 85% { opacity: .9; } 100% { transform: translate(var(--dx), calc(var(--h) + 24px)) rotate(var(--rot)); opacity: 0; } }
