/* The two brand faces, served from this origin. See static/fonts/README.md for
   why they are not loaded from Google: a stylesheet fetched from a third party
   announces every visitor to it before anybody has agreed to anything.

   One variable file per family per subset carries the whole weight range, so
   the three heading weights and three body weights cost two downloads, not six.
   font-display:swap keeps the text readable while they arrive - the page is
   words, and words nobody can read yet are worse than words in the wrong face. */
@font-face{font-family:"Bricolage Grotesque";font-style:normal;font-weight:500 800;font-display:swap;src:url(fonts/bricolage-grotesque-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:"Bricolage Grotesque";font-style:normal;font-weight:500 800;font-display:swap;src:url(fonts/bricolage-grotesque-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}
@font-face{font-family:"Spline Sans";font-style:normal;font-weight:400 600;font-display:swap;src:url(fonts/spline-sans-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:"Spline Sans";font-style:normal;font-weight:400 600;font-display:swap;src:url(fonts/spline-sans-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}

/* Confident Editorial. Tokens are canonical (brand book, appendix A) and are not
   re-derived here.

   Known and accepted: a generic design linter flags the 3px cobalt top edge on a
   rounded card as a cliche. It is kept anyway. That edge is the brand book's own
   .face component and the one structural use of the primary colour in the whole
   system, so removing it to satisfy a general-purpose heuristic would trade a
   deliberate identity for a default. Reviewed 2026-08-07, not an oversight. The only deliberate departure from the book is the container
   width: the book sizes .app for a phone frame, and a phone frame floating in the
   middle of a desktop screen reads as an unfinished port rather than a product.
   The card metaphor, the type and every colour stay exactly as specified. */
:root{
  --paper:#F1EDE3; --ink:#1D1B14; --muted:#5C584C;
  --line:#D8D2C2;
  --accent:#274BD3; --accent-deep:#1E3AA8;
  --accent2:#DDA435;              /* amber - micro accent only */
  --correct:#2E7D5B; --wrong:#B4472F;
  /* The same green, dark enough to pass AA as small text. The bright one
     stays for fills and rules, where the 3:1 non-text floor applies. */
  --correct-ink:#1F5B40;
}

*{box-sizing:border-box}

body{
  margin:0;color:var(--ink);font-family:"Spline Sans",sans-serif;line-height:1.5;
  /* Top-aligned, like the front page. Vertical centring made sense while every
     screen was a raised panel floating on paper; without the panel it put the
     wordmark two hundred pixels down a page whose front door starts at the top,
     and walking between the two looked like a jump. */
  min-height:100vh;display:flex;flex-direction:column;
  padding:32px 18px 56px;
  background:
    radial-gradient(120% 80% at 50% -10%, #F8F5EC 0%, var(--paper) 60%, #E7E1D1 100%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Ccircle cx='1' cy='1' r='.6' fill='%231D1B1407'/%3E%3C/svg%3E");
  background-blend-mode:multiply;
  background-attachment:fixed;
}

.skip{position:absolute;left:-9999px}
.skip:focus{left:18px;top:18px;background:var(--ink);color:var(--paper);padding:10px 14px;border-radius:10px;z-index:9}

.disp{font-family:"Bricolage Grotesque",sans-serif}
.lbl,.hk{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:2px}
.hk{color:var(--accent);margin:0 0 6px}
.lbl{color:var(--muted);margin:0 0 6px}

/* The cobalt edge along the top of the card is the anchor: it is the first thing
   on the page and it is the only structural use of the primary colour. */
/* No panel, on purpose. Every screen here used to sit inside a raised beige
   card with a cobalt top edge, and the front page does not: walking from one to
   the other changed the whole visual language, and the app read as a set of
   small windows bolted to a wide, open page. The content now lies straight on
   the paper, as it does on the front page, and the cobalt edge goes back to the
   one thing it was ever about, the flashcard. The width ladder stays: that is
   what keeps a line of text readable, and it is the same ladder the front page
   holds its column to. */
.app{
  max-width:560px;margin:0 auto;padding:30px 28px 32px;
}
/* On a laptop the card was still 560px wide with 290px of empty paper down each
   side, which reads as a phone screenshot rather than as a product. It grows,
   but only so far: a line of prose past about 80 characters is harder to read,
   not easier, so the card takes the room a reader can use and leaves the rest.
   The padding grows with it, because a wider card with the same inset looks
   like a stretched one. */
@media (min-width:900px){
  .app{max-width:720px;padding:38px 40px 40px}
}
/* One width for every screen, and this is the second attempt at it. The first
   gave the two-column screens a wider tier of their own and left the rest
   narrower - defensible on paper, wrong in the hand: the card visibly jumped
   on every move between screens. A shell that resizes while you walk through it
   reads as broken whatever the reasoning. What changes between screens is the
   composition inside the card, never the card. */
@media (min-width:1240px){
  .app{max-width:980px;padding:44px 52px 46px}
}

/* Two columns where the screen genuinely has two jobs. Left is what somebody
   came to do: the library and the way into a session, the deck and its four
   actions. Right is what supports that and never leads it - how sure you have
   been, how far through the concepts you are, what the deck does not cover.

   Two columns are two elements, not eight children with column numbers pinned
   to them. The first attempt did it the pinned way with dense packing, and the
   packer put the supporting panel beside the opening sentence rather than
   beside the list: that row then grew to the panel's height and left a hole
   above the decks. Wrapping each column is both simpler and correct by
   construction, and it keeps DOM order equal to reading order. */
@media (min-width:1240px){
  .app.home,.app.deck{
    display:grid;grid-template-columns:minmax(0,1fr) minmax(230px,290px);
    column-gap:46px;align-items:start;
  }
  /* The panel is read after the task, so it lines up with the content beside it
     rather than with the heading above it. */
  .side{padding-top:6px}
}

/* Two columns on a wide screen: the form, and beside it what the form produces.
   A single card alone on a desktop canvas reads as unfinished, and a product
   about flashcards that shows no flashcard until you have waited a minute for one
   asks to be believed instead of showing. Below 900px this stacks and the preview
   drops away: on a phone the form already fills the screen. */
.stage{display:grid;gap:26px;max-width:560px;margin:0 auto;align-items:start}
@media (min-width:900px){
  .stage{max-width:720px}
}
/* Two columns at the same width and the same breakpoint as every other screen
   that splits. It used to split at 900px and jump to 980 while the rest were
   still 720, so walking from the entry screen to any other one resized the
   page. One system: one column to 1240, two after it, one width throughout. */
@media (min-width:1240px){
  .stage{grid-template-columns:minmax(0,1fr) minmax(260px,340px);max-width:980px;gap:44px}
}

.aside{display:none}
@media (min-width:1240px){
  .aside{display:block;padding-top:8px}
}

/* Deliberately quiet: this is an illustration of the output, not a control. It
   must never compete with the one real action on the screen. */
.preview{opacity:.8}
.preview .face{box-shadow:0 12px 26px rgba(29,27,20,.10);margin:0}
/* Only the card leans. The caption stays level: a rotated line of small text
   reads as a mistake, not as a flourish. */
.preview-stack{position:relative;isolation:isolate;transform:rotate(-1.2deg)}
/* Two hairlines peeking out below: a deck, not a single card. */
.preview-stack::before,.preview-stack::after{
  content:"";position:absolute;left:12px;right:12px;top:14px;bottom:-8px;
  border:1px solid var(--line);border-radius:16px;background:linear-gradient(180deg,#fff,#F7F3EA);z-index:-1}
.preview-stack::after{left:22px;right:22px;bottom:-16px;opacity:.55}
.preview-note{color:var(--muted);font-size:12px;font-weight:500;text-align:center;margin:30px 6px 0}

.h1{font-family:"Bricolage Grotesque",sans-serif;font-weight:800;font-size:34px;line-height:1.02;letter-spacing:-1px;margin:0 0 14px}
.h1 .dot{color:var(--accent2)}
.sub{color:var(--muted);margin:0 0 22px}

/* An empty library: the one screen in the app with room for the character.
   Beside the words on a wide screen, above them when the column narrows, and
   never in place of them: the sentence carries the whole message on its own. */
.blank{display:flex;gap:22px;align-items:flex-start;flex-wrap:wrap;margin-top:2px}
.blank .sub{flex:1 1 260px;margin:0}
.blank .clip{flex:0 0 auto;margin-top:-6px}
.blank button{margin-top:14px}
.blank-words{flex:1 1 260px}

/* A ready-made deck and the button that takes it. The button sits under
   the row rather than beside it, so a long title never squeezes it. */
.offer{padding-bottom:12px}
.offer .add{width:auto;padding:8px 14px;font-size:13px;margin-top:2px}

/* What the quiz turned into: new cards, written from the questions that were
   missed. It sits between the score and the way out, so it is ruled off above
   rather than boxed: a box here would read as a second result competing with
   the one the learner came for. */
.made{margin:26px 0 6px;padding-top:20px;border-top:1px solid var(--line)}
.made .lbl{margin-bottom:6px}
.made .row{margin-top:12px}
/* Card fronts are questions, so they wrap. The row styles elsewhere clip to one
   line, which is right for a title and wrong for a sentence. */
.made-list li{padding:12px 6px;font-size:15px;color:var(--ink)}
.made-list li::before{content:"+";color:var(--correct-ink);font-weight:700;margin-right:10px}
/* The result is written into the same live region that carried the working
   state, so that a screen reader hears how it ended. That node is styled as a
   quiet status line everywhere else in the app, and here it is the payoff, so
   it takes the weight the moment has earned without becoming a second node. */
.made .status[data-kind=info]{color:var(--ink);font-size:16px;margin-top:14px}
.made .status:focus{outline:2px solid var(--accent);outline-offset:4px;border-radius:4px}

/* How much of the subject this deck is, as against how much of this deck the
   learner is. It sits below the mastery path and is ruled off from it the same
   way, and deliberately carries no second progress bar: two bars side by side
   would read as two measures of the same thing when they measure opposite
   things. The gaps are the useful half, so they are the part that is listed. */
.cover{margin:22px 0 4px;padding-top:18px;border-top:1px solid var(--line)}
.cover .lede{font-size:16px;margin:6px 0 10px}
.cover .fine{margin-top:12px}
.gaps li{padding:11px 6px;font-size:15px;color:var(--ink)}
/* An absence, marked as one. Amber is the micro-accent and this is exactly the
   size of thing it is for: one glyph, never a field of colour. */
.gaps li::before{content:"-";color:var(--accent2);font-weight:700;margin-right:12px}
/* The tail count is a note about the list, not another gap, so it takes neither
   the marker nor the weight of one. */
.gaps .gap-more{color:var(--muted);font-size:13px}
.gaps .gap-more::before{content:none}

/* The cards of a deck, as a list you can scan rather than a wall you cannot.
   The question is the row; the answer waits behind it. Two hundred questions is
   something somebody reads, two hundred question-and-answer pairs is not, and
   the deck screen already learned once what happens when a panel grows past the
   thing it describes. */
.cards .card-head{align-items:flex-start;gap:12px}
/* The number is the anchor a learner counts by, so it comes first and holds a
   fixed width: ragged numbers turn a list into a stack of unrelated lines. */
.cards .row-note{min-width:2.2em;padding-top:2px}
/* Questions wrap. The row styles elsewhere clip to one line, which is right for
   a deck title and wrong for a sentence somebody has to be able to read. */
/* min-width:0 because a flex item will not shrink below its longest word
   otherwise, and overflow-wrap because study material contains URLs and long
   identifiers. Without both, one pasted link pushes the row past the card and
   puts a horizontal scrollbar on a phone. */
.cards .card-q{font-family:"Bricolage Grotesque",sans-serif;font-weight:700;
  color:var(--ink);flex:1;min-width:0;overflow-wrap:anywhere}
/* An open row needs to look open to somebody who is not using a screen reader,
   and the tint alone cannot do it: it is the same tint the closed rows take
   under the pointer. A plus and a minus, in text, so it survives any font. */
.cards .card-head::after{content:"+";color:var(--muted);font-weight:700;
  margin-left:auto;padding-left:12px}
.cards .card-head[aria-expanded=true]::after{content:"-"}
.cards .card-head[aria-expanded=true]{background:#FBF8F1}
/* The body carries the open row's tint too. With it on the head alone, the
   answer and the rewrite controls sat on bare paper between two ruled rows and
   read as page furniture rather than as the inside of card 1. */
.cards .card-body{padding:2px 6px 18px 6px;background:#FBF8F1}
/* Where there is width for it, the answer lines up under the question instead
   of under the number: 6px of row padding, the 2.2em number column at 12px, and
   the 12px gap between them. Not on a phone, where 44px of indent is taken off
   a column that is already narrow and the tint alone ties the two together. */
@media (min-width:520px){
  .cards .card-body{padding-left:44px}
}
.cards .card-a{margin:0;color:var(--muted);font-size:15px;overflow-wrap:anywhere}
/* A card the model left half-written. Said in words on the row, and set apart
   from the text of a card that has some, so it does not read as the question. */
.cards .card-missing{font-style:italic;color:var(--muted);font-weight:500}
/* Inside an open card the repair sits closer than it does on the study screen:
   there is no work in progress here to keep it away from. */
.cards .mend{margin-top:14px;padding-top:12px}

/* Asking the tutor to rewrite the card in front of you. Below the grades and
   quieter than them in every dimension: grading is what the study screen is
   for, and a repair offer that competes with the work would be answering a
   question almost nobody is asking at that moment. The rule above it separates
   it from the judgement without making it a panel of its own. */
.mend{margin-top:22px;padding-top:14px;border-top:1px solid var(--line)}
.mend .fine{margin:0 0 10px}
/* A press, not a button. It has to read as an aside next to three grading
   buttons rather than as a fourth one, so it borrows the weight of the small
   print around it and marks itself with a hairline rather than a box. */
.asklink{appearance:none;background:none;border:0;padding:0;cursor:pointer;
  font-family:inherit;font-size:13px;color:var(--muted);
  border-bottom:1px solid var(--line)}
.asklink:hover{color:var(--ink);border-bottom-color:var(--ink)}
.mend .row{margin:0;gap:10px}
.mend .status{margin-top:10px}
.mend .status[data-kind=info]{color:var(--ink)}
/* No smaller type or tighter padding here, and that is deliberate: the base
   button is already 13px, so a "small" variant would only shrink the target
   below what the rest of the client offers. What makes this quieter is where it
   sits and what it says, not how hard it is to press. */

/* How sure this learner has been. The last thing on the home screen and the
   smallest: no figure, no bar, no colour of its own. It carries a direction in
   a sentence, and a direction rendered as a chart would claim a precision that
   three buttons and a self-rating do not have. The rule sets it apart from the
   library above without making it a card competing with one. */
.sure{margin:26px 0 0;padding-top:16px;border-top:1px solid var(--line)}
.sure .fine{margin:0}
/* The qualifier sits under the sentence it qualifies and lighter than it, so
   it reads as the small print it is rather than as a second finding. */
.sure .sure-note{margin-top:8px;font-size:12px;color:var(--muted)}

/* Where this learner stands in the deck, concept by concept. Quiet by
   construction: it sits between the deck's counts and its buttons, and it must
   read as information rather than compete with the action. Green marks what is
   known, the cobalt edge marks the one concept to start on, and every state is
   also written in words - the colour is the second signal, never the only one. */
.path{margin:22px 0 4px;padding-top:18px;border-top:1px solid var(--line)}
.bar{display:block;height:6px;border-radius:3px;background:var(--line);overflow:hidden;margin:8px 0 14px}
.bar span{display:block;height:100%;background:var(--correct);border-radius:3px}
.nodes{list-style:none;padding:0;margin:0}
.node{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:baseline;gap:12px;
  padding:7px 0 7px 12px;border-left:2px solid var(--line)}
.node[data-bucket=mastered]{border-left-color:var(--correct)}
.node[data-current]{border-left-color:var(--accent);border-left-width:3px;padding-left:11px}
.node-term{font-weight:500;color:var(--muted)}
.node[data-bucket=mastered] .node-term{color:var(--ink)}
.node[data-current] .node-term{color:var(--ink);font-weight:600}
/* margin-left:auto, not space-between alone. A long concept name pushes the
   state onto its own line, and space-between then parks that lone item on the
   left while its shorter neighbours keep theirs on the right - a list where
   half the labels are in one column and half in the other. */
.node-state{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:1.2px;
  color:var(--muted);white-space:nowrap;margin-left:auto}
.node[data-bucket=mastered] .node-state{color:var(--correct-ink)}
.node[data-bucket=untested]{border-left-style:dashed}
.node[data-bucket=untested] .node-state{color:var(--muted)}
.node[data-current] .node-state{color:var(--accent)}
.node-tally{flex-basis:100%;font-size:12px;color:var(--muted);margin-top:2px}
.node-more{padding:9px 0 0 12px;font-size:12px;color:var(--muted)}

/* The screen after the last card of the day. Same order as the empty library
   and the same kind of moment - nothing is being worked on - but centred and
   with no offset, because the dose here is taller. The bottom margin matches
   what .sub carried before this block existed, so the gap above the buttons is
   unchanged. */
.landed{display:flex;gap:22px;align-items:center;flex-wrap:wrap;margin:0 0 22px}
.landed .sub{flex:1 1 260px;margin:0}
.landed .clip{flex:0 0 auto}

/* 16px is not a taste decision here. Safari on iPhone zooms the page to about
   107% when a field smaller than that takes focus, and it does not zoom back
   out when the field is left: the rest of the visit happens on a page that is
   scaled and pans sideways. One value rather than a media query, because 15px
   was never part of the brand canon. */
textarea,input[type=url],input[type=email],input[type=password]{
  width:100%;font-family:inherit;font-size:16px;color:var(--ink);
  background:#fff;border:1px solid var(--line);border-radius:10px;padding:12px 14px;
}
textarea{resize:vertical;min-height:132px;line-height:1.5}
/* The one control the browser insists on styling itself. The real input stays
   present and focusable; only its native chrome is clipped away, so keyboard use
   and the accessible name survive while the seam does not. */
.filepick{position:relative;display:block}
.filepick input[type=file]{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer}
.filepick span{display:block;text-align:center;padding:12px 18px;border-radius:10px;
  font-size:13px;font-weight:600;background:#fff;color:var(--ink);border:1px solid var(--line);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.filepick input[type=file]:focus-visible+span{outline:2px solid var(--accent);outline-offset:2px}

:is(textarea,input,button,a):focus-visible{outline:2px solid var(--accent);outline-offset:2px}

.row{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:12px}

/* The hidden attribute has to win, and without this it does not.
   The browser's own rule is [hidden]{display:none} at one class of weight, and
   any author rule setting display beats it: .row above is enough. On the study
   screen that meant the grade buttons, marked hidden until the answer is shown,
   were on screen and pressable the whole time - somebody could answer "Knew it"
   on a card whose answer they had never seen, which is the one thing a
   flashcard is for. Marked important because this is a correctness rule about
   an attribute, not a style anything should be allowed to override. */
[hidden]{display:none !important}
.row>*{flex:1 1 180px}

button{font-family:"Spline Sans",sans-serif;font-size:13px;font-weight:600;letter-spacing:.3px;
  padding:12px 18px;border-radius:10px;cursor:pointer;border:1px solid transparent}
button:disabled{opacity:.55;cursor:default}

/* The amber underline is the second half of the anchor, and the only other place
   amber appears. It marks the one action that moves the person forward. */
.btn-primary{background:var(--ink);color:var(--paper);position:relative}
.btn-primary::after{content:"";position:absolute;left:16px;right:16px;bottom:7px;height:2px;background:var(--accent2);border-radius:2px}
.btn-primary:disabled::after{background:var(--line)}

.btn-quiet{background:#fff;color:var(--ink);border-color:var(--line)}

.status{margin:16px 0 0;font-size:14px;color:var(--muted);min-height:1.4em}
/* The reserved line earns its keep on screens where a message arrives at the
   end of what you are reading and would otherwise shove the page down. In a
   session it sits between the card and the footer and says nothing on all but
   a handful of cards, so what it reserves is a hole in the middle of the one
   screen somebody looks at for minutes at a time. It gets its height back the
   moment it has something to say. */
.app.study .status:empty{min-height:0;margin:0}
.status[data-kind=error]{color:var(--wrong);font-weight:600}
.status[data-kind=working]{color:var(--accent);font-weight:600}

/* The working state breathes so a minute of generation reads as progress rather
   than as a frozen screen. Stilled for anyone who asks for less motion. */
.status[data-kind=working]::after{content:"";display:inline-block;width:6px;height:6px;margin-left:8px;
  border-radius:50%;background:var(--accent2);animation:pulse 1.1s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:.25}50%{opacity:1}}
@media (prefers-reduced-motion:reduce){
  .status[data-kind=working]::after{animation:none;opacity:1}
}

/* One real card from the deck, quoted on the screen that reports what was
   built. Deliberately not a .face: the cobalt edge, the lift and the centring
   belong to the card somebody is being asked to answer, and a quotation of one
   is not that. A hairline box says "an example of the contents" instead. */
.sample{border:1px solid var(--line);border-radius:14px;padding:18px 20px;margin:22px 0 0;background:#fff}
.sample .sq{font-family:"Bricolage Grotesque",sans-serif;font-weight:700;font-size:18px;
  line-height:1.25;letter-spacing:-.2px;margin:0;overflow-wrap:anywhere}
.sample .sa{font-size:15px;color:var(--muted);margin:12px 0 0;padding-top:12px;
  border-top:1px solid var(--line);overflow-wrap:anywhere}

/* The cobalt edge lives here and nowhere else. It was on the shell as well,
   which showed the mark twice in one view, one nested in the other; the shell
   has since stopped being a panel at all, so this is the only thing left that
   is literally a card, and the three pixels finally mean what they say. */
.face{border:1px solid var(--line);border-radius:16px;padding:34px 26px;margin:6px 0 18px;
  background:linear-gradient(180deg,#fff,#F7F3EA);box-shadow:0 18px 34px rgba(29,27,20,.12);
  border-top:3px solid var(--accent);text-align:center}
.face .qk{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:2px;color:var(--accent);margin-bottom:16px}
/* The card is centred, so a single unbroken token - a URL, a class name, a
   chemical formula - spills off BOTH edges. The right spill puts a scrollbar
   on the document; the left spill goes to negative x, where no amount of
   panning reaches it. A learner would be shown a question they cannot finish
   reading and then asked to grade themselves on it. The cards list already
   carries this guard; the two screens that set the same generated text 8px
   larger never got it. */
/* The question gives, because its length cannot. Questions are written by a
   model from somebody's material and run from four words to forty; at one fixed
   size a long one took eleven lines of 23px bold on a phone and left no room on
   screen for the card, the button or the footer. The ceiling is the size a
   laptop already had - a display face is part of what makes this read as a card
   rather than a form - and the floor keeps a short question from shrinking to
   nothing on a narrow screen. */
.face .q{font-family:"Bricolage Grotesque",sans-serif;font-weight:700;
  font-size:clamp(19px,5vw,23px);line-height:1.2;letter-spacing:-.3px;margin:0;
  overflow-wrap:anywhere}
/* Two moments, and only two.

   A screen arrives rather than appearing: 200ms of opacity and six pixels of
   rise, from an already-visible default so a browser that ignores the
   animation shows the page rather than a blank card. Anything longer reads as
   waiting; anything larger reads as a slide deck.

   And the answer, which is the single most repeated moment in the product: a
   learner presses "Show the answer" perhaps forty times in a session. It is
   not a flip - the question stays on screen above the answer, so there is no
   second face to turn - it is the card giving something up.

   Both are removed outright for anyone who has asked for less movement, not
   slowed down: the movement is a flourish, and a flourish is exactly what
   somebody with a vestibular disorder is asking us to drop. */
@keyframes noeta-arrive{from{opacity:.001;transform:translateY(6px)}to{opacity:1;transform:none}}
@keyframes noeta-reveal{from{opacity:.001;transform:translateY(-4px)}to{opacity:1;transform:none}}
.arrive{animation:noeta-arrive 200ms cubic-bezier(.16,1,.3,1) both}
.face .a:not([hidden]){animation:noeta-reveal 260ms cubic-bezier(.16,1,.3,1) both}
@media (prefers-reduced-motion:reduce){
  .arrive,.face .a:not([hidden]){animation:none}
}

.face .a{font-size:16px;color:var(--muted);margin:20px 0 0;padding-top:18px;border-top:1px solid var(--line);overflow-wrap:anywhere}

.meta{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:baseline;gap:4px 12px;
  color:var(--muted);font-size:12px;font-weight:500;
  border-top:1px solid var(--line);margin-top:20px;padding-top:14px}
/* The tally is short and the sentence beside it is not, so on a phone the tally
   was the one being squeezed: "0 known so" over "far", next to a full line of
   prose. It keeps its line now, and the sentence drops below when both will not
   fit. */
.meta>span:first-child{white-space:nowrap}

.count{font-family:"Bricolage Grotesque",sans-serif;font-weight:800;font-size:30px;letter-spacing:-.5px;color:var(--ink);display:block;line-height:1}
.stat{flex:1;text-align:center}
.stat span:last-child{font-size:11px;text-transform:uppercase;letter-spacing:1.5px;color:var(--muted)}

/* ---- navigation -----------------------------------------------------------
   Sits above the card, outside it, at the quietest weight in the system. It is
   orientation, not a control, and it must not compete with the one primary
   action inside the card below it. It also earns its keep twice: because the
   nav names the section, the screens under it drop the eyebrow above their
   heading rather than saying the same word twice. */
/* Held to the card's own width, not the wide stage: on the create screen the
   stage runs to 940px but the card that owns the page is still 560, and a nav
   starting 190px to the left of it reads as belonging to something else. */
/* Wider between rows than between links: the touch areas below reach 11px past
   each link, so a 20px row gap would have two of them overlapping in the middle
   and a tap toward one line landing on the other. */
/* The one-tap way in sits above the form and takes the full column, because it
   is the path most people take and a button the width of a word next to a form
   reads as an afterthought. The line under it separates two ways of doing the
   same thing without shouting "OR" in a box. */
.wide{width:100%;justify-content:center}
.orelse{margin:14px 0 18px;color:var(--muted);font-size:12px;letter-spacing:.06em;
  text-transform:uppercase;display:flex;align-items:center;gap:12px}
.orelse::before,.orelse::after{content:"";flex:1;height:1px;background:var(--line)}

/* Which plan this account is on, said on the account screen rather than only on
   the plan screen. Quiet: it is a fact about you, not an offer. The link is the
   only inline anchor inside the app, so it carries its own underline rather
   than borrowing one. */
.whichplan{margin:-4px 0 18px;color:var(--muted);font-size:14px}
.whichplan a{color:var(--ink);text-decoration:none;
  border-bottom:1px solid var(--line);padding-bottom:1px}

/* The bar the mark and the sections share. On a phone the mark sits above the
   row, which is the only way both fit; from 560px up they sit on one line with
   the mark at the left, which is where a person looks for it. */
.topbar{max-width:560px;margin:0 auto 16px;padding:0 4px;display:flex;
  flex-direction:column;gap:10px}
/* Tracks the card underneath it at every width. A navigation that starts 130px
   to the left of the thing it belongs to reads as belonging to something else,
   which is the mistake the create screen already carries a note about. */
@media (min-width:900px){.topbar{max-width:720px}}
@media (min-width:1240px){.topbar{max-width:980px}}
/* The same size and tracking as the wordmark on the front page. It was 17px
   here and 26px there, so the name changed size the moment somebody opened the
   app, which is the smallest and most avoidable way to look like two products. */
.mark{align-self:flex-start;font-family:var(--display);font-weight:800;font-size:26px;
  letter-spacing:-.5px;color:var(--ink);text-decoration:none;line-height:1;
  padding:6px 2px;margin:-6px -2px}
.mark span{color:var(--accent)}
@media (min-width:560px){
  .topbar{flex-direction:row;align-items:baseline;gap:22px}
}
.chrome{display:flex;gap:24px 20px;padding:0;flex-wrap:wrap}
.chrome a{position:relative;color:var(--muted);text-decoration:none;font-size:11px;font-weight:600;
  text-transform:uppercase;letter-spacing:2px;padding-bottom:3px;border-bottom:2px solid transparent;
  transition:color 160ms ease-out}
/* The link is 11px type on a 22px line, which is a fine thing to read and a
   poor thing to hit with a thumb. The touch area is grown to about 44px here
   rather than with padding, because padding would drag the amber underline
   away from the word it underlines. The 20px gap between links leaves them
   separate even after this. */
.chrome a::before{content:"";position:absolute;left:-6px;right:-6px;top:-11px;bottom:-11px}
/* The amber underline, the same mark the primary button carries, and it means
   the same thing in both places: this is where you are. */
.chrome a[aria-current=page]{color:var(--ink);border-bottom-color:var(--accent2)}

/* ---- the left edge -------------------------------------------------------
   Measured before this was written: on a 1512 by 860 screen the library filled
   46 per cent of the height and a session 53, so half the page was background.
   No column width fixes that, and the fix everything in this category has
   landed on independently - Anki, Quizlet, RemNote, Readwise, Notion, Linear,
   Todoist, Obsidian, Mochi - is a navigation that runs the full height of the
   window. It gives the page a left edge, and because it reaches the bottom the
   layout is finished whether or not the content is.
   Wide screens only. Below this the same markup stays the horizontal strip it
   already was, which is the right shape for a narrow window and needs no
   drawer, no overlay and no second state to get wrong.
   The threshold is 1000 and not 1100, which is where it started. A laptop
   window that is not maximised sits in the low thousands - the report that
   sent me back here was taken at 1013 - so a hundred pixels of caution meant
   the person who asked for this could not see any of it. At 1000 the rail
   takes 250 and the column keeps 710, which is inside the 720 bracket the
   ladder already had, so nothing is squeezed to make room.
   The content keeps the width ladder it had; it is centred in the space beside
   the rail rather than in the window, which is what stops it drifting left. */
@media (min-width:1000px){
  body{padding-left:290px}
  .topbar{position:fixed;left:0;top:0;bottom:0;width:250px;max-width:none;margin:0;
    padding:34px 26px;flex-direction:column;align-items:flex-start;gap:34px;
    border-right:1px solid var(--line);overflow-y:auto}
  .chrome{flex-direction:column;flex-wrap:nowrap;gap:20px;align-self:stretch}
  /* The touch pad that makes an 11px link thumbable is 22px tall on a row of
     them and would overlap its neighbours in a column, swallowing clicks meant
     for the link above. Kept, narrowed to the gap it now has. */
  .chrome a::before{top:-8px;bottom:-8px}
}

/* ---- the bottom anchor ----------------------------------------------------
   A session is the emptiest screen in the product and the one somebody looks at
   for minutes. Vertical centring is the obvious answer and the wrong one: the
   question and the answer are different heights, so centring makes the text
   jump at the exact moment it is being read. The Anki project argued this out
   and decided against it, and is removing the option where it exists.
   What the screen was missing is what their reviewer has instead: the buttons
   sitting on the bottom edge. The space between the card and them is then
   inside something rather than left over. Only where the rail is fixed, so the
   height it no longer takes in the flow is the height this can claim. */
@media (min-width:1000px){
  /* Narrower than the list screens. A question is prose and reads at a prose
     measure; 980 was the width the library needs for rows, not the width a
     sentence wants. */
  .app.study{display:flex;flex-direction:column;max-width:760px;
    min-height:calc(100vh - 88px)}
  /* Anchoring the buttons alone just moves the hole into the middle, and giving
     the card all of the space makes a near-square panel holding one line. So
     the card is a proportioned object and the leftover splits evenly above and
     below it - which is what the two auto margins do - leaving everything after
     it sitting on the bottom edge.
     The height comes from the window, not from the text. That is the whole
     point: revealing an answer changes what the card says and never how tall it
     is, so nothing moves under the eye at the moment it is reading. That
     movement is the objection that sank vertical centring everywhere else in
     this category, and a fixed box is the way to keep the composition without
     it. */
  /* Every number here exists to hold something still, and each one was put in
     after a measurement showed it moving.
     Fixed height, so revealing an answer cannot change the box. A fixed top
     offset rather than an auto margin, because an auto margin above splits the
     leftover space, and the leftover shrinks the moment the row below grows
     from one button to three plus a line - which moved the card up 31px and the
     question 62. And the text starts at the top of the card instead of being
     centred in it, because centring question-and-answer together moves the
     question up by half the answer every time.
     Only the space below the card flexes now. The eye reads a question that is
     in the same place on every card and stays there when the answer arrives. */
  .app.study .face{flex:0 0 auto;height:min(42vh,360px);overflow:auto;
    margin:5vh 0 auto;padding-top:44px;
    display:flex;flex-direction:column;justify-content:flex-start}
}

/* ---- the sentence that opens a screen -------------------------------------
   Home leads with what to do next rather than with a row of figures, so the one
   number that matters is set inline at display weight instead of being lifted
   into a statistics strip that answers no question. */
.lede{font-family:"Bricolage Grotesque",sans-serif;font-weight:700;font-size:19px;
  line-height:1.3;letter-spacing:-.3px;margin:0 0 20px;color:var(--ink)}
.lede em{font-style:normal;font-weight:800;border-bottom:2px solid var(--accent2)}

/* ---- list rows ------------------------------------------------------------
   Hairline-separated rows, deliberately not boxes: the card metaphor is spoken
   for by the flashcard, and a grid of small cards inside the app card would be
   one container nested in another. */
.rows{list-style:none;padding:0;margin:0 0 8px;border-top:1px solid var(--line)}
.rows li{border-bottom:1px solid var(--line)}
.row-item{display:flex;justify-content:space-between;align-items:baseline;gap:14px;width:100%;
  text-align:left;background:none;border:0;border-radius:0;padding:15px 6px;font-size:15px;
  transition:background 160ms ease-out}
/* The name wraps rather than being clipped. It used to end in an ellipsis, and
   on a phone that ate the half of a deck title that says what the deck is
   about: "Study Skills: Active ..." names nothing. A row two lines tall costs
   less than a row nobody can read. */
.row-name{font-family:"Bricolage Grotesque",sans-serif;font-weight:700;color:var(--ink);
  min-width:0;overflow-wrap:anywhere}
/* The row itself does not wrap, so the note holds its place on the right and
   the name takes the two lines. */
.row-note{font-size:12px;font-weight:500;color:var(--muted);white-space:nowrap}
/* Due is the only thing in a row worth interrupting a scan for, so it is the one
   part that takes the primary colour. */
.row-due{color:var(--accent);font-weight:600}

/* ---- quiz options ---------------------------------------------------------
   Stacked full-width choices rather than a row: the options are sentences of
   unequal length, and a row would set their width by their wording instead of
   giving each the same claim on the eye. Left-aligned for the same reason.
   Once answered they stay on screen, marked, because the explanation under
   them is only worth reading next to what was actually chosen. */
.opts{display:grid;gap:8px;margin:0 0 4px}
.opt{width:100%;text-align:left;background:#fff;color:var(--ink);border:1px solid var(--line);
  border-radius:10px;padding:14px 16px;font-size:15px;font-weight:500;line-height:1.35;
  transition:border-color 160ms ease-out,background 160ms ease-out}
/* Disabled here means answered, not unavailable, so the text stays at full
   strength; only the two marked options carry any colour. */
.opt:disabled{opacity:1;cursor:default}
.opt[data-mark=right]{border-color:var(--correct);color:var(--correct);font-weight:700}
.opt[data-mark=wrong]{border-color:var(--wrong);color:var(--wrong);text-decoration:line-through}
/* The verdict needs air above it. Butted straight against the options it reads
   as a caption on the last one rather than a judgement on the answer. */
.verdict{margin-top:22px}
.lede[data-kind=right]{color:var(--correct)}
.lede[data-kind=wrong]{color:var(--wrong)}

/* Loading shows the shape of what is coming rather than a spinner floating
   where content will be. Stilled for anyone who asks for less motion. */
.skel{height:15px;border-radius:3px;background:var(--line);opacity:.55;animation:breathe 1.4s ease-in-out infinite}
@keyframes breathe{0%,100%{opacity:.3}50%{opacity:.65}}
@media (prefers-reduced-motion:reduce){.skel{animation:none}}

/* ---- plan comparison ------------------------------------------------------
   Two columns divided by a rule, not two cards inside the card. The numerals
   carry the whole comparison - two a day against fifteen is the entire
   difference between the plans - so they are set at display weight because they
   are the content, not because a figure looks impressive. */
.compare{display:grid;gap:0;margin:4px 0 22px;border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
@media (min-width:520px){.compare{grid-template-columns:1fr 1fr}}
.tier{padding:20px 4px}
/* .count is reused from the deck screen, where it sits in a flex stat cell and
   the paragraph margin never shows. Here it is a paragraph in normal flow, so
   the inherited 1em margin opens a gap between the figure and the unit it
   belongs to and reads as two unrelated lines. */
.tier .count{margin:6px 0 0}
@media (min-width:520px){
  .tier+.tier{border-left:1px solid var(--line);padding-left:24px}
}
@media (max-width:519px){.tier+.tier{border-top:1px solid var(--line)}}
.tier-name{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:2px;color:var(--muted);margin:0}
.tier-lead .tier-name{color:var(--accent)}
.tier-unit{font-size:12px;font-weight:500;color:var(--muted);margin:2px 0 12px}
.tier-note{font-size:14px;color:var(--muted);margin:0;line-height:1.45}
.tier-price{font-size:14px;font-weight:600;color:var(--ink);margin:0 0 8px}

/* Standing detail under an action: what it costs, where it takes you, what it
   leaves behind. Quieter than .sub because it is read after the decision, not
   in order to make it, but still at a size a person can actually read. */
.fine{font-size:13px;color:var(--muted);line-height:1.5;margin:12px 0 0}
/* Sits right under the two controls it describes, not a paragraph away: what
   a file picker accepts is the question somebody has while looking at it, and
   the answer used to live only on the landing page they already scrolled past. */
.hint{margin:8px 2px 0}

/* A field label above an input, distinct from .lbl which labels a whole group. */
.field{display:block;margin:14px 0 6px;font-size:11px;font-weight:600;
  text-transform:uppercase;letter-spacing:2px;color:var(--muted)}

/* ---- what happens under a pointer ----------------------------------------
   Gathered here and asked for, because a phone has no pointer to hover with.
   A touch browser matches :hover on tap and then keeps matching it until you
   tap somewhere else, so a button left blue after a press reads as still
   working, and a row left tinted reads as selected. The one rule that turns a
   hover off (.offer, where the row is a description and the button beside it is
   the control) belongs in here with the rest for the same reason. */
@media (hover:hover){
  .btn-primary:hover:not(:disabled){background:var(--accent-deep)}
  .btn-quiet:hover:not(:disabled){border-color:var(--ink)}
  .filepick:hover span{border-color:var(--ink)}
  .chrome a:hover{color:var(--ink)}
  .row-item:hover{background:#FBF8F1}
  .offer .row-item:hover{background:none}
  .opt:hover:not(:disabled){border-color:var(--ink);background:#FBF8F1}
}
