/* Português brasileiro, mobile first.
 *
 * Built for a Galaxy S22 (360x780 CSS px) and centred in a phone-width column
 * on anything bigger, so the phone is the design target rather than an
 * afterthought. Four screens carry the look: the Clash-Royale-style loader, the
 * Calm-style home, the talk screen, and the Duolingo-style end screen.
 *
 * Everything that can be touched is at least 44px, nothing sits under the
 * Android gesture bar (safe-area insets), and the whole thing is one column —
 * no layout that only works at desktop width. */

/* Nunito, vendored so it works offline and over the LAN with no CDN. One
   variable file covers 400-1000, which is what lets the headings go properly
   heavy without loading a second face. SIL Open Font License 1.1 — see
   fonts/OFL.txt. */
@font-face{
  font-family:'Nunito';font-style:normal;font-weight:400 1000;font-display:swap;
  src:url('/static/fonts/nunito-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,
    U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,
    U+2212,U+2215,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Nunito';font-style:normal;font-weight:400 1000;font-display:swap;
  src:url('/static/fonts/nunito-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,
    U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,
    U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;
}

*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}

/* Scrollbars are chrome, and this is an app that should look like an app.
   Scrolling still works everywhere — only the bar is gone. */
*{scrollbar-width:none}
*::-webkit-scrollbar{width:0;height:0;display:none}
:root{
  --bg:#f6f4f1; --card:#fff; --line:#e7e5e4; --line2:#f2f0ee;
  --ink:#161a1d; --mut:#6b7280; --fade:#9aa3ad;
  --sea:#0d3b52; --sea2:#14536f; --sky:#1c8cb1; --sand:#f2dcae;
  --acc:#12855a; --acc-bg:#eefaf4; --warn:#b45309; --warn-bg:#fffbeb;
  --bad:#c02626; --br:#e0632b; --gold:#f4b528;
  --safe-t:env(safe-area-inset-top,0px);
  --safe-b:env(safe-area-inset-bottom,0px);
  --dock-h:150px;
}
html,body{height:100%}
body{font:16px/1.5 'Nunito',ui-rounded,system-ui,-apple-system,"Segoe UI",
  Roboto,sans-serif;
  background:var(--sea);color:var(--ink);overscroll-behavior-y:none;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
/* While the loading screen owns the viewport nothing behind it may scroll —
   a scrollbar next to full-bleed key art looks like a broken page. */
html.booting,html.booting body{overflow:hidden}
.app{max-width:460px;margin:0 auto;min-height:100%;background:var(--bg);
  position:relative;overflow:hidden}
/* Tablet / big phone: still the phone column, floated on a dark ground. The
   laptop layout starts at 900px, at the bottom of this file. */
@media (min-width:520px) and (max-width:899px){
  body{background:#0a2b3d}
  .app{min-height:100dvh;box-shadow:0 0 60px rgba(0,0,0,.45)}
}
.screen{min-height:100dvh}
.pad{padding:calc(var(--safe-t) + 16px) 16px calc(var(--safe-b) + 28px)}
/* !important on purpose. Hidden must mean hidden: `.row{display:flex}` later in
   this file was quietly beating `.hide` on source order, and an ID rule beat it
   on specificity once before and left an invisible full-screen click trap. A
   class named `hide` is never the loser of a tiebreak. */
.hide{display:none !important}

h1{font-size:26px;line-height:1.15;letter-spacing:-.03em;font-weight:800}
h2{font-size:11px;text-transform:uppercase;letter-spacing:.1em;color:var(--mut);
  margin:22px 0 8px;font-weight:800}
.eyebrow{font-size:11px;text-transform:uppercase;letter-spacing:.13em;
  font-weight:800;color:var(--br);margin-bottom:3px}
.sub{color:var(--mut);font-size:14px}
.note{color:var(--fade);font-size:12.5px;margin-top:4px}
.pt{font-size:18px;line-height:1.4}
.en{color:var(--mut);font-size:14px}
.mono{font-variant-numeric:tabular-nums}
.muted{color:var(--mut)}
.err{color:var(--bad);font-size:13.5px}
hr{border:0;border-top:1px solid var(--line2);margin:14px 0}
ul{margin:6px 0 0 18px}
li{margin:3px 0}

/* ---------------------------------------------------------------- controls
 * Everything pressable moves. The timing is deliberately quick on the way down
 * and springy on the way back — that little overshoot is most of what makes a
 * button feel good under a thumb. */
button{font:inherit;font-weight:800;cursor:pointer;border:0;color:#fff;
  background:var(--ink);border-radius:13px;padding:11px 18px;
  transition:transform .16s cubic-bezier(.34,1.56,.64,1),
             box-shadow .16s ease,background .16s ease,filter .16s ease}
button:active:not(:disabled){transform:scale(.93);transition-duration:.06s}
button:disabled{opacity:.45;cursor:default}
.ghost{background:#fff;border:1px solid var(--line);color:var(--ink)}
.pill{background:#fff;border:1px solid var(--line);color:var(--ink);
  border-radius:99px;padding:9px 16px;font-size:13.5px;font-weight:800;
  min-height:42px;box-shadow:0 2px 0 rgba(22,26,29,.07)}
.pill:active:not(:disabled){transform:translateY(2px) scale(.97);box-shadow:none}
.pill.on{background:var(--ink);color:#fff;border-color:var(--ink)}

/* Icon-only controls: the pictures say it, the words were noise. */
.iconPill{width:46px;height:46px;padding:0;flex:0 0 46px;border-radius:99px;
  display:flex;align-items:center;justify-content:center}
.iconPill svg{width:22px;height:22px;fill:none;stroke:var(--ink);stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}
.iconPill.on svg{stroke:#fff}

/* The one button that ends the conversation, once it is actually over. */
.pill.pulse{background:var(--br);border-color:var(--br);color:#fff;
  animation:callToPress 1.5s ease-in-out infinite}
@keyframes callToPress{
  0%,100%{transform:scale(1);box-shadow:0 2px 0 rgba(22,26,29,.07)}
  50%{transform:scale(1.075);box-shadow:0 6px 18px rgba(224,99,43,.42)}
}
.pill.wide{width:100%;justify-content:center}
.chip{background:#fff;border:1.5px solid var(--line);color:var(--mut);
  border-radius:99px;padding:10px 18px;font-size:14px;min-height:44px}
.chip.on{border-color:var(--acc);color:var(--acc);background:var(--acc-bg)}
.row{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
input[type=text],textarea{width:100%;flex:1;min-width:0;padding:12px 14px;
  border:1px solid var(--line);border-radius:12px;font:inherit;font-size:16px;
  background:#fff;color:var(--ink)}
textarea{resize:vertical;min-height:88px}
label.lb{display:block;font-size:11px;text-transform:uppercase;
  letter-spacing:.09em;color:var(--mut);margin-bottom:6px;font-weight:800}

.iconBtn{width:44px;height:44px;padding:0;border-radius:99px;flex:0 0 44px;
  background:rgba(255,255,255,.18);backdrop-filter:blur(6px);
  border:1px solid rgba(255,255,255,.3);display:flex;align-items:center;
  justify-content:center}
.iconBtn svg{width:22px;height:22px;fill:none;stroke:#fff;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
.iconBtn.dark{background:#fff;border-color:var(--line)}
.iconBtn.dark svg{stroke:var(--ink)}
.iconBtn:active{transform:scale(.94)}

.card{background:var(--card);border:1px solid var(--line);border-radius:16px;
  padding:16px;margin-bottom:12px}
.card.acc{border-color:#bfe6d2;background:var(--acc-bg)}
.card.warn{border-color:#fcd34d;background:var(--warn-bg)}

.navRow{display:flex;gap:12px;align-items:flex-start;margin-bottom:16px}
.navTitle{flex:1;min-width:0}
.navSub{color:var(--mut);font-size:13.5px;margin-top:3px}

/* ============================================================ LOADING
   Clash Royale: art to the edges, the name across the top in a chunky plaque,
   one line of copy at the bottom over a fat progress bar. */
/* Constrained to the same column as the app: fixed+inset:0 alone would stretch
   a portrait poster across a laptop window and crop it to a smear. */
.boot{position:fixed;top:0;bottom:0;left:50%;transform:translateX(-50%);
  width:min(460px,100%);z-index:60;display:flex;flex-direction:column;
  justify-content:space-between;background:var(--sea);overflow:hidden}
.boot.hide{display:none}
.boot.out{opacity:0;transition:opacity .45s ease;pointer-events:none}
@media (min-width:520px){.boot{box-shadow:0 0 60px rgba(0,0,0,.45)}}
.bootArt{position:absolute;inset:0;background:url('/static/img/rio-load.jpg')
  center/cover no-repeat;animation:bootDrift 24s ease-in-out infinite alternate}
.bootShade{position:absolute;inset:0;background:
  linear-gradient(180deg,rgba(6,26,38,.72) 0%,rgba(6,26,38,0) 26%,
  rgba(6,26,38,0) 46%,rgba(6,26,38,.86) 84%)}
/* Gentle: enough that the screen is alive, not so much that it crops the bay
   and the city out of the picture. */
@keyframes bootDrift{from{transform:scale(1.02) translateY(.6%)}
  to{transform:scale(1.07) translateY(-.8%)}}
.bootTop{position:relative;padding:calc(var(--safe-t) + 30px) 20px 0;text-align:center}
.bootBottom{position:relative;padding:0 20px calc(var(--safe-b) + 30px);text-align:center}

.logo{display:inline-flex;flex-direction:column;align-items:center;line-height:.9}
.logo1,.logo2{font-weight:900;letter-spacing:.01em;color:#ffd76a;
  text-transform:uppercase;
  -webkit-text-stroke:2.5px #17364a;paint-order:stroke fill;
  text-shadow:0 4px 0 #17364a,0 7px 14px rgba(0,0,0,.5)}
.logo1{font-size:38px}
.logo2{font-size:26px;color:#fff;letter-spacing:.06em;margin-top:2px}

.bootMsg{color:#fff;font-size:15.5px;font-weight:700;margin-bottom:4px;
  text-shadow:0 2px 8px rgba(0,0,0,.6)}
.bootNote{color:#ffd7a8;font-size:13px;min-height:18px;margin-bottom:12px;
  text-shadow:0 2px 8px rgba(0,0,0,.6)}
.bar{position:relative;height:26px;border-radius:99px;overflow:hidden;
  background:rgba(6,26,38,.6);border:2px solid rgba(255,255,255,.55);
  box-shadow:inset 0 2px 6px rgba(0,0,0,.45)}
/* One flat blue. The bar used to carry two sliding bands of colour scrolling
   left to right forever, which read as a broken loading spinner rather than as
   progress. It now only moves when the server says a real stage started, and a
   single soft pulse of brightness is enough to show the app is not frozen. */
.bar i{display:block;height:100%;width:0;border-radius:99px;
  background:#2b8fe0;
  transition:width .55s cubic-bezier(.3,.8,.4,1);position:relative;
  animation:barBreathe 2.4s ease-in-out infinite}
@keyframes barBreathe{0%,100%{filter:brightness(1)}50%{filter:brightness(1.16)}}
.barPct{position:absolute;inset:0;display:flex;align-items:center;
  justify-content:center;font-size:13px;font-weight:900;color:#fff;
  text-shadow:0 1px 3px rgba(0,0,0,.8);font-variant-numeric:tabular-nums}
.bootErr{margin-top:14px;color:#ffb4a8;font-size:14px}
.bootErr button{margin-top:10px}
@media (prefers-reduced-motion:reduce){
  .bootArt{animation:none}.bar i{animation:none;transition:none}
  .ringFlame{animation:none}
}

/* ============================================================ HOME
   Calm: photograph bleeding off the top, the ring floating over it, the week
   underneath, then the one thing you came to press. */
.hero{position:relative;padding:calc(var(--safe-t) + 14px) 16px 26px;
  color:#fff;text-align:center;overflow:hidden}
.heroArt{position:absolute;inset:0;background:url('/static/img/rio-hero.jpg')
  center 22%/cover no-repeat}
.heroArt::after{content:'';position:absolute;inset:0;background:
  linear-gradient(180deg,rgba(8,42,60,.34) 0%,rgba(10,52,72,.5) 42%,
  var(--sea) 96%)}
.heroTop,.ringWrap,.week,.heroLine{position:relative}
.heroTop{display:flex;align-items:center;justify-content:space-between;gap:10px}
.wordmark{font-size:19px;font-weight:800;letter-spacing:-.01em;
  text-shadow:0 2px 10px rgba(0,0,0,.45)}
.wordmark b{font-weight:900}

.ringWrap{width:158px;height:158px;margin:26px auto 0;position:relative}
.streakRing{width:100%;height:100%;transform:rotate(-90deg);
  filter:drop-shadow(0 8px 20px rgba(0,0,0,.35))}
.streakRing circle{fill:none;stroke-width:11;stroke-linecap:round}
.rTrack{stroke:rgba(255,255,255,.2)}
.rFill{stroke:#7fe3c0;stroke-dasharray:326.7;stroke-dashoffset:326.7;
  transition:stroke-dashoffset 1s cubic-bezier(.2,.8,.3,1),stroke .8s ease}
.ringInner{position:absolute;inset:0;display:flex;align-items:center;
  justify-content:center;pointer-events:none}
.streakStat{text-align:center;color:#fff}
.streakLab{font-size:12px;font-weight:700;text-transform:uppercase;
  letter-spacing:.12em;opacity:.88;margin-top:2px}

/* The wrapper carries the type size, not the number, so the flame can be sized
   in em and follow every breakpoint and every digit count on its own. One set
   of numbers to keep in sync instead of two. */
.numWrap{position:relative;display:inline-flex;align-items:center;
  justify-content:center;font-size:42px;line-height:1}
.numWrap.digits-2{font-size:34px}
.numWrap.digits-3{font-size:28px}
.ringNum{position:relative;z-index:1;font-size:1em;font-weight:900;
  letter-spacing:-.05em;line-height:1;font-variant-numeric:tabular-nums;
  color:#fff;
  text-shadow:0 1px 0 rgba(120,30,0,.45),0 2px 10px rgba(80,20,0,.5)}
.ringNum.digits-3{letter-spacing:-.06em}
/* The digits are exactly 1em tall (line-height 1), so the flame is that plus
   .2em of tip above and .12em of base below: it starts just under the number
   and finishes a little over it, which is the whole of the effect. Anything
   taller stops being a number sitting in a fire and becomes a fire with a
   number in front of it. */
.ringFlame{position:absolute;left:50%;top:-.2em;height:1.32em;width:1.2em;
  transform:translateX(-50%);transform-origin:50% 88%;pointer-events:none;
  filter:drop-shadow(0 4px 14px rgba(180,50,0,.45));
  animation:flicker 2.6s ease-in-out infinite}
.ringFlame .fOuter{fill:url(#homeFlameOuter)}
.ringFlame .fInner{fill:url(#homeFlameInner);opacity:.9}
/* The number does not flicker with it. Fire moves, a count does not. */
@keyframes flicker{
  0%,100%{transform:translateX(-50%) scale(1)}
  40%{transform:translateX(-50%) scale(1.03,1.02)}
  70%{transform:translateX(-50%) scale(.98,1.04)}
}

.week{display:flex;justify-content:center;gap:9px;margin-top:20px}
.week div{width:34px;text-align:center}
/* A done day is painted with its own score (see scoreColour in app.js), so the
   week reads as how it went and not only whether it happened. The border is a
   darker shade of the same colour rather than white, or a low score ends up
   looking like a mistake in the design. */
.week .dot{width:34px;height:34px;border-radius:99px;background:rgba(255,255,255,.16);
  border:2px solid transparent;display:flex;align-items:center;justify-content:center;
  font-size:15px;font-weight:800;color:rgba(255,255,255,.75);
  transition:background .4s ease,border-color .4s ease}
.week .dot.done{color:#fff;text-shadow:0 1px 3px rgba(0,0,0,.4)}
.week .dot.today{box-shadow:0 0 0 2px rgba(255,255,255,.9)}
.week .d{font-size:11px;margin-top:5px;opacity:.75;font-weight:700}
.heroLine{margin-top:16px;font-size:14px;opacity:.92;padding:0 10px;
  text-shadow:0 2px 8px rgba(0,0,0,.35)}

.sheetTop{background:var(--bg);border-radius:22px 22px 0 0;margin-top:-14px;
  padding:20px 16px calc(var(--safe-b) + 28px);position:relative}

/* the "Special Holiday Offer" slot: the one button that matters */
/* The game-button trick: a hard shadow underneath that the button presses
   down into. It is the difference between a link and something you want to
   hit. */
.cta{display:flex;align-items:center;gap:14px;width:100%;text-align:left;
  background:linear-gradient(135deg,#16607f,#0d3b52);color:#fff;
  border-radius:18px;padding:16px 18px;margin-bottom:12px;
  box-shadow:0 5px 0 #07293a,0 12px 24px rgba(13,59,82,.26)}
.cta:active:not(:disabled){transform:translateY(5px) scale(.995);
  box-shadow:0 0 0 #07293a,0 4px 10px rgba(13,59,82,.2)}
.cta.solid{justify-content:center;background:var(--br);font-size:17px;padding:17px;
  box-shadow:0 5px 0 #a8441c,0 12px 22px rgba(224,99,43,.3)}
.cta.solid:active:not(:disabled){transform:translateY(5px) scale(.995);
  box-shadow:0 0 0 #a8441c,0 4px 10px rgba(224,99,43,.24)}
.cta:disabled{box-shadow:0 5px 0 rgba(0,0,0,.12)}
.ctaIcon{width:46px;height:46px;flex:0 0 46px;border-radius:14px;
  background:rgba(255,255,255,.16);display:flex;align-items:center;justify-content:center}
.ctaIcon svg{width:24px;height:24px;fill:none;stroke:#fff;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
.ctaText{flex:1;min-width:0}
.ctaText b{display:block;font-size:16.5px;font-weight:800}
.ctaText small{display:block;font-size:13px;opacity:.82;margin-top:2px}
.ctaChev{font-size:26px;opacity:.6;line-height:1}
/* the mission now sits under the button, so it needs the gap above it */
#missionCard:not(:empty){margin-top:16px}
.capCard{margin-top:16px}

/* Install prompt. Quiet on purpose — it is an offer, not the thing the screen
   is for, so it never competes with the start button. */
.installBar{display:flex;align-items:center;gap:12px;width:100%;text-align:left;
  background:#fff;border:1px solid var(--line);color:var(--ink);
  border-radius:15px;padding:13px 15px;margin-bottom:12px;
  box-shadow:0 2px 0 rgba(22,26,29,.06)}
.installBar:active{transform:translateY(2px);box-shadow:none}
.installBar svg{width:22px;height:22px;flex:0 0 22px;fill:none;stroke:var(--br);
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.installBar b{display:block;font-size:14.5px}
.installBar small{display:block;font-size:12.5px;color:var(--mut);
  font-weight:600;margin-top:1px}
.installSteps{margin:8px 0 0 18px;font-size:14px}
.installSteps li{margin:5px 0}

.stat{display:flex;gap:20px;flex-wrap:wrap}
.stat div span{display:block}
.stat .k{font-size:10.5px;text-transform:uppercase;letter-spacing:.08em;
  color:var(--mut);font-weight:800}
.stat .v{font-size:22px;font-weight:800;letter-spacing:-.02em}
.item{border-top:1px solid var(--line2);padding:12px 0}
.item:first-of-type{border-top:0}
.bar2{height:7px;background:var(--line2);border-radius:99px;overflow:hidden;margin-top:6px}
.bar2 i{display:block;height:100%;background:var(--acc);border-radius:99px}
.skill{padding:9px 0;border-top:1px solid var(--line2)}
.skill:first-child{border-top:0}
.skill .row{justify-content:space-between;gap:10px;align-items:baseline;
  flex-wrap:nowrap}
.skill .lab{font-size:14px;min-width:0}
.skill .sc{white-space:nowrap;flex:0 0 auto}
.skill .sc{font-size:12px;color:var(--mut);font-variant-numeric:tabular-nums}
.cat{font-size:10.5px;text-transform:uppercase;letter-spacing:.09em;
  color:var(--fade);font-weight:800;margin:16px 0 4px}
.cat:first-child{margin-top:0}

/* ============================================================ TALK SCREEN */
/* Exactly one viewport tall, never more: the conversation scrolls inside
   .talkBody so the mic is always under his thumb. min-height alone let the
   whole page grow and pushed the mic off the bottom of the screen. */
#stepRole{display:flex;flex-direction:column;height:100dvh;max-height:100dvh;
  overflow:hidden;
  background:linear-gradient(180deg,#f3f7f8 0%,#f7f3ee 60%,#f6f4f1 100%)}
.talkTop{padding:calc(var(--safe-t) + 14px) 16px 0}
.talkTop h1{font-size:23px}
.goal{font-size:13.5px;color:var(--mut);background:#fff;border:1px solid var(--line);
  border-radius:14px;padding:11px 14px;margin-bottom:6px;
  max-height:23vh;overflow-y:auto}
.goal:empty{display:none}
.goal b{color:var(--ink)}
.goal ul{margin:5px 0 0 17px}

/* One scrolling column between the header and the dock. It used to be the
   history alone that scrolled, with everything below it pinned — which meant a
   hint card had nowhere to go and got cut in half by the dock. Everything
   scrolls together now, and renderTalk parks it at the bottom, so the line he
   is answering is always the thing in view. */
.talkBody{flex:1 1 auto;min-height:0;padding:10px 16px 4px;
  display:flex;flex-direction:column;overflow-y:auto}
.avatarWrap,#sayCard,#hintCard,#micErr,#history{flex:0 0 auto}
.avatarWrap{text-align:center;margin:4px 0 14px}
.avatar{width:104px;height:104px;border-radius:99px;margin:0 auto;
  display:flex;align-items:center;justify-content:center;
  font-size:42px;font-weight:800;color:#fff;
  background:linear-gradient(150deg,#1f7fa5,#0d3b52);
  border:4px solid #fff;box-shadow:0 10px 26px rgba(13,59,82,.28)}
.avatar.speaking{animation:speak 1.4s ease-in-out infinite}
@keyframes speak{50%{transform:scale(1.045)}}
.avatarName{font-size:12px;text-transform:uppercase;letter-spacing:.1em;
  font-weight:800;color:var(--mut);margin-top:9px;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;max-width:300px;margin-left:auto;margin-right:auto}
/* Before anyone has spoken there is nothing to scroll, so the avatar and the
   opening card sit in the centre instead of at the top with a hole under them. */
.talkBody:has(#history:empty){justify-content:center}
#history{display:flex;flex-direction:column;gap:8px;margin-bottom:12px}

/* "Tap to reveal text" — the Portuguese is heard first, and revealing it is
   recorded as a struggle signal exactly like the old show-text button. */
.sayCard{background:#fff;border-radius:18px;padding:22px 18px;text-align:center;
  box-shadow:0 8px 22px rgba(22,26,29,.08);min-height:104px;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;
  animation:popIn .3s cubic-bezier(.2,.9,.3,1);cursor:pointer;
  transition:transform .16s cubic-bezier(.34,1.56,.64,1),box-shadow .16s}
.sayCard:active{transform:scale(.985);box-shadow:0 4px 12px rgba(22,26,29,.08)}
@keyframes popIn{from{opacity:0;transform:translateY(9px) scale(.985)}
  to{opacity:1;transform:none}}
.sayCard .veil{display:flex;align-items:center;gap:9px;color:var(--mut);
  font-size:15px;font-weight:600}
.sayCard .veil svg{width:20px;height:20px;fill:none;stroke:var(--mut);stroke-width:2}
.sayCard .pt{font-size:19px;font-weight:600;line-height:1.55}
.sayCard .pt .wTap{cursor:pointer;border-radius:4px;padding:0 1px;
  border-bottom:2px solid rgba(22,26,29,.12)}
.sayCard .pt .wTap.on{background:rgba(127,227,192,.35);
  border-bottom-color:var(--acc)}
.sayCard .enReveal{margin-top:12px;background:none;border:none;
  color:var(--mut);font-size:13px;text-decoration:underline;cursor:pointer;
  padding:4px 0}
.wordTip{position:fixed;z-index:80;transform:translate(-50%,-100%);
  background:#162029;color:#fff;padding:8px 12px;border-radius:10px;
  font-size:14px;font-weight:600;box-shadow:0 8px 24px rgba(0,0,0,.25);
  pointer-events:none;max-width:min(240px,90vw);text-align:center}
.sayCard .en{margin-top:6px}
.sayCard.thinking{gap:0}
#history{display:flex;flex-direction:column;gap:8px;margin-bottom:12px}
.hTurn{font-size:13.5px;padding:9px 13px;border-radius:14px;max-width:86%;
  color:var(--mut);background:#fff;border:1px solid var(--line);
  animation:popIn .26s ease-out}
.hTurn.me{align-self:flex-end;background:#eef4f7;border-color:#dae7ee;color:#3d4a52}
.hTurn .who{font-size:10px;text-transform:uppercase;letter-spacing:.09em;
  font-weight:800;color:var(--fade);margin-bottom:2px}
.hTurn .tags{margin-top:4px}

.dock{flex:0 0 auto;background:linear-gradient(180deg,
  rgba(246,244,241,0),var(--bg) 22%);padding:10px 16px calc(var(--safe-b) + 14px)}
.dockPills{display:flex;gap:7px;justify-content:center;flex-wrap:wrap;margin-bottom:12px}
.micRow{display:flex;align-items:center;justify-content:center;gap:14px}
.mic{width:88px;height:88px;border-radius:99px;padding:0;flex:0 0 88px;
  background:linear-gradient(150deg,#2b566b,#12384c);
  box-shadow:0 10px 24px rgba(18,56,76,.35);display:flex;align-items:center;
  justify-content:center;border:4px solid #fff}
.mic svg{width:34px;height:34px;fill:none;stroke:#fff;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
.mic:active:not(:disabled){transform:scale(.96)}
.mic.rec{background:linear-gradient(150deg,#e2603a,#c0341c);
  animation:recPulse 1.3s ease-in-out infinite}
@keyframes recPulse{
  0%,100%{box-shadow:0 10px 24px rgba(192,52,28,.4),0 0 0 0 rgba(224,99,43,.5)}
  60%{box-shadow:0 10px 24px rgba(192,52,28,.4),0 0 0 18px rgba(224,99,43,0)}
}
.hintBtn{width:52px;height:52px;flex:0 0 52px;border-radius:99px;padding:0;
  background:#fff;border:1px solid var(--line);
  box-shadow:0 3px 0 rgba(22,26,29,.08),0 6px 14px rgba(22,26,29,.08);
  display:flex;align-items:center;justify-content:center}
.hintBtn svg{width:30px;height:24px;fill:none;stroke:var(--br);stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round;color:var(--br)}
.hintBtn:active:not(:disabled){transform:translateY(3px) scale(.96);
  box-shadow:0 0 0 rgba(22,26,29,.08)}
/* Takes the hint button's place while recording, so the row never shifts under
   his thumb mid-sentence. */
.cancelBtn{border-color:#f3c8c8}
.cancelBtn svg{stroke:var(--bad);stroke-width:2.4}
.micLab{width:52px;font-size:12.5px;color:var(--mut);font-weight:700;line-height:1.25}
.interim{text-align:center;font-size:14px;color:var(--mut);min-height:20px;
  margin-top:8px}
#hintCard .card{margin-bottom:0}

/* ============================================================ END SCREEN
   Duolingo: one loud verdict, tiles of numbers, then the actions pinned to the
   bottom so the last thing on screen is the thing to press. */
/* Same shape as the talk screen: the result scrolls, the button that ends the
   session is always on screen — Duolingo pins it and it is the right call. */
#stepDebrief{display:flex;flex-direction:column;height:100dvh;max-height:100dvh;
  overflow:hidden;background:#fff}
.endScroll{flex:1;overflow-y:auto;padding:calc(var(--safe-t) + 22px) 16px 16px}
/* The bottom margin is the point: the checklist under the score used to run
   straight into the "Before you go" card with nothing between them, so the two
   read as one block. This is the same rhythm as the gaps everywhere else on
   the page rather than a value picked to look right once. */
.endHead{text-align:center;position:relative;margin-bottom:26px}
.burst{position:absolute;left:50%;top:78px;width:1px;height:1px}
.spark{position:absolute;width:16px;height:16px;margin:-8px;
  background:var(--gold);clip-path:polygon(50% 0,61% 39%,100% 50%,61% 61%,50% 100%,39% 61%,0 50%,39% 39%);
  opacity:0;animation:sparkPop .9s ease-out forwards}
@keyframes sparkPop{
  0%{opacity:0;transform:translate(0,0) scale(.2)}
  35%{opacity:1}
  100%{opacity:0;transform:translate(var(--dx),var(--dy)) scale(1)}
}
.scoreWrap{position:relative;width:172px;height:172px;margin:0 auto}
.ring{width:100%;height:100%;transform:rotate(-90deg)}
.ring circle{fill:none;stroke-width:10;stroke-linecap:round}
.ring .track{stroke:#eeecea}
.ring .fill{stroke:#c02626;stroke-dasharray:326.7;stroke-dashoffset:326.7}
.scoreNum{position:absolute;inset:0;display:flex;align-items:center;
  justify-content:center;font-size:52px;font-weight:900;letter-spacing:-.04em;
  font-variant-numeric:tabular-nums}
.verdict{margin-top:14px;font-size:30px;font-weight:900;letter-spacing:-.03em;
  opacity:0;transform:translateY(8px);transition:opacity .4s,transform .4s}
.verdict.in{opacity:1;transform:none}
.verdict.good{color:var(--acc)}
.verdict.mid{color:var(--gold)}
.verdict.bad{color:var(--br)}
.scoreReason{margin:14px auto 0;max-width:340px;opacity:0;transform:translateY(6px);
  transition:opacity .45s ease,transform .45s ease}
.scoreReason.in{opacity:1;transform:none}
.checks{list-style:none;margin:0;text-align:left;display:inline-flex;
  flex-direction:column;gap:9px}
.checks li{display:flex;align-items:flex-start;gap:10px;margin:0;font-size:15.5px}
.checks .mk{font-weight:900;line-height:1.4;flex:0 0 auto}
.checks li.ok .mk{color:var(--acc)}
.checks li.no .mk{color:var(--bad)}
.checks li.no{color:var(--mut)}

/* SOS rather than a shape: he is looking for this button in the two seconds
   before he gives up on a sentence, and letters beat an icon he has to decode. */
.sosIcon text{fill:currentColor;stroke:none;font-size:11px;font-weight:900;
  letter-spacing:.06em;font-family:inherit}

/* One row above the finish button: a label and three choices. Colour carries
   the meaning before the words do, and the cross is a real option rather than
   an afterthought, because most sessions he will not want to move anything.
   Nothing here is ever disabled: he can change his mind until he leaves. */
.diffRow{display:flex;align-items:center;gap:7px;margin-bottom:10px;
  transition:transform .28s cubic-bezier(.4,0,.9,.3),opacity .24s ease,
             margin-bottom .28s ease,max-height .28s ease;
  max-height:60px;overflow:hidden}
/* Choosing the cross means "leave it alone", so the row leaves. It drops
   downward and out rather than blinking off, which reads as dismissed instead
   of as something that broke. */
.diffRow.dismissed{transform:translateY(24px);opacity:0;max-height:0;
  margin-bottom:0;pointer-events:none}
.diffLab{flex:1;font-size:13px;font-weight:800;color:var(--mut)}
.diffRow .pill{min-height:38px;padding:7px 13px;font-size:13px}
.diffEasy{color:var(--acc);border-color:#bfe6d2}
.diffHard{color:var(--bad);border-color:#f0c4c4}
.diffSame{color:var(--mut);flex:0 0 auto;padding:7px 12px}
/* The selected one fills in rather than just darkening its text: at a glance on
   a phone, a border change is not a state change. */
.diffEasy.picked{background:var(--acc);border-color:var(--acc);color:#fff}
.diffHard.picked{background:var(--bad);border-color:var(--bad);color:#fff}
.diffSame.picked{background:var(--ink);border-color:var(--ink);color:#fff}

/* Bottom-right of the mission card, out of the way of the text but obviously
   attached to it. */
.missionBox{position:relative;padding-bottom:16px}
.swapBtn{position:absolute;right:10px;bottom:10px;width:34px;height:34px;
  padding:0;border-radius:99px;background:#fff;border:1px solid #bfe6d2;
  color:var(--acc);font-size:17px;font-weight:900;line-height:1;
  display:flex;align-items:center;justify-content:center}
.swapBtn:active:not(:disabled){transform:scale(.92)}
.swapBtn.spin{animation:swapSpin .9s linear infinite}
@keyframes swapSpin{to{transform:rotate(360deg)}}

.endActions{padding:12px 16px calc(var(--safe-b) + 14px);
  border-top:1px solid var(--line);background:#fff;display:flex;
  flex-direction:column;gap:9px}

.rehearse .item{border-left:3px solid var(--line);padding-left:13px;
  border-top:0;margin-top:14px}
.rehearse .item.done{border-left-color:var(--acc)}
.rehearse .broke{font-size:13px;color:var(--mut)}
.rehearse .heard{font-size:12.5px;color:var(--mut);margin-top:6px}
.rehearse .heard b{color:var(--ink)}
.rehearse .attempt{margin-top:10px;display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.rehearse .verdictLine{font-size:13px;font-weight:700}
.rehearse .verdictLine.ok{color:var(--acc)}
.rehearse .verdictLine.mid{color:var(--warn)}

.pair{display:flex;flex-direction:column;gap:6px}
.pair-row{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;
  padding:8px 11px;border-radius:10px;border-left:3px solid transparent}
.pair-row.bad{background:#fef2f2;border-left-color:var(--bad)}
.pair-row.good{background:var(--acc-bg);border-left-color:var(--acc)}
.pair-row .pt{font-weight:700}
.pair-row .en{font-size:12.5px}

.tag{display:inline-block;font-size:10px;font-weight:800;padding:2px 8px;
  border-radius:99px;text-transform:uppercase;letter-spacing:.06em}
.tag.ok{background:var(--acc);color:#fff}
.tag.mid{background:#fcd34d;color:#78350f}
.tag.no{background:var(--bad);color:#fff}
.tag.grey{background:#e7e5e4;color:var(--mut)}

/* Tighter, darker and quicker than they were. Grey dots drifting slowly apart
   read as the app having given up; this reads as somebody typing. */
.dots{display:inline-flex;gap:4px;align-items:center;padding:4px 0}
.dots i{width:8px;height:8px;border-radius:99px;background:#1c1917;
  animation:dotjump .72s infinite ease-in-out}
.dots i:nth-child(2){animation-delay:.11s}
.dots i:nth-child(3){animation-delay:.22s}
@keyframes dotjump{0%,62%,100%{transform:translateY(0);opacity:.55}
  31%{transform:translateY(-8px);opacity:1}}
@media (prefers-reduced-motion:reduce){.dots i{animation:none;opacity:.6}}

/* ============================================================ ONBOARDING
   Cinematic prologue (loading blue → white → blue → white), then questions.
   Motion is deliberately premium: staggered words, soft slide, quiet reward. */
#onboarding{display:flex;flex-direction:column;height:100dvh;max-height:100dvh;
  overflow:hidden;background:var(--sea);position:relative}
.obPhase{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;
  position:relative}
.obPhase.hide{display:none}

/* ---- story prologue --------------------------------------------------- */
#obStory{position:absolute;inset:0;z-index:12;background:var(--sea);
  cursor:pointer;touch-action:manipulation;
  transition:background .45s ease,opacity .95s ease;opacity:1}
/* Whole prologue fades so the quiz under it can actually show through. */
#obStory.fading-out{opacity:0;pointer-events:none}
#obStory.hide{display:none;opacity:1}
.obStoryVignette{position:absolute;inset:0;z-index:3;pointer-events:none;
  background:radial-gradient(ellipse 80% 70% at 50% 46%,
    transparent 30%, rgba(0,0,0,.18) 100%);
  opacity:.55;transition:opacity .5s ease}
#obStory[data-tone="white"] .obStoryVignette{
  background:radial-gradient(ellipse 80% 70% at 50% 46%,
    transparent 40%, rgba(13,59,82,.08) 100%);opacity:.9}

.obStoryPips{position:absolute;top:calc(var(--safe-t) + 18px);left:0;right:0;
  z-index:6;display:flex;justify-content:center;gap:7px;pointer-events:none}
.obStoryPips i{width:7px;height:7px;border-radius:99px;background:rgba(255,255,255,.22);
  transition:transform .35s cubic-bezier(.2,1.2,.3,1),background .35s,width .35s}
.obStoryPips i.on{width:22px;background:#ffd76a;transform:scale(1.05);
  box-shadow:0 0 12px rgba(255,215,106,.55)}
.obStoryPips i.done{background:rgba(255,215,106,.55)}
#obStory[data-tone="white"] .obStoryPips i{background:rgba(22,26,29,.14)}
#obStory[data-tone="white"] .obStoryPips i.on{
  background:var(--br);box-shadow:0 0 12px rgba(224,99,43,.4)}
#obStory[data-tone="white"] .obStoryPips i.done{background:rgba(224,99,43,.45)}

.obStoryStage{position:absolute;inset:0;overflow:hidden}
.obStoryPanel{position:absolute;inset:0;display:flex;align-items:center;
  justify-content:center;padding:calc(var(--safe-t) + 48px) 30px calc(var(--safe-b) + 96px);
  z-index:1;will-change:transform,opacity}
.obStoryPanel.under{z-index:0;transform:scale(.985)}
.obStoryPanel.tone-sea{background:radial-gradient(120% 90% at 50% 35%,#14536f 0%,var(--sea) 58%,#0a2a3d 100%);
  color:#fff}
.obStoryPanel.tone-white{background:radial-gradient(120% 90% at 50% 40%,#fff 0%,#f6f4f1 70%,#ebe6df 100%);
  color:var(--ink)}

.obStoryText{font-size:clamp(27px,7.4vw,38px);font-weight:800;line-height:1.2;
  letter-spacing:-.035em;text-align:center;max-width:15.5em;margin:0}
.obStoryText .obW{display:inline-block;opacity:0;
  transform:translateY(18px) scale(.94);filter:blur(5px);
  transition:opacity .7s cubic-bezier(.2,.8,.2,1),
    transform .85s cubic-bezier(.16,1,.3,1),
    filter .75s ease;
  transition-delay:calc(var(--i) * 55ms + 80ms)}
.obStoryPanel.in .obStoryText .obW,
.obStoryPanel.ready .obStoryText .obW{opacity:1;transform:none;filter:none}
.obStoryText b{font-weight:950;color:var(--br);
  text-shadow:0 0 28px rgba(224,99,43,.35)}
.obStoryPanel.tone-sea .obStoryText b{color:#ffd76a;
  text-shadow:0 0 32px rgba(255,215,106,.45)}
/* Soft lift when the emphasis lands */
.obStoryPanel.in .obStoryText b{animation:obEmph 1.1s cubic-bezier(.2,1.2,.3,1) both;
  animation-delay:calc(var(--emph-i, 8) * 55ms + 200ms)}
@keyframes obEmph{
  0%{transform:scale(1)}
  55%{transform:scale(1.08)}
  100%{transform:scale(1)}
}

/* Leaving panel exits off the left edge (opposite of the former right exit). */
.obStoryPanel.slide-out{transform:translateX(-110%) scale(.97);
  opacity:.95;
  transition:transform .78s cubic-bezier(.16,1,.3,1),opacity .5s ease;z-index:2}
.obStoryPanel.under.settle{transform:scale(1);
  transition:transform .7s cubic-bezier(.16,1,.3,1) .05s}

/* Soft fade into the last white beat */
.obStoryPanel.fade-out{opacity:0;transform:scale(1.03);
  transition:opacity 1.15s ease,transform 1.2s ease;z-index:1}
.obStoryPanel.fade-in{opacity:0;transform:scale(.97);z-index:2;
  transition:opacity 1.15s ease,transform 1.25s cubic-bezier(.16,1,.3,1)}
.obStoryPanel.fade-in.in{opacity:1;transform:scale(1)}

/* Flash on advance — a tiny dopamine spark */
.obStoryFlash{position:absolute;inset:0;z-index:8;pointer-events:none;
  background:radial-gradient(circle at 50% 50%,rgba(255,255,255,.18),transparent 55%);
  opacity:0}
.obStoryFlash.go{animation:obFlash .42s ease-out forwards}
@keyframes obFlash{0%{opacity:.9}100%{opacity:0}}

.obContinue{position:absolute;left:50%;bottom:calc(var(--safe-b) + 26px);
  transform:translateX(-50%) scale(.88);z-index:5;
  display:flex;flex-direction:column;align-items:center;gap:10px;
  background:transparent;border:0;color:inherit;font:inherit;
  padding:10px 16px;min-height:52px;opacity:0;pointer-events:none;
  transition:opacity .55s ease,transform .55s cubic-bezier(.16,1,.3,1)}
.obContinue.show{opacity:1;pointer-events:auto;transform:translateX(-50%) scale(1)}
.obContinue.pulse{animation:obPulse 1.7s ease-in-out infinite}
.obContinueHint{font-size:11.5px;font-weight:800;letter-spacing:.16em;
  text-transform:uppercase;opacity:.7}
.obContinueDot{width:10px;height:10px;border-radius:99px;
  background:currentColor;opacity:.85;
  box-shadow:0 0 0 0 currentColor}
#obStory[data-tone="white"] .obContinue{color:var(--ink)}
#obStory[data-tone="sea"] .obContinue,
#obStory:not([data-tone]) .obContinue{color:rgba(255,255,255,.9)}
#obStory[data-tone="sea"] .obContinueDot{background:#ffd76a;
  box-shadow:0 0 14px rgba(255,215,106,.55)}
#obStory[data-tone="white"] .obContinueDot{background:var(--br);
  box-shadow:0 0 14px rgba(224,99,43,.4)}
@keyframes obPulse{
  0%,100%{transform:translateX(-50%) scale(1);opacity:.72}
  50%{transform:translateX(-50%) scale(1.07);opacity:1}
}
.obContinue.pulse .obContinueDot{animation:obDotPulse 1.7s ease-in-out infinite}
@keyframes obDotPulse{
  0%,100%{transform:scale(1);box-shadow:0 0 0 0 currentColor}
  50%{transform:scale(1.25);box-shadow:0 0 0 10px transparent}
}

/* ---- quiz ------------------------------------------------------------- */
#obQuiz{background:linear-gradient(180deg,#eef4f6 0%,var(--bg) 42%)}
.obTop{display:flex;align-items:center;gap:12px;
  padding:calc(var(--safe-t) + 16px) 16px 4px}
.obDots{display:flex;gap:6px;flex:1;justify-content:center;padding-right:0}
.obDots i{width:22px;height:5px;border-radius:99px;background:var(--line);
  transition:background .3s,width .3s}
.obDots i.on{background:var(--br);width:30px}
.obDots i.done{background:#f0b18e}
.obBody{flex:1 1 auto;min-height:0;overflow-y:auto;padding:12px 16px 8px}
.obStep{font-size:12px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;
  color:var(--mut);margin-bottom:8px}
.obBody h1{font-size:27px;margin-bottom:8px;letter-spacing:-.02em}
.obBody .sub{margin-bottom:18px;font-size:15px;line-height:1.45}
.obFoot{flex:0 0 auto;padding:10px 16px calc(var(--safe-b) + 16px);
  display:flex;flex-direction:column;gap:8px;
  background:linear-gradient(180deg,transparent,var(--bg) 28%)}

.obChoice{display:flex;flex-direction:column;gap:9px}
.obChoice button{background:#fff;border:2px solid var(--line);color:var(--ink);
  text-align:left;font-size:16px;font-weight:700;padding:15px 16px;
  border-radius:15px;min-height:56px;box-shadow:0 2px 0 rgba(22,26,29,.06);
  transition:border-color .15s,background .15s,transform .1s,box-shadow .15s}
.obChoice button.on{border-color:var(--br);background:#fff6f1;color:var(--br);
  box-shadow:0 0 0 3px rgba(224,99,43,.12)}
.obChoice button.on::after{content:'✓';float:right;font-weight:900}
.obChoice button:active{transform:translateY(2px) scale(.995);box-shadow:none}
.obTags{display:flex;flex-wrap:wrap;gap:8px}
.obTags button{background:#fff;border:2px solid var(--line);color:var(--ink);
  border-radius:99px;padding:11px 15px;font-size:14.5px;font-weight:700;min-height:46px;
  transition:border-color .15s,background .15s,transform .12s}
.obTags button.on{border-color:var(--br);background:#fff6f1;color:var(--br);
  transform:scale(1.03)}
.obExtra{margin-top:16px}
.obExtraLab{font-size:14.5px;font-weight:800;letter-spacing:-.01em;
  margin-bottom:8px;color:var(--ink);line-height:1.3}
.obExtra input[type=text],.obExtra textarea{width:100%}
.obExtra textarea{min-height:88px;font-size:16.5px}
.obExtra input[type=text]{font-size:16.5px;padding:14px}
#obField{opacity:0;transform:translateY(8px);
  transition:opacity .35s cubic-bezier(.16,1,.3,1),transform .4s cubic-bezier(.16,1,.3,1)}
#obField.in{opacity:1;transform:none}
#obField textarea{min-height:120px;font-size:16.5px}
#obField input[type=text]{font-size:16.5px;padding:14px}

@media (prefers-reduced-motion:reduce){
  .obStoryText .obW{transition:none;opacity:1;transform:none;filter:none}
  .obStoryPanel.slide-out,.obStoryPanel.fade-out,.obStoryPanel.fade-in,
  .obStoryPanel.under.settle{transition:none}
  #obStory{transition:none}
  #obStory.fading-out{opacity:0}
  .obContinue.pulse,.obContinue.pulse .obContinueDot,
  .obStoryPanel.in .obStoryText b,.obStoryFlash.go{animation:none}
  .obContinue.show{opacity:.9;transform:translateX(-50%) scale(1)}
  #obField{transition:none;opacity:1;transform:none}
  #obField.in{opacity:1;transform:none}
}

/* ============================================================ PAYWALL */
#paywall{display:flex;flex-direction:column;height:100dvh;max-height:100dvh;
  overflow:hidden;background:var(--sea);color:#fff}
.payArt{position:absolute;inset:0;background:url('/static/img/rio-hero.jpg')
  center 26%/cover no-repeat}
.payArt::after{content:'';position:absolute;inset:0;background:
  linear-gradient(180deg,rgba(8,42,60,.5) 0%,rgba(10,45,64,.86) 42%,var(--sea) 78%)}
.payBody{position:relative;flex:1 1 auto;min-height:0;overflow-y:auto;
  padding:calc(var(--safe-t) + 34px) 20px 10px;display:flex;flex-direction:column;
  justify-content:flex-end}
.payBody h1{font-size:34px;margin:2px 0 16px;text-shadow:0 2px 14px rgba(0,0,0,.35)}
.payBody .eyebrow{color:#ffd7a8}
.payList{list-style:none;margin:0 0 20px;display:flex;flex-direction:column;gap:11px}
.payList li{display:flex;gap:11px;font-size:15.5px;line-height:1.4;opacity:.95}
.payList li::before{content:'✓';color:#7fe3c0;font-weight:900;flex:0 0 auto}
.payPrice{display:flex;align-items:baseline;gap:9px;margin-bottom:6px}
.payPrice b{font-size:32px;font-weight:900;letter-spacing:-.03em}
.payPrice span{font-size:13.5px;opacity:.8}
.payFoot{position:relative;flex:0 0 auto;
  padding:10px 20px calc(var(--safe-b) + 16px);display:flex;
  flex-direction:column;gap:9px}
.payFoot .pill{background:rgba(255,255,255,.14);border-color:rgba(255,255,255,.28);
  color:#fff;box-shadow:none}
#payTest{color:#ffd7a8}
#payErr{color:#ffb4a8}

/* ============================================================ menu sheet */
.scrim{position:fixed;inset:0;z-index:44;background:rgba(10,26,36,.5);
  opacity:0;transition:opacity .25s}
.scrim.in{opacity:1}
.sheet{position:fixed;left:0;right:0;bottom:0;z-index:45;margin:0 auto;
  max-width:460px;background:#fff;border-radius:22px 22px 0 0;
  padding:8px 12px calc(var(--safe-b) + 16px);display:flex;flex-direction:column;
  transform:translateY(100%);transition:transform .3s cubic-bezier(.2,.9,.3,1)}
.sheet.in{transform:none}
.grab{width:42px;height:4px;border-radius:99px;background:var(--line);
  margin:6px auto 10px}
.sheet button{background:none;color:var(--ink);text-align:left;font-size:16px;
  padding:15px 14px;border-radius:13px;min-height:52px}
.sheet button:active{background:var(--line2)}
.sheet button.on{background:var(--ink);color:#fff}

/* ============================================================ DEV NAV
   Deliberately ugly. It is scaffolding, and it should never be mistaken for
   part of the app in a screenshot. */
#devTab{position:fixed;left:10px;bottom:calc(var(--safe-b) + 10px);z-index:70;
  background:#111827;color:#7fe3c0;font-size:11px;font-weight:900;
  letter-spacing:.1em;padding:8px 11px;border-radius:8px;opacity:.75;
  box-shadow:0 4px 14px rgba(0,0,0,.3)}
#devTab:active{transform:scale(.94)}
#devPanel{position:fixed;inset:auto 0 0 0;z-index:71;max-height:86dvh;
  overflow-y:auto;margin:0 auto;max-width:460px;
  background:#111827;color:#e5e7eb;border-radius:16px 16px 0 0;
  padding:12px 14px calc(var(--safe-b) + 16px);
  font:13px/1.45 ui-monospace,SFMono-Regular,Menlo,monospace;
  box-shadow:0 -14px 40px rgba(0,0,0,.5)}
.devHead{display:flex;justify-content:space-between;align-items:center;
  margin-bottom:10px}
.devHead b{color:#7fe3c0;letter-spacing:.12em;font-size:12px}
#devPanel .pill{background:#1f2937;border:1px solid #374151;color:#e5e7eb;
  font-family:inherit;font-size:12.5px;font-weight:700;min-height:38px;
  text-align:left;box-shadow:none;padding:9px 12px}
#devPanel .pill:active{background:#374151;transform:none}
#devPanel .pill.on{background:#7fe3c0;color:#062018;border-color:#7fe3c0}
#devPanel .pill.wide{margin-bottom:6px}
.devGroup{margin-bottom:14px;display:flex;flex-direction:column}
.devLabel{font-size:10.5px;text-transform:uppercase;letter-spacing:.12em;
  color:#9ca3af;margin-bottom:6px;font-weight:800}
.devNow{background:#0b1220;border:1px solid #1f2937;border-radius:9px;
  padding:9px 11px;margin-bottom:14px;white-space:pre-wrap;color:#9ca3af;
  font-size:11.5px;line-height:1.55}
.devNow b{color:#7fe3c0}
.devOut{background:#0b1220;border:1px solid #1f2937;border-radius:9px;
  padding:10px;margin-top:6px;max-height:44dvh;overflow:auto;font-size:11px;
  white-space:pre-wrap;word-break:break-word;color:#cbd5e1}

/* ============================================================ streak fire */
#fireOverlay{position:fixed;inset:0;z-index:50;display:flex;align-items:center;
  justify-content:center;opacity:0;cursor:pointer;transition:opacity .35s ease;
  pointer-events:none;
  background:radial-gradient(circle at 50% 42%,
    rgba(120,44,17,.96) 0%, rgba(23,20,18,.99) 62%)}
#fireOverlay.in{opacity:1;pointer-events:auto}
.fireInner{display:flex;flex-direction:column;align-items:center;gap:2px;
  transform:scale(.35);opacity:0}
#fireOverlay.in .fireInner{animation:firePop .75s cubic-bezier(.2,1.5,.4,1) forwards}
.fireArt{width:132px;height:165px;filter:drop-shadow(0 0 38px rgba(249,115,22,.8))}
#fireOverlay.in .fireArt{animation:fireFlicker 1.6s ease-in-out .6s infinite}
#fireOverlay.in .fInner{animation:fireCore 1.05s ease-in-out infinite;
  transform-origin:32px 62px}
@keyframes fireCore{0%,100%{transform:scaleY(1)}50%{transform:scaleY(.86)}}
.fireNum{font-size:64px;font-weight:900;color:#fff;letter-spacing:-.04em;
  font-variant-numeric:tabular-nums;margin-top:8px}
.fireLab{color:#fed7aa;font-size:13px;text-transform:uppercase;
  letter-spacing:.12em;font-weight:800}
@keyframes firePop{0%{transform:scale(.35);opacity:0}
  60%{transform:scale(1.12);opacity:1}100%{transform:scale(1);opacity:1}}
@keyframes fireFlicker{0%,100%{transform:scale(1) rotate(-1deg)}
  50%{transform:scale(1.06) rotate(1.5deg)}}
@media (prefers-reduced-motion:reduce){
  #fireOverlay,.fireInner{transition:none}
  #fireOverlay.in .fireInner{animation:none;transform:none;opacity:1}
  #fireOverlay.in .fireArt,#fireOverlay.in .fInner{animation:none}
  .pill.pulse,.bar i::after{animation:none}
  .verdict,.scoreReason{transition:none}
  .spark{display:none}
  .sayCard,.hTurn{animation:none}
  button{transition:none}
}

/* =========================================================== LAPTOP =========
 * Same markup, same code — the phone column stops being the shape of the app
 * and the screens spread into the width. Nothing here is a second UI to keep
 * in sync: every rule below is a re-layout of elements the phone already has.
 * ========================================================================== */
@media (min-width:900px){
  body{background:var(--bg)}
  .app{max-width:none;width:100%;min-height:100dvh;box-shadow:none;overflow:visible}

  /* ---- loading: the poster centred on a blurred blow-up of itself, which is
     what a portrait key art looks like on a wide screen without stretching. */
  .boot{left:0;transform:none;width:100%}
  .boot::before{content:'';position:absolute;inset:-60px;
    background:url('/static/img/rio-poster.jpg') center/cover no-repeat;
    filter:blur(38px) brightness(.42) saturate(1.1)}
  .bootArt{background-image:url('/static/img/rio-poster.jpg');
    background-size:contain;background-position:center 46%;animation:none;
    filter:drop-shadow(0 24px 60px rgba(0,0,0,.55))}
  .bootShade{background:linear-gradient(180deg,rgba(6,26,38,.78) 0%,
    rgba(6,26,38,.1) 22%,rgba(6,26,38,.1) 52%,rgba(6,26,38,.92) 88%)}
  .bootTop{padding-top:38px}
  .logo1{font-size:52px}
  .logo2{font-size:34px}
  .bootBottom{padding-bottom:40px}
  .bootBottom>*{max-width:620px;margin-left:auto;margin-right:auto}
  .bootMsg{font-size:17px}

  /* ---- home: the hero becomes a full-height panel, the actions sit beside it
     instead of underneath, so the fold stops existing. */
  #home{display:grid;grid-template-columns:minmax(380px,44%) minmax(0,1fr);
    min-height:100dvh}
  .hero{padding:34px 30px;display:flex;flex-direction:column;justify-content:center;
    border-radius:0}
  .heroArt{background-position:center 30%}
  .heroArt::after{background:linear-gradient(180deg,rgba(8,42,60,.42) 0%,
    rgba(10,52,72,.62) 55%,rgba(10,52,72,.82) 100%)}
  .heroTop{position:absolute;top:26px;left:30px;right:30px}
  .ringWrap{width:196px;height:196px;margin-top:14px}
  .numWrap{font-size:48px}
  .numWrap.digits-2{font-size:40px}
  .numWrap.digits-3{font-size:32px}
  .week{gap:12px;margin-top:26px}
  .heroLine{font-size:15px}
  .sheetTop{margin-top:0;border-radius:0;padding:46px 44px;
    display:flex;flex-direction:column;justify-content:center;align-items:center;
    max-height:100dvh;overflow-y:auto}
  .sheetTop>*{width:100%;max-width:560px}
  .cta{padding:20px 22px}
  .ctaText b{font-size:18px}

  /* ---- everything reachable from the menu: a readable column, not full bleed */
  .pad{padding:40px 32px 60px;max-width:820px;margin:0 auto}

  /* ---- the conversation: history down the side, the line he has to deal with
     and the mic in the middle where his eyes already are. */
  #stepRole{display:grid;grid-template-columns:minmax(0,1fr);
    grid-template-rows:auto minmax(0,1fr) auto}
  .talkTop{padding:24px 40px 8px;max-width:1180px;margin:0 auto;width:100%}
  .talkTop h1{font-size:28px}
  /* The goal is reference material, not the thing he is doing right now — it
     gets a ceiling so it can never eat the height the conversation needs. */
  .goal{max-height:88px}
  .talkBody{display:grid;gap:0 32px;padding:14px 40px 10px;
    max-width:1180px;margin:0 auto;width:100%;overflow-y:auto;
    grid-template-columns:minmax(260px,340px) minmax(0,1fr);
    grid-template-rows:auto minmax(0,1fr) auto auto;
    grid-template-areas:"hist av" "hist card" "hist err" "hist hint"}
  #history{grid-area:hist;overflow-y:auto;margin:0;padding-right:6px;
    max-height:100%;align-self:start}
  /* Nothing said yet: drop the empty left column so the stage is centred
     rather than shoved to the right of a blank space. */
  .talkBody:has(#history:empty){grid-template-columns:0 minmax(0,1fr);gap:0}
  .hTurn{max-width:100%}
  .avatarWrap{grid-area:av;align-self:start;margin:0 0 18px}
  .avatar{width:116px;height:116px;font-size:46px}
  /* start, not center: when the window is short this row can end up smaller
     than the card, and a centred item overflows *upwards* into the avatar. */
  #sayCard{grid-area:card;align-self:start;justify-self:center;
    width:100%;max-width:620px}
  .sayCard{padding:34px 26px;min-height:150px}
  .sayCard .pt{font-size:22px}
  #micErr{grid-area:err;justify-self:center}
  #hintCard{grid-area:hint;justify-self:center;width:100%;max-width:620px}
  .dock{padding:8px 40px 22px;background:none}
  .dockPills,.micRow,#typeRow,.interim{max-width:620px;margin-left:auto;margin-right:auto}
  #typeRow{display:flex}

  /* ---- the end screen: one centred column, actions still pinned */
  .endScroll{padding:40px 32px 24px}
  .endScroll>*{max-width:640px;margin-left:auto;margin-right:auto}
  .verdict{font-size:36px}
  .endActions{align-items:center}
  .endActions>*{max-width:640px;width:100%}

  /* ---- the menu: a sheet slid up from the bottom edge of a 15" screen is a
     long way from the pointer, so on a laptop it is a plain centred panel. */
  .sheet{top:50%;bottom:auto;left:50%;right:auto;width:360px;max-width:360px;
    margin:0;border-radius:18px;padding:10px;
    transform:translate(-50%,-46%) scale(.97);opacity:0;
    transition:transform .18s ease,opacity .18s ease;
    box-shadow:0 30px 80px rgba(0,0,0,.35)}
  .sheet.in{transform:translate(-50%,-50%) scale(1);opacity:1}
  .grab{display:none}
  .sheet button{font-size:15px;padding:13px;min-height:46px}
  .scrim{background:rgba(10,26,36,.42)}

  /* Hover states only exist where there is a pointer. */
  .pill:hover:not(:disabled),.ghost:hover:not(:disabled){background:#f1efec}
  .cta:hover:not(:disabled){filter:brightness(1.08)}
  .iconBtn:hover{background:rgba(255,255,255,.3)}
  .iconBtn.dark:hover{background:#f1efec}
}

/* Short laptop screens: the hero panel has to give up some height. */
@media (min-width:900px) and (max-height:760px){
  .ringWrap{width:150px;height:150px}
  .numWrap{font-size:36px}
  .numWrap.digits-2{font-size:30px}
  .numWrap.digits-3{font-size:24px}
  .week .dot{width:30px;height:30px}
  .sheetTop{padding:30px 44px}
  .avatar{width:92px;height:92px;font-size:38px}
  .avatarWrap{margin-bottom:12px}
  .sayCard{min-height:120px;padding:24px}
}
