Source
This app’s code and revision history are public. Download a copy or explore the files below.
Public
The app’s code and revision history are public.
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 7efc97d0dde0b2a3e729ccf9
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
src/styles.css
/* Lingo. Shared look comes from /kit.css; this is the app's own layout. Phones first. */
body.app-mode .landing-only { display: none !important; }
body:not(.app-mode) #app:empty { display: none; }
body:not(.app-mode) #head-stats { display: none; }
body.app-mode .site-foot { display: none; }
body.session-mode .site-head { display: none; }
.page-pad { padding-block: 56px 80px; }
.narrow { max-width: 640px; }
.loading { display: flex; align-items: center; gap: 12px; color: var(--muted); padding: 32px 0; }
.small { font-size: 14px; }
.grow { flex: 1; min-width: 0; }
.gold { color: var(--gold); }
.cobalt { color: var(--cobalt); }
.sage { color: var(--sage); }
.inline-icon { display: inline-block; vertical-align: -2px; }
.brand-app { color: var(--muted); font-weight: 500; }
.brand-sep { color: var(--line-strong); font-weight: 300; }
.nav-cta { background: var(--ink-solid); color: #fff !important; }
.nav-cta:hover { background: #2a2c31 !important; }
@media (max-width: 420px) { .brand-word { display: none; } }
/* Landing hero with the language picker */
.app-hero { padding: 40px 0 72px; background: linear-gradient(180deg, #faf6ee 0%, #fdfdfc 100%); border-bottom: 1px solid #f0ebe1; }
.app-hero-grid { display: grid; grid-template-columns: minmax(0, 1fr) 420px; grid-template-areas: "text start" "more start"; column-gap: 56px; align-items: center; }
.hero-text { grid-area: text; align-self: end; }
.hero-more { grid-area: more; align-self: start; }
#start { grid-area: start; }
.app-hero h1 { margin-top: 14px; }
.app-hero .lede { margin-top: 18px; }
.hero-points { list-style: none; padding: 0; margin-top: 26px; display: grid; gap: 10px; font-size: 15.5px; color: #3c4048; }
.hero-points li { display: flex; gap: 10px; align-items: baseline; }
.hero-points li::before { content: ""; flex: 0 0 8px; height: 8px; border-radius: 50%; background: var(--cobalt); transform: translateY(-2px); }
.hero-art { width: 210px; height: auto; margin-bottom: -6px; }
@media (max-width: 900px) { .app-hero-grid { grid-template-columns: minmax(0, 1fr); grid-template-areas: "text" "start" "more"; row-gap: 24px; } .app-hero { padding-top: 16px; } .hero-art { width: 150px; } .app-hero .lede { margin-top: 4px; } }
.hero-art, .done-art { filter: brightness(1.02); }
.start { padding: 20px; display: grid; gap: 12px; }
.start-label { font: 600 13.5px/1.2 var(--sans); color: #33363c; }
.start-list { list-style: none; padding: 0; display: grid; gap: 8px; }
.start-lang, .start-continue { display: flex; align-items: center; gap: 14px; min-height: 64px; padding: 10px 14px; border: 1px solid var(--line); border-bottom-width: 3px; border-radius: 14px; background: #fff; color: var(--ink); transition: border-color .15s, transform .1s; }
.start-lang:hover, .start-continue:hover { border-color: var(--line-strong); color: var(--ink); }
.start-lang:active, .start-continue:active { transform: translateY(2px); border-bottom-width: 1px; margin-bottom: 2px; }
.start-lang svg, .start-continue svg { margin-left: auto; color: var(--muted); flex: none; }
.start-continue { background: var(--cobalt-wash); border-color: #cddaf3; }
.start-continue > span { display: grid; gap: 2px; }
.start-name { display: grid; gap: 1px; }
.start-name strong { font: 600 16px/1.2 var(--sans); }
.start-name span { font-size: 13.5px; color: var(--muted); }
.course-code { display: grid; place-items: center; flex: none; width: 44px; height: 44px; border-radius: 12px; background: var(--paper-warm); border: 1px solid #ece2cf; font: 650 14px/1 var(--sans); letter-spacing: .04em; color: #6b5a3c; }
.small-code { width: 36px; height: 36px; border-radius: 10px; font-size: 12.5px; }
/* Landing sections */
.steps { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 28px; margin-top: 28px; padding: 0; counter-reset: step; }
.steps li { list-style: none; counter-increment: step; }
.steps li::before { content: counter(step); display: grid; place-items: center; width: 34px; height: 34px; margin-bottom: 14px; border-radius: 50%; background: var(--ink-solid); color: #fff; font: 650 15px var(--sans); }
.steps h3, .features h3 { font: 600 18px/1.3 var(--sans); }
.steps p, .features p { margin-top: 8px; color: #4a4e56; }
.features { list-style: none; padding: 0; margin-top: 26px; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
.features li { padding: 20px; border: 1px solid var(--line); border-radius: 16px; background: #fff; }
@media (max-width: 860px) { .steps, .features { grid-template-columns: minmax(0, 1fr); } }
.api-grid { display: grid; grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); gap: 40px; align-items: start; }
.api-grid .code { font-size: 12.5px; }
@media (max-width: 900px) { .api-grid { grid-template-columns: minmax(0, 1fr); } }
.more-apps { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; list-style: none; padding: 0; margin-top: 22px; }
.more-apps a { display: grid; gap: 4px; padding: 16px 18px; border: 1px solid var(--line); border-radius: 14px; background: #fff; color: var(--ink); }
.more-apps a:hover { border-color: var(--line-strong); box-shadow: var(--shadow); color: var(--ink); }
.more-apps strong { font: 400 22px/1.1 var(--serif); }
.more-apps span { color: var(--muted); font-size: 14px; }
.compare td:first-child { font-weight: 600; }
.compare { min-width: 620px; }
/* Header stats in app mode */
body.app-mode .site-nav { display: none; }
#head-stats { margin-left: auto; }
.head-stats { display: flex; align-items: center; gap: 4px; }
.stat { display: inline-flex; align-items: center; gap: 5px; min-height: 40px; padding: 0 10px; border-radius: 10px; color: var(--ink); font: 650 15px/1 var(--sans); }
.stat:hover { background: #0000000a; color: var(--ink); }
.stat-of { color: var(--faint); font-weight: 500; }
.streak { color: var(--faint); }
.streak.on { color: var(--gold); }
.streak.on:hover { color: var(--gold); }
.xp svg { color: var(--cobalt); }
.course-chip { min-height: 32px; padding: 0 8px; border: 1px solid #ece2cf; background: var(--paper-warm); color: #6b5a3c; font-size: 13px; letter-spacing: .04em; }
@media (max-width: 420px) { .stat-of { display: none; } .stat { padding: 0 7px; } }
/* App shell: tab bar on phones, side nav and cards on wide screens */
.shell { width: var(--page); margin-inline: auto; padding: 20px 0 110px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 24px; }
.shell-main { min-width: 0; }
.side-nav, .shell-side { display: none; }
.tab-bar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 30; display: grid; grid-template-columns: repeat(4, 1fr); padding: 6px 8px calc(6px + env(safe-area-inset-bottom)); background: #fdfdfcf2; -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); border-top: 1px solid var(--line); }
.tab-bar a { display: grid; justify-items: center; gap: 3px; min-height: 52px; padding: 6px 0; border-radius: 12px; color: var(--muted); font: 600 12px/1 var(--sans); }
.tab-bar a[aria-current="page"] { color: var(--cobalt-ink); background: var(--cobalt-wash); }
@media (min-width: 960px) {
.shell { grid-template-columns: 200px minmax(0, 1fr); padding-bottom: 80px; }
.tab-bar { display: none; }
.side-nav { display: grid; align-content: start; gap: 4px; position: sticky; top: 84px; height: max-content; }
.side-nav a { display: flex; align-items: center; gap: 12px; min-height: 48px; padding: 0 14px; border-radius: 12px; border: 1px solid transparent; color: #33363c; font: 600 15px/1 var(--sans); }
.side-nav a:hover { background: #0000000a; color: var(--ink-solid); }
.side-nav a[aria-current="page"] { color: var(--cobalt-ink); background: var(--cobalt-wash); border-color: #d1ddf5; }
}
@media (min-width: 1180px) {
.shell { grid-template-columns: 200px minmax(0, 1fr) 300px; }
.shell-side { display: grid; align-content: start; gap: 14px; position: sticky; top: 84px; height: max-content; }
}
.side-card { display: grid; gap: 12px; padding: 18px; }
.goal-bar, .mini-bar { position: relative; height: 12px; border-radius: 99px; background: #eceff3; overflow: hidden; }
.goal-bar span, .mini-bar span { position: absolute; inset: 0 auto 0 0; border-radius: 99px; background: var(--sage); transition: width .6s ease; }
.mini-bar { height: 6px; }
.streak-line { display: flex; align-items: center; gap: 12px; }
.streak-flame { display: grid; place-items: center; width: 48px; height: 48px; border-radius: 14px; background: var(--panel); color: var(--faint); }
.streak-flame.on { background: #f8efdc; color: var(--gold); }
.streak-n { font: 400 24px/1.1 var(--serif); letter-spacing: -.015em; }
.week { list-style: none; padding: 0; display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.week li { display: grid; justify-items: center; gap: 4px; }
.week-dot { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%; background: #eceff3; color: #fff; }
.week li.on .week-dot { background: var(--gold); }
.week li.frozen .week-dot { background: var(--cobalt-wash); color: var(--cobalt); }
.week li.today .week-dot { box-shadow: 0 0 0 2px #fff, 0 0 0 3.5px var(--line-strong); }
.week-day { font: 600 11.5px/1 var(--sans); color: var(--muted); }
.freeze-line { display: flex; align-items: center; gap: 6px; }
.freeze-line svg { color: var(--cobalt); flex: none; }
/* Learn: the path */
.learn { max-width: 600px; margin-inline: auto; }
.course-title { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 18px; }
.course-title .h1 { margin-top: 6px; }
.unit { margin-top: 18px; }
.unit-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; padding: 18px 20px; border-radius: var(--radius-lg); background: var(--paper-warm); border: 1px solid #ece2cf; }
.unit.locked .unit-head { background: #f4f5f7; border-color: var(--line); }
.unit-text { display: grid; gap: 4px; min-width: 0; }
.unit-title { font: 380 28px/1.12 var(--serif); letter-spacing: -.02em; }
.unit-about { color: #4a4e56; font-size: 15px; }
.unit-tip { justify-self: start; display: inline-flex; align-items: center; gap: 7px; min-height: 36px; margin-top: 6px; padding: 0 12px; border: 1px solid #e3d6bd; border-radius: 99px; background: #fffaf0; color: #5b4a2c; font: 600 13.5px/1.2 var(--sans); cursor: pointer; text-align: left; }
.unit-tip:hover { border-color: #d6c4a0; }
.unit.locked .unit-tip { background: #fff; border-color: var(--line); color: #3c4048; }
.jump-btn { flex: none; }
.path { list-style: none; padding: 58px 0 10px; display: grid; justify-items: center; gap: 34px; }
.node-row { display: grid; justify-items: center; position: relative; width: 100%; }
.node-row.is-current:not(:first-child) { margin-top: 16px; }
.node-wrap { position: relative; display: grid; justify-items: center; transition: transform .2s; }
.node { display: grid; place-items: center; width: 70px; height: 66px; border: 0; border-radius: 50%; cursor: pointer; color: #fff; transition: transform .1s, box-shadow .1s; }
.node.done { background: var(--sage); box-shadow: 0 6px 0 #3b5d45; }
.node.current { background: var(--cobalt); box-shadow: 0 6px 0 var(--cobalt-ink), 0 0 0 7px #fdfdfc, 0 0 0 11px #d6e1f6; }
.node.locked { background: #e3e6ea; color: #9aa0a9; box-shadow: 0 6px 0 #cdd2d9; }
.node:active { transform: translateY(5px); }
.node.done:active { box-shadow: 0 1px 0 #3b5d45; }
.node.current:active { box-shadow: 0 1px 0 var(--cobalt-ink), 0 0 0 7px #fdfdfc, 0 0 0 11px #d6e1f6; }
.node.locked:active { box-shadow: 0 1px 0 #cdd2d9; }
.node-start { position: absolute; bottom: calc(100% + 8px); z-index: 2; padding: 8px 12px; border-radius: 10px; background: #fff; border: 1px solid var(--line-strong); color: var(--cobalt-ink); font: 700 13px/1 var(--sans); letter-spacing: .06em; text-transform: uppercase; white-space: nowrap; animation: bob 1.6s ease-in-out infinite; }
.node-start::after { content: ""; position: absolute; left: 50%; top: 100%; width: 10px; height: 10px; background: #fff; border-right: 1px solid var(--line-strong); border-bottom: 1px solid var(--line-strong); transform: translate(-50%, -5px) rotate(45deg); }
@keyframes bob { 50% { transform: translateY(-4px); } }
.node-pop { width: min(300px, 100%); margin-top: 16px; padding: 16px; border-radius: 16px; background: #fff; border: 1px solid var(--line); box-shadow: var(--shadow); display: grid; gap: 6px; animation: rise .16s ease; }
.node-pop.current { border-color: #cddaf3; background: var(--cobalt-wash); }
.node-pop-title { font: 600 17px/1.3 var(--sans); }
.node-pop-sub { color: var(--muted); font-size: 14px; }
.node-pop .btn { margin-top: 6px; }
.course-end { margin: 28px 0 10px; }
.tip { display: grid; gap: 12px; }
.tip p { color: #33363c; }
.tip-examples { list-style: none; padding: 0; display: grid; gap: 8px; margin-top: 4px; }
.tip-examples li { display: grid; gap: 2px; padding: 10px 12px; border-radius: 10px; background: var(--paper-warm); border: 1px solid #ece2cf; }
.tip-examples strong { font: 500 17px/1.3 var(--serif); }
.tip-examples span { font-size: 14px; color: var(--muted); }
/* Courses */
.courses .lede { margin-top: 10px; }
.course-grid { list-style: none; padding: 0; margin-top: 22px; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr)); gap: 12px; }
.course-card { display: grid; gap: 6px; width: 100%; padding: 18px; border: 1px solid var(--line); border-bottom-width: 3px; border-radius: 16px; background: #fff; text-align: left; cursor: pointer; }
.course-card:hover { border-color: var(--line-strong); }
.course-card.current { border-color: #b9cbef; background: var(--cobalt-wash); }
.course-card .course-code { margin-bottom: 6px; }
.course-name { font: 400 26px/1.1 var(--serif); letter-spacing: -.015em; }
.course-meta { color: var(--muted); font-size: 14px; }
.course-go { display: inline-flex; align-items: center; gap: 6px; margin-top: 6px; color: var(--cobalt-ink); font: 600 14.5px var(--sans); }
/* Session (lesson, practice, test) */
body.session-mode { background: #fff; }
.session { display: flex; flex-direction: column; height: 100vh; height: 100dvh; }
.session-scroll { flex: 1; min-height: 0; overflow-y: auto; }
.session-top { flex: none; display: flex; align-items: center; gap: 12px; width: min(760px, calc(100% - 24px)); margin: 0 auto; padding: 14px 0 10px; background: #fff; }
.bar { position: relative; flex: 1; height: 16px; border-radius: 99px; background: #eceff3; overflow: hidden; }
.bar span { position: absolute; inset: 0 auto 0 0; border-radius: 99px; background: var(--sage); transition: width .45s cubic-bezier(.3, 1.4, .5, 1); }
.bar span::after { content: ""; position: absolute; left: 8px; right: 8px; top: 4px; height: 4px; border-radius: 99px; background: #ffffff55; }
.row-pill { flex: none; padding: 6px 10px; border-radius: 99px; background: var(--sage-wash); color: #2f5a3c; font: 650 12.5px/1 var(--sans); animation: rise .2s ease; }
.retry-note { width: min(640px, calc(100% - 32px)); margin: 6px auto 0; color: var(--brick); font: 600 13.5px var(--sans); }
.session-body { width: min(640px, calc(100% - 32px)); margin: 0 auto; padding: 18px 0 28px; animation: slide .22s ease; }
@keyframes slide { from { opacity: 0; transform: translateX(14px); } }
.ex { display: grid; gap: 22px; }
.ex-title { font: 400 clamp(26px, 6vw, 32px)/1.15 var(--serif); letter-spacing: -.02em; }
.prompt { display: flex; align-items: flex-start; gap: 12px; }
.prompt-text { font: 500 21px/1.4 var(--sans); padding: 10px 14px; border: 1px solid var(--line); border-radius: 14px; background: #fff; position: relative; }
.say { display: inline-grid; place-items: center; flex: none; width: 46px; height: 46px; border: 0; border-radius: 14px; background: var(--cobalt); color: #fff; cursor: pointer; box-shadow: 0 4px 0 var(--cobalt-ink); }
.say:active { transform: translateY(3px); box-shadow: 0 1px 0 var(--cobalt-ink); }
.say.inline { width: 32px; height: 32px; margin-left: 8px; border-radius: 9px; vertical-align: middle; box-shadow: none; }
.listen-btns { display: inline-flex; align-items: flex-end; gap: 12px; }
.listen-btns.big .say { width: 96px; height: 96px; border-radius: 22px; box-shadow: 0 6px 0 var(--cobalt-ink); }
.listen-btns.big .say.slow { width: 64px; height: 64px; border-radius: 18px; background: #fff; color: var(--cobalt); border: 2px solid #cddaf3; box-shadow: 0 4px 0 #cddaf3; }
.listen-stage { display: grid; place-items: center; padding: 6px 0; }
.target-text { display: inline; }
.hint-word { position: relative; display: inline; }
.hint-word button { padding: 0; border: 0; background: none; font: inherit; color: inherit; cursor: pointer; text-decoration: underline dotted #9aa0a9; text-underline-offset: 5px; text-decoration-thickness: 2px; }
.hint-pop { position: absolute; left: 0; top: calc(100% + 8px); z-index: 5; display: grid; gap: 4px; min-width: 150px; max-width: 260px; padding: 9px 12px; border-radius: 10px; background: var(--ink-solid); color: #fff; font: 500 14px/1.35 var(--sans); white-space: nowrap; box-shadow: 0 10px 30px #0003; }
.hint-pop b { font-weight: 650; margin-right: 4px; }
.options { display: grid; gap: 10px; }
@media (min-width: 640px) { .options { grid-template-columns: 1fr 1fr; } }
.option { display: flex; align-items: center; gap: 12px; min-height: 60px; padding: 12px 16px; border: 2px solid var(--line); border-bottom-width: 4px; border-radius: 14px; background: #fff; text-align: left; font: 500 17px/1.3 var(--sans); cursor: pointer; transition: border-color .12s, background .12s; }
.option:hover:not(:disabled) { background: #fafbfc; }
.option.chosen { border-color: var(--cobalt); background: var(--cobalt-wash); color: var(--cobalt-ink); }
.option-key { display: grid; place-items: center; flex: none; width: 26px; height: 26px; border: 1.5px solid var(--line-strong); border-radius: 8px; color: var(--muted); font: 650 12.5px/1 var(--sans); }
.option.chosen .option-key { border-color: var(--cobalt); color: var(--cobalt); }
@media (pointer: coarse) { .option-key { display: none; } }
.match { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; }
.match-col { display: grid; gap: 10px; align-content: start; }
.tile-btn { min-height: 56px; padding: 10px 12px; border: 2px solid var(--line); border-bottom-width: 4px; border-radius: 14px; background: #fff; font: 500 16px/1.25 var(--sans); cursor: pointer; overflow-wrap: anywhere; }
.tile-btn.chosen { border-color: var(--cobalt); background: var(--cobalt-wash); color: var(--cobalt-ink); }
.tile-btn.matched { border-color: #cfe2d4; background: var(--sage-wash); color: #7b9a84; animation: pop .3s ease; }
.tile-btn.miss { border-color: #e9b9ab; background: var(--brick-wash); animation: shake .35s ease; }
.build { display: grid; gap: 22px; }
.answer-line { display: flex; flex-wrap: wrap; align-content: flex-start; gap: 8px; min-height: 112px; padding: 10px 0; background: repeating-linear-gradient(transparent 0 51px, var(--line) 51px 53px); }
.bank, .chips-row { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
.tile-slot { display: inline-flex; border-radius: 12px; background: #eceff3; }
.tile { min-height: 46px; padding: 8px 14px; border: 2px solid var(--line); border-bottom-width: 4px; border-radius: 12px; background: #fff; font: 500 17px/1.2 var(--sans); cursor: pointer; animation: pop .16s ease; }
.tile:hover:not(:disabled) { border-color: var(--line-strong); }
.tile.used { visibility: hidden; }
.chips-row .tile.used { visibility: visible; border-color: var(--cobalt); background: var(--cobalt-wash); color: var(--cobalt-ink); }
.type-box { display: grid; gap: 10px; }
.answer-input { min-height: 120px; font-size: 18px; border-width: 2px; border-radius: 14px; background: #fafbfc; }
.chars { display: flex; flex-wrap: wrap; gap: 6px; }
.char { min-width: 40px; min-height: 40px; padding: 0 8px; border: 1px solid var(--line-strong); border-bottom-width: 3px; border-radius: 10px; background: #fff; font: 500 17px var(--sans); cursor: pointer; }
.blank-sentence { font: 400 26px/1.6 var(--serif); }
.blank-slot { display: inline-block; min-width: 96px; margin: 0 4px; padding: 0 10px; border-bottom: 3px solid var(--line-strong); text-align: center; font-family: var(--sans); font-size: 20px; }
.blank-slot.filled { border-color: var(--cobalt); color: var(--cobalt-ink); animation: pop .2s ease; }
.blank-meaning { margin-top: -10px; color: var(--muted); }
.speak-stage { display: grid; justify-items: center; gap: 14px; padding: 12px 0; }
.mic { display: inline-flex; align-items: center; justify-content: center; gap: 10px; min-height: 64px; width: min(100%, 420px); padding: 0 20px; border: 2px solid #cddaf3; border-bottom-width: 5px; border-radius: 16px; background: #fff; color: var(--cobalt-ink); font: 650 16px var(--sans); cursor: pointer; }
.mic.on { background: var(--cobalt-wash); border-color: var(--cobalt); animation: pulse 1.2s ease-in-out infinite; }
@keyframes pulse { 50% { box-shadow: 0 0 0 8px #2e5ec41a; } }
.heard { font: 400 20px/1.4 var(--serif); color: #33363c; }
.cant { justify-self: center; color: var(--muted); }
.session-foot { flex: none; border-top: 2px solid var(--line); background: #fff; }
.foot-inner, .sheet-inner { display: flex; align-items: center; gap: 12px; width: min(760px, calc(100% - 32px)); margin: 0 auto; padding: 16px 0 calc(16px + env(safe-area-inset-bottom)); }
.foot-inner .check { flex: 1; }
.foot-inner .skip { flex: none; }
@media (min-width: 700px) { .foot-inner { justify-content: space-between; } .foot-inner .check { flex: none; min-width: 180px; } .sheet-btn { min-width: 180px; } }
.sheet { animation: up .2s ease; }
.sheet.right { background: var(--sage-wash); border-top-color: #cfe2d4; }
.sheet.wrong { background: var(--brick-wash); }
.sheet-inner { flex-wrap: wrap; justify-content: space-between; }
.sheet-text { display: grid; gap: 4px; min-width: 0; flex: 1 1 260px; }
.sheet-title { display: flex; align-items: center; gap: 10px; font: 400 26px/1.15 var(--serif); letter-spacing: -.015em; }
.sheet.right .sheet-title { color: #2f5a3c; }
.sheet.wrong .sheet-title { color: #7c2f1d; }
.sheet-icon { display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%; background: #fff; }
.sheet.right .sheet-icon { color: var(--sage); animation: pop .35s cubic-bezier(.3, 1.6, .5, 1); }
.sheet.wrong .sheet-icon { color: var(--brick); }
.sheet-answer { font: 600 17px/1.4 var(--sans); }
.sheet.right .sheet-answer { color: #2f5a3c; font-weight: 500; }
.sheet.wrong .sheet-answer { color: #7c2f1d; }
.sheet-meaning { font-size: 14.5px; color: #4a4e56; }
.sheet-btn { flex: 1 1 100%; }
@media (min-width: 700px) { .sheet-btn { flex: none; } }
.session.is-right .session-body { animation: flash-right .6s ease; border-radius: 18px; }
@keyframes flash-right { 0% { box-shadow: 0 0 0 0 #4f7a5c00; } 30% { box-shadow: 0 0 0 10px #4f7a5c22; } 100% { box-shadow: 0 0 0 18px #4f7a5c00; } }
.session.is-wrong .session-body { animation: shake .35s ease; }
@keyframes up { from { transform: translateY(100%); } }
@keyframes pop { 0% { transform: scale(.92); } 60% { transform: scale(1.04); } 100% { transform: scale(1); } }
@keyframes shake { 20%, 60% { transform: translateX(-5px); } 40%, 80% { transform: translateX(5px); } }
/* Session done */
.finish { display: grid; justify-items: center; gap: 18px; padding: 32px 0 60px; text-align: center; }
.done-art { width: 200px; height: auto; animation: pop .5s ease; }
.done-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; width: 100%; max-width: 460px; }
.done-stat { display: grid; gap: 6px; padding: 14px 10px; border: 2px solid var(--line); border-radius: 14px; background: #fff; }
.done-stat:first-child { border-color: #d1ddf5; background: var(--cobalt-wash); }
.done-label { font: 650 11.5px/1 var(--sans); letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.done-value { display: inline-flex; align-items: center; justify-content: center; gap: 6px; font: 400 28px/1 var(--serif); }
.done-stat:first-child svg { color: var(--cobalt); }
.done-notes { list-style: none; padding: 0; display: grid; gap: 8px; }
.done-notes li { display: inline-flex; align-items: center; justify-content: center; gap: 8px; font: 600 16px/1.4 var(--sans); color: #33363c; }
.done-notes svg { color: var(--sage); flex: none; }
.done-streak { color: #7a5a17 !important; }
.done-streak svg { color: var(--gold) !important; animation: pop .5s ease; }
.done-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; width: 100%; }
.done-actions .btn { min-width: 200px; }
@media (max-width: 600px) { .done-actions .btn { width: 100%; } }
.goal-pick { display: grid; gap: 12px; width: 100%; max-width: 460px; text-align: left; }
.goal-options { display: grid; gap: 8px; }
.goal-option { display: grid; gap: 2px; padding: 12px 16px; border: 2px solid var(--line); border-bottom-width: 4px; border-radius: 14px; background: #fff; text-align: left; cursor: pointer; }
.goal-option strong { font: 600 16px/1.3 var(--sans); }
.goal-option span { font-size: 14px; color: var(--muted); }
.goal-option:hover { border-color: var(--line-strong); }
.goal-option.chosen { border-color: var(--cobalt); background: var(--cobalt-wash); }
.goal-options.compact { grid-template-columns: 1fr 1fr; }
/* Practice and words */
.practice .lede, .words .lede, .leagues .muted { margin-top: 8px; }
.practice-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); gap: 14px; margin-top: 22px; }
.practice-card { display: grid; gap: 10px; align-content: start; padding: 20px; }
.practice-card .btn { margin-top: 6px; }
.practice-icon { display: grid; place-items: center; width: 44px; height: 44px; border-radius: 12px; background: var(--cobalt-wash); color: var(--cobalt); }
.practice-perk { display: flex; align-items: center; gap: 6px; font-size: 14px; color: var(--cobalt-ink); }
.back-link { display: inline-block; margin-bottom: 8px; font: 600 14.5px var(--sans); }
.back-link::before { content: "‹ "; }
.words-search { margin-top: 18px; max-width: 360px; }
.words-unit { margin-top: 24px; }
.word-list { list-style: none; padding: 0; margin-top: 10px; border: 1px solid var(--line); border-radius: 14px; background: #fff; overflow: hidden; }
.word-list li { display: grid; grid-template-columns: 34px minmax(0, 1fr) minmax(0, 1fr) auto; align-items: center; gap: 12px; min-height: 54px; padding: 8px 14px; border-top: 1px solid var(--line); }
.word-list li:first-child { border-top: 0; }
.word-list .say { width: 34px; height: 34px; border-radius: 10px; box-shadow: none; }
.word-t { font: 500 17px/1.3 var(--serif); overflow-wrap: anywhere; }
.word-e { color: var(--muted); font-size: 14.5px; overflow-wrap: anywhere; }
.bars { display: inline-flex; align-items: flex-end; gap: 2px; height: 16px; }
.bars span { width: 4px; border-radius: 2px; background: #dfe3e8; }
.bars span:nth-child(1) { height: 5px; } .bars span:nth-child(2) { height: 8px; } .bars span:nth-child(3) { height: 12px; } .bars span:nth-child(4) { height: 16px; }
.bars span.on { background: var(--gold); }
@media (max-width: 520px) { .word-list li { grid-template-columns: 34px minmax(0, 1fr) auto; } .word-e { grid-column: 2; grid-row: 2; margin-top: -6px; } .word-list .bars { grid-row: 1 / span 2; grid-column: 3; } .word-list .say { grid-row: 1 / span 2; } }
/* Leagues */
.league-head { display: flex; align-items: center; gap: 16px; }
@media (max-width: 600px) { .league-head { flex-direction: column; align-items: flex-start; gap: 12px; } .league-badge { width: 52px; height: 52px; border-radius: 15px; } }
.league-badge { display: grid; place-items: center; flex: none; width: 64px; height: 64px; border-radius: 18px; background: #f8efdc; color: var(--gold); border: 1px solid #ecdcb6; }
.league-you { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin-top: 20px; padding: 16px 18px; }
.league-name { font: 400 24px/1.2 var(--serif); margin-top: 6px; overflow-wrap: anywhere; }
.league-rank { margin: 20px 0 10px; font-size: 16.5px; }
.board { list-style: none; padding: 0; margin: 10px 0 14px; border: 1px solid var(--line); border-radius: 16px; background: #fff; overflow: hidden; }
.board li { display: grid; grid-template-columns: 36px minmax(0, 1fr) auto auto; align-items: center; gap: 12px; min-height: 56px; padding: 8px 16px; border-top: 1px solid var(--line); }
.board li:first-child { border-top: 0; }
.board li.me { background: var(--cobalt-wash); }
.board li.gap { border-top: 2px dashed var(--line-strong); }
.board-rank { display: grid; place-items: center; width: 30px; height: 30px; border-radius: 50%; font: 650 14px/1 var(--sans); color: var(--muted); }
.top-1 .board-rank { background: #f3e3bd; color: #6e5114; }
.top-2 .board-rank { background: #e6e8ec; color: #4a4e56; }
.top-3 .board-rank { background: #f1dccf; color: #7a3f27; }
.board-name { font: 600 15.5px/1.3 var(--sans); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.you-tag { margin-left: 8px; padding: 2px 7px; border-radius: 99px; background: var(--cobalt); color: #fff; font: 650 11px/1 var(--sans); vertical-align: 2px; }
.board-lang { font: 650 12px/1 var(--sans); color: var(--muted); letter-spacing: .04em; }
.board-xp { font: 650 14.5px/1 var(--sans); }
.empty { display: grid; justify-items: center; gap: 14px; margin-top: 20px; padding: 32px 20px; border: 1px dashed var(--line-strong); border-radius: 16px; text-align: center; }
/* Profile */
.profile-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; }
.profile-head .h1 { margin-top: 6px; overflow-wrap: anywhere; }
.stat-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin-top: 22px; }
.stat-box { display: grid; gap: 4px; padding: 14px 16px; border: 1px solid var(--line); border-radius: 14px; background: #fff; }
.stat-box strong { font: 400 28px/1.1 var(--serif); }
.stat-box span { font-size: 13.5px; color: var(--muted); }
@media (max-width: 700px) { .stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.profile-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr)); gap: 14px; margin-top: 14px; align-items: start; }
.profile-courses { list-style: none; padding: 0; display: grid; gap: 12px; }
.profile-courses li { display: grid; gap: 8px; }
.profile-courses a { display: flex; align-items: center; gap: 12px; color: var(--ink); }
.account-card { background: var(--cobalt-wash); border-color: #d1ddf5; }
/* Sheets (from Split) */
.modal-backdrop { position: fixed; inset: 0; z-index: 60; display: grid; place-items: center; padding: 20px; background: #15171b66; animation: fade .15s ease; overflow: hidden; }
.modal { display: flex; flex-direction: column; width: min(560px, 100%); max-height: min(88vh, 900px); border-radius: 18px; background: #fff; box-shadow: 0 30px 80px -20px #0006; animation: rise .18s ease; }
.modal-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 18px 20px 6px; }
.modal-title { font: 400 26px/1.2 var(--serif); letter-spacing: -.015em; }
.modal-body { padding: 12px 20px 20px; overflow-y: auto; }
.modal-foot { display: flex; align-items: center; gap: 10px; padding: 14px 20px calc(14px + env(safe-area-inset-bottom)); border-top: 1px solid var(--line); }
.icon-btn { display: grid; place-items: center; flex: none; width: 40px; height: 40px; border: 0; border-radius: 10px; background: none; cursor: pointer; color: var(--muted); }
.icon-btn:hover { background: #0000000a; color: var(--ink); }
@keyframes fade { from { opacity: 0; } }
@keyframes rise { from { opacity: 0; transform: translateY(10px); } }
@media (max-width: 600px) {
.modal-backdrop { place-items: end stretch; padding: 0; }
.modal { width: 100%; max-height: 92vh; border-radius: 18px 18px 0 0; }
}
.toast { bottom: calc(96px + env(safe-area-inset-bottom)); max-width: calc(100% - 32px); text-align: center; }
body.session-mode .toast { top: 16px; bottom: auto; }
@media (min-width: 960px) { .toast { bottom: 24px; } }
Change history
Point OpenApps links to openapps.free
Bind agent address in policy
Use OpenApps service URLs and add contribution strip
Bounded source sync
Bounded source sync
Bounded source sync