/* Nexley — a syllabus study book.
   Direction: editorial & calm. Feint-ruled paper, a bookish serif, syllabus
   outcome codes as the structural device, one highlighter-yellow mark used
   sparingly. The rail is the quietest surface; the page you write on is the
   brightest. Grounded in the real materials of study: syllabus documents,
   ruled paper, highlighters, revision guides.

   09-05: the base neutrals (paper/surface/card/ink/ink-2/muted/rule) were
   repaletted onto Apple's own system gray scale — "professional notebook AND
   Apple-style formal brand" was the brief. Eucalypt (--structure), the
   highlighter (--mark) and the serif reading/display faces are unchanged:
   those are what makes this a notebook and not a spreadsheet. */

/* Newsreader — the display voice. Self-hosted and precached in sw.js so headings
   look the same on a Windows laptop, an iPad and an Android phone, and still look
   right with no network. One weight (400), variable optical size 6–72, so a big
   title and a small one are drawn with the contrast each size wants.
   SIL Open Font Licence 1.1 — see fonts/OFL.txt. */
@font-face{
  font-family:Newsreader;font-style:normal;font-weight:400;font-display:swap;
  src:url(fonts/newsreader-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:Newsreader;font-style:normal;font-weight:400;font-display:swap;
  src:url(fonts/newsreader-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;
}

:root{
  /* --- day ---
     09-05 repaletted toward a neutral system gray scale (the values below track
     Apple's own light-mode label/separator grays) instead of the original warm
     sage-green neutrals. The brief was "professional notebook AND Apple-style
     formal brand" — eucalypt (--structure), the highlighter (--mark) and the
     serif reading faces stay exactly as they were, because those are the
     notebook identity. What changes is everything that used to read as
     "school paper": the base surfaces are neutral now, not warm-green. */
  /* 2026-09-06: WARMED BACK UP, deliberately, after Alec's "it still looks
     plain and vibecoded". The September repalette moved these onto Apple's
     neutral system grays, which is a defensible chrome palette and made the
     app quieter — but a study notebook rendered in #F5F5F3 on #FFFFFF is a
     settings screen, not a notebook. These values combine two of the archived
     concepts rather than copying either: Foolscap's warm sheet and desk, and
     Ledger's ink, lines and record-keeping accents. Eucalypt and the
     highlighter are untouched, as they have been through every pass. */
  --paper:#EFEBE1;      /* desk / working ground — Foolscap's desk */
  --surface:#E7E2D6;    /* rail, recessed chrome — deliberately dimmer */
  --card:#FFFDF7;       /* the page: cards + the writing surface. Sheet, not white. */
  --ink:#1A1815;        /* warm near-black, never pure #000 */
  --ink-2:#443F37;
  /* Darkened from #7E7669 on 2026-09-08. Measured, not taste: the old value gave
     4.41:1 on --card, 3.77 on --paper and 3.47 on --surface, so every caption,
     eyebrow and bit of metadata in the app failed WCAG AA in the LIGHT theme —
     and most of them are set at 9-13px, where it matters most. Dark theme was
     already clean. #6A6255 is the LIGHTEST value that clears 4.5:1 against all
     three light grounds (worst case 4.65 on --surface), chosen deliberately over
     something darker so the quiet, recessive character of muted text survives.
     test/test_contrast.js re-derives this; do not nudge it by eye. */
  --muted:#6A6255;      /* mono metadata, captions */
  --rule:#D5CEC0;       /* hairlines — the ruled line */
  --rule-soft:#E6E1D6;
  --structure:#21493B;  /* eucalypt: links, primary actions, active state. Reads as ink. */
  --structure-soft:#DEE7E0;
  /* The accent is one token, so a whole-app recolour is four lines rather than
     a find-and-replace. Set on <html> by the settings dialog. Every accent is
     dark enough to read as INK on paper — a study app whose primary colour is
     a bright hue looks like a toy, and the brief has been "professional, not
     vibecoded" from the beginning. */
  --mark:#E7E24C;       /* the one accent. Selection, coverage fill, current-note spine. */
  --mark-ink:#23261A;
  --danger:#A03A22;
  /* The margin rule down the left of a page. Foolscap's red, desaturated —
     at full strength it reads as an error state, which is the one thing a
     margin line must never do. */
  --margin-rule:#C98B86;
  /* Brass, from Ledger. The SECOND accent, and it means one thing: a formal
     record — a real mark, sat under real conditions. Eucalypt is the app
     talking; brass is the record. Never used decoratively. */
  --brass:#9A7B2E;
  --brass-soft:#F1E9D6;
  --shade:26 24 21;     /* rgb for shadows — warm, matching the ink */

  /* ============================================================
     SCALES — the system. Every measurement in this file resolves to one of
     these. Before this existed the stylesheet carried 22 distinct font sizes,
     12 gap values and 10 border-radii, none of them related to each other:
     the specific thing that makes an interface look assembled rather than
     designed. If a value you need is not on a scale, the answer is almost
     always the nearest step, not a new one.
     ============================================================ */

  /* type — ~1.16 ratio, anchored on --t-base, the UI default */
  --t-2xs:.5625rem;   /*  9px  mono micro-labels */
  --t-xs:.625rem;     /* 10px  eyebrows, chips */
  --t-sm:.6875rem;    /* 11px  captions, metadata */
  --t-base:.8125rem;  /* 13px  UI text — the anchor */
  --t-md:.9375rem;    /* 15px  list titles, emphasis */
  --t-lg:1.0625rem;   /* 17px  reading */
  --t-xl:1.1875rem;   /* 19px  brand, dialog titles */
  --t-2xl:clamp(1.35rem, 1.15rem + .9vw, 1.7rem);   /* section headings */
  --t-3xl:clamp(1.7rem, 1.35rem + 1.6vw, 2.25rem);

  /* space — 2,4,6,9,12,16,22,30,42. Not a strict geometric run: the small end
     needs fine control for chrome, the large end only needs a few steps. */
  --s-1:2px; --s-2:4px; --s-3:6px; --s-4:9px; --s-5:12px;
  --s-6:16px; --s-7:22px; --s-8:30px; --s-9:42px;

  /* radius — three steps and a pill. Ten was nine too many.

     The steps are ~1.6x apart, which is the same restraint the spacing scale
     uses, and each has ONE job so a value never has to be chosen by eye. The
     pill is a token rather than a literal because "fully round" was being
     written three different ways — 99px, 50%, and this — and three spellings of
     one idea is how a scale stops being a scale. Nothing outside this block
     should contain a raw px radius; test/test_radius.js enforces that. */
  --r-sm:4px;    /* chips, pips, small controls */
  --r-md:7px;    /* buttons, fields, rows */
  --r-lg:11px;   /* cards, dialogs, the intro */
  --r-pill:999px;

  /* semantic status — separate from --structure, which is the brand accent and
     never means "good". These three mean state and nothing else. */
  --good:#2C6B4F; --good-soft:#DCE9E1;
  /* #8A6512 measured 4.42:1 on its own soft ground — under AA by a hair, which is
     the worst kind of miss because it looks fine. #856011 is the smallest step
     that clears it (4.75). */
  --warn:#856011; --warn-soft:#F1EAD5;
  --bad:#A03A22;  --bad-soft:#F3E0D9;

  /* display = the app's voice (titles, headings). text = the writing surface, left on
     the system serif deliberately: it is the face you stare at for an hour, it costs
     nothing, and Iowan/Palatino are excellent at it. */
  --display:Newsreader,"Iowan Old Style","Palatino Linotype",Palatino,"Book Antiqua","Times New Roman",serif;
  --text:"Iowan Old Style","Palatino Linotype",Palatino,"Book Antiqua",Georgia,serif;
  --ui:"Segoe UI Variable Text","Segoe UI",system-ui,-apple-system,"Helvetica Neue",Arial,sans-serif;
  --code:ui-monospace,"Cascadia Code","SF Mono","JetBrains Mono",Menlo,Consolas,monospace;

  --measure:66ch;       /* the writing column */
  --margin:154px;       /* the marginalia column beside it, on a wide page */
  --leading:1.75;       /* note-body line-height, also the ruled-paper pitch */
  --gutter:clamp(22px,5vw,64px);

  /* ---------- icons ----------
     Inline SVG as mask images: they take `currentColor`, so an icon follows
     the active state of the item it sits in without a second asset, and they
     are part of the stylesheet so there is no extra request and nothing to go
     missing offline. Deliberately geometry rather than emoji — an emoji is a
     different typeface on every platform, which is the fastest way to make an
     interface look assembled rather than designed. */
  --i-home:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23000%22%3E%3Cpath%20d%3D%22M3%209.5%2012%203l9%206.5V20a1%201%200%200%201-1%201h-5v-6H9v6H4a1%201%200%200%201-1-1z%22%2F%3E%3C%2Fsvg%3E");
  --i-book:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23000%22%3E%3Cpath%20d%3D%22M4%204a2%202%200%200%201%202-2h13v18H6a2%202%200%200%200-2%202z%22%2F%3E%3Cpath%20d%3D%22M8%206h8M8%2010h8%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.6%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E");
  --i-capture:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23000%22%3E%3Cpath%20d%3D%22M4%207h4l1.5-2h5L16%207h4a1%201%200%200%201%201%201v10a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V8a1%201%200%200%201%201-1z%22%2F%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2213%22%20r%3D%223.4%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E");
  --i-review:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23000%22%3E%3Cpath%20d%3D%22M12%204a8%208%200%201%201-7.4%205H2l3.5-4L9%209H6.7A6%206%200%201%200%2012%206z%22%2F%3E%3Cpath%20d%3D%22M12%208v5l3.5%202%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.8%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E");
  --i-tasks:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23000%22%3E%3Cpath%20d%3D%22M4%205h3v3H4zM4%2010.5h3v3H4zM4%2016h3v3H4z%22%2F%3E%3Cpath%20d%3D%22M9.5%206.5h11M9.5%2012h11M9.5%2017.5h11%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.8%22%2F%3E%3C%2Fsvg%3E");
  --i-marks:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23000%22%3E%3Cpath%20d%3D%22M4%2019h3v-7H4zM10.5%2019h3V5h-3zM17%2019h3v-10h-3z%22%2F%3E%3C%2Fsvg%3E");
  --i-inbox:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23000%22%3E%3Cpath%20d%3D%22M3%2013%206%205h12l3%208v6a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1z%22%2F%3E%3Cpath%20d%3D%22M3%2013h5l1%202h6l1-2h5%22%20stroke%3D%22%23000%22%20stroke-width%3D%221.6%22%20fill%3D%22none%22%2F%3E%3C%2Fsvg%3E");
  --i-comp:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23000%22%3E%3Cpath%20d%3D%22M7%204h10v3a5%205%200%200%201-10%200z%22%2F%3E%3Cpath%20d%3D%22M11%2011h2v4h-2z%22%2F%3E%3Cpath%20d%3D%22M8%2019h8v2H8z%22%2F%3E%3Cpath%20d%3D%22M5%205h2v2a3%203%200%200%201-2-2zM17%205h2a3%203%200%200%201-2%202z%22%2F%3E%3C%2Fsvg%3E");
  --i-more:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23000%22%3E%3Ccircle%20cx%3D%225%22%20cy%3D%2212%22%20r%3D%222%22%2F%3E%3Ccircle%20cx%3D%2212%22%20cy%3D%2212%22%20r%3D%222%22%2F%3E%3Ccircle%20cx%3D%2219%22%20cy%3D%2212%22%20r%3D%222%22%2F%3E%3C%2Fsvg%3E");

  /* color-scheme decides what colours the BROWSER paints its own widgets in —
     select menus, scrollbars, the caret, date pickers. "light dark" means "this
     page handles both", so the browser follows the OS. That is wrong here,
     because Nexley has its own theme switch: a reader on a dark-mode machine who
     chooses the light theme got a light page with dark native controls on it.
     Measured 2026-09-09: SIX selects painted #3B3B3B on the light theme,
     including the one on the sign-up form. Follow the app's theme instead — the
     three rules below mirror exactly how --paper and the rest are chosen. */
  color-scheme:light;
}
/* --- night --- warm near-black. Desk dark, page a touch lit.
   Applied when the OS is dark AND the reader hasn't forced light, or when the
   reader has explicitly chosen dark via the rail toggle. */
/* --- night --- same repalette: neutral Apple-dark-mode grays (paper/surface/
   card/ink/ink-2/muted/rule) instead of the original green-tinted near-blacks.
   structure/mark/danger/good/warn/bad are the notebook's own colours and are
   unchanged. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --paper:#121110; --surface:#191816; --card:#1E1D1A;
    --ink:#F3EFE6; --ink-2:#C6BFB2; --muted:#8B8377;
    --rule:#38352F; --rule-soft:#2A2823;
    --margin-rule:#8E5A56; --brass:#C9A552; --brass-soft:#2A2417;
    --structure:#8ECBAB; --structure-soft:#243228;
    --mark:#D9DB5C; --mark-ink:#17180F; --danger:#E48B65; --shade:0 0 0;
    --good:#6FB08C; --good-soft:#17251D; --warn:#D3A845; --warn-soft:#2A2413; --bad:#E48B65; --bad-soft:#2E1A13;
  }
}
:root[data-theme="dark"]{
  --paper:#121110; --surface:#191816; --card:#1E1D1A;
  --ink:#F3EFE6; --ink-2:#C6BFB2; --muted:#8B8377;
  --rule:#38352F; --rule-soft:#2A2823;
  --margin-rule:#8E5A56; --brass:#C9A552; --brass-soft:#2A2417;
  --structure:#8ECBAB; --structure-soft:#243228;
  --mark:#D9DB5C; --mark-ink:#17180F; --danger:#E48B65; --shade:0 0 0;
    --good:#6FB08C; --good-soft:#17251D; --warn:#D3A845; --warn-soft:#2A2413; --bad:#E48B65; --bad-soft:#2E1A13;
}

/* The same two conditions again, for the browser's own widgets. Deliberately
   placed AFTER both palette blocks rather than beside the `color-scheme:light`
   default up top: test/test_contrast.js finds the dark palette by matching the
   FIRST `:root[data-theme="dark"]` rule, and a one-line rule sitting in front of
   it made the whole palette invisible to the test. Keep the palette blocks the
   first of their kind. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){ color-scheme:dark; }
}
:root[data-theme="dark"]{ color-scheme:dark; }

/* ---------- accents ----------
   Four, each with a light-theme and a dark-theme pair, because a colour that
   reads as ink on paper is washed out on charcoal and has to be lifted. */
:root[data-accent="ink"]{--structure:#2F3336;--structure-soft:#E3E5E6}
:root[data-accent="burgundy"]{--structure:#6E2438;--structure-soft:#F0E2E6}
:root[data-accent="brass"]{--structure:#7A5E1E;--structure-soft:#F1E9D6}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"])[data-accent="ink"]{--structure:#C8CDD0;--structure-soft:#2A2D2F}
  :root:not([data-theme="light"])[data-accent="burgundy"]{--structure:#DFA0B0;--structure-soft:#2E1A20}
  :root:not([data-theme="light"])[data-accent="brass"]{--structure:#D8B45E;--structure-soft:#2A2417}
}
:root[data-theme="dark"][data-accent="ink"]{--structure:#C8CDD0;--structure-soft:#2A2D2F}
:root[data-theme="dark"][data-accent="burgundy"]{--structure:#DFA0B0;--structure-soft:#2E1A20}
:root[data-theme="dark"][data-accent="brass"]{--structure:#D8B45E;--structure-soft:#2A2417}

*{box-sizing:border-box}
/* JS toggles visibility with el.hidden; class rules (.gate/.app {display:grid}, etc.)
   outrank the UA [hidden] rule. Keep this ahead of every layout rule. */
[hidden]{display:none!important}
html,body{height:100%}
body{
  margin:0;background:var(--paper);color:var(--ink);
  /* .875rem, not 14px. Identical on a default browser, and the difference is the
     whole point: rem follows the reader's own text-size setting and px ignores it.
     The --t-* scale was already rem, but anything without a token inherits THIS,
     so a fixed value here quietly opted every unclassed line of text out of the
     setting. Roughly a third of phone users have changed that setting, some to
     310%, and Apple's own guidance is to survive 200%. */
  font-family:var(--ui);font-size:.875rem;line-height:1.5;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  overflow:hidden;overscroll-behavior:none;
  -webkit-tap-highlight-color:transparent;
  -webkit-text-size-adjust:100%;text-size-adjust:100%;
}
::selection{background:var(--mark);color:var(--mark-ink)}
button,.snav,.ncard,.sw,.nrow,.dp{touch-action:manipulation}
h1,h2,h3{margin:0;font-weight:400;text-wrap:balance}
p{margin:0}
button,input,select,textarea{font:inherit;color:inherit}
:focus-visible{outline:2px solid var(--structure);outline-offset:2px;border-radius:var(--r-sm)}
a{color:var(--structure)}

/* the mark — an actual concentric target, drawn in eucalypt */
.glyph{width:24px;height:24px;flex:none}
.glyph circle{fill:none;stroke:var(--structure);stroke-width:1.3}
.glyph.lg{width:38px;height:38px}
.glyph.xl{width:48px;height:48px;opacity:.4}

/* a label is set like a syllabus code: mono, spaced, quiet */
.eyebrow{font-family:var(--code);font-size:var(--t-xs);letter-spacing:.16em;
  text-transform:uppercase;color:var(--muted)}

/* ---------- buttons ---------- */
.btn{border:1px solid var(--rule);background:var(--card);border-radius:var(--r-md);
  padding:8px 15px;font-size:var(--t-base);cursor:pointer;color:var(--ink-2);
  transition:border-color .14s,color .14s,background .14s}
.btn:hover{border-color:var(--structure);color:var(--ink)}
.btn.primary{background:var(--structure);border-color:var(--structure);color:var(--paper);
  font-weight:500}
.btn.primary:hover{filter:brightness(1.1)}
.btn.ghost{background:transparent}
.btn.danger{color:var(--danger);border-color:transparent}
.btn.danger:hover{border-color:var(--danger)}
.linkbtn{background:none;border:0;padding:5px 0;font-size:var(--t-sm);color:var(--muted);
  cursor:pointer;text-align:left;transition:color .14s}
.linkbtn:hover{color:var(--structure)}

/* ---------- waiting ----------
   Placeholder bars for the three panes that wait on the network. Sized like the
   rows they stand in for, so nothing jumps when the real content lands.

   The sweep is slow (1.4s) and low-contrast on purpose: a fast bright shimmer is
   the single most "vibecoded" tell in the whole pattern, and this app's surfaces
   are paper. It should read as a page being set, not as a loading spinner wearing
   a costume. Motion is dropped entirely for anyone who asked for less of it — the
   bars still hold the space, which is most of the value. */
.skel{height:14px;border-radius:var(--r-sm);margin:0 0 10px;background:var(--surface);
  background-image:linear-gradient(90deg,transparent 0%,var(--card) 50%,transparent 100%);
  background-size:200% 100%;background-repeat:no-repeat;
  animation:skel-sweep 1.4s ease-in-out infinite}
.skel:last-child{margin-bottom:0}
@keyframes skel-sweep{
  from{background-position:150% 0}
  to{background-position:-50% 0}
}
@media (prefers-reduced-motion: reduce){
  .skel{animation:none}
}

/* ---------- pressed ----------
   Every state above this line is a HOVER state, and hover does not exist on an
   iPad — which is the device this app is aimed at. Before this block, pressing
   any control gave no feedback at all until whatever it did finished: on a fast
   action that reads as nothing happening, and on a slow one it reads as broken,
   which is how people end up tapping twice.

   Deliberately not a colour: this app's whole palette is load-bearing (a green
   means an earned result), so press feedback is WEIGHT — the surface the hover
   would have given, arriving instantly, plus a 1px drop on the things that look
   like physical buttons. Rows and list items get the surface only; nudging a
   full-width row makes the whole list feel loose.

   touch-action stops the 300ms double-tap-zoom delay, and the transparent tap
   highlight stops iOS painting its own blue box over the top of all of this. */
button, .tab, [role="button"]{touch-action:manipulation;-webkit-tap-highlight-color:transparent}

.btn:active, .q-annotate:active{background:var(--surface);border-color:var(--structure);
  transform:translateY(1px)}
.btn.primary:active{filter:brightness(.93);transform:translateY(1px)}
.btn.danger:active{border-color:var(--danger);background:var(--surface)}
.linkbtn:active{color:var(--structure)}
.snav:active, .topic-head:active, .dp:active, .tab:active, .node-add:active{background:var(--card)}

/* A disabled control must not move. Half of what a press animation communicates
   is "that registered", and a disabled button that dips says the opposite of
   what disabling it meant. */
button:disabled:active{transform:none;filter:none}

/* set by the head script for a visitor who has never seen the intro — keeps the
   sign-in card from flashing for the moment before boot decides */
:root.pre-intro .gate{display:none}
/* the same guard from the other end: a stored session exists, so the returning
   student is signed in and must not be shown "Create your account" while boot
   confirms it. Dropped by showGate() the moment the gate is genuinely wanted. */
:root.pre-session .gate{display:none}

/* ---------- what this is ----------
   Sits above the gate (z 60 vs 50) so it covers the sign-in card that paints on first
   load. One idea per panel, the same materials as the app itself — outcome codes,
   coverage pips, the highlighter mark — so the intro is a preview, not a brochure. */
.intro{position:fixed;inset:0;display:grid;place-items:center;padding:24px;
  background:var(--surface);z-index:60;overflow-y:auto}
.intro-card{width:min(560px,100%);background:var(--card);border:1px solid var(--rule);
  border-radius:var(--r-lg);padding:26px 30px 22px;display:flex;flex-direction:column;gap:var(--s-7);
  box-shadow:0 30px 70px -46px rgb(var(--shade)/.5)}
.intro-top{display:flex;align-items:center;gap:var(--s-4)}
.intro-top b{font-family:var(--display);font-weight:400;font-size:var(--t-xl)}
.intro-skip{margin-left:auto}

.intro-panels{position:relative;min-height:296px}
.ipanel{display:none;flex-direction:column;gap:var(--s-5)}
.ipanel.on{display:flex}
.ipanel h2{font-family:var(--display);font-size:var(--t-3xl);
  letter-spacing:-.015em;line-height:1.2}
.ipanel p{font-family:var(--text);font-size:var(--t-lg);line-height:1.6;color:var(--ink-2);
  max-width:46ch}

/* the little demonstration under each claim — real app furniture, quietened */
.idemo{margin-top:6px;border:1px solid var(--rule-soft);border-radius:var(--r-md);
  padding:14px 15px;background:var(--paper);display:flex;flex-direction:column;gap:var(--s-4)}
.idemo-row{display:flex;align-items:center;gap:var(--s-5)}
.idemo-code{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);flex:none;width:66px}
.idemo-name{font-size:var(--t-base);color:var(--ink-2);flex:1;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.idemo-quiet{align-items:flex-start;justify-content:center}
.idemo-quiet p{font-size:var(--t-md);color:var(--muted);max-width:none}

.intro-foot{display:flex;align-items:center;gap:var(--s-5)}
.dots{display:flex;gap:var(--s-3)}
.dots i{width:6px;height:6px;border-radius:var(--r-pill);background:var(--rule);
  transition:background .16s}
.dots i.on{background:var(--mark)}

@media (max-width:520px){
  .intro{padding:0;place-items:stretch}
  .intro-card{border:0;border-radius:0;min-height:100dvh;padding:22px 20px;
    justify-content:space-between;box-shadow:none}
  .intro-panels{min-height:0;flex:1;display:flex;align-items:center}
}

/* ---------- sign in ---------- */
.gate{position:fixed;inset:0;display:grid;place-items:center;padding:24px;
  background:var(--surface);z-index:50}
.gate-card{width:min(384px,100%);background:var(--card);border:1px solid var(--rule);
  border-radius:var(--r-lg);padding:40px 36px 30px;display:flex;flex-direction:column;gap:var(--s-6);
  box-shadow:0 30px 70px -46px rgb(var(--shade)/.5)}
.gate-card h1{font-family:var(--display);font-size:var(--t-3xl);letter-spacing:-.015em;
  margin-top:4px}
.gate-sub{color:var(--ink-2);font-size:var(--t-md);line-height:1.5}
#gateForm{display:flex;flex-direction:column;gap:var(--s-5);margin-top:4px}
.field{display:flex;flex-direction:column;gap:var(--s-3)}
.field>span{font-family:var(--code);font-size:var(--t-xs);letter-spacing:.12em;
  text-transform:uppercase;color:var(--muted)}
.field>span em{font-style:normal;text-transform:none;letter-spacing:0}
/* select is here with input, not left to the browser. `.field input` was styled
   and `.field select` was not, so the year picker on the SIGN-UP FORM — the one
   screen where a control that looks broken costs the most — fell back to a bare
   native widget beside three styled boxes. Same rule for both, so the next field
   added to a form cannot land half-styled depending on which tag it uses.
   `appearance` is left alone deliberately: the native chevron is the one part of
   a select worth keeping, and replacing it is surface with no gain. */
.field input,.field select{border:1px solid var(--rule);border-radius:var(--r-md);padding:11px 12px;
  background:var(--paper);color:var(--ink);font-size:var(--t-md)}
.field input:focus,.field select:focus{outline:none;border-color:var(--structure);
  box-shadow:0 0 0 3px var(--structure-soft)}
.setrow select{border:1px solid var(--rule);border-radius:var(--r-md);padding:7px 9px;
  background:var(--card);color:var(--ink);font-size:var(--t-base);font-family:inherit}
.setrow select:focus{outline:none;border-color:var(--structure)}
.gate-err{color:var(--danger);font-size:var(--t-base)}
.gate-or{text-align:center;font-family:var(--code);font-size:var(--t-xs);letter-spacing:.14em;
  color:var(--muted);text-transform:uppercase}
.gate-switch{text-align:center;font-size:var(--t-base);margin-top:2px}
.gate-switch a{text-decoration:none}
.gate-switch a:hover{text-decoration:underline}
/* Centred text that breaks wherever it runs out of room leaves a single word
   stranded on the last line — "…sync to your / account", "Privacy & / Terms".
   balance evens the lines out, and nowrap keeps a link from splitting in half
   whatever the width. */
.gate-foot{font-family:var(--code);font-size:var(--t-xs);letter-spacing:.06em;color:var(--muted);
  text-align:center;text-wrap:balance;line-height:1.7}
.gate-foot a{white-space:nowrap}
.gate-links{display:flex;flex-wrap:wrap;justify-content:center;gap:var(--s-3) var(--s-6)}

/* ---------- app shell ---------- */
.app{display:grid;grid-template-columns:216px 328px 1fr;height:100vh;height:100dvh}

/* rail — the quietest surface */
.rail{background:var(--surface);border-right:1px solid var(--rule);
  display:flex;flex-direction:column;gap:var(--s-6);padding:20px 14px;overflow:hidden}
.rail-brand{display:flex;align-items:center;gap:var(--s-4);padding:0 4px}
.rail-brand b{font-family:var(--display);font-weight:400;font-size:var(--t-xl);letter-spacing:.005em}
.rail-brand .phase{margin-left:auto;font-family:var(--code);font-size:var(--t-2xs);
  letter-spacing:.08em;color:var(--muted)}
/* the three ways of working — set like the tabs on a syllabus document, not like app chrome */
.modes{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--s-1);margin:-6px -4px -4px;
  border-bottom:1px solid var(--rule)}
.mode{background:none;border:0;border-bottom:2px solid transparent;margin-bottom:-1px;
  padding:7px 0;font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.06em;
  text-transform:uppercase;color:var(--muted);cursor:pointer;transition:color .14s}
.mode:hover{color:var(--ink-2)}
.mode.on{color:var(--ink);border-bottom-color:var(--mark)}

.rail-search input{width:100%;border:1px solid var(--rule);border-radius:var(--r-md);
  padding:8px 11px;background:var(--card);font-size:var(--t-base)}
.rail-search input:focus{outline:none;border-color:var(--structure)}
.rail-nav{display:flex;flex-direction:column;gap:1px;overflow-y:auto;flex:1;
  margin:0 -4px;padding:0 4px}
.snav{display:flex;align-items:center;gap:var(--s-4);padding:8px 9px;border-radius:var(--r-md);
  cursor:pointer;border:1px solid transparent;text-align:left;background:none;width:100%;
  transition:background .12s}
.snav:hover{background:var(--card)}
.snav.on{background:var(--card);border-color:var(--rule-soft)}
.snav.on .nm{color:var(--ink)}
.snav .dot{width:7px;height:7px;border-radius:var(--r-sm);flex:none}
.snav .nm{flex:1;font-size:var(--t-base);color:var(--ink-2);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.snav .ct{font-family:var(--code);font-size:var(--t-sm);color:var(--muted);
  font-variant-numeric:tabular-nums}
.snav .edit{opacity:0;font-size:var(--t-sm);color:var(--muted);background:none;border:0;
  cursor:pointer;padding:2px 4px;transition:opacity .12s}
.snav:hover .edit{opacity:1}
.add-subject{border:1px dashed var(--rule);background:none;border-radius:var(--r-md);padding:9px;
  font-size:var(--t-base);color:var(--muted);cursor:pointer;transition:border-color .14s,color .14s}
.add-subject:hover{border-color:var(--structure);color:var(--structure)}
/* sync state — quiet when it works, impossible to miss when it doesn't */
.syncline{display:flex;align-items:center;gap:var(--s-3);width:100%;text-align:left;
  background:none;border:0;border-top:1px solid var(--rule);padding:9px 0 8px;
  font-size:var(--t-sm);color:var(--muted);cursor:pointer;font-family:inherit}
.syncline .sdot{width:6px;height:6px;border-radius:var(--r-pill);flex:none;background:var(--muted)}
.syncline.ok .sdot{background:var(--structure)}
.syncline.warn{color:var(--danger)}
.syncline.warn .sdot{background:var(--danger)}
.syncline:hover{color:var(--ink-2)}
.syncline.warn:hover{color:var(--danger)}

/* Sits under the sync line and reads as part of the same quiet status block —
   an observation, not a score. No colour, no icon, nothing to click. */
.streakline{font-family:var(--code);font-size:var(--t-xs);letter-spacing:.06em;
  color:var(--muted);padding:0 0 8px}

.rail-foot{display:flex;flex-direction:column;gap:1px;border-top:1px solid var(--rule);
  padding-top:12px}
.storage-note{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.04em;
  margin-top:8px;line-height:1.45}
.storage-note.ok{color:var(--muted)}
.storage-note.warn{color:var(--danger)}

/* index column — the table of contents: syllabus tree, or a flat list */
.browser{background:var(--paper);border-right:1px solid var(--rule);
  display:flex;flex-direction:column;overflow:hidden}
.browser-head{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--s-5);
  padding:22px 20px 16px;border-bottom:1px solid var(--rule)}
.bh-text{min-width:0}
.browser-head .eyebrow{display:block;margin-bottom:5px}
.browser-head h2{font-family:var(--display);font-size:var(--t-2xl);letter-spacing:-.005em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.bh-actions{display:flex;align-items:center;gap:var(--s-4);flex:none}
.iconbtn.ghost{border:1px solid var(--rule);color:var(--ink-2);background:none;
  border-radius:var(--r-md);padding:6px 11px;font-size:var(--t-sm);cursor:pointer;transition:border-color .14s,color .14s}
.iconbtn.ghost:hover{border-color:var(--structure);color:var(--structure)}
.browser-body{overflow-y:auto;flex:1;padding:10px 8px 8px}

/* coverage — how much of the syllabus is written up. Nexley's whole reason to exist,
   so it gets the mark. */
.coverage{display:flex;align-items:center;gap:var(--s-5);padding:11px 20px;
  border-bottom:1px solid var(--rule-soft);background:var(--surface)}
.cov-track{flex:1;height:3px;background:var(--rule);overflow:hidden}
.cov-track i{display:block;height:100%;background:var(--mark)}
.cov-label{font-family:var(--code);font-size:var(--t-xs);letter-spacing:.06em;color:var(--muted);
  white-space:nowrap;font-variant-numeric:tabular-nums}

/* syllabus tree — the outcome code is the identifier, set like a document section number */
.topic{margin-bottom:2px}
.topic-head{display:flex;align-items:center;gap:var(--s-4);width:100%;background:none;
  border:1px solid transparent;border-radius:var(--r-md);padding:9px 8px;cursor:pointer;text-align:left}
.topic-head:hover{background:var(--card)}
.topic-head .tw{flex:1;min-width:0}
.topic-head .code{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.1em;
  color:var(--muted);display:block;text-transform:uppercase}
.topic-head .nm{font-size:var(--t-base);font-weight:600;color:var(--ink-2);display:block;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-top:1px}
.chev{font-family:var(--code);font-size:var(--t-xs);color:var(--muted);width:11px;flex:none;
  transition:transform .16s}
.topic.closed .chev{transform:rotate(-90deg)}
.topic.closed .topic-body{display:none}
.topic-body{padding-left:11px;border-left:1px solid var(--rule-soft);margin-left:12px}
.node-add{background:none;border:0;color:var(--muted);font-size:var(--t-xs);cursor:pointer;
  padding:6px 8px;font-family:var(--code);letter-spacing:.08em}
.node-add:hover{color:var(--structure)}

.dp{display:flex;align-items:center;gap:var(--s-4);width:100%;background:none;
  border:1px solid transparent;border-radius:var(--r-md);padding:8px;cursor:pointer;text-align:left}
.dp:hover{background:var(--card)}
.dp.on{background:var(--card);border-color:var(--rule-soft)}
.pip{width:8px;height:8px;border-radius:var(--r-pill);flex:none;border:1.5px solid var(--rule)}
.pip.has{background:var(--mark);border-color:var(--mark)}    /* written up */
.pip.given{background:var(--rule);border-color:var(--muted)}
.dp .tw{flex:1;min-width:0}
.dp .code{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.08em;color:var(--muted);
  display:block;text-transform:uppercase}
.dp .nm{font-size:var(--t-base);color:var(--ink-2);display:block;line-height:1.4;margin-top:1px}
.dp .ct{font-family:var(--code);font-size:var(--t-xs);color:var(--muted);flex:none}
.dp .edit,.topic-head .edit{opacity:0;font-size:var(--t-xs);color:var(--muted);background:none;
  border:0;cursor:pointer;padding:2px 4px;flex:none;transition:opacity .12s}
.dp:hover .edit,.topic-head:hover .edit{opacity:1}

.notes-under{padding-left:19px;display:flex;flex-direction:column;margin:1px 0 8px}
.nrow{display:flex;align-items:center;gap:var(--s-4);width:100%;background:none;border:0;
  border-radius:var(--r-sm);padding:7px 8px;cursor:pointer;text-align:left}
.nrow:hover{background:var(--card)}
.nrow.on{background:var(--card);box-shadow:inset 2px 0 0 var(--mark)}
.nrow .kind{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.12em;padding:2px 5px;
  border-radius:var(--r-sm);flex:none;text-transform:uppercase}
.nrow .kind.mine{background:var(--structure-soft);color:var(--structure)}
.nrow .kind.given{background:var(--surface);color:var(--muted)}
.nrow .t{flex:1;font-size:var(--t-base);color:var(--ink-2);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.nrow.on .t{color:var(--ink)}
.nrow .dt{font-family:var(--code);font-size:var(--t-2xs);color:var(--muted);flex:none}

.emptytree{padding:24px 14px;color:var(--muted);font-size:var(--t-base);line-height:1.65;
  display:flex;flex-direction:column;gap:var(--s-4);align-items:flex-start}

/* flat note list */
.notes{overflow-y:auto;flex:1;padding:8px 8px 8px}
.ncard{display:flex;flex-direction:column;gap:var(--s-3);padding:14px 14px;border-radius:var(--r-md);
  cursor:pointer;border:1px solid transparent;background:none;width:100%;text-align:left;
  transition:background .12s}
.ncard+.ncard{border-top:1px solid var(--rule-soft);border-radius:0}
.ncard:hover{background:var(--card)}
.ncard.on{background:var(--card);box-shadow:inset 2px 0 0 var(--mark)}
.ncard .top{display:flex;align-items:center;gap:var(--s-4)}
.ncard .dot{width:6px;height:6px;border-radius:var(--r-sm);flex:none}
.ncard .t{font-size:var(--t-md);font-weight:600;color:var(--ink);overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;flex:1}
.ncard .ex{font-size:var(--t-base);color:var(--muted);line-height:1.45;
  display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.ncard .dt{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.04em;color:var(--muted)}
.ncard mark{background:var(--mark);color:var(--mark-ink)}
.listempty{padding:28px 20px;color:var(--muted);font-size:var(--t-md);line-height:1.65}

/* tabs — kept minimal, they sit on the page edge */
.tabs{display:flex;gap:1px;overflow-x:auto;padding:8px 12px 0;
  border-bottom:1px solid var(--rule);background:var(--paper);scrollbar-width:thin}
.tab{display:flex;align-items:center;gap:var(--s-4);flex:none;max-width:190px;
  border:1px solid var(--rule);border-bottom:0;border-radius:var(--r-md) var(--r-md) 0 0;
  background:var(--surface);padding:7px 11px;cursor:pointer;font-size:var(--t-sm);color:var(--muted)}
.tab.on{background:var(--card);color:var(--ink)}
.tab .tl{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tab .x{background:none;border:0;color:var(--muted);cursor:pointer;font-size:var(--t-base);
  line-height:1;padding:2px 3px;border-radius:var(--r-sm);flex:none}
.tab .x:hover{color:var(--danger)}

/* ---------- the page: where a note is written ---------- */
.editor{display:flex;flex-direction:column;overflow:hidden;background:var(--card);
  container-type:inline-size;container-name:page}
.ed-page{flex:1;min-height:0;display:flex;flex-direction:column}
.ed-head{display:flex;align-items:center;justify-content:space-between;gap:var(--s-5);
  padding:14px var(--gutter);border-bottom:1px solid var(--rule-soft)}
.crumb{display:flex;align-items:center;gap:var(--s-4);font-family:var(--code);font-size:var(--t-sm);
  letter-spacing:.04em;color:var(--muted);overflow:hidden;white-space:nowrap;text-transform:uppercase}
.crumb .dot{width:6px;height:6px;border-radius:var(--r-sm);flex:none}
.crumb b{color:var(--ink-2);font-weight:400}
.backbtn{display:none;background:none;border:1px solid var(--rule);border-radius:var(--r-md);
  padding:7px 13px;font-size:var(--t-base);color:var(--ink-2);cursor:pointer;flex:none}
.backbtn:hover{border-color:var(--structure);color:var(--structure)}
@media (max-width:1080px){ .backbtn{display:block} }

.ed-actions{display:flex;align-items:center;gap:var(--s-5);flex:none}
.saved{font-family:var(--code);font-size:var(--t-xs);letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted)}
.saved.dirty{color:var(--structure)}
.iconbtn{background:none;border:1px solid transparent;border-radius:var(--r-sm);padding:5px 10px;
  font-size:var(--t-sm);color:var(--muted);cursor:pointer}
.iconbtn:hover{color:var(--danger);border-color:var(--danger)}

/* title — big, light, serif. The fresh-page moment. */
.ed-title{border:0;background:none;padding:26px var(--gutter) 6px;font-family:var(--display);
  /* the + needs whitespace: clamp(a,b+c,d) is a parse error and silently drops the
     whole declaration, which is how this title spent Pass 1 rendering at 14px */
  font-weight:400;font-size:var(--t-3xl);letter-spacing:-.015em;
  line-height:1.15;width:100%;max-width:calc(var(--measure) + 2 * var(--gutter));
  /* height comes from autosize() — it wraps instead of scrolling sideways.
     flex:none because .ed-page is a flex column: without it a long title is
     SHRUNK below the height autosize just measured, and overflow:hidden eats
     the last line. It looked fine on a laptop only because there was spare
     vertical room; on a phone it clipped every time. */
  display:block;resize:none;overflow:hidden;color:inherit;flex:none}
.ed-title:focus{outline:none}
.ed-title::placeholder{color:var(--muted);opacity:.55}

/* apparatus — one quiet line above the writing, not a control panel */
.ed-meta{display:flex;align-items:center;gap:var(--s-5) var(--s-7);padding:4px var(--gutter) 16px;
  flex-wrap:wrap;max-width:calc(var(--measure) + 2 * var(--gutter))}
.sel{display:flex;align-items:baseline;gap:var(--s-3)}
.sel>span{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.14em;
  text-transform:uppercase;color:var(--muted)}
.sel select{border:0;border-bottom:1px solid var(--rule);border-radius:0;padding:3px 2px;
  background:none;font-size:var(--t-base);color:var(--ink-2)}
.sel select:focus{outline:none;border-color:var(--structure)}
.stamp{font-family:var(--code);font-size:var(--t-xs);letter-spacing:.04em;color:var(--muted);
  margin-left:auto;text-align:right;white-space:nowrap}
/* narrow: one short "edited" line so the apparatus row never wraps. The full
   provenance appears in the margin on a wide page. */
.st-cr{display:none}
.wc{font-family:var(--code);font-size:var(--t-xs);letter-spacing:.04em;color:var(--muted);
  white-space:nowrap;font-variant-numeric:tabular-nums}
.kindtoggle{display:flex;border:1px solid var(--rule);border-radius:var(--r-sm);overflow:hidden}
.kindtoggle button{background:none;border:0;padding:5px 12px;font-size:var(--t-sm);
  color:var(--muted);cursor:pointer}
.kindtoggle button.on{background:var(--structure);color:var(--paper)}

/* toolbar — understated, sits just above the writing */
.toolbar{display:flex;align-items:center;gap:var(--s-1);padding:0 var(--gutter) 14px;flex-wrap:wrap}
.toolbar button{border:1px solid transparent;background:none;border-radius:var(--r-sm);
  padding:5px 9px;font-size:var(--t-base);cursor:pointer;color:var(--muted);min-width:30px}
.toolbar button:hover{background:var(--paper);color:var(--ink-2)}
.toolbar button.on{background:var(--structure-soft);color:var(--ink)}
.tsep{width:1px;height:16px;background:var(--rule);margin:0 6px}

/* the writing column — a fixed measure of feint-ruled paper.
   --line is the single source of truth for the pitch: the text sits on it, the ruling
   is drawn at it, and headings are measured in it. Anything that changes the writing
   font size must set --line with it (see .font-hand and the coarse-pointer block) or
   the words drift off the lines. Deliberately NOT written with `em` — a custom property
   resolves its units against the element that USES it, so `1em` inside --line would
   mean the heading's font size, not the body's. */
/* The SURFACE scrolls, not the body. The ink canvas is a sibling of the text
   sized to the whole content height, so it has to scroll with it — pinning the
   canvas to the visible box instead clipped every stroke at the fold, which is
   exactly what the first version of this did. */
.ed-surface{flex:1;overflow-y:auto;position:relative;min-height:0}
.ed-body{overflow:visible;height:auto;min-height:100%;padding:14px var(--gutter) 40vh;
  font-family:var(--text);font-size:var(--t-lg);
  --line:calc(var(--leading) * 1.0625rem);line-height:var(--line);
  color:var(--ink);outline:none;max-width:calc(var(--measure) + 2 * var(--gutter));
  /* The ruling itself now comes from [data-paper] below, so the reader can
     choose lines, wide lines, a grid, or nothing — see the paper block. This
     only fixes where it starts and how it scrolls. */
  background-position:0 calc(14px + .4em);
  background-attachment:local;
  position:relative}

/* The margin rule. One vertical line down the left of the writing column, the
   single cheapest mark that turns ruled lines into a NOTEBOOK — ruling alone
   reads as a table. Drawn as a pseudo-element rather than a second background
   layer so it spans the full scroll height instead of tiling with the lines,
   and so `:empty` can keep it while dropping the ruling.

   Sits inside the gutter, not in the text: `--gutter` is a clamp that can get
   small on a phone, so the line is placed at 60% of it and the text is never
   crowded by it. Off entirely below the breakpoint where the gutter stops
   being wide enough to hold it — see the narrow block near the bottom. */
.ed-body::before{content:"";position:absolute;top:0;bottom:0;
  left:calc(var(--gutter) * .6);width:1px;background:var(--margin-rule);
  opacity:.5;pointer-events:none}
/* A pseudo-element is not a child, so :empty still matches and the placeholder
   still shows — the margin rule stays, which is the point: an empty page in a
   notebook is still a page. */

/* a heading has to occupy a whole number of ruled lines or the paper stops lining up
   under it. One line for itself, one line of space above — expressed in the BODY's
   line unit, not the heading's own em, which is bigger and would drift. */
.ed-body h3{font-family:var(--display);font-weight:400;font-size:var(--t-2xl);
  line-height:var(--line);margin:var(--line) 0 0}
.ed-body ul,.ed-body ol{padding-left:26px;margin:0}
.ed-body li{margin:0}
.ed-body mark{background:var(--mark);color:var(--mark-ink);padding:0 .1em}
/* the handwriting face wants more air — move the ruled pitch with it, not just the
   line-height, or the writing sits between the lines instead of on them */
.ed-body.font-hand{font-family:"Segoe Script","Bradley Hand","Brush Script MT",cursive;
  font-size:var(--t-xl);--line:calc(1.85 * 1.15rem)}
.ed-body.font-serif{font-family:var(--text)}

.ed-foot{display:flex;justify-content:space-between;gap:var(--s-5);padding:10px var(--gutter);
  border-top:1px solid var(--rule-soft);font-family:var(--code);font-size:var(--t-xs);
  color:var(--muted);letter-spacing:.04em}

/* ---------- the margin ----------
   Once the page is wide enough to hold a 66ch column AND a margin beside it, the
   apparatus leaves the top of the note and becomes marginalia: subject, syllabus
   code, kind, provenance and length ranged right against a margin rule, the way
   a study book annotates its own pages. Below that width the flex column above
   stands unchanged, so this can only ever add — never break the writing surface. */
@container page (min-width:880px){
  .ed-page{
    display:grid;
    grid-template-columns:calc(var(--gutter) + var(--margin)) minmax(0,1fr);
    grid-template-rows:auto auto auto auto 1fr auto;
  }
  .tabs   {grid-column:1/-1;grid-row:1}
  .ed-head{grid-column:1/-1;grid-row:2}
  .ed-title{grid-column:2;grid-row:3;padding-left:0;padding-right:var(--gutter);
    padding-bottom:18px;max-width:calc(var(--measure) + var(--gutter))}
  /* the margin already names the subject and the outcome code — don't say it twice */
  .crumb{display:none}
  .ed-actions{margin-left:auto}   /* keep them right once the crumb is gone */
  .toolbar {grid-column:2;grid-row:4;padding-left:0;padding-right:var(--gutter)}
  .ed-surface {grid-column:2;grid-row:5;
    max-width:calc(var(--measure) + var(--gutter))}
  .ed-body {padding-left:0;padding-right:var(--gutter)}
  .ed-foot{grid-column:1/-1;grid-row:6}

  .ed-meta{grid-column:1;grid-row:3/6;
    flex-direction:column;align-items:stretch;text-align:right;gap:var(--s-6);
    padding:32px 22px 20px var(--gutter);max-width:none;overflow:hidden;
    /* The real margin rule, now that there is a margin. It takes the same
       colour as the narrow-layout line below so the app has ONE margin rule
       that moves, not two different-looking ones. */
    border-right:1px solid var(--margin-rule)}
  /* and the drawn-in line is not needed here — the margin itself is the rule */
  .ed-body::before{display:none}
  .ed-meta>*{min-width:0;max-width:100%}
  .ed-meta .sel{flex-direction:column;align-items:stretch;gap:var(--s-2)}
  .ed-meta .sel select{width:100%;text-align:right;padding-right:0;
    text-overflow:ellipsis}
  .ed-meta .kindtoggle{align-self:flex-end}
  .ed-meta .stamp{margin-left:0;white-space:normal;line-height:1.7}
  .ed-meta .st-cr,.ed-meta .st-ed{display:block}
  .ed-meta .wc{white-space:normal}
}

/* nothing open (wide screens only) */
.empty{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--s-5);padding:40px;text-align:center;background:var(--card)}
.empty h3{font-family:var(--display);font-weight:400;font-size:var(--t-2xl)}
.empty p{color:var(--muted);font-size:var(--t-md);max-width:36ch;line-height:1.6}

/* ============================================================
   MODE PANES — Classwork and Review
   Both take the whole working area (everything right of the rail) because
   neither is a browse-then-open shape; they are one continuous surface.
   ============================================================ */
.pane{grid-column:2/-1;grid-row:1;background:var(--paper);
  display:flex;flex-direction:column;min-height:0;overflow:hidden}
.app.moded .browser,.app.moded .editor,.app.moded .empty{display:none!important}

/* Wraps rather than squeezing: on a phone the heading and the subject picker
   cannot both fit on one line, and without this the heading gets crushed into
   a three-line column beside the select instead of the select simply moving
   underneath it. */
.pane-head{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--s-4) var(--s-6);
  flex-wrap:wrap;padding:22px var(--gutter) 16px;border-bottom:1px solid var(--rule);flex:none}
.pane-head .bh-text{min-width:min(100%,16ch)}
.pane-head h2{font-family:var(--display);font-size:var(--t-2xl);
  letter-spacing:-.01em;font-weight:400}
.pane-head .eyebrow{display:block;margin-bottom:5px}
.pane-sel{display:flex;flex-direction:column;gap:var(--s-2);flex:none}
.pane-sel > span{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted)}
.pane-sel select,.cw-compose select{border:1px solid var(--rule);border-radius:var(--r-md);
  background:var(--card);color:var(--ink);padding:7px 9px;font-size:var(--t-base);
  font-family:inherit;max-width:220px}
.pane-body{overflow-y:auto;flex:1;min-height:0;padding:18px var(--gutter) 40px}

/* ---------- classwork ---------- */
/* The composer is pinned above the list on purpose: in a lesson the only thing
   that matters is that the box is already there and already focused. */
.cw-compose{flex:none;padding:16px var(--gutter);border-bottom:1px solid var(--rule);
  background:var(--surface);display:flex;flex-direction:column;gap:var(--s-4)}
.cw-compose textarea{width:100%;resize:vertical;min-height:76px;
  border:1px solid var(--rule);border-radius:var(--r-md);background:var(--card);
  color:var(--ink);padding:12px 13px;font-family:var(--text);font-size:var(--t-lg);
  line-height:1.55}
.cw-compose textarea:focus{outline:none;border-color:var(--structure)}
.cw-bar{display:flex;align-items:center;gap:var(--s-5)}
.cw-bar .hint{flex:1}

.cw-day{margin-bottom:22px}
.cw-day > h4{font-family:var(--code);font-size:var(--t-xs);letter-spacing:.11em;
  text-transform:uppercase;color:var(--muted);font-weight:400;
  padding-bottom:7px;border-bottom:1px solid var(--rule-soft);margin-bottom:10px}
.cap{display:flex;gap:var(--s-5);align-items:flex-start;padding:11px 12px;border-radius:var(--r-md);
  border:1px solid transparent}
.cap:hover{background:var(--card);border-color:var(--rule-soft)}
.cap .cdot{width:8px;height:8px;border-radius:var(--r-pill);flex:none;margin-top:7px}
.cap .cbody{flex:1;min-width:0}
.cap .ctext{font-family:var(--text);font-size:var(--t-md);line-height:1.55;color:var(--ink);
  white-space:pre-wrap;overflow-wrap:anywhere}
.cap .cmeta{display:flex;gap:var(--s-4);align-items:center;margin-top:6px;
  font-size:var(--t-sm);color:var(--muted)}
.cap .cacts{display:flex;gap:var(--s-2);flex:none;opacity:0;transition:opacity .12s}
.cap:hover .cacts,.cap:focus-within .cacts{opacity:1}
@media (pointer:coarse){ .cap .cacts{opacity:1} }
.cap .cacts button{border:1px solid var(--rule);background:var(--card);border-radius:var(--r-md);
  padding:5px 9px;font-size:var(--t-sm);color:var(--ink-2);cursor:pointer;font-family:inherit}
.cap .cacts button:hover{border-color:var(--structure);color:var(--ink)}
.cap-quote{font-family:var(--text);font-size:var(--t-md);line-height:1.55;color:var(--ink-2);
  border-left:2px solid var(--rule);padding-left:11px;max-height:6.5em;overflow:hidden;
  white-space:pre-wrap}

/* ---------- review ---------- */
.rv-progress{height:2px;background:var(--rule-soft);margin-bottom:26px;flex:none}
.rv-progress i{display:block;height:100%;background:var(--structure);width:0;
  transition:width .2s ease}
.rv-session{display:flex;flex-direction:column;min-height:0}
.rv-card{background:var(--card);border:1px solid var(--rule);border-radius:var(--r-lg);
  padding:clamp(20px,3.5vw,38px);max-width:64ch;margin:0 auto;width:100%}
.rv-where{font-family:var(--code);font-size:var(--t-xs);letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);margin-bottom:14px}
.rv-front{font-family:var(--text);font-size:var(--t-2xl);
  line-height:1.5;color:var(--ink);white-space:pre-wrap;overflow-wrap:anywhere}
.rv-back{font-family:var(--text);font-size:var(--t-lg);line-height:1.65;color:var(--ink-2);
  white-space:pre-wrap;overflow-wrap:anywhere;
  margin-top:20px;padding-top:20px;border-top:1px solid var(--rule-soft)}
.rv-ask{display:flex;flex-direction:column;align-items:center;gap:var(--s-5);
  margin:26px auto 0;max-width:64ch;text-align:center}
.rv-ask .hint{max-width:44ch}

.rv-grades{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--s-4);
  max-width:64ch;margin:26px auto 0;width:100%}
.grade{border:1px solid var(--rule);background:var(--card);border-radius:var(--r-md);
  padding:12px 8px;cursor:pointer;font-family:inherit;font-size:var(--t-md);color:var(--ink);
  display:flex;flex-direction:column;align-items:center;gap:var(--s-2)}
.grade em{font-style:normal;font-family:var(--code);font-size:var(--t-xs);color:var(--muted);
  letter-spacing:.04em}
.grade:hover{border-color:var(--structure)}
.grade.again:hover{border-color:var(--warn)}
.grade.easy:hover{border-color:var(--good)}

/* deck + empty states inside review */
.rv-note{max-width:56ch;color:var(--ink-2);font-family:var(--text);font-size:var(--t-lg);
  line-height:1.65;margin-bottom:14px}
.rv-stats{display:flex;flex-wrap:wrap;gap:var(--s-7);padding:16px 0 22px;
  border-bottom:1px solid var(--rule-soft);margin-bottom:20px}
.rv-stat{display:flex;flex-direction:column;gap:var(--s-2)}
.rv-stat b{font-family:var(--display);font-size:var(--t-3xl);font-weight:400;line-height:1;
  font-variant-numeric:tabular-nums}
.rv-stat span{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted)}
/* The divider above a section of the deck view — mistakes, then suggestions.
   Was an inline cssText block on the suggestions heading; it is a class now
   because a second section needs the same rule. */
.rv-head{font-family:var(--code);font-size:var(--t-xs);letter-spacing:.11em;
  text-transform:uppercase;color:var(--muted);font-weight:400;margin:26px 0 4px;
  padding-bottom:7px;border-bottom:1px solid var(--rule-soft)}
.deckrow{display:flex;gap:var(--s-5);align-items:flex-start;padding:11px 12px;border-radius:var(--r-md);
  border:1px solid transparent}
.deckrow:hover{background:var(--card);border-color:var(--rule-soft)}
.deckrow .dfront{flex:1;min-width:0;font-family:var(--text);font-size:var(--t-md);
  line-height:1.5;color:var(--ink)}
.deckrow .dmeta{display:flex;gap:var(--s-4);margin-top:5px;font-size:var(--t-sm);color:var(--muted)}
.deckrow .dacts{display:flex;gap:var(--s-2);flex:none;opacity:0;transition:opacity .12s}
.deckrow:hover .dacts,.deckrow:focus-within .dacts{opacity:1}
@media (pointer:coarse){ .deckrow .dacts{opacity:1} }
.deckrow .dacts button{border:1px solid var(--rule);background:var(--card);
  border-radius:var(--r-md);padding:5px 9px;font-size:var(--t-sm);color:var(--ink-2);
  cursor:pointer;font-family:inherit}
.due-chip{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.07em;
  text-transform:uppercase;padding:2px 6px;border-radius:var(--r-sm);border:1px solid var(--rule)}
.due-chip.now{border-color:var(--warn);color:var(--warn)}
.due-chip.new{border-color:var(--structure);color:var(--structure)}

.paste.short{min-height:78px;height:auto}

/* a warning that carries advice, not just state — used by Tasks for "where the
   marks are at risk". Amber against the paper so it reads as attention, not alarm. */
.gap{border:1px solid var(--warn);border-left-width:3px;border-radius:var(--r-md);
  padding:var(--s-5) var(--s-6);background:var(--warn-soft)}
.gap .k{color:var(--warn);display:block;margin-bottom:var(--s-3)}
.gap p{font-size:var(--t-base);color:var(--ink-2);line-height:1.6;max-width:70ch}
.gap b{color:var(--ink);font-weight:600}

/* auto-filing suggestion — sits between the toolbar and the writing surface so
   it is noticed without interrupting, and disappears the moment it is answered. */
.filehint{display:flex;align-items:center;gap:var(--s-4);flex-wrap:wrap;
  margin:0 0 var(--s-5);padding:var(--s-4) var(--s-5);
  border:1px solid var(--structure);border-left-width:3px;border-radius:var(--r-md);
  background:var(--structure-soft)}
.fh-text{font-size:var(--t-base);color:var(--ink);flex:1;min-width:14ch}
.fh-why{font-family:var(--code);font-size:var(--t-xs);color:var(--muted)}
.filehint button{border:1px solid var(--rule);background:var(--card);
  border-radius:var(--r-sm);padding:var(--s-2) var(--s-4);font-size:var(--t-sm);
  color:var(--ink-2);cursor:pointer}
.fh-yes{border-color:var(--structure)!important;color:var(--structure)!important}
.filehint button:hover{border-color:var(--structure);color:var(--ink)}
/* Past you is an observation, not a suggestion — nothing to accept or decline,
   so it sits on the quiet ground rather than the accented one the filing hint
   uses to ask for a decision. */
.filehint.past{border-color:var(--rule);border-left-color:var(--muted);
  background:var(--surface)}
.filehint.past .fh-why{font-family:var(--text);font-style:italic;
  font-size:var(--t-sm);color:var(--ink-2)}
/* Cross-subject links carry a whole dot-point title on each button, so the
   default `.fh-text{flex:1}` squeezed the sentence to about 14 characters and
   broke "This also comes up in PDHPE." across two lines with half the row
   empty. The sentence is short and fixed; let it size to its content and let
   the buttons wrap to the next line instead, which is what they already do
   on a phone. */
#crossLink .fh-text{flex:0 0 auto}

/* confidence bands. Colour carries the reading, but never alone — every use
   pairs it with a word, because a coloured dot on its own is unreadable to
   anyone with a colour vision deficiency and meaningless to everyone else. */
.conf{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.09em;
  text-transform:uppercase;padding:1px var(--s-3);border-radius:var(--r-sm);
  border:1px solid currentColor;white-space:nowrap}
.conf.untouched{color:var(--muted)}
.conf.shaky{color:var(--bad)}
.conf.building{color:var(--warn)}
.conf.solid{color:var(--good)}

/* toast */
.toast{position:fixed;left:50%;bottom:26px;transform:translateX(-50%);
  background:var(--ink);color:var(--paper);padding:11px 18px;border-radius:var(--r-md);
  font-size:var(--t-base);z-index:80;box-shadow:0 14px 36px -18px rgb(var(--shade)/.6)}

/* the diagnostics attached to a bug report — shown to the reporter, never hidden */
.diag{font-family:var(--code);font-size:var(--t-xs);line-height:1.7;color:var(--muted);
  background:var(--paper);border:1px solid var(--rule-soft);border-radius:var(--r-md);
  padding:11px 12px;display:flex;flex-direction:column;gap:var(--s-1);
  max-height:132px;overflow-y:auto;word-break:break-word}
.diag b{color:var(--ink-2);font-weight:400}

/* snapshots */
.dlgbody{padding:26px;display:flex;flex-direction:column;gap:var(--s-5)}
.dlgnote{font-size:var(--t-base);color:var(--muted);line-height:1.6}
.snaplist{display:flex;flex-direction:column;gap:var(--s-4);max-height:46vh;overflow-y:auto}
.snaprow{display:flex;align-items:center;justify-content:space-between;gap:var(--s-5);
  border:1px solid var(--rule);border-radius:var(--r-md);padding:13px 14px;background:var(--paper)}
.snaprow b{display:block;font-size:var(--t-md);font-weight:600}
.snaprow span{display:block;font-family:var(--code);font-size:var(--t-sm);color:var(--muted);
  margin-top:2px}
.paste{width:100%;min-height:190px;resize:vertical;border:1px solid var(--rule);
  border-radius:var(--r-md);padding:12px;background:var(--paper);font-family:var(--code);
  font-size:var(--t-sm);line-height:1.65;color:var(--ink)}
.paste:focus{outline:none;border-color:var(--structure)}

/* ============================================================
   THE PLAN — weeks, and what lands in them.
   An over-committed week is marked with the warn colour, not the danger one:
   it is a fact about arithmetic, not a failure.
   ============================================================ */
/* Sits inside the single Tasks scroller, below whatever the unpacker produced.
   The rule is only drawn when there is something above it to divide from. */
.pl-body{padding-top:var(--s-6)}
#tkBody:not(:empty) + .pl-body{border-top:1px solid var(--rule);margin-top:var(--s-7)}
.pl-bar{display:flex;align-items:baseline;gap:var(--s-5);margin-bottom:var(--s-5)}
.pl-bar h3{font-family:var(--display);font-weight:400;font-size:var(--t-2xl);
  letter-spacing:-.015em;flex:1}

.pl-head{display:flex;justify-content:flex-end;margin-bottom:var(--s-6)}
.pl-cap{display:flex;align-items:center;gap:var(--s-4)}
.pl-cap span{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.08em;
  text-transform:uppercase;color:var(--muted)}
.pl-cap input{width:62px;text-align:right;border:1px solid var(--rule);
  border-radius:var(--r-md);background:var(--paper);color:var(--ink);
  padding:6px 9px;font-family:inherit;font-size:var(--t-base)}
.pl-cap input:focus{outline:none;border-color:var(--structure)}

.pl-week{padding:var(--s-6) 0;max-width:60ch}
.pl-week + .pl-week{border-top:1px solid var(--rule-soft)}
.pl-whead{display:flex;align-items:baseline;gap:var(--s-5)}
.pl-whead b{font-family:var(--display);font-weight:400;font-size:var(--t-xl)}
.pl-load{font-family:var(--code);font-size:var(--t-sm);color:var(--muted);margin-left:auto}
.pl-week.over .pl-load{color:var(--warn)}

.pl-over{font-family:var(--text);font-size:var(--t-md);line-height:1.6;
  color:var(--warn);background:var(--warn-soft);border-radius:var(--r-md);
  padding:10px 12px;margin-top:var(--s-4)}

.pl-row{display:flex;align-items:center;gap:var(--s-4);width:100%;text-align:left;
  background:none;border:0;border-bottom:1px solid var(--rule-soft);
  padding:10px 0;cursor:pointer;color:inherit;font-family:inherit}
.pl-row:hover{background:var(--card)}
.pl-row:last-child{border-bottom:0}
.pl-row .dot{width:7px;height:7px;border-radius:var(--r-pill);flex:none}
.pl-title{font-family:var(--text);font-size:var(--t-md);color:var(--ink);
  flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pl-hours{font-family:var(--code);font-size:var(--t-sm);color:var(--ink-2);flex:none}
.pl-hours.none{color:var(--muted);font-style:italic}
.pl-due{font-family:var(--code);font-size:var(--t-sm);color:var(--muted);flex:none;
  min-width:9ch;text-align:right}

.tk-save{margin-top:var(--s-6)}
.fld-sel{border:1px solid var(--rule);border-radius:var(--r-md);background:var(--paper);
  color:var(--ink);padding:9px 11px;font-family:inherit;font-size:var(--t-md);width:100%}
.fld-check{flex-direction:row;align-items:center;gap:var(--s-4)}
.fld-check > span{font-family:var(--text);font-size:var(--t-md);text-transform:none;
  letter-spacing:0;color:var(--ink-2)}

@media (pointer:coarse){
  .pl-row{padding:13px 0}
  .pl-cap input,.fld-sel{font-size:1rem}
}

/* ============================================================
   MARKS — papers grouped by the conditions they were sat under.
   The layout carries the rule: each condition is its own block with its own
   figure and its own rule above it, so there is nowhere for a combined number
   to sit even visually. See app.js 12h.
   ============================================================ */
.mk-bar{padding:0 var(--gutter) var(--s-5)}

.mk-group{padding:var(--s-6) 0}
.mk-group + .mk-group{border-top:1px solid var(--rule)}

.mk-ghead{display:flex;align-items:baseline;gap:var(--s-5);flex-wrap:wrap;
  padding-bottom:var(--s-5)}
.mk-ghead h3{font-family:var(--display);font-weight:400;font-size:var(--t-2xl);
  letter-spacing:-.015em;flex:none}

/* BRASS, NOT EUCALYPT, and this is the whole reason brass exists as a token.
   Eucalypt is the app talking — links, buttons, the note you have open, the
   accent you can change in Settings. Brass means one thing and appears in one
   place: a formal record. A real mark, in a real paper, sat under real
   conditions.

   The distinction is not decoration. Everything else in Nexley that produces a
   number — AI feedback, a comp score, coverage — is deliberately kept away
   from the Marks screen, and giving the real ones their own colour is how that
   rule becomes visible instead of just being written down in a comment.

   It is also the one colour in the app the accent setting does not touch,
   because "what I actually got" should not change appearance depending on a
   preference. */
.mk-pct{font-family:var(--display);font-weight:400;font-size:var(--t-2xl);
  color:var(--brass);margin-left:auto;font-variant-numeric:tabular-nums}
.mk-rowpct{color:var(--brass)}
/* The condition each group was sat under, in brass on its own soft ground —
   the label that makes "never averaged across conditions" legible at a glance
   rather than only true underneath. */
.mk-cond{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.08em;
  text-transform:uppercase;color:var(--brass);background:var(--brass-soft);
  border-radius:var(--r-pill);padding:2px 9px;flex:none}
.mk-sub{font-family:var(--code);font-size:var(--t-sm);color:var(--muted);flex:none}

.mk-row{display:flex;align-items:baseline;gap:var(--s-5);width:100%;text-align:left;
  background:none;border:0;border-bottom:1px solid var(--rule-soft);
  padding:11px 0;cursor:pointer;color:inherit;font-family:inherit}
.mk-row:hover{background:var(--card)}
.mk-row:last-child{border-bottom:0}
.mk-title{font-family:var(--text);font-size:var(--t-md);color:var(--ink);
  flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mk-weight{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.06em;
  color:var(--muted);border:1px solid var(--rule);border-radius:var(--r-pill);
  padding:2px 7px;flex:none}
.mk-when{font-family:var(--code);font-size:var(--t-sm);color:var(--muted);flex:none}
.mk-score{font-family:var(--code);font-size:var(--t-sm);color:var(--ink-2);flex:none;
  min-width:7ch;text-align:right}
.mk-rowpct{font-family:var(--code);font-size:var(--t-sm);color:var(--ink);flex:none;
  min-width:6ch;text-align:right}

/* said out loud, because an absent number looks like a missing feature */
.mk-why{max-width:56ch;margin-top:var(--s-7);padding-top:var(--s-6);
  border-top:1px solid var(--rule-soft);
  font-family:var(--text);font-size:var(--t-md);line-height:1.65;color:var(--muted)}

/* where the marks went — the bar is share of total marks dropped, so the
   longest one is where the next hour of study should go */
.mk-loss{margin-top:var(--s-8);padding-top:var(--s-7);border-top:1px solid var(--rule)}
.mk-loss h3{font-family:var(--display);font-weight:400;font-size:var(--t-2xl);
  letter-spacing:-.015em}
.mk-lossnote{max-width:56ch;margin-top:var(--s-4);
  font-family:var(--text);font-size:var(--t-md);line-height:1.6;color:var(--muted)}

.mk-lossrow{margin-top:var(--s-6);max-width:56ch}
.mk-losstop{display:flex;align-items:baseline;gap:var(--s-5)}
.mk-losstop b{font-family:var(--text);font-size:var(--t-lg);font-weight:400;color:var(--ink)}
.mk-lossn{font-family:var(--code);font-size:var(--t-sm);color:var(--muted);margin-left:auto}
.mk-lossbar{height:6px;border-radius:var(--r-pill);background:var(--rule-soft);
  margin-top:var(--s-3);overflow:hidden}
.mk-lossbar i{display:block;height:100%;background:var(--structure);border-radius:var(--r-pill)}
.mk-lossfix{font-family:var(--text);font-size:var(--t-md);line-height:1.55;
  color:var(--ink-2);margin-top:var(--s-3)}

/* the content gaps, and the button that closes the loop into Review */
.mk-gaps{margin-top:var(--s-7);padding-top:var(--s-6);border-top:1px solid var(--rule-soft);
  max-width:56ch}
.mk-gaps h4{font-family:var(--display);font-weight:400;font-size:var(--t-xl)}
.mk-gap{display:flex;align-items:center;gap:var(--s-5);flex-wrap:wrap;
  padding:11px 0;border-bottom:1px solid var(--rule-soft)}
.mk-gap:last-child{border-bottom:0}
.mk-gapname{font-family:var(--text);font-size:var(--t-md);color:var(--ink);flex:1;min-width:150px}
.mk-gapbtn{flex:none;font-size:var(--t-sm)}

/* the per-question editor. One line per question on a laptop; it wraps on a
   phone rather than turning into a horizontally scrolling table. */
.qlist{display:flex;flex-direction:column;gap:var(--s-5)}
.qblock{display:flex;flex-direction:column;gap:var(--s-3);padding-bottom:var(--s-5);
  border-bottom:1px solid var(--rule-soft)}
.qblock:last-child{padding-bottom:0;border-bottom:0}
.qrow{display:flex;align-items:center;gap:var(--s-3);flex-wrap:wrap}
.qrow input,.qrow select{border:1px solid var(--rule);border-radius:var(--r-md);
  background:var(--paper);color:var(--ink);padding:7px 9px;
  font-family:inherit;font-size:var(--t-base)}
.qrow input:focus,.qrow select:focus{outline:none;border-color:var(--structure)}
.q-label{flex:1;min-width:64px}
/* Groups "got / of" as one flex item so .qrow's wrap breaks between fields, never
   inside this pair. */
.q-marks{display:inline-flex;align-items:center;gap:var(--s-2)}
.q-num{text-align:right}
/* Both of these are overrides of `.fld input[type=number]`, which sits later in
   this file AND is more specific than a bare `.q-num`, so it silently won:
     - its width:100% stretched each mark box to fill the whole marks group,
       which is why a question row read as two huge boxes and pushed the remove
       button onto a line of its own;
     - its padding left a right-aligned "got"/"of" placeholder sitting under the
       browser's native spinner.
   Scoped to .fld .qrow so it outranks that rule without reaching for an id. */
.fld .qrow input.q-num{width:64px;padding-right:var(--s-6)}
.q-slash{color:var(--muted);font-family:var(--code);font-size:var(--t-sm)}
.q-why{flex:1;min-width:132px;max-width:200px}
.q-point{flex:2;min-width:150px;max-width:280px}
/* a dot point only matters for a content gap */
.q-point.off{opacity:.35}
/* a question answered perfectly has nothing to explain */
.qrow.full .q-why{opacity:.35}
.q-rm{background:none;border:0;color:var(--muted);font-size:var(--t-lg);
  line-height:1;padding:4px 6px;cursor:pointer;border-radius:var(--r-sm)}
.q-rm:hover{color:var(--danger);background:var(--bad-soft)}
.qadd{align-self:flex-start}
.dlgnote.warn{color:var(--bad)}

/* ---------- the marked script — see app.js "the marked script" ---------- */
.q-script-wrap{display:flex;flex-direction:column;gap:var(--s-3);padding-left:var(--s-6)}
/* The collapsed state. A quiet link, not a button: it competes with Save and
   "+ Add a question" for attention otherwise, and it is the least important
   control in the dialog. */
.q-addscript{align-self:flex-start;background:none;border:0;padding:2px 0;
  font-size:var(--t-sm);color:var(--muted);cursor:pointer;border-radius:var(--r-sm)}
.q-addscript:hover{color:var(--structure)}
/* height is set from content by autosize() in app.js — no inner scrollbar, so
   the dialog is the only thing the wheel moves */
.q-response{width:100%;resize:none;overflow:hidden;min-height:2.6em;font-family:var(--text);
  font-size:var(--t-base);line-height:1.55;border:1px solid var(--rule);
  border-radius:var(--r-md);background:var(--paper);color:var(--ink);padding:var(--s-4)}
.q-response:focus{outline:none;border-color:var(--structure)}
.q-script{font-family:var(--text);font-size:var(--t-base);line-height:1.8;
  border:1px solid var(--rule-soft);border-radius:var(--r-md);background:var(--card);
  padding:var(--s-5);color:var(--ink-2)}
.q-span-mark{background:none;border-radius:var(--r-sm);padding:0 1px;cursor:pointer;color:inherit;
  border-bottom:2px solid currentColor}
.q-span-mark.pos{color:var(--good);background:var(--good-soft)}
.q-span-mark.neg{color:var(--bad);background:var(--bad-soft)}
.q-span-mark.on{outline:2px solid currentColor;outline-offset:1px}
.q-annotate{align-self:flex-start;background:none;border:1px dashed var(--rule);
  border-radius:var(--r-md);padding:5px 11px;font-size:var(--t-sm);color:var(--muted);cursor:pointer}
.q-annotate:hover{border-color:var(--structure);color:var(--structure)}
.q-actions{display:flex;flex-wrap:wrap;gap:var(--s-3);align-items:center}
.q-spanlist{display:flex;flex-direction:column;gap:var(--s-2)}

/* Handwriting. Sits under the typed body in the same column, on the same
   ruled paper, because it is part of the note rather than an attachment to it. */
/* ============================================================
   HANDWRITING — one surface, not a box under the text
   ------------------------------------------------------------
   Rebuilt 09-06 after Alec: "notes and pen notes should be the same thing",
   and after actually reading how Apple Notes does it. Apple keeps typed text
   and handwriting in separate BLOCKS with a margin separator between them,
   which their own reviewers call the limitation it is. Nexley goes further:
   the canvas is an overlay across the whole writing column, so a pencil marks
   the page wherever the page is — over a paragraph, in the margin, across a
   diagram you typed a caption under.

   It works because the canvas is pointer-events:none by default. A finger
   scrolls, a mouse selects and types, both straight through the glass. A PEN
   is routed to it explicitly by app.js, which is what makes "just start
   writing" possible without a mode switch — the thing an iPad user expects.
   The Draw toggle turns pointer-events back on for laptops with no stylus.
   ============================================================ */
.ed-surface{position:relative}
/* top/left/width/height are all set in JS from the body's own box — see
   sizeInk(). Nothing here may set them, or the two disagree. */
/* The pen and highlighter palette. ink.js reads these by token name at draw
   time, so a stroke saved in dark mode comes back correct in daylight — a
   stored hex would freeze it at whichever theme drew it. The hi- variants are
   the same hues at highlighter strength; they are separate tokens rather than
   an alpha applied to the pen colour because a readable highlight is not the
   pen colour made transparent, it is a lighter, yellower relative of it. */
.ink-canvas{position:absolute;
  --ink-accent:var(--structure);
  --ink-ink:var(--ink);
  --ink-green:#2C6B4F;
  --ink-red:#A03A22;
  --ink-blue:#2B4D82;
  --ink-hi-ink:#E7E24C;
  --ink-hi-green:#7FD4A6;
  --ink-hi-red:#F2A08A;
  --ink-hi-blue:#8FC0F0;
  --ink-stroke:var(--ink);pointer-events:none;z-index:2;
  /* Height is set in JS to the full content height, not to 100% of the
     scroller — see sizeInk(). pan-y so a finger still scrolls in draw mode. */
  touch-action:pan-y}
.ed-surface.drawing .ink-canvas{pointer-events:auto;cursor:crosshair}

.paper-sel{font-family:var(--ui);font-size:var(--t-base);color:var(--ink-2);
  background:none;border:0;padding:2px 4px;cursor:pointer}
.paper-sel:focus{outline:none;color:var(--structure)}
/* The palette floats over the page and is dragged by its grip. Fixed
   positioning, so it stays put while the note scrolls underneath — a toolbar
   that scrolls away is one you have to chase. */
.ink-palette{position:fixed;left:auto;right:28px;bottom:26px;z-index:40;
  display:flex;flex-direction:column;gap:var(--s-2);
  background:var(--card);border:1px solid var(--rule);border-radius:var(--r-lg);
  padding:var(--s-3) var(--s-4) var(--s-4);
  box-shadow:0 6px 22px rgb(var(--shade) / .16), 0 1px 3px rgb(var(--shade) / .10)}
.ink-grip{display:flex;align-items:center;gap:var(--s-3);cursor:grab;
  touch-action:none}          /* the grip must not scroll the page while dragging */
.ink-grip:active{cursor:grabbing}
.ink-grip-dots{flex:1;height:8px;border-radius:var(--r-pill);
  background-image:radial-gradient(circle at 2px 2px, var(--rule) 1.4px, transparent 1.5px);
  background-size:7px 8px}
.ink-fold{border:0;background:none;color:var(--muted);cursor:pointer;
  font-size:var(--t-md);line-height:1;padding:0 var(--s-2)}
.ink-fold:hover{color:var(--ink)}
.ink-row{display:flex;gap:var(--s-2);align-items:center;flex-wrap:wrap;max-width:330px}
.inktool{border:1px solid transparent;background:none;border-radius:var(--r-sm);
  padding:6px 9px;font-size:var(--t-sm);color:var(--ink-2);cursor:pointer;white-space:nowrap}
.inktool:hover{border-color:var(--rule);color:var(--ink)}
.inktool.on{border-color:var(--structure);color:var(--structure);background:var(--structure-soft)}
.inktool.quiet{color:var(--muted)}
.ink-sep{width:1px;align-self:stretch;background:var(--rule);margin:2px var(--s-2)}
.ink-swatches{display:flex;gap:var(--s-3);align-items:center}
.swatch{width:22px;height:22px;border-radius:var(--r-pill);border:2px solid transparent;
  cursor:pointer;padding:0;background:var(--sw)}
.swatch[data-colour="ink"]{--sw:var(--ink)}
.swatch[data-colour="green"]{--sw:#2C6B4F}
.swatch[data-colour="red"]{--sw:#A03A22}
.swatch[data-colour="blue"]{--sw:#2B4D82}
/* The ring, not a fill change, marks the chosen one — a swatch has to keep
   showing its own colour to be a swatch at all. */
.swatch.on{border-color:var(--ink);box-shadow:0 0 0 2px var(--card) inset}
.ink-palette.hi .swatch[data-colour="ink"]{--sw:#E7E24C}
.ink-palette.hi .swatch[data-colour="green"]{--sw:#7FD4A6}
.ink-palette.hi .swatch[data-colour="red"]{--sw:#F2A08A}
.ink-palette.hi .swatch[data-colour="blue"]{--sw:#8FC0F0}
.ink-hint{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.06em;
  text-transform:uppercase;color:var(--muted);margin:0}
/* Folded: the grip alone, so it can still be found and moved. */
.ink-palette.folded .ink-row,.ink-palette.folded .ink-hint{display:none}
@media (max-width:640px){
  /* On a phone a floating panel in the corner covers the thing you are writing
     on. Pin it to the bottom edge instead, full width, and let it wrap. */
  .ink-palette{left:8px;right:8px;bottom:8px}
  .ink-row{max-width:none}
}

/* ---------- paper ----------
   Lines and grids, per note, the way Apple Notes offers them — and for the
   same reason: handwriting on blank white drifts, and a grid is what makes a
   diagram sit straight. Applied to the WRITING SURFACE, so it is behind both
   the typed text and the ink rather than being a property of one of them.

   --line is the typed body's own line unit, so "lines" means the lines your
   typing already sits on, not a decorative rule at some unrelated pitch. */
.ed-body[data-paper="blank"]{background-image:none}
.ed-body[data-paper="lines"]{background-image:repeating-linear-gradient(to bottom,
  transparent 0, transparent calc(var(--line) - 1px),
  var(--rule-soft) calc(var(--line) - 1px), var(--rule-soft) var(--line))}
.ed-body[data-paper="lines-wide"]{background-image:repeating-linear-gradient(to bottom,
  transparent 0, transparent calc(var(--line) * 2 - 1px),
  var(--rule-soft) calc(var(--line) * 2 - 1px), var(--rule-soft) calc(var(--line) * 2))}
.ed-body[data-paper="grid"]{background-image:
  repeating-linear-gradient(to bottom, transparent 0, transparent calc(var(--line) - 1px),
    var(--rule-soft) calc(var(--line) - 1px), var(--rule-soft) var(--line)),
  repeating-linear-gradient(to right, transparent 0, transparent calc(var(--line) - 1px),
    var(--rule-soft) calc(var(--line) - 1px), var(--rule-soft) var(--line))}
.ed-body[data-paper="grid-small"]{background-image:
  repeating-linear-gradient(to bottom, transparent 0, transparent calc(var(--line) / 2 - 1px),
    var(--rule-soft) calc(var(--line) / 2 - 1px), var(--rule-soft) calc(var(--line) / 2)),
  repeating-linear-gradient(to right, transparent 0, transparent calc(var(--line) / 2 - 1px),
    var(--rule-soft) calc(var(--line) / 2 - 1px), var(--rule-soft) calc(var(--line) / 2))}
/* An empty note keeps its paper. A blank ruled page is still a page — the old
   rule here dropped the ruling entirely and made an empty note look broken. */
.ed-body:empty::before{content:"Start writing";color:var(--muted);opacity:.5}

/* A photographed page, previewed before it is committed to the note. Sized so
   a tall page and a wide whiteboard both fit without the dialog jumping. */
.photo-stage{display:flex;align-items:center;justify-content:center;
  min-height:220px;max-height:46vh;padding:var(--s-5);
  border:1px dashed var(--rule);border-radius:var(--r-md);background:var(--surface)}
.photo-stage img{max-width:100%;max-height:42vh;border-radius:var(--r-sm);
  box-shadow:0 2px 10px rgb(var(--shade) / .12)}
.photo-controls{display:flex;align-items:center;gap:var(--s-5);margin-top:var(--s-4);
  flex-wrap:wrap}
.photo-check{display:flex;align-items:center;gap:var(--s-3);font-size:var(--t-base);
  color:var(--ink-2);cursor:pointer}
/* A photograph in a note is a page, so it gets the width of the page and the
   same rounded edge as everything else on it. */
.ed-body img{max-width:100%;height:auto;border-radius:var(--r-sm);
  margin:var(--s-4) 0;display:block}
/* A cleaned page is white paper with black marks. At night that is a torch in
   the face, and worse, the pen is near-white so you would write on it
   invisibly. Inverting turns it into a dark page with light marks — the same
   thing a dark-mode PDF reader does — and the notebook stays one object.
   Only .scan is inverted; a colour photograph would become a negative. */
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .ed-body img.scan{filter:invert(1)}
}
:root[data-theme="dark"] .ed-body img.scan{filter:invert(1)}

/* Comps. The code is set in the mono face everywhere it appears, because it
   gets read aloud and typed by somebody else — it is data, not prose. */
.comp-join,.comp-mine{display:flex;gap:var(--s-4);align-items:flex-end}
/* Trap 13, again: `.fld input[type=number]{width:100%}` is (0,2,1) and sits
   later in this file, so it silently beats `.q-num{width:64px}` and stretched
   the score box across the whole dialog.

   And the FIRST attempt at this fix did not work either, which is the part
   worth writing down: `.comp-mine input.q-num` is also (0,2,1), an exact tie,
   and this block sits EARLIER in the file than the .fld rule — so the tie was
   broken by source order in favour of the thing being overridden. Three class
   levels is what it takes. Third time this one rule has quietly won an
   argument it should not have been in; check the computed width, never the
   selector you wrote. */
.comp-mine .fld input.q-num{width:96px;flex:none}
.comp-mine .fld{flex:none}
.code-in{font-family:var(--code);letter-spacing:.18em;text-transform:uppercase}
.comp-row{display:flex;flex-direction:column;gap:var(--s-2);width:100%;text-align:left;
  border:1px solid var(--rule);border-radius:var(--r-md);background:var(--card);
  padding:var(--s-4) var(--s-5);margin:0 0 var(--s-3);cursor:pointer}
.comp-row:hover{border-color:var(--structure)}
.comp-title{font-family:var(--display);font-size:var(--t-md);color:var(--ink)}
.comp-meta{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.06em;
  text-transform:uppercase;color:var(--muted)}
.comp-q{display:flex;gap:var(--s-4);align-items:baseline;padding:var(--s-3) 0;
  border-top:1px solid var(--rule)}
.comp-qn{font-family:var(--code);font-size:var(--t-xs);color:var(--muted);min-width:2ch}
.comp-qp{flex:1;font-size:var(--t-base);color:var(--ink-2);line-height:1.6}
.comp-qm{font-family:var(--code);font-size:var(--t-2xs);color:var(--muted);white-space:nowrap}
.comp-entry{display:flex;justify-content:space-between;align-items:baseline;gap:var(--s-4);
  padding:var(--s-3) var(--s-4);border-radius:var(--r-sm)}
/* Brass, not eucalypt: this is a record of what happened, which is the one
   thing that colour means anywhere in the app. */
.comp-entry.me{background:var(--brass-soft)}
/* The row stays baseline-aligned so the name and the score sit on one line;
   only the circle centres itself against them. .comp-who takes the slack so
   space-between still pushes the score to the right edge. */
.comp-entry .avatar{align-self:center}
.comp-who{flex:1;min-width:0;font-size:var(--t-base);color:var(--ink)}
.comp-score{font-family:var(--code);font-size:var(--t-sm);color:var(--ink-2);
  font-variant-numeric:tabular-nums}
.comp-score.pending{color:var(--muted);font-style:italic}

/* A note somebody else wrote. Deliberately looks like a quotation and not
   like one of your own note cards: it sits on the recessed surface, and its
   body keeps the reading serif so it reads as writing rather than as data. */
.share-row{border:1px solid var(--rule);border-left:3px solid var(--structure);
  border-radius:var(--r-md);background:var(--surface);
  padding:var(--s-5);margin:0 0 var(--s-4);display:flex;flex-direction:column;gap:var(--s-3)}
.share-head{font-family:var(--display);font-size:var(--t-md);color:var(--ink)}
.share-meta{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.06em;
  text-transform:uppercase;color:var(--muted);
  display:flex;align-items:center;gap:var(--s-2)}
/* The sender's face sits inline with the metadata line rather than above it —
   an inbox row is scanned, and a second stacked element slows that down. */
.share-meta .avatar{width:18px;height:18px;font-size:var(--t-2xs);letter-spacing:0}
/* Capped and scrollable: an inbox is a list to triage, not a reading view, and
   one long note must not push everything else off the screen. */
.share-body{font-family:var(--text);font-size:var(--t-base);line-height:1.7;
  color:var(--ink-2);white-space:pre-wrap;max-height:9.5em;overflow-y:auto}

/* AI feedback. Read as a quotation of someone else's opinion, never as a
   result: no accent fill, no big number, and the score sits in the mono face
   the app uses everywhere for machine-generated figures. */
.ai-head{margin:0 0 var(--s-4);font-size:var(--t-base);color:var(--ink)}
.ai-crit{display:flex;gap:var(--s-4);align-items:baseline;
  padding:var(--s-3) 0;border-top:1px solid var(--rule)}
.ai-score{font-family:var(--code);font-size:var(--t-xs);color:var(--ink-2);
  white-space:nowrap;min-width:5ch}
.ai-crit.unclear .ai-score{color:var(--warn)}
.ai-crit-text{font-size:var(--t-sm);color:var(--ink-2);line-height:1.6}
.ai-crit-text b{color:var(--ink);font-weight:500}
.ai-fixhead{margin:var(--s-5) 0 var(--s-2);font-family:var(--code);
  font-size:var(--t-2xs);letter-spacing:.09em;text-transform:uppercase;color:var(--muted)}
.ai-fixes{margin:0;padding-left:var(--s-6);font-size:var(--t-sm);
  color:var(--ink-2);line-height:1.7}
.ai-bad{margin:0;font-size:var(--t-sm);color:var(--bad);line-height:1.6}
.q-span-row{display:flex;align-items:center;gap:var(--s-3);flex-wrap:wrap;
  padding:var(--s-2) 0;border-radius:var(--r-sm)}
.q-span-row.on{background:var(--structure-soft)}
.q-span-quote{font-family:var(--text);font-style:italic;font-size:var(--t-sm);color:var(--ink-2);
  flex:1;min-width:160px}
.q-span-kind{width:auto}
.q-span-note{flex:2;min-width:150px}

/* Same specificity problem as above, and it mattered most here: iPads are the
   stated target, and both of these were being beaten by `.fld input[...]`. */
@media (pointer:coarse){
  .fld .qrow input,.fld .qrow select{font-size:1rem;padding:9px 10px}
  .fld .qrow input.q-num{width:72px;padding-right:var(--s-7)}
}

/* dialog form fields — shared with any later dialog that needs labelled inputs */
.fld{display:flex;flex-direction:column;gap:var(--s-3)}
.fld > span{font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted)}
.fld input[type=text],.fld input[type=number],.fld input[type=date]{
  border:1px solid var(--rule);border-radius:var(--r-md);background:var(--paper);
  color:var(--ink);padding:9px 11px;font-family:inherit;font-size:var(--t-md);width:100%}
.fld input:focus{outline:none;border-color:var(--structure)}
.fld-row{display:flex;gap:var(--s-5);flex-wrap:wrap}
.fld-row .fld{flex:1;min-width:92px}

.dlgerr{font-family:var(--text);font-size:var(--t-md);line-height:1.55;
  color:var(--bad);background:var(--bad-soft);border-radius:var(--r-md);
  padding:10px 12px}

@media (pointer:coarse){
  /* iOS zooms the page when a focused input is under 16px */
  .fld input[type=text],.fld input[type=number],.fld input[type=date]{font-size:1rem}
  .mk-row{padding:14px 0}
}

/* ============================================================
   FEEDBACK — the board. Every value here is a scale token; the only new idea
   is the status pill, and it borrows the semantic status colours rather than
   the brand accent, because "Shipped" means state, not Nexley.
   ============================================================ */
#fbDialog{width:min(560px,94vw)}

/* the unread dot on the rail button. The one piece of unsolicited attention the
   app asks for, so it is the highlighter mark and nothing louder. */
.fbdot{display:inline-block;width:6px;height:6px;border-radius:var(--r-pill);
  background:var(--mark);margin-left:var(--s-2);vertical-align:middle}

.chips{display:flex;flex-wrap:wrap;gap:var(--s-3)}
.chip{font-family:var(--ui);font-size:var(--t-xs);letter-spacing:.06em;
  text-transform:uppercase;color:var(--muted);background:var(--paper);
  border:1px solid var(--rule);border-radius:var(--r-pill);
  padding:6px 11px;cursor:pointer}
.chip:hover{color:var(--structure);border-color:var(--structure-soft)}
.chip.on{color:var(--structure);background:var(--structure-soft);
  border-color:var(--structure)}

.fbhead-title{font-family:var(--display);font-weight:400;font-size:var(--t-xl);
  padding-top:var(--s-5);border-top:1px solid var(--rule-soft)}

.fblist{display:flex;flex-direction:column;gap:var(--s-5);
  max-height:40vh;overflow-y:auto}

.fbitem{border:1px solid var(--rule);border-radius:var(--r-md);
  padding:13px 14px;background:var(--paper);
  display:flex;flex-direction:column;gap:var(--s-3)}

.fbhead{display:flex;align-items:center;gap:var(--s-4);flex-wrap:wrap}
.fbkind,.fbdate{font-family:var(--code);font-size:var(--t-sm);color:var(--muted)}
.fbdate{margin-left:auto}

.fbstatus{font-family:var(--ui);font-size:var(--t-2xs);letter-spacing:.08em;
  text-transform:uppercase;border-radius:var(--r-pill);padding:3px 9px}
.fbstatus.wait{color:var(--muted);background:var(--surface)}
.fbstatus.go{color:var(--structure);background:var(--structure-soft)}
.fbstatus.done{color:var(--good);background:var(--good-soft)}
.fbstatus.off{color:var(--muted);background:var(--surface);
  text-decoration:line-through;text-decoration-thickness:1px}

.fbbody{font-family:var(--text);font-size:var(--t-md);line-height:1.6;color:var(--ink-2);
  white-space:pre-wrap;word-break:break-word}

/* A reply is the whole point of the table existing, so it gets the one indented,
   marked treatment in the dialog rather than reading as more metadata. */
.fbreply{border-left:2px solid var(--mark);padding-left:var(--s-5);
  display:flex;flex-direction:column;gap:var(--s-2)}
.fbreply b{font-family:var(--ui);font-size:var(--t-2xs);letter-spacing:.08em;
  text-transform:uppercase;color:var(--muted);font-weight:400}
.fbreply p{font-family:var(--text);font-size:var(--t-md);line-height:1.6;color:var(--ink);
  white-space:pre-wrap;word-break:break-word}

/* dialog */
dialog{border:1px solid var(--rule);border-radius:var(--r-lg);background:var(--card);
  color:var(--ink);padding:0;width:min(440px,92vw);
  box-shadow:0 34px 76px -44px rgb(var(--shade)/.6)}
dialog::backdrop{background:rgb(var(--shade)/.5)}
/* 440 is right for the dialogs that ask one or two things. Recording a paper is
   the one data-entry form in the app with a repeating multi-field row, and at
   440 every question wraps onto three lines — a wall of boxes rather than a
   table of questions. It gets the width its content actually needs. */
#pprDialog{width:min(680px,94vw)}
dialog form{padding:26px;display:flex;flex-direction:column;gap:var(--s-6)}
dialog h3{font-family:var(--display);font-weight:400;font-size:var(--t-2xl)}
.swatches{display:flex;gap:var(--s-4);flex-wrap:wrap}
.sw{width:26px;height:26px;border-radius:var(--r-md);border:2px solid transparent;cursor:pointer}
.sw.on{border-color:var(--ink);box-shadow:0 0 0 2px var(--card) inset}
.dlg-actions{display:flex;align-items:center;gap:var(--s-4);margin-top:4px}
/* The non-<form> dialogs. `dialog` itself is padding:0 so its <form> can own the
   inset; a dialog built from a plain div got no padding at all and its contents
   sat hard against the border. Same numbers as `dialog form` on purpose — a
   dialog should not be able to tell you which markup it happens to be made of. */
.dlg{padding:26px;display:flex;flex-direction:column;gap:var(--s-6)}
.dlg .field{gap:var(--s-4)}
/* This one opens over nothing — no gate behind it, no app yet — so it carries the
   mark itself rather than appearing as an unattributed box demanding a birthday. */
.dlg-mark{display:flex;align-items:center;gap:var(--s-3);color:var(--structure)}
.dlg-mark b{font-family:var(--display);font-weight:400;font-size:var(--t-xl);color:var(--ink)}

/* ============================================================
   TOUCH & PEN — iPads and pen-capable PCs are the real target
   ============================================================ */
@media (pointer:coarse){
  .snav .edit,.dp .edit,.topic-head .edit{opacity:1}
  .snav{padding:12px 10px;min-height:44px}
  .ncard{padding:15px 14px;min-height:56px}
  .dp{padding:12px 8px;min-height:44px}
  .topic-head{padding:12px 8px;min-height:46px}
  .nrow{padding:11px 8px;min-height:44px}
  .toolbar button{min-width:44px;min-height:44px;padding:9px 12px}
  .btn{padding:11px 18px;min-height:44px}
  .iconbtn{padding:9px 14px;min-height:44px}
  .iconbtn.ghost{padding:9px 14px;min-height:44px}
  .linkbtn{padding:11px 0;min-height:40px}
  .add-subject{padding:13px;min-height:44px}
  .tab{padding:10px 12px;min-height:42px}
  .kindtoggle button{padding:9px 14px;min-height:42px}
  .sw{width:38px;height:38px}

  .rail-brand .phase{display:none}
  .paste{font-size:max(1rem, 16px)}
  /* iOS zooms the page when a focused field is under 16px — hence the 16px floor,
     which max() keeps. What it no longer does is CAP the size there: a reader who
     has turned their text up gets bigger fields, instead of every other surface
     growing while the one they type into stays put. */
  /* .ed-title is NOT in this list: it is an input, but its clamp already puts it well
     above 16px, and forcing 16px here shrank the note title to nothing on the exact
     devices this app is for. */
  .field input,.rail-search input,.sel select,.ed-body{font-size:max(1rem, 16px)}
  /* iPads are the real target, so this is the case that has to line up: move the
     ruled pitch with the bigger touch text, don't just loosen line-height.
     The ruling is derived from the SAME max() as the text above it, so the two
     cannot drift apart when a reader scales up — which is exactly the bug class
     that already bit once here (line-height 1.9 against a 1.75 ruling). */
  .ed-body{--line:calc(1.9 * max(1rem, 16px))}
  .ed-body.font-hand{--line:calc(1.9 * 1.15rem)}
}

/* iPad safe areas */
@supports(padding:max(0px)){
  .rail{padding-left:max(14px,env(safe-area-inset-left));
        padding-bottom:max(20px,env(safe-area-inset-bottom))}
  .ed-foot{padding-bottom:max(10px,env(safe-area-inset-bottom))}
  .notes,.browser-body{padding-bottom:max(8px,env(safe-area-inset-bottom))}
}

/* ============================================================
   LAYOUT — three panes wide · index-or-page on tablet · stacked on phone
   ============================================================ */
@media (max-width:1180px){
  .app{grid-template-columns:204px 1fr}
  .browser{grid-column:2;grid-row:1}
  .editor,.empty,.pane{grid-column:2;grid-row:1}
  /* the page takes over the working area while a note is open */
  .app.editing .browser{display:none}
  /* the placeholder would stack on top of the index and block it — the index
     column is already the nothing-open view here */
  .empty{display:none}
  :root{--gutter:clamp(20px,4vw,40px)}
}
/* menu button + drawer scrim — phone only */
.menu-btn{display:none;position:fixed;top:9px;left:10px;z-index:44;
  width:38px;height:38px;border:1px solid var(--rule);border-radius:var(--r-md);
  background:var(--card);color:var(--ink-2);font-size:var(--t-lg);line-height:1;cursor:pointer}
.rail-scrim{display:none;position:fixed;inset:0;z-index:38;background:rgb(var(--shade)/.4)}

@media (max-width:680px){
  .app{grid-template-columns:1fr;height:100dvh;overflow:hidden}
  .menu-btn{display:block}
  .app.nav-open .rail-scrim{display:block}

  /* rail is a proper drawer — keeps its vertical layout, slides in */
  .rail{position:fixed;top:0;left:0;bottom:0;width:min(280px,82vw);z-index:40;
    border-right:1px solid var(--rule);
    transform:translateX(-100%);transition:transform .22s ease;
    will-change:transform}
  .app.nav-open .rail{transform:none;box-shadow:0 0 60px -10px rgb(var(--shade)/.5)}

  /* working area fills the screen, one view at a time */
  .browser,.editor,.empty,.pane{grid-column:1;grid-row:1}
  .browser,.editor{height:100dvh}
  .pane{height:100dvh}
  .pane-head{padding-left:56px}   /* clear the menu button */
  .browser-head{padding-left:56px}      /* clear the menu button */
  .app.editing .browser{display:none}
  .app.editing .menu-btn{display:none}  /* the page uses the back button, not the menu */
  .app:not(.editing) .editor{display:none}
  .ed-head{padding-left:16px;padding-right:16px}
  .ed-meta{gap:var(--s-5)}
  :root{--gutter:18px}
}

@media (prefers-reduced-motion:reduce){ .rail{transition:none} }

@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}


/* ============================================================
   THE RAIL, RESTRUCTURED (09-06)
   ------------------------------------------------------------
   Alec: "I still have seen no restructuring of home page, side bars, themes,
   colourings." He was right. The rail had five text chips for navigation and
   then eleven identical text links below them — snapshots, export, import,
   share, feedback, report, lock, theme and three social ones — so the subjects
   a student is actually studying competed for attention with "Report a
   problem", and nothing in the column had more weight than anything else.

   Navigation now looks like navigation: one column, a mark beside each item,
   the current one filled. Everything that is not navigation moved into
   Settings, where a list is the right shape.
   ============================================================ */
.modes{display:flex;flex-direction:column;gap:2px;padding:var(--s-4) var(--s-3)}
.mode{display:flex;align-items:center;gap:var(--s-4);width:100%;
  padding:8px var(--s-4);border:0;border-radius:var(--r-md);background:none;
  font-family:var(--ui);font-size:var(--t-base);letter-spacing:0;text-transform:none;
  color:var(--ink-2);cursor:pointer;text-align:left}
.mode:hover{background:var(--surface);color:var(--ink)}
.mode.on{background:var(--structure-soft);color:var(--structure);font-weight:500}
.mode span{flex:1}
/* A count only appears when there is something to count. */
.mcount{font-family:var(--code);font-size:var(--t-2xs);background:var(--structure);
  color:var(--card);border-radius:var(--r-pill);padding:1px 6px;font-weight:400}

/* Icons are drawn with current colour so they follow the active state, and
   they are geometry rather than emoji — an emoji is a different typeface on
   every platform and instantly makes an interface look assembled. */
.mi{width:16px;height:16px;flex:none;display:inline-block;
  background:currentColor;opacity:.85;
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:center;mask-position:center;
  -webkit-mask-size:contain;mask-size:contain}
.mi-home{-webkit-mask-image:var(--i-home);mask-image:var(--i-home)}
.mi-book{-webkit-mask-image:var(--i-book);mask-image:var(--i-book)}
.mi-capture{-webkit-mask-image:var(--i-capture);mask-image:var(--i-capture)}
.mi-review{-webkit-mask-image:var(--i-review);mask-image:var(--i-review)}
.mi-tasks{-webkit-mask-image:var(--i-tasks);mask-image:var(--i-tasks)}
.mi-marks{-webkit-mask-image:var(--i-marks);mask-image:var(--i-marks)}
.mi-inbox{-webkit-mask-image:var(--i-inbox);mask-image:var(--i-inbox)}
.mi-comp{-webkit-mask-image:var(--i-comp);mask-image:var(--i-comp)}
.mi-more{-webkit-mask-image:var(--i-more);mask-image:var(--i-more)}

/* ---------- the foot: who you are, and one door ---------- */
.rail-foot{display:flex;align-items:center;gap:var(--s-3);
  padding:var(--s-4);border-top:1px solid var(--rule);margin-top:auto}
.acct{display:flex;align-items:center;gap:var(--s-3);flex:1;min-width:0;
  background:none;border:0;padding:var(--s-2);border-radius:var(--r-md);
  cursor:pointer;text-align:left}
.acct:hover{background:var(--surface)}
.avatar{width:26px;height:26px;flex:none;border-radius:var(--r-pill);
  background:var(--structure-soft);color:var(--structure);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--ui);font-size:var(--t-sm);font-style:normal;text-transform:uppercase}

/* Four tones, assigned from a hash of the username so a person looks the same
   everywhere — see avatarFor() in app.js for why it is four and not a rainbow.
   None of them is a verdict colour: good/warn/bad stay reserved for a result
   that was actually earned.

   These are their own token pairs rather than reuses of existing ones, for two
   measured reasons. First, --structure is user-configurable through data-accent,
   so an avatar built on it would change hue when someone re-accents the app —
   an identity mark that moves is not an identity mark. Second, --margin-rule and
   --muted have no light/dark pair the way --structure and --brass do, and tinting
   them with color-mix produced 2.37:1 and 3.66:1 against their own backgrounds —
   below even the 3.0 floor, let alone 4.5. Every pair below was measured: the
   worst of the eight is 6.1:1. Re-measure if you touch them. */
:root{
  --av0:#21493B; --av0-bg:#DEE7E0;   /* eucalypt */
  --av1:#6B5219; --av1-bg:#F1E9D6;   /* brass */
  --av2:#7A3B36; --av2-bg:#F0E0DE;   /* the margin rose, deepened to carry text */
  --av3:#3F4A52; --av3-bg:#E1E6E9;   /* slate */
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --av0:#8ECBAB; --av0-bg:#243228;
    --av1:#C9A552; --av1-bg:#2A2417;
    --av2:#DFA0A0; --av2-bg:#2E1A1A;
    --av3:#A8B6C0; --av3-bg:#232A2F;
  }
}
:root[data-theme="dark"]{
  --av0:#8ECBAB; --av0-bg:#243228;
  --av1:#C9A552; --av1-bg:#2A2417;
  --av2:#DFA0A0; --av2-bg:#2E1A1A;
  --av3:#A8B6C0; --av3-bg:#232A2F;
}
.avatar.av-0{background:var(--av0-bg);color:var(--av0)}
.avatar.av-1{background:var(--av1-bg);color:var(--av1)}
.avatar.av-2{background:var(--av2-bg);color:var(--av2)}
.avatar.av-3{background:var(--av3-bg);color:var(--av3)}
.avatar.av-none{background:var(--rule-soft);color:var(--ink-2)}

/* Smaller, for list rows where the name is doing the work */
.avatar.sm{width:21px;height:21px;font-size:var(--t-xs)}
.acct-name{flex:1;min-width:0;font-size:var(--t-base);color:var(--ink-2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rail-tools{display:flex;gap:2px}
.toolbtn{position:relative;width:32px;height:32px;border:0;background:none;
  border-radius:var(--r-md);color:var(--muted);cursor:pointer;
  display:flex;align-items:center;justify-content:center}
.toolbtn:hover{background:var(--surface);color:var(--ink)}
.toolbtn .fbdot{position:absolute;top:5px;right:5px}

/* ---------- settings ---------- */
.setgroup{margin:0 0 var(--s-7)}
.setgroup h4{margin:0 0 var(--s-4);font-family:var(--code);font-size:var(--t-2xs);
  letter-spacing:.09em;text-transform:uppercase;color:var(--muted);font-weight:400;
  padding-bottom:var(--s-3);border-bottom:1px solid var(--rule)}
.setrow{display:flex;align-items:center;gap:var(--s-5);margin:0 0 var(--s-4);flex-wrap:wrap}
/* A real <label for>, not a span: it gives the checkbox an accessible name and
   makes the sentence itself a tap target, which on a touch screen is the
   difference between a 16px box and the whole row. */
.setlabel{flex:1;min-width:9ch;font-size:var(--t-base);color:var(--ink-2);cursor:pointer}
.setlinks{display:flex;flex-direction:column;gap:var(--s-2);align-items:flex-start}
.segmented{display:flex;border:1px solid var(--rule);border-radius:var(--r-md);overflow:hidden}
.segmented button{border:0;background:none;padding:6px 12px;font-size:var(--t-sm);
  color:var(--ink-2);cursor:pointer}
.segmented button+button{border-left:1px solid var(--rule)}
.segmented button.on{background:var(--structure);color:var(--card)}
.accents{display:flex;gap:var(--s-3)}
.accents button{width:26px;height:26px;border-radius:var(--r-pill);cursor:pointer;
  border:2px solid transparent;padding:0;background:var(--a)}
.accents button[data-accent="eucalypt"]{--a:#21493B}
.accents button[data-accent="ink"]{--a:#2F3336}
.accents button[data-accent="burgundy"]{--a:#6E2438}
.accents button[data-accent="brass"]{--a:#7A5E1E}
.accents button.on{border-color:var(--ink);box-shadow:0 0 0 2px var(--card) inset}


/* ============================================================
   HOME
   ------------------------------------------------------------
   The app used to open on the notebook: a list of files. That answers "what
   have I written" and never "what should I do now", and the second is the
   question a student actually arrives with. Home answers it from data the app
   already had and was only ever showing one screen at a time.

   Cards are sized by what they hold rather than stamped to a grid, and the
   ones with nothing to say are not rendered at all — an empty dashboard full
   of zeroes is worse than a short one.
   ============================================================ */
.home-grid{display:grid;gap:var(--s-5);
  grid-template-columns:repeat(auto-fit,minmax(260px,1fr));
  padding:0 0 var(--s-8)}
.hcard{border:1px solid var(--rule);border-radius:var(--r-lg);background:var(--card);
  padding:var(--s-6);display:flex;flex-direction:column;gap:var(--s-4)}
.hcard h3{margin:0;font-family:var(--code);font-size:var(--t-2xs);letter-spacing:.09em;
  text-transform:uppercase;color:var(--muted);font-weight:400}
.hcard.wide{grid-column:1/-1}
/* One number, when the number is the point. Tabular so a 9 and a 14 line up. */
.hbig{font-family:var(--display);font-size:var(--t-3xl);line-height:1;color:var(--ink);
  font-variant-numeric:tabular-nums}
.hsub{font-size:var(--t-base);color:var(--ink-2);line-height:1.6}
.hrow{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-4);
  padding:var(--s-3) 0;border-top:1px solid var(--rule-soft)}
.hrow:first-of-type{border-top:0}
.hrow-name{flex:1;min-width:0;font-size:var(--t-base);color:var(--ink);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hrow-meta{font-family:var(--code);font-size:var(--t-2xs);color:var(--muted);white-space:nowrap}
.hbar{height:6px;border-radius:var(--r-pill);background:var(--surface);overflow:hidden}
.hbar i{display:block;height:100%;background:var(--structure)}
.hlink{align-self:flex-start;background:none;border:0;padding:0;cursor:pointer;
  font-size:var(--t-base);color:var(--structure)}
.hlink:hover{text-decoration:underline}
.hcard.due{border-color:var(--warn)}


/* ---------- empty states ----------
   Centred, but capped at a readable measure so the sentence does not run the
   width of a desktop. The mark is the navigation icon for that screen at low
   opacity: recognisable, already in the stylesheet, and not a cartoon of a
   student at a desk — which is the house style of every study app on the
   store and the thing this one is trying not to look like. */
.estate{display:flex;flex-direction:column;align-items:center;text-align:center;
  gap:var(--s-4);padding:var(--s-9) var(--s-6);margin:0 auto;max-width:44ch}
.estate-mark{width:34px;height:34px;color:var(--muted);opacity:.45}
.estate h3{margin:0;font-family:var(--display);font-weight:400;font-size:var(--t-2xl);
  color:var(--ink);letter-spacing:-.01em}
.estate p{margin:0;font-size:var(--t-base);line-height:1.7;color:var(--ink-2);
  text-wrap:pretty}
.estate .btn{margin-top:var(--s-3)}


/* ---------- touch targets ----------
   Deliberately the LAST thing in this file. These rules lost to the base ones the
   first time they were written, because they sat in the coarse block at ~line 1500
   while `.toolbtn{width:32px}` is declared at ~1690 — equal specificity, later
   wins, and the fix silently did nothing. A measurement caught it; reading the
   diff would not have.

   Apple's minimum is 44x44 and it is not a rounding suggestion: 32px is about the
   width of a fingertip, so every tap is a small gamble on the device this app is
   actually aimed at. `.edit` was the worst at 27x21 — and the coarse block was
   already unhiding it on touch, which made it a live target without ever giving it
   a size to be hit at.

   Sizes are min-* with the painted box left alone, so nothing looks different; the
   extra area is padding around the same mark. */
@media (pointer:coarse){
  .toolbtn{width:44px;height:44px}
  .acct{min-height:44px;padding:6px 4px}
  /* the app's primary navigation - Home, Notebook, Classwork, Review, Tasks,
     Marks. These sit in .modes, not .rail-nav; the first selector written here
     matched nothing at all, which a measurement caught and a read-through would
     not have. */
  .modes .mode{min-height:44px}
  .snav .edit,.dp .edit,.topic-head .edit{min-width:44px;min-height:44px;
    display:inline-flex;align-items:center;justify-content:center}
  .nrow{min-height:44px}
  #syncState{min-height:44px}
}
