/* =================================================================
   TEACHER'S PLAN — THE DOCUMENT EDITOR
   Replaces teacher-plan-editor.css.

   The page IS the teacher's plan. Every colour and every face here comes
   from styles/modules/teacher-plan.css, because an author looking at this
   screen is looking at what the teacher will read on the day, and a
   second palette would make the two disagree quietly.

   The editing affordances are the quiet layer on top: greys, hairlines,
   and controls that are visible at rest but never louder than the words.

   Cleaned up in v0.25.56.0 (S7): the editor's own colours (the red of an
   error, the save alarm, the draft offer, the conflict panel, the phone
   frame) are --tpd-* tokens, white ink is --as-on-brand. Each carries its
   value as a fallback: the sheet is also loaded without core.css. The one
   screen !important left is the [hidden] guard, the one shape the
   stylesheet check allows.
   ================================================================= */

/* The root carries .tp-v4.tp-embedded as well as .tpd, because every colour
   token AND every .tp-* descendant rule the document renderer emits — the
   chips, the lane labels, the notation highlight — is declared under .tp-v4
   in styles/modules/teacher-plan.css. .tp-embedded is the modifier that file
   provides for exactly this: the tokens and the descendant rules without the
   full-screen shell that sizes itself to the viewport.

   Hence the three-class selector. .tp-v4.tp-embedded sets height:auto,
   background:transparent and font-size:14px, and a two-class .tpd would lose
   to it on specificity — the editor would render with a see-through
   background over whatever module was behind it. */
.tpd.tp-v4.tp-embedded {
    /* THE PAGE SCROLLS. bug-2026-09-04-060707: "there is no way to scroll
       whole page style like in the mockup. I'm stuck with a small inner
       scrolling display."

       This was `position: fixed` inside a measured viewport offset, with the
       document scrolling in a pane of its own. Two things were wrong with
       that. The offset was computed in DOCUMENT space and applied in VIEWPORT
       space, so opening the editor from a scrolled page put it most of the
       way down the window; and even correct, a fixed box is not what the
       mockup showed. The editor is now as tall as its content, in normal
       flow, and the app's own header scrolls away above it — which is what
       was asked for, and what the paged editor did before this replaced it
       (v0.7.126.0).

       --tpd-top is where the app's own strips end; --tpd-stick adds this
       editor's sticky toolbar and words strip. Both are measured, because
       the toolbar wraps to two rows on a narrow window. */
    display: flex; flex-direction: column;
    min-height: calc(100vh - var(--tpd-top, 0px));
    background: var(--tp-panel-2);
    color: var(--tp-ink);
    font-family: var(--tp-sans);
    --tp-fs: 1;
    --tpd-phone-w: 30%;
    /* teacher-plan.css declares --tp-serif and no sans or mono companion.
       Naming them here rather than repeating the stack at each use. */
    --tp-sans: system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    --tp-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* Black is the set edge in the light theme; on a dark ground it reads as a
   hole punched in the page, so the dark theme borrows the ink colour, which
   is what every other line on that theme is drawn in. */
[data-theme="dark"] .tpd.tp-v4 { --tpd-set-edge: var(--tp-ink); }

/* While the editor is open it is the only thing in the module container.
   The Deck Builder's own header and its other sections are not hidden by
   JavaScript — they simply have no business being between the app's top bar
   and the plan. */
body.tpd-open #moduleContainer > *:not(.tpd) { display: none; }
body.tpd-open #moduleContainer { padding: 0; }

/* And one level further up, for the same reason.
   #navContext is a SIBLING of #moduleContainer, not a child of it, so the
   rule above never reached it: opening a lesson left "Deck Builder ›
   Teacher's Plan  ‹ ›" sitting between the app's top bar and the toolbar —
   41px naming a place the editor already names in its own crumb, above an
   editor that already carries its own Close, with arrows that step you out
   of the plan without asking. Measured against the mockup, hiding it is the
   whole difference: every x, width and height then agrees to the pixel.

   Scoped to body.tpd-open, which destroy() removes, so closing the editor
   brings the Deck Builder's own screen — the coverage grid you pick a lesson
   from — straight back. */
body.tpd-open .container > *:not(#moduleContainer) { display: none; }

/* The `hidden` attribute must win over every display rule in this file.
   The UA stylesheet's [hidden] { display: none } is a bare attribute
   selector, so ANY class rule setting display beats it — .tpd-checkschip
   {display: inline-flex} left a hidden chip on screen, and the mockups hit
   exactly this once already with .pw-flow. One rule, at the top, rather
   than remembering it per class. */
.tpd [hidden] { display: none !important; }

/* ---------- toolbar ---------- */
.tpd-bar {
    position: sticky; top: var(--tpd-top, 0px); z-index: 60;
    flex: 0 0 auto;
    display: flex; align-items: center; gap: .5em; flex-wrap: wrap;
    padding: .5em .8em;
    background: var(--tp-panel);
    border-bottom: 1px solid var(--tp-rule);
    box-shadow: 0 1px 3px rgba(0, 0, 0, .06);
}
.tpd-id { display: flex; align-items: baseline; gap: .45em; min-width: 0; }
.tpd-id b { font-size: .98em; }
.tpd-id em {
    font-style: normal; color: var(--tp-ink-faint); font-size: .85em;
    max-width: 28em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tpd-spacer { flex: 1 1 auto; }

.tpd-btn {
    display: inline-flex; align-items: center; gap: .35em;
    font: inherit; font-size: .84em; font-weight: 600; cursor: pointer;
    padding: .35em .7em;
    background: var(--tp-panel-2); color: var(--tp-ink);
    border: 1px solid var(--tp-rule); border-radius: .45em;
}
.tpd-btn:hover:not(:disabled) { background: var(--tp-panel-3); }
.tpd-btn:disabled { opacity: .4; cursor: default; }
.tpd-btn[aria-pressed="true"] {
    background: var(--tp-teacher-wash); border-color: var(--tp-teacher); color: var(--tp-teacher);
}

.tpd-fs { display: inline-flex; border: 1px solid var(--tp-rule); border-radius: .45em; overflow: hidden; }
.tpd-fs button {
    font: inherit; border: 0; cursor: pointer;
    background: var(--tp-panel-2); color: var(--tp-ink); padding: .2em .55em; line-height: 1.6;
}
.tpd-fs button:nth-child(1) { font-size: .72em; }
.tpd-fs button:nth-child(2) { font-size: .86em; }
.tpd-fs button:nth-child(3) { font-size: 1em; }
.tpd-fs button[aria-pressed="true"] { background: var(--tp-teacher); color: var(--as-on-brand, #fff); }

/* One honest sentence about whether the server has the work. Same
   vocabulary as the card editor's, so an author moving between the two
   does not have to learn the same fact twice. */
/* A button, so a failed write has somewhere to press. At rest it is a
   label that happens to be focusable — hence the default cursor. */
.tpd-save {
    display: inline-flex; align-items: center; gap: .4em;
    font: inherit; font-size: .78em; font-weight: 700; white-space: nowrap;
    padding: .25em .6em; border-radius: 999px; cursor: default;
    border: 1px solid var(--tp-rule); color: var(--tp-ink-faint); background: var(--tp-panel-2);
}
.tpd-save i {
    width: .5em; height: .5em; border-radius: 50%; background: currentColor; flex: 0 0 auto;
}
.tpd-save[data-state="saved"] { color: var(--tp-student); border-color: var(--tp-student); background: var(--tp-student-wash); }
.tpd-save[data-state="saving"] { color: var(--tp-teacher); border-color: var(--tp-teacher); background: var(--tp-teacher-wash); }
.tpd-save[data-state="saving"] i { animation: tpd-pulse 1s ease-in-out infinite; }
.tpd-save[data-state="error"] {
    color: var(--tpd-danger, #B91C1C); border-color: var(--tpd-danger, #B91C1C); background: var(--tpd-danger-wash, #FEE2E2); cursor: pointer;
}
.tpd-save[data-state="error"]:hover { background: var(--tpd-danger-soft, #FCA5A5); }
@keyframes tpd-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }
@media (prefers-reduced-motion: reduce) { .tpd-save[data-state="saving"] i { animation: none; } }

/* ---------- the split ---------- */
.tpd-split {
    flex: 1 1 auto; min-height: 0;
    display: grid;
    grid-template-columns: 1fr 8px var(--tpd-phone-w);
    align-items: start;
}
.tpd[data-phone="off"] .tpd-split { grid-template-columns: 1fr 0 0; }
.tpd[data-phone="off"] .tpd-grip,
.tpd[data-phone="off"] .tpd-phone-pane { display: none; }

/* No scrollbar of its own: the window is the scroller. */
/* THE TRAILING SPACE IS LOAD-BEARING. bug-2026-09-06-195157: "The phone view
   for the teacher does not scroll all the way down preventing us from seeing
   some content."

   The phone shows whatever the document is showing at a reading line
   --tpd-stick + 90px down the window. With 8em (112px) of slack at the foot
   of the plan the page bottoms out before the LAST set can be scrolled up to
   that line, so the line keeps picking the set before it: on Cebuano L1 at
   1272x658 the page ran out with the last set's top at 254 and the line at
   196, and the phone stuck on "Set 6 of 7" — the closing set of the lesson
   was the one part of it a teacher could never see on the phone.

   So the plan ends with enough room to scroll its last line to the reading
   line, and no less than the 8em it had. Measured off --tpd-stick rather than
   guessed, for the same reason the sticky offsets are: the toolbar wraps and
   the words strip opens and closes, and a hard-coded figure would be right
   for one window. @media print resets this to 0, so the page still ends where
   the plan does. */
.tpd-doc-pane {
    overflow: visible;
    padding: 1.4em 1.6em max(8em, calc(100vh - var(--tpd-stick, 6em) - 90px));
    position: relative;
}

/* THE PLAN USES THE PANE IT IS IN. bug-2026-09-07-162758: "the edit view
   should fill up more of the space of its section. Expand it to fill 90% of
   the section horizontally."

   This was a flat `max-width: 60em` — 840px at the embedded 14px — centred in
   a pane that is 1048px of content at the reporter's 1697px window. 208px of
   the pane was margin, and the plan was 49% of the screen. Worse, the grip
   between the panes drags the phone down to 18%, and every pixel that gave
   back became more margin: past about 933px of pane the grip did nothing to
   the document at all.

   NOT a flat `max-width: 90%`, which is a regression everywhere the pane is
   narrow — below 933px the plan already filled it, and 90% would carve a
   10% gutter out of a phone. The two combined can only ever widen: `min(60em,
   100%)` is exactly what it did before, and `max()` takes that or 90%,
   whichever is more. Measured against the CONTAINING BLOCK rather than a
   viewport media query, because the phone pane is draggable to 55% — on a
   1200px window that leaves a ~500px pane while a `max-width: 900px` query
   never fires. */
.tpd-doc { max-width: max(90%, min(60em, 100%)); margin: 0 auto; }

/* Everything ☰ Sets, a check or a correction can navigate to. Without this
   the sticky toolbar and words strip cover whatever was just jumped to. */
.tpd-set, .tpd-band, .tpd-sugg, .tpd-line, .tpd-f {
    scroll-margin-top: calc(var(--tpd-stick, 6em) + 1em);
}

.tpd-grip {
    background: var(--tp-rule); cursor: col-resize;
    position: sticky; top: var(--tpd-stick, 6em);
    height: calc(100vh - var(--tpd-stick, 6em));
}
.tpd-grip::after {
    content: ''; position: absolute; left: 2px; top: 50%; width: 4px; height: 34px;
    margin-top: -17px; border-radius: 3px; background: var(--tp-ink-faint); opacity: .5;
}
.tpd-grip:hover, .tpd-grip[data-drag="true"] { background: var(--tp-teacher); }

/* ---------- editable anywhere ---------- */
.tpd-f {
    border-radius: .3em;
    outline: 1px solid transparent;
    transition: background .12s, outline-color .12s;
}
.tpd[data-editing="off"] .tpd-f { cursor: default; }
.tpd:not([data-editing="off"]) .tpd-f { cursor: text; }
.tpd:not([data-editing="off"]) .tpd-f:hover { background: var(--tp-panel-2); outline-color: var(--tp-rule); }
.tpd .tpd-f:focus {
    background: var(--tp-panel); outline: 2px solid var(--tp-teacher);
    box-shadow: 0 0 0 4px var(--tp-teacher-wash);
}
.tpd-f:empty::before {
    content: attr(data-ph); color: var(--tp-ink-faint); font-style: italic; font-weight: 400;
}
.tpd[data-editing="off"] .tpd-f:empty { display: none; }
.tpd-num { font-variant-numeric: tabular-nums; }

/* ---------- controls ---------- */
.tpd-ic {
    border: 1px solid transparent; background: none; color: var(--tp-ink-faint); cursor: pointer;
    border-radius: .3em; width: 1.7em; height: 1.7em; display: inline-grid; place-items: center;
    font-size: .8em; line-height: 1; padding: 0;
}
.tpd-ic:hover:not(:disabled) { background: var(--tp-panel-3); color: var(--tp-ink); border-color: var(--tp-rule); }
.tpd-ic:disabled { opacity: .3; cursor: default; }
.tpd-ic-del:hover:not(:disabled) { background: var(--tpd-danger-wash, #FEE2E2); color: var(--tpd-danger, #B91C1C); border-color: var(--tpd-danger-soft, #FCA5A5); }
.tpd-tools { display: inline-flex; gap: .1em; vertical-align: middle; }

/* ---------- dragging ---------- */
/* bug-2026-09-07-020159: "we need to add a function to gram and move via
   mouse any element from any point to another point."

   A GRIP, not a draggable row. The page is built out of contenteditable
   fields, and making their container draggable takes text selection with it:
   you could no longer put the caret mid-line by dragging across it. */
.tpd-grip-h {
    cursor: grab;
    /* The one place touch scrolling is given up, so a finger drag moves the
       line instead of the page — and only here, so scrolling the plan with a
       finger anywhere else is untouched. */
    touch-action: none;
    font-size: .9em;
    letter-spacing: -.1em;
}
.tpd-grip-h:active { cursor: grabbing; }

/* While something is in the air. Applied to the ROOT, so the whole document
   can stop competing for the pointer at once: a drag that selects text on its
   way past three paragraphs is a drag nobody can aim. */
.tpd[data-dragging] { cursor: grabbing; }
.tpd[data-dragging] * { user-select: none; }
.tpd[data-dragging] .tpd-f { cursor: grabbing; }

/* What is being carried, including everything nested inside it — a set drags
   its lines, an activity drags its sets. Faded rather than hidden: the plan must
   not reflow under the pointer while the author is aiming at it. */
.tpd [data-dragged] { opacity: .4; }

/* Where it will land. One line, positioned against the editor root, which is
   the nearest positioned ancestor that survives a repaint. */
.tpd-dropline {
    position: absolute; z-index: 70; pointer-events: none;
    height: 0; margin-top: -1.5px;
    border-top: 3px solid var(--tp-teacher);
    border-radius: 2px;
    box-shadow: 0 0 0 1px var(--tp-panel), 0 1px 6px rgba(0, 0, 0, .25);
}
.tpd-dropline::before {
    content: ''; position: absolute; left: -4px; top: -4px;
    width: 8px; height: 8px; border-radius: 50%; background: var(--tp-teacher);
}

/* Reading mode has no controls, so it has no grips either. */
.tpd[data-editing="off"] .tpd-grip-h { display: none; }

.tpd-add {
    font: inherit; font-size: .74em; font-weight: 700; cursor: pointer;
    padding: .2em .55em; border-radius: .35em;
    border: 1px dashed var(--tp-rule); background: none; color: var(--tp-ink-faint);
}
.tpd-add:hover {
    border-style: solid; border-color: var(--tp-teacher);
    color: var(--tp-teacher); background: var(--tp-teacher-wash);
}

/* Reading mode strips every affordance, leaving a page to print or teach
   from. It is the same document — nothing re-renders. */
.tpd[data-editing="off"] .tpd-tools,
.tpd[data-editing="off"] .tpd-add,
.tpd[data-editing="off"] .tpd-gap,
.tpd[data-editing="off"] .tpd-line-tools,
.tpd[data-editing="off"] .tpd-gut,
.tpd[data-editing="off"] .tpd-chip-x,
.tpd[data-editing="off"] .tpd-ghost,
.tpd[data-editing="off"] .tpd-mark,
.tpd[data-editing="off"] .tpd-rowwarn,
.tpd[data-editing="off"] .tpd-words { display: none; }
.tpd[data-editing="off"] .tpd-empty { display: none; }

/* ---------- insert gaps ---------- */
.tpd-gap { display: flex; align-items: center; gap: .5em; margin: .35em 0; }
/* The fade is on the LINE and the CHIP, never on the container: opacity
   on the container makes a stacking context, and the menu is a descendant
   of it — the whole menu would render at 55% with the plan showing
   through it. */
.tpd-gap-line {
    flex: 1 1 auto; height: 1px; background: var(--tp-rule);
    opacity: .6; transition: opacity .12s;
}
.tpd-gap:hover .tpd-gap-line, .tpd-gap:focus-within .tpd-gap-line { opacity: 1; }
.tpd-gap-chip {
    width: 1.5em; height: 1.5em; border-radius: 50%; flex: 0 0 auto; cursor: pointer;
    border: 1px solid var(--tp-rule); background: var(--tp-panel);
    color: var(--tp-ink-faint); font: inherit; font-size: .82em; font-weight: 700; line-height: 1;
    display: grid; place-items: center; padding: 0;
    opacity: .65; transition: opacity .12s, border-color .12s, color .12s, background .12s;
}
.tpd-gap:hover .tpd-gap-chip, .tpd-gap-chip:focus {
    opacity: 1; border-color: var(--tp-teacher); color: var(--tp-teacher); background: var(--tp-teacher-wash);
}

/* ---------- menus ---------- */
.tpd-menuwrap { position: relative; display: inline-block; }
.tpd-menu {
    position: absolute; z-index: 40; left: 0; margin-top: .25em; min-width: 17em;
    background: var(--tp-panel); border: 1px solid var(--tp-rule); border-radius: .5em;
    box-shadow: 0 12px 28px rgba(0, 0, 0, .18); padding: .3em; text-align: left;
    /* One scroll for the menu, not one per section: nested scrollers in a
       popover fight, and the wheel lands in whichever the pointer is over.
       This is the value the menu opens with; _placeMenu() then replaces it
       with the room actually available above or below the chip INSIDE THE
       VIEWABLE BAND — below --tpd-stick, above the foot of the visual
       viewport — and shifts the menu into that band if the anchor leaves it
       outside (bug-2026-09-08-202314, reopened: the first fix measured the
       window, and 62vh of the window is not 62vh of what anyone can see).
       It stays here for print, where nothing measures anything. */
    max-height: min(62vh, 34em); overflow-y: auto;
    /* Reaching the end of the menu must not start scrolling the plan behind
       it. The page is the scroller since v0.8.0.3, so without this the wheel
       chains straight through and the author loses the anchor they were
       aiming at — the second half of the same report. */
    overscroll-behavior: contain;
}
.tpd-menu[hidden] { display: none; }
.tpd-menu-wide { min-width: 24em; }
.tpd-menu-r { left: auto; right: 0; }
.tpd-menu-up { bottom: 100%; margin-bottom: .3em; margin-top: 0; }
.tpd-menu-head {
    font-size: .64em; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
    color: var(--tp-ink-faint); margin: .3em .4em .3em;
}
.tpd-menu-note { font-size: .78em; color: var(--tp-ink-faint); margin: .3em .4em; }
.tpd-menu-sep { border-top: 1px solid var(--tp-rule); margin: .35em 0; }
.tpd-menu button {
    display: flex; align-items: center; gap: .5em; width: 100%; text-align: left;
    font: inherit; font-size: .84em; cursor: pointer;
    border: 0; background: none; border-radius: .35em; padding: .32em .4em; color: var(--tp-ink);
}
.tpd-menu button:hover:not(:disabled) { background: var(--tp-panel-2); }
.tpd-menu button:disabled { opacity: .45; cursor: default; }
.tpd-mk, .tpd-mx { flex: 0 0 auto; }
.tpd-mx { margin-left: auto; font-size: .8em; color: var(--tp-ink-faint); }
.tpd-mk {
    display: inline-grid; place-items: center; width: 1.5em; height: 1.5em;
    border-radius: .35em; font-size: .78em; font-weight: 800;
}
.tpd-m-teacher { background: var(--tp-teacher-wash); color: var(--tp-teacher); }
.tpd-m-student { background: var(--tp-student-wash); color: var(--tp-student); }
.tpd-m-note    { background: var(--tp-note-wash);    color: var(--tp-note); }
.tpd-m-do      { background: var(--tp-do-wash);      color: var(--tp-do); }

/* ---------- overview ---------- */
.tpd-overview h1 { font-family: var(--tp-serif); font-size: 1.85em; margin: 0 0 .05em; line-height: 1.15; }
.tpd-sub { color: var(--tp-ink-faint); font-size: 1.02em; margin: 0 0 .3em; }
.tpd-lessonno { font-size: .8em; color: var(--tp-ink-faint); margin: 0 0 1.4em; }
.tpd-ovblk { margin-bottom: 1.1em; }
.tpd-ovblk > .tp-eyebrow { display: flex; align-items: center; gap: .5em; flex-wrap: wrap; }

/* WHAT THE TEACHER WILL NOT SEE. bug-2026-09-06-202719: "the line 'Reading
   the plan' cannot be deleted or moved."

   plan-render.js draws an overview block only while it has content, so an
   empty one is already off the teacher's page — the editor just never said
   so, and an author looking at "Reading the plan · 0" with a delete that
   does nothing has no way to tell a placeholder from a section shipped
   blank. It says so here, in the note colour the rest of this file uses for
   a fact about the plan rather than a fault in it. */
.tpd-offpage-flag { display: none; }
.tpd:not([data-editing="off"]) .tpd-ovblk[data-offpage="true"] > .tp-eyebrow > .tpd-offpage-flag {
    display: inline;
    font-size: .78em; font-weight: 700; color: var(--tp-ink-faint);
    background: var(--tp-panel-2); border: 1px dashed var(--tp-rule);
    border-radius: 999px; padding: 0 .5em; text-transform: none; letter-spacing: 0;
}
/* Reading mode is the page as it will be taught from, and an empty block is
   not on that page at all — neither is a note about it. */
/* So the flag rule above names .tpd:not([data-editing="off"]); without it the
   base rule's display: none stands (v0.25.56.0, was an !important here). */
.tpd-goals {
    background: var(--tp-teacher-wash); border: 1px solid var(--tp-teacher);
    border-radius: .7em; padding: .85em 1em;
}
.tpd-goals > .tp-eyebrow { color: var(--tp-teacher); }
.tpd-goal { display: flex; align-items: flex-start; gap: .5em; margin-bottom: .3em; }
.tpd-goal-b { color: var(--tp-teacher); line-height: 1.5; font-size: .8em; }
.tpd-goal-f { flex: 1 1 auto; font-size: .92em; line-height: 1.5; }
/* The classroom setting is a list of authored lines, so its rows are the
   goal's rows — without the wash, which is the goals block's own signal. */
.tpd-setline { display: flex; align-items: flex-start; gap: .5em; margin-bottom: .3em; }
.tpd-setline-b { color: var(--tp-ink-faint); line-height: 1.5; font-size: .8em; }
.tpd-setline-f { flex: 1 1 auto; font-size: .92em; line-height: 1.5; }
/* Authors put line breaks in these; the teacher has to see them. */
.tpd-setline-f .tpd-f { white-space: pre-wrap; }
.tpd-chipset { display: flex; flex-wrap: wrap; gap: .3em; align-items: center; }
.tpd-chipwrap { display: inline-flex; align-items: stretch; }
.tpd-chipwrap .tp-chip { border-radius: 0; }
.tpd-chipwrap[data-s] .tpd-mark { border-radius: .35em 0 0 .35em; border-right: 0; }
.tpd-chip-x {
    border: 1px solid var(--tp-rule); border-left: 0; background: var(--tp-panel-2);
    color: var(--tp-ink-faint); font-size: .7em; padding: 0 .35em;
    border-radius: 0 .35em .35em 0; width: auto; height: auto;
}
.tpd-chip-x:hover { background: var(--tpd-danger-wash, #FEE2E2); color: var(--tpd-danger, #B91C1C); }
.tpd[data-editing="off"] .tpd-chipwrap .tp-chip { border-radius: .35em; }
.tpd-legrow { display: flex; gap: .55em; align-items: baseline; margin-bottom: .3em; font-size: .85em; }
.tpd-legrow code { flex: 0 0 7em; color: var(--tp-teacher); }
.tpd-legrow-m { flex: 1 1 auto; }

/* ---------- word marks ---------- */
/* Shape and lesson number carry the meaning; colour only reinforces it,
   so the page still reads in greyscale and on a projector. */
.tpd-mark {
    display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
    font: inherit; font-size: .7em; font-weight: 800; line-height: 1; padding: 0 .32em;
    border: 1px solid var(--tp-rule); border-radius: .3em; background: var(--tp-panel-2);
    font-variant-numeric: tabular-nums; white-space: nowrap; flex: 0 0 auto;
    /* AN EXPLICIT BOX, BOTH WAYS, BECAUSE THE GLYPHS ARE NOT OURS.
       ● ✕ ? ◀ ▶ come out of whatever the platform falls back to, and every
       platform falls back somewhere different. The height was pinned first,
       after the box around ✕ measured 22px where the one around ● measured
       19 and the taller mark shunted its whole chip row. The width has the
       same fault and it is the more visible one: a mark is only as wide as
       its glyph, so a run of marks drawn from two fallback fonts is a run of
       boxes at two widths, and the strip reads as ragged rather than as a
       column of states.
       min-width squares it off at the pinned height, so ● and ✕ and ? are
       one size and ▶2 grows only for the digit it actually adds. */
    height: 1.7em; min-width: 1.7em; box-sizing: border-box;
    /* AND THEY MUST BE DRAWN AS TEXT, NOT AS EMOJI.
       ▶ U+25B6 and ✕ U+2715 have emoji presentations. Windows resolves them
       through Segoe UI Emoji and Android through Noto Color Emoji, which
       hands back a wide, full-colour picture where this wants a small
       monochrome symbol in the mark's own state colour — a different size
       and a different look on those machines, from the same stylesheet.
       The variation selector belongs in the markup (plan-doc-editor.js
       appends U+FE0E); this is the same instruction for engines that read
       it from CSS. */
    font-variant-emoji: text;
}
.tpd-mark:hover { box-shadow: 0 0 0 2px var(--tp-teacher-wash); }
.tpd-mark[data-s="same"] { color: var(--tp-student); border-color: var(--tp-student); background: var(--tp-student-wash); }
.tpd-mark[data-s="before"] { color: var(--tp-ink-soft); }
.tpd-mark[data-s="after"] { color: var(--tp-note); border-color: var(--tp-note); background: var(--tp-note-wash); }
.tpd-mark[data-s^="fuzzy-"] { border-style: dashed; background: var(--tp-panel-2); }
.tpd-mark[data-s="fuzzy-same"] { color: var(--tp-student); border-color: var(--tp-student); }
.tpd-mark[data-s="fuzzy-after"] { color: var(--tp-note); border-color: var(--tp-note); }
.tpd-mark[data-s^="link-"] { color: var(--tp-teacher); border-color: var(--tp-teacher); background: var(--tp-teacher-wash); }
.tpd-mark[data-s="ask"] { color: var(--as-on-brand, #fff); background: var(--tp-note); border-color: var(--tp-note); }
.tpd-mark[data-s="gone"] { color: var(--as-on-brand, #fff); background: var(--tpd-danger, #B91C1C); border-color: var(--tpd-danger, #B91C1C); }
.tpd-mark[data-s="none"] { color: var(--tpd-danger, #B91C1C); border-color: var(--tpd-danger, #B91C1C); background: var(--tpd-danger-wash, #FEE2E2); border-style: dashed; }

/* ---------- where an edited heading lands ---------- */
.tpd-scope {
    display: flex; align-items: center; gap: .4em; flex-wrap: wrap;
    font-size: .74em; color: var(--tp-ink-faint); margin: .3em 0 1em;
}
.tpd-scope-lbl { font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
.tpd-scope-b {
    font: inherit; font-size: 1em; font-weight: 650; cursor: pointer;
    padding: .2em .6em; border-radius: 999px;
    border: 1px solid var(--tp-rule); background: var(--tp-panel-2); color: var(--tp-ink-soft);
}
.tpd-scope-b:hover { background: var(--tp-panel-3); }
.tpd-scope-b[aria-pressed="true"] {
    background: var(--tp-teacher); border-color: var(--tp-teacher); color: var(--as-on-brand, #fff);
}
.tpd-scope-n {
    font: inherit; font-size: 1em; font-weight: 700; cursor: pointer;
    padding: .2em .6em; border-radius: 999px; margin-left: .3em;
    border: 1px solid var(--tp-note); background: var(--tp-note-wash); color: var(--tp-note);
}
.tpd-scope-n:hover { background: var(--tp-note); color: var(--as-on-brand, #fff); }

/* A heading is a field like any other, but the ONE with a blast radius wider
   than the line it sits on. Marked at rest, and marked differently once this
   lesson words it its own way — a per-lesson override is invisible on every
   other lesson, so the lesson that has one has to say so. */
.tpd-f[data-edit^="label/"] {
    border-bottom: 1px dotted var(--tp-ink-faint);
}
.tpd[data-labelscope="lesson"] .tpd-f[data-edit^="label/"] {
    border-bottom-color: var(--tp-teacher);
}
.tpd-f[data-edit^="label/"][data-own="1"] {
    border-bottom: 1px solid var(--tp-teacher); color: var(--tp-teacher);
}
.tpd[data-editing="off"] .tpd-scope { display: none; }
.tpd[data-editing="off"] .tpd-f[data-edit^="label/"] { border-bottom: 0; color: inherit; }

/* The three keys the teaching screen reads and this page does not. */
.tpd-offpage {
    margin: 1.4em 0 .4em; font-size: .84em;
    border-top: 1px solid var(--tp-rule); padding-top: .7em;
}
.tpd-offpage > summary {
    cursor: pointer; list-style: none; color: var(--tp-ink-faint);
    font-size: .8em; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
}
.tpd-offpage > summary::-webkit-details-marker { display: none; }
.tpd-offpage > summary::before { content: "▸ "; }
.tpd-offpage[open] > summary::before { content: "▾ "; }
.tpd-offpage > summary:hover { color: var(--tp-teacher); }
.tpd-wrow {
    display: flex; gap: .7em; align-items: baseline;
    padding: .25em 0; border-bottom: 1px dotted var(--tp-rule);
}
.tpd-wkey { flex: 0 0 22em; max-width: 45%; color: var(--tp-ink-faint); font-size: .92em; }
.tpd-wval { flex: 1 1 auto; min-width: 0; }
@media (max-width: 700px) {
    .tpd-wrow { flex-direction: column; gap: 0; }
    .tpd-wkey { flex: 0 0 auto; max-width: none; }
}

/* A lane heading is plan wording as much as a section heading is, so it wears
   the same underline — but at the lane's own size, which is small and
   letter-spaced, so the dots have to be tighter than the section rule. */
.tp-lane-label .tpd-f[data-edit^="label/"],
.tpd-emptyset .tpd-f[data-edit^="label/"] {
    border-bottom-style: dotted;
}
.tpd-emptyset { font-style: italic; }

/* ---------- the Sets sheet, and the checks ---------- */
.tpd-checkschip {
    font: inherit; font-size: .78em; font-weight: 700; cursor: pointer; white-space: nowrap;
    display: inline-flex; align-items: center; gap: .3em;
    padding: .25em .6em; border-radius: 999px;
    border: 1px solid var(--tp-rule); background: var(--tp-panel-2); color: var(--tp-ink-soft);
}
.tpd-checkschip b {
    display: inline-grid; place-items: center; min-width: 1.5em; height: 1.5em;
    border-radius: 999px; color: var(--as-on-brand, #fff); font-size: .9em; padding: 0 .3em;
}
.tpd-chk-err { background: var(--tpd-danger, #B91C1C); }
.tpd-chk-warn { background: var(--tp-note); }

.tpd-sheetwrap { position: absolute; inset: 0; z-index: 950; }
.tpd-scrim { position: absolute; inset: 0; background: rgba(0, 0, 0, .35); border: 0; }
.tpd-sheet {
    position: absolute; top: 0; bottom: 0; left: 0; width: min(28em, 88vw);
    display: flex; flex-direction: column;
    background: var(--tp-panel); border-right: 1px solid var(--tp-rule);
    box-shadow: 4px 0 24px rgb(0 0 0 / 22%);
}
.tpd-sheet-h {
    flex: 0 0 auto; display: flex; align-items: center; gap: .5em;
    padding: .6em .8em; border-bottom: 1px solid var(--tp-rule);
}
.tpd-sheet-body { flex: 1 1 auto; overflow-y: auto; padding: .6em .8em 2em; }
.tpd-sheet-body h4 {
    font-size: .66em; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
    color: var(--tp-ink-faint); margin: .9em 0 .4em;
}
.tpd-sheet-body h4:first-child { margin-top: 0; }
.tpd-chks { display: grid; gap: .2em; }
.tpd-chk {
    display: block; width: 100%; text-align: left; font: inherit; font-size: .84em;
    padding: .3em .5em .3em 1.4em; border-radius: .35em; position: relative;
    border: 1px solid transparent; background: none; color: var(--tp-ink); cursor: pointer;
}
.tpd-chk::before {
    content: ""; position: absolute; left: .5em; top: .75em;
    width: .5em; height: .5em; border-radius: 50%; background: var(--tp-note);
}
.tpd-chk[data-sev="error"]::before { background: var(--tpd-danger, #B91C1C); }
.tpd-chk:hover { background: var(--tp-panel-2); border-color: var(--tp-rule); }
.tpd-chk-static { cursor: default; color: var(--tp-ink-soft); }
.tpd-chk-static:hover { background: none; border-color: transparent; }
.tpd-chk-clean { font-size: .84em; color: var(--tp-student); font-weight: 650; margin: 0; }

.tpd-sheet-ev { margin-bottom: .6em; }
.tpd-sheet-evb, .tpd-sheet-set {
    display: flex; align-items: baseline; gap: .5em; width: 100%; text-align: left;
    font: inherit; cursor: pointer; padding: .3em .5em; border-radius: .35em;
    border: 1px solid transparent; background: none; color: var(--tp-ink);
}
.tpd-sheet-evb { font-size: .9em; background: var(--tp-panel-2); }
.tpd-sheet-set { font-size: .85em; padding-left: 1.6em; }
.tpd-sheet-evb:hover, .tpd-sheet-set:hover { background: var(--tp-teacher-wash); border-color: var(--tp-teacher); }
.tpd-sheet-evb em, .tpd-sheet-set em { font-style: normal; color: var(--tp-ink-faint); font-size: .85em; }
.tpd-sheet-n {
    flex: 0 0 auto; min-width: 1.5em; font-size: .78em; font-weight: 800;
    color: var(--tp-ink-faint); font-variant-numeric: tabular-nums;
}
.tpd-sheet-flag { margin-left: auto; font-size: .8em; color: var(--tp-note); font-weight: 700; }

/* ---------- which plan is open ---------- */
.tpd-where { display: inline-flex; gap: .35em; align-items: center; }
.tpd-where-sel {
    font: inherit; font-size: .82em; font-weight: 600; cursor: pointer;
    max-width: 14em; padding: .2em .4em;
    border: 1px solid var(--tp-rule); border-radius: .4em;
    background: var(--tp-panel-2); color: var(--tp-ink);
}
.tpd-where-sel:disabled { opacity: .5; cursor: progress; }

/* ---------- corrections a teacher flagged ---------- */
.tpd-suggchip {
    font: inherit; font-size: .78em; font-weight: 700; cursor: pointer; white-space: nowrap;
    padding: .25em .6em; border-radius: 999px;
    border: 1px solid var(--tp-note); background: var(--tp-note-wash); color: var(--tp-note);
}
.tpd-suggchip:hover { filter: brightness(.97); }

.tpd-suggs { display: grid; gap: .5em; margin: .5em 0 .7em; }
.tpd-sugg {
    border: 1px solid var(--tp-note); border-left-width: 4px; border-radius: .5em;
    background: var(--tp-note-wash); padding: .55em .7em; font-size: .9em;
}
.tpd-sugg[data-state="drift"] { border-color: var(--tp-ink-faint); background: var(--tp-panel-2); }
.tpd-sugg-who { margin: 0 0 .35em; font-size: .82em; font-weight: 700; color: var(--tp-ink-soft); }
.tpd-sugg-warn { font-weight: 700; color: var(--tp-note); }
.tpd-sugg-diff { display: grid; gap: .15em; }
.tpd-sugg-was {
    margin: 0; text-decoration: line-through; color: var(--tp-ink-faint);
}
.tpd-sugg-new { margin: 0; font-weight: 650; color: var(--tp-ink); }
.tpd-sugg-none { margin: 0; font-style: italic; color: var(--tp-ink-faint); }
.tpd-sugg-note {
    margin: .35em 0 0; font-size: .88em; color: var(--tp-ink-soft);
    border-left: 2px solid var(--tp-rule); padding-left: .5em;
}
.tpd-sugg-acts { display: flex; gap: .4em; align-items: center; flex-wrap: wrap; margin-top: .45em; }
.tpd-sugg-acts .tpd-btn { font-size: .78em; padding: .2em .6em; }
.tpd-btn-primary {
    background: var(--tp-teacher); border-color: var(--tp-teacher); color: var(--as-on-brand, #fff);
}
.tpd-btn-primary:hover:not(:disabled) { filter: brightness(1.08); background: var(--tp-teacher); }
.tpd-sugg-hint { font-size: .78em; color: var(--tp-ink-faint); }
.tpd-sugg-flash { animation: tpd-flash 1.1s ease-out 1; }
@keyframes tpd-flash {
    0% { box-shadow: 0 0 0 0 var(--tp-note); }
    30% { box-shadow: 0 0 0 5px var(--tp-note-wash); }
    100% { box-shadow: 0 0 0 0 transparent; }
}
@media (prefers-reduced-motion: reduce) { .tpd-sugg-flash { animation: none; } }

/* The heading a moved run disappeared into (bug-2026-09-08-211017). A lane
   heading is derived per RUN, so a run dropped beside a run of its own kind
   leaves one heading where there were two — correct, and invisible. This is
   what says which heading now speaks for those lines. Same flash as a
   suggestion lands with, held longer: nothing moved on screen to draw the
   eye, so it has to survive the moment the author looks up. */
.tpd-merged { animation: tpd-flash 2.2s ease-out 1; border-radius: .35em; }
@media (prefers-reduced-motion: reduce) {
    .tpd-merged { animation: none; box-shadow: 0 0 0 3px var(--tp-note-wash); }
}

/* The visible half of _announce(). .tpd-live is clipped to a pixel for screen
   readers; this is for everyone else, and only for the things an author
   cannot see happen. Fixed to the window because the page is the scroller
   and a message pinned to the document would scroll away from the eye that
   needs it. */
.tpd-toast {
    /* Bottom LEFT, not centred: centred puts it half over the teacher's phone,
       which is the one thing on this screen the author is checking against. */
    position: fixed; z-index: 960; left: calc(var(--shell-left, 0px) + 1.5em); bottom: 1.4em;
    max-width: min(34em, calc(100vw - 3em)); margin: 0;
    padding: .6em .9em; border-radius: .5em;
    background: var(--tp-panel); color: var(--tp-ink);
    border: 1px solid var(--tp-note); border-left: 4px solid var(--tp-note);
    box-shadow: 0 12px 28px rgba(0, 0, 0, .22);
    font-size: .86em; line-height: 1.35; text-align: left;
}
@media print { .tpd-toast { display: none; } }

.tpd-flag {
    font: inherit; font-size: .8em; line-height: 1; cursor: pointer; flex: 0 0 auto;
    padding: .1em .3em; border-radius: .3em;
    border: 1px solid var(--tp-note); background: var(--tp-note-wash); color: var(--tp-note);
}
.tpd-flag:hover { background: var(--tp-note); color: var(--as-on-brand, #fff); }

/* ---------- the words panel ---------- */
/* Three views over one strip: the lesson's Sentence Words (read only —
   they belong to Sentence Tools), the word cards the deck teaches here,
   and the two lists against each other. */
.tpd-words {
    position: sticky; top: var(--tpd-words-top, 3em); z-index: 55;
    flex: 0 0 auto;
    background: var(--tp-panel);
    border-bottom: 1px solid var(--tp-rule);
    max-height: 42vh; overflow-y: auto;
}
.tpd-words-head {
    display: flex; align-items: center; gap: .55em; flex-wrap: wrap;
    padding: .45em .8em;
    position: sticky; top: 0; z-index: 2;
    background: var(--tp-panel); border-bottom: 1px solid transparent;
}
.tpd-words[data-open="true"] .tpd-words-head { border-bottom-color: var(--tp-rule); }
.tpd-words-toggle {
    display: inline-flex; align-items: center; gap: .4em;
    font: inherit; font-size: .8em; font-weight: 800; letter-spacing: .04em;
    text-transform: uppercase; color: var(--tp-ink-soft);
    background: none; border: 0; cursor: pointer; padding: .2em 0;
}
.tpd-caret { transition: transform .15s ease; font-size: .85em; color: var(--tp-ink-faint); }
.tpd-words[data-open="false"] .tpd-caret { transform: rotate(-90deg); }
.tpd-words[data-open="false"] .tpd-words-body { display: none; }
.tpd-words-body { padding: 0 .8em .7em; }

.tpd-tabs { display: inline-flex; border: 1px solid var(--tp-rule); border-radius: .45em; overflow: hidden; }
.tpd-tabs button {
    font: inherit; font-size: .74em; font-weight: 700; cursor: pointer;
    border: 0; border-right: 1px solid var(--tp-rule);
    background: var(--tp-panel-2); color: var(--tp-ink-soft); padding: .3em .65em;
}
.tpd-tabs button:last-child { border-right: 0; }
.tpd-tabs button:hover { background: var(--tp-panel-3); }
.tpd-tabs button[aria-pressed="true"] { background: var(--tp-teacher); color: var(--as-on-brand, #fff); }
.tpd-tabs button.tpd-tab-warn b { color: var(--tp-note); }
.tpd-tabs button.tpd-tab-warn[aria-pressed="true"] b { color: var(--as-on-brand, #fff); }

.tpd-find {
    display: inline-flex; align-items: center; gap: .35em;
    border: 1px solid var(--tp-rule); border-radius: .45em;
    background: var(--tp-panel-2); padding: 0 .5em;
}
.tpd-find input {
    font: inherit; font-size: .82em; border: 0; background: none;
    color: inherit; width: 11em; padding: .3em 0;
}
.tpd-find input:focus { outline: none; }

.tpd-wv { display: none; }
.tpd-words[data-view="bank"] .tpd-wv[data-wv="bank"],
.tpd-words[data-view="cards"] .tpd-wv[data-wv="cards"],
.tpd-words[data-view="diff"] .tpd-wv[data-wv="diff"] { display: block; }

.tpd-flow { display: flex; flex-wrap: wrap; gap: .3em; align-items: center; }
.tpd-gtag {
    font-size: .64em; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
    color: var(--tp-ink-faint); margin-left: .8em; align-self: center;
}
.tpd-gtag:first-child { margin-left: 0; }

/* Words carried in from earlier lessons.
   Separated by a rule and set in the plan's own quieter colour, because they
   are a different KIND of thing from the words above them: this lesson's
   Sentence Words are owned by the sentence tools and read-only here, while
   these are the plan's own list of what it still expects the student to
   know. Same chips, so they can be clicked into a line the same way. */
.tpd-carried {
    margin-top: .55em; padding-top: .5em;
    border-top: 1px dashed var(--tp-rule);
}
.tpd-carried .tpd-flow + .tpd-flow { margin-top: .3em; }
.tpd-carried .tpd-w { background: transparent; }
.tpd-gtag-carried { color: var(--tp-shape); }

/* "+ Add from earlier lessons" and its picker (v0.25.78.0). The words the
   author picks join the plan's own carried list; each chip gets an × that
   takes it back out — the pill's bargain, shown on hover or focus. */
.tpd-carried { position: relative; }
.tpd-carried-head { margin-bottom: .35em; }
.tpd-carried-empty { display: flex; flex-wrap: wrap; align-items: center; gap: .6em; }
.tpd-carried-none { font-size: .85em; color: var(--tp-ink-faint); }
.tpd-carried-x {
    display: none; padding: 0 .3em; border: 0; background: none; cursor: pointer;
    color: var(--tp-ink-faint); font-size: 1em; line-height: 1;
}
.tpd-carried .tpd-bw:hover .tpd-carried-x,
.tpd-carried .tpd-bw:focus-within .tpd-carried-x { display: inline; }
.tpd-carried-x:hover, .tpd-carried-x:focus { color: var(--tpd-remove-hover, var(--tp-ink)); }

.tpd-pick.tpd-pick-carry { width: 34em; max-width: min(34em, 92vw); }
.tpd-carry-filters {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .4em .5em;
    margin-bottom: .5em;
}
.tpd-carry-filters label {
    display: flex; flex-direction: column; gap: .15em;
    font-size: .8em; color: var(--tp-ink-soft);
}
.tpd-carry-filters select,
.tpd-carry-filters input {
    font: inherit; font-size: .95em; padding: .3em .4em; min-width: 0;
    border: 1px solid var(--tp-rule); border-radius: 6px;
    background: var(--tp-panel-2); color: var(--tp-ink);
}
.tpd-carry-filters input { grid-column: 1 / -1; font-size: .9em; }
.tpd-carry-list {
    display: flex; flex-wrap: wrap; gap: .3em;
    max-height: 18em; overflow-y: auto; padding: .1em 0;
}
.tpd-carry-opt {
    display: inline-flex; align-items: baseline; gap: .35em;
    padding: .25em .55em; border: 1px solid var(--tp-rule); border-radius: 999px;
    background: var(--tp-panel); color: var(--tp-ink); cursor: pointer;
    font: inherit; font-size: .88em;
}
.tpd-carry-opt i { color: var(--tp-ink-faint); font-style: normal; }
.tpd-carry-opt:hover, .tpd-carry-opt:focus-visible { border-color: var(--tp-teacher); }
.tpd-carry-opt[aria-pressed="true"] {
    border-color: var(--tp-teacher); background: var(--tp-teacher-wash);
}
.tpd-carry-l { font-size: .8em; color: var(--tp-shape); }
.tpd-carry-foot {
    display: flex; align-items: center; justify-content: flex-end; gap: .5em;
    margin-top: .6em; padding-top: .6em; border-top: 1px solid var(--tp-rule);
}
.tpd-carry-count { margin-right: auto; font-size: .85em; color: var(--tp-ink-soft); }
@media (max-width: 600px) {
    .tpd-carry-filters { grid-template-columns: 1fr; }
}

.tpd-bw { display: inline-flex; align-items: stretch; }
.tpd-bw .tpd-mark { border-radius: .35em 0 0 .35em; border-right: 0; }
.tpd-w {
    font: inherit; font-size: .84em; cursor: pointer;
    padding: .18em .5em; border-radius: .35em;
    border: 1px solid var(--tp-rule); background: var(--tp-panel-2); color: var(--tp-ink);
}
.tpd-bw .tpd-w { border-radius: 0 .35em .35em 0; }
.tpd-w:hover { background: var(--tp-teacher); border-color: var(--tp-teacher); color: var(--as-on-brand, #fff); }
.tpd-w-ghost { opacity: .5; border-style: dashed; }
.tpd-w-aff {
    font-family: var(--tp-mono);
    background: var(--tp-do-wash); color: var(--tp-ink-soft); cursor: default;
}
.tpd-w-aff:hover { background: var(--tp-do-wash); border-color: var(--tp-rule); color: var(--tp-ink-soft); }
/* Faded, not removed: a filtered word keeps its place, so the row does not
   reflow under the pointer on every keystroke in the find box. */
.tpd-dim { opacity: .18; }
.tpd-hint { font-size: .75em; color: var(--tp-ink-faint); margin: .5em 0 0; }
.tpd-affixbox { margin-top: .6em; border-top: 1px solid var(--tp-rule); padding-top: .6em; }

.tpd-diffbar { display: flex; align-items: center; gap: .6em; flex-wrap: wrap; font-size: .78em;
    color: var(--tp-ink-faint); margin-bottom: .6em; }
.tpd-askpill {
    font-size: 1em; color: var(--tp-note); background: var(--tp-note-wash);
    border: 1px solid var(--tp-note); border-radius: 999px; padding: .1em .7em;
}
.tpd-okpill { color: var(--tp-student); }
.tpd-diff { display: grid; grid-template-columns: 1fr 1fr; gap: 1em; }
@media (max-width: 900px) { .tpd-diff { grid-template-columns: 1fr; } }
.tpd-diff h4 {
    font-size: .66em; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
    color: var(--tp-ink-faint); margin: 0 0 .4em;
}
.tpd-drow { display: flex; align-items: center; gap: .5em; padding: .28em .4em;
    border-radius: .35em; font-size: .85em; }
.tpd-drow:nth-of-type(even) { background: var(--tp-panel-2); }
.tpd-drow b { flex: 0 0 auto; }
.tpd-drow em { font-style: normal; color: var(--tp-ink-faint); font-size: .88em; }
.tpd-drow .tpd-btn { font-size: .78em; padding: .15em .55em; }
.tpd-ok { font-size: .85em; color: var(--tp-student); font-weight: 650; }
.tpd-dupe {
    grid-column: 1 / -1; font-size: .82em; padding: .5em .7em; border-radius: .4em;
    background: var(--tp-note-wash); border: 1px solid var(--tp-note); color: var(--tp-ink);
}
.tpd-diffnote { grid-column: 1 / -1; font-size: .76em; color: var(--tp-ink-faint); line-height: 1.55; }
.tpd-legendline {
    font-size: .76em; color: var(--tp-ink-soft); margin-top: .7em;
    display: flex; gap: 1.1em; flex-wrap: wrap; align-items: center;
}
.tpd-legendline > span { display: inline-flex; align-items: center; gap: .35em; }
/* In the legend a mark is a sample, not a control. */
.tpd-legendline .tpd-mark { cursor: default; }

/* ---------- the linker ---------- */
/* Fixed, because the marks that open it live inside a scrolling pane and
   inside the words panel — an absolutely-positioned child of either is
   clipped by it. It stays a child of .tpd so the tokens below resolve;
   position: fixed answers to the viewport, so no ancestor clips it. */
.tpd-pick {
    position: fixed; z-index: 1000; width: 30em; max-width: 92vw; max-height: 70vh; overflow-y: auto;
    background: var(--tp-panel); color: var(--tp-ink);
    border: 1px solid var(--tp-rule); border-radius: .6em;
    box-shadow: 0 18px 44px rgba(0, 0, 0, .28); padding: .7em .8em;
    font-family: var(--tp-sans);
}
.tpd-pick h4 { margin: 0 0 .1em; font-size: .95em; }
.tpd-pick-sub { margin: 0 0 .6em; font-size: .8em; color: var(--tp-ink-faint); }
.tpd-pick-search {
    width: 100%; font: inherit; font-size: .85em; padding: .35em .5em; margin-bottom: .5em;
    border: 1px solid var(--tp-rule); border-radius: .4em;
    background: var(--tp-panel-2); color: var(--tp-ink);
}
.tpd-pick-opt {
    display: flex; align-items: center; gap: .5em; width: 100%; text-align: left; cursor: pointer;
    font: inherit; font-size: .86em;
    border: 1px solid transparent; background: none; border-radius: .4em; padding: .35em .45em;
}
.tpd-pick-opt:hover { background: var(--tp-panel-2); border-color: var(--tp-rule); }
.tpd-pick-opt[aria-pressed="true"] { background: var(--tp-teacher-wash); border-color: var(--tp-teacher); }
.tpd-pick-opt b { flex: 0 0 auto; }
.tpd-pick-opt em { font-style: normal; color: var(--tp-ink-faint); font-size: .9em; }
.tpd-pick-score {
    margin-left: auto; font-size: .8em; font-weight: 800; font-variant-numeric: tabular-nums;
    color: var(--tp-ink-faint); flex: 0 0 auto;
}
.tpd-pick-bar { width: 3.4em; height: .4em; border-radius: 999px; background: var(--tp-panel-3);
    overflow: hidden; flex: 0 0 auto; }
.tpd-pick-bar i { display: block; height: 100%; background: var(--tp-student); }
.tpd-pick-sep { border-top: 1px solid var(--tp-rule); margin: .5em 0; }
.tpd-pick-none b { color: var(--tpd-danger, #B91C1C); }
.tpd-pick-hint { font-size: .76em; color: var(--tp-ink-faint); margin: .5em 0 0; }

.tpd-rowwarn {
    font-size: .78em; font-weight: 700; color: var(--tp-note);
    background: var(--tp-note-wash); border: 1px solid var(--tp-note);
    border-radius: 999px; padding: 0 .5em; text-transform: none; letter-spacing: 0;
}
.tpd-rowwarn[data-n="0"] { display: none; }
.tpd-ghost {
    font: inherit; font-size: .8em; font-weight: 600; cursor: pointer;
    padding: .15em .5em; border-radius: .35em;
    border: 1px dashed var(--tp-ink-faint); background: none; color: var(--tp-ink-faint);
}
.tpd-ghost:hover {
    border-style: solid; border-color: var(--tp-student);
    color: var(--tp-student); background: var(--tp-student-wash);
}

/* ---------- folding ---------- */
/* The chevron sits at the HEAD of the band, before the ACTIVITY chip, because
   that is where a disclosure lives in every outline anyone has used. It is a
   real button with aria-expanded, and the folded region is `hidden` rather
   than clipped, so a screen reader agrees with the screen.

   The one honest cost: the browser's own Ctrl+F will not find text inside a
   folded activity. That is true of every collapse, and Unfold all is one
   press away in the toolbar. */
.tpd-chev {
    flex: 0 0 auto; font-size: .8em; line-height: 1; min-width: 1.9em;
}
/* What a folded band is hiding. Hiding the detail is the point; hiding that
   there IS detail is how an author loses a whole activity without noticing. */
.tpd-band-sum {
    flex: 0 0 auto; font-size: .78em; opacity: .85;
    font-variant-numeric: tabular-nums; white-space: nowrap;
}
.tpd-band[data-collapsed="true"] { margin-bottom: .55em; }

/* ---------- Instruksyon sets ---------- */
/* READ, NEVER GRADED — so it has to be distinguishable from the sets around
   it at a glance while scrolling a twenty-one-set lesson. Dashed rather than
   solid, and in the note amber rather than the set edge: this is a briefing
   in the margin of the lesson, not another teachable unit.
   The chip says the word instead of a number, because the set carries no
   number — numbering it would put the editor's count out of step with the
   teaching screen's "Set 3 of 7" the moment one appears mid-activity. */
.tpd-set-instr {
    border-style: dashed; border-color: var(--tp-note);
    background: var(--tp-note-wash);
}
.tpd-set-n-instr {
    background: var(--tp-note); color: var(--tp-panel);
}

/* ---------- activity bands ---------- */
.tpd-band {
    display: flex; align-items: center; gap: .6em; flex-wrap: wrap;
    padding: .5em .85em; margin: 1.8em 0 .2em;
    background: var(--tp-teacher); color: var(--as-on-brand, #fff); border-radius: .5em;
    /* Below the toolbar and the words strip, which stick above it. `top: 0`
       parked the band under both of them. */
    position: sticky; top: var(--tpd-stick, 6em); z-index: 20;
    scroll-margin-top: var(--tpd-stick, 6em);
    box-shadow: 0 2px 8px rgba(0, 0, 0, .14);
}
.tpd-band .tpd-f:hover { background: rgba(255, 255, 255, .15); outline-color: rgba(255, 255, 255, .4); }
.tpd-band .tpd-f:focus { background: var(--tpd-band-field, #fff); color: var(--tp-ink); outline-color: var(--as-on-brand, #fff); box-shadow: none; }
/* A LABEL, NOT A COUNTER. It used to be a 1.7em square holding one digit,
   which told you the ordinal and left you to infer what was being counted —
   on a page where activities, sets and lines are all numbered. It says the word
   now, so the box is sized by its content: the height is still pinned, and
   the padding grows with the number of digits. */
.tpd-band-n {
    height: 1.7em; padding: 0 .55em; border-radius: .35em;
    background: rgba(255, 255, 255, .22);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: .78em; font-weight: 800; letter-spacing: .06em;
    white-space: nowrap; flex: 0 0 auto; box-sizing: border-box;
}
.tpd-band-nm { font-size: 1em; font-weight: 700; }
.tpd-band-en { font-size: .84em; opacity: .82; }
.tpd-band .tpd-ic { color: var(--as-on-brand, #fff); }
.tpd-band .tpd-ic:hover:not(:disabled) { background: rgba(255, 255, 255, .2); border-color: rgba(255, 255, 255, .35); }
.tpd-evdesc { font-size: .85em; color: var(--tp-ink-soft); font-style: italic; margin: .5em 0 .6em; }
/* One editable line naming the card, with the delete for the whole card at
   its end — so it wraps rather than pushing the 🗑 off a narrow window. */
.tpd-setupcard .tp-eyebrow { display: flex; align-items: center; gap: .5em; flex-wrap: wrap; }

/* ---------- sets ---------- */
/* A SET IS A BOX. It was a hairline rule across the top and nothing else,
   so where one set ended and the next began was a judgement call in a lesson
   whose sets run to a screen apiece. A closed border says it outright — and
   in black rather than --tp-rule, because this is the one division on the
   page a teacher has to see at a glance while scrolling.
   --tpd-set-edge, not a literal, so the dark theme can swap it: black on a
   dark ground is not a border, it is a hole. */
/* NO scroll-margin-top HERE. It carried a hard-coded 4.2em, and this rule is
   480 lines below the one that gives .tpd-set the MEASURED offset
   (`calc(var(--tpd-stick) + 1em)`), so at equal specificity it won: going to
   a set put its top 58.8px down a window with 196px of sticky chrome over it
   — the set landed under the toolbar and the words strip, which is the one
   thing that offset exists to prevent.

   It went unseen because a short plan cannot scroll far enough to obey
   scrollIntoView: the page hit its own end first and the set stopped in
   clear air by accident, and `going to a set puts it below the sticky
   chrome` passed on exactly that. Giving the plan its trailing space
   (v0.8.0.7, bug-2026-09-06-195157) removed the accident and the test failed
   the same hour — which is what a test that asserts the position rather than
   the property is for. */
.tpd-set {
    padding: .9em 1em 1.1em;
    border: 1px solid var(--tpd-set-edge, #000); border-radius: .5em;
    margin: 0 0 .9em;
}
.tpd-set[data-cur="true"] {
    background: linear-gradient(90deg, var(--tp-teacher-wash), transparent 55%);
}
.tpd-set-h { display: flex; align-items: center; gap: .5em; margin-bottom: .5em; }
.tpd-set-n {
    height: 1.8em; padding: 0 .55em; border-radius: .35em; background: var(--tp-panel-3);
    display: inline-flex; align-items: center; justify-content: center; color: var(--tp-ink-soft);
    font-size: .72em; font-weight: 800; letter-spacing: .06em;
    white-space: nowrap; flex: 0 0 auto; box-sizing: border-box;
}
.tpd-set-t { flex: 1 1 auto; font-size: .98em; font-weight: 700; }
.tpd-lane-h { display: flex; align-items: center; gap: .4em; }
.tpd-part {
    margin-left: .5em; font-weight: 650; letter-spacing: 0; text-transform: none;
    font-size: .92em; opacity: .75;
}
.tpd-addline { margin-top: .4em; }

/* ---------- lines ---------- */
/* The reserve on the right is MEASURED — _syncToolsWidth() publishes
   --tpd-tools-w as the distance from the line's right edge to the row
   controls' left edge. It was a flat 7em, which is 98px against a bar that
   measures 103, so every line's text ran under its own controls; on a note,
   whose body is a filled bordered box rather than bare text, the bar sat on
   the wash. Five pixels is the small version of that bug: the bar is five
   buttons sized in em, so anything that renders them larger widens it while a
   hard-coded reserve stays exactly where it was. The 7em is the fallback for
   the first paint and for print, where the controls are hidden anyway. */
.tpd-line {
    position: relative; padding-left: 2.1em;
    padding-right: calc(var(--tpd-tools-w, 7em) + .3em);
    border-radius: .35em;
}
/* The lane mark and the run number share one gutter and swap on hover. That
   is NOT the bug above: neither is a control — the lane is already named by
   its heading and drawn by the wash, and the number only counts within a run.
   Nothing is reachable only by hovering. */
.tpd-gut {
    position: absolute; left: 0; top: .1em; width: 1.6em; height: 1.6em;
    display: grid; place-items: center; border-radius: .35em;
    font-size: .72em; font-weight: 800; opacity: 0; transition: opacity .12s;
}
.tpd-line:hover .tpd-gut, .tpd-line:focus-within .tpd-gut { opacity: 1; }
.tpd-line-n {
    position: absolute; left: 0; top: .25em; font-size: .64em;
    color: var(--tp-ink-faint); width: 2em; text-align: center;
}
.tpd-line:hover .tpd-line-n { opacity: 0; }
/* ALWAYS ON SCREEN, never revealed by hover.
   bug-2026-09-02-234641: "The move and delete buttons for each line should
   not go away and only come with hover, but should always be shown." The
   paged editor rested five control groups at opacity: 0, which hid the
   editor's whole vocabulary from anyone who had not yet swept a pointer over
   a row — and on a touch screen the reveal never fires at all. v0.7.132.10
   removed the fade there; this file reintroduced it, and this is the same
   fix. The line already reserves padding-right for the cluster, so showing
   it costs no layout. */
.tpd-line-tools {
    position: absolute; right: .2em; top: 0; display: flex; gap: .05em;
    background: var(--tp-panel); border: 1px solid var(--tp-rule); border-radius: .4em; padding: .1em;
}

/* The washes the teacher's screen gives these two lanes. The page has to
   look like the plan, not like a form. */
.tpd-line[data-lane="note"] .tpd-body {
    background: var(--tp-note-wash); border: 1px solid var(--tp-note); border-left-width: 3px;
    border-radius: .5em; padding: .55em .7em;
}
.tpd-line[data-lane="note"] { margin-bottom: .5em; }
.tpd-line[data-lane="note"] .tpd-body b {
    display: block; font-size: .8em; font-weight: 800; letter-spacing: .07em;
    text-transform: uppercase; color: var(--tp-note);
}
.tpd-line[data-lane="do"] .tpd-body {
    background: var(--tp-do-wash); border-radius: .45em; padding: .35em .6em;
}
.tpd-line[data-lane="do"] { margin-bottom: .3em; }
.tpd-empty .tpd-body { border: 1px dashed var(--tp-rule); border-radius: .35em; padding: .2em .4em; }

/* ---------- the phone ---------- */
/* Sticky, not a column of a viewport-height grid. With the page scrolling,
   a phone that scrolled away with the document would be useless — it exists
   to show what the teacher holds for the part you are standing on. */
.tpd-phone-pane {
    position: sticky; top: var(--tpd-stick, 6em);
    height: calc(100vh - var(--tpd-stick, 6em));
    background: var(--tp-panel-3); border-left: 1px solid var(--tp-rule);
    display: flex; flex-direction: column; align-items: center; gap: .5em;
    padding: .9em .8em; min-width: 0; overflow: hidden;
}
.tpd-phone-cap {
    flex: 0 0 auto; margin: 0;
    font-size: .62em; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
    color: var(--tp-ink-faint);
}
.tpd-phone {
    flex: 1 1 auto; width: 100%; max-width: 24em; min-height: 0;
    background: var(--tpd-phone-frame, #14161F); border-radius: 2.2em; padding: .55em;
    box-shadow: 0 10px 30px rgba(0, 0, 0, .3), inset 0 0 0 2px rgba(255, 255, 255, .08);
    display: flex; flex-direction: column;
}
.tpd-notch { flex: 0 0 auto; height: 1.1em; display: grid; place-items: center; }
.tpd-notch span { width: 4.5em; height: .32em; border-radius: 999px; background: rgba(255, 255, 255, .35); }
.tpd-screen {
    flex: 1 1 auto; min-height: 0; background: var(--tp-panel); border-radius: 1.7em;
    display: flex; flex-direction: column; overflow: hidden;
}
.tpd-scr-head {
    flex: 0 0 auto; display: flex; align-items: center; gap: .45em;
    padding: .5em .8em; border-bottom: 1px solid var(--tp-rule);
    background: var(--tp-panel-2); border-radius: 1.7em 1.7em 0 0;
}
.tpd-scr-head b { font-size: .72em; }
.tpd-scr-head span { font-size: .62em; color: var(--tp-ink-faint); margin-left: auto; }
.tpd-stage { flex: 1 1 auto; overflow-y: auto; padding: .8em .85em; }
.tpd-tm-head { margin-bottom: .8em; padding-bottom: .6em; border-bottom: 1px solid var(--tp-rule); }
.tpd-tm-head .tp-k {
    display: block; font-size: .6em; font-weight: 700; letter-spacing: .08em;
    text-transform: uppercase; color: var(--tp-ink-faint);
}
.tpd-tm-head h5 {
    margin: .15em 0 0; font-family: var(--tp-serif);
    font-size: 1.1em; font-weight: 600; line-height: 1.25;
}
.tpd-meter { flex: 0 0 auto; height: 3px; background: var(--tp-panel-3); }
.tpd-meter div { height: 100%; background: var(--tp-student); transition: width .25s ease; }
.tpd-acts {
    flex: 0 0 auto; display: grid; gap: .3em; padding: .5em .6em;
    grid-template-columns: 2.2em 1fr 1.15fr 1.35fr 2.2em;
    border-top: 1px solid var(--tp-rule); background: var(--tp-panel-2);
}
.tpd-acts button {
    font: inherit; font-size: .68em; font-weight: 700; min-height: 2.9em;
    border-radius: .5em; border: 1.5px solid; padding: .2em;
    display: inline-flex; align-items: center; justify-content: center;
}
.tpd-acts button:disabled { opacity: .55; cursor: default; }
.tpd-a-nav { background: var(--tp-panel); border-color: var(--tp-rule); color: var(--tp-ink-faint); font-size: .95em; cursor: pointer; }
.tpd-a-skip { background: var(--tp-panel); border-color: var(--tp-rule); color: var(--tp-ink-soft); }
.tpd-a-help { background: var(--tp-note-wash); border-color: var(--tp-note); color: var(--tp-note); }
.tpd-a-learn { background: var(--tp-student); border-color: var(--tp-student); color: var(--as-on-brand, #fff); }
.tpd-pos {
    flex: 0 0 auto; margin: 0; padding: 0 .6em .55em; font-size: .6em;
    color: var(--tp-ink-faint); text-align: center;
    background: var(--tp-panel-2); border-radius: 0 0 1.7em 1.7em;
}
.tpd-fit { flex: 0 0 auto; margin: 0; font-size: .68em; color: var(--tp-ink-faint); text-align: center; }
.tpd-fit b { color: var(--tp-ink); }
.tpd-fit[data-over="true"] b { color: var(--tp-note); }

/* ---------- screen-reader live region ---------- */
.tpd-live {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* ---------- narrow ---------- */
@media (max-width: 900px) {
    .tpd-split { grid-template-columns: 1fr; }
    .tpd-grip, .tpd-phone-pane { display: none; }
    .tpd-doc-pane { padding: 1em .9em 6em; }
    .tpd-line { padding-right: 0; }
    .tpd-line-tools { position: static; opacity: 1; margin-top: .2em; }
}

/* ---------- print ---------- */
/* Reading mode is already the clean page; printing just drops the chrome
   around it and lets the document flow. */
@media print {
    body.tpd-open > *:not(#moduleContainer) { display: none !important; }
    body.tpd-open #moduleContainer > *:not(.tpd) { display: none !important; }
    .tpd {
        position: static; height: auto; min-height: 0;
    }
    .tpd-bar, .tpd-words, .tpd-grip, .tpd-phone-pane,
    .tpd-gap, .tpd-tools, .tpd-line-tools, .tpd-add, .tpd-mark, .tpd-rowwarn,
    .tpd-ghost, .tpd-scope, .tpd-suggs, .tpd-flag, .tpd-offpage-flag,
    .tpd-grip-h, .tpd-dropline {
        display: none !important;
    }
    .tpd-f[data-edit^="label/"] { border-bottom: 0; color: inherit; }
    .tpd-split { display: block; }
    .tpd-doc-pane { overflow: visible; padding: 0; }
    .tpd-band { position: static; box-shadow: none; }
    .tpd-set, .tpd-band, .tpd-line, .tpd-f { scroll-margin-top: 0; }
}

/* ---------- the save alarm ---------- */
/* A run of failed saves, said loudly.
   bug-2026-09-08-020621: a small red pill reported thirty hours of refused
   writes accurately and nobody read it, because it looks the same after one
   failure as after four hundred. This is deliberately not a pill: it takes
   the top of the panel, it does not fade, and it carries the one action that
   gets the author's work out of the browser. Sized so it cannot be mistaken
   for the toast that reports a single failure. */
.tp-save-alarm {
    /* Fixed to the screen since v0.23.0.0: it was placed inside a panel the
       open editor hides, so it could not be seen while a lesson was open. */
    position: fixed;
    left: calc(var(--shell-left, 0px) + 16px);
    bottom: 16px;
    z-index: 10050;
    width: min(680px, calc(100vw - 32px));
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 18px;
    margin: 0;
    border: 2px solid var(--tpd-alarm, #dc3545);
    border-radius: 8px;
    background: var(--tpd-alarm-bg, #fff5f5);
    color: var(--tpd-alarm-ink, #6b1720);
    font-size: .95rem;
    line-height: 1.45;
    box-shadow: 0 4px 14px rgb(220 53 69 / 22%);
}

.tp-save-alarm-body { flex: 1 1 320px; }
.tp-save-alarm-body strong { display: block; font-size: 1.05rem; margin-bottom: 2px; }
/* Three buttons in the signed-out variant (Sign in again / Save again /
   Download my copy), so this wraps rather than pushing them off a narrow
   window — the bar an author must be able to act on is the wrong place to
   discover a horizontal scrollbar. */
.tp-save-alarm-actions { display: flex; gap: 8px; flex-shrink: 0; flex-wrap: wrap; }

@media (prefers-color-scheme: dark) {
    .tp-save-alarm { background: var(--tpd-alarm-bg-dark, #2a1416); color: var(--tpd-alarm-ink-dark, #ffd8dc); }
}

body[data-theme="dark"] .tp-save-alarm { background: var(--tpd-alarm-bg-dark, #2a1416); color: var(--tpd-alarm-ink-dark, #ffd8dc); }

/* ---------- the kept copy ---------- */
/* The offer to restore what plan-draft.js kept in this browser.
   Deliberately the save alarm's shape and NOT its colour: an alarm says
   something is wrong, and this says something was saved. It sits at the top
   of the editor because it is about the lesson underneath it, it does not
   fade — an author who has just opened a lesson may be reading before they
   are deciding — and it goes when either button is pressed. */
.tp-draft-offer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 18px;
    margin: 0 0 12px;
    border: 2px solid var(--tp-accent, #0d6efd);
    border-radius: 8px;
    background: var(--tpd-offer-bg, #f2f7ff);
    color: var(--tpd-offer-ink, #10315c);
    font-size: .95rem;
    line-height: 1.45;
    box-shadow: 0 4px 14px rgb(13 110 253 / 18%);
}

.tp-draft-offer-body { flex: 1 1 320px; }
.tp-draft-offer-body strong { display: block; font-size: 1.05rem; margin-bottom: 2px; }
.tp-draft-offer-actions { display: flex; gap: 8px; flex-shrink: 0; flex-wrap: wrap; }

/* PINNED (v0.24.0.0). The offer used to sit at the top of the lesson and
   scroll away while the author worked further down. It now floats bottom-left
   (the conflict panel has bottom-right) until it is answered. */
.tp-draft-offer.tp-draft-floating {
    position: fixed;
    left: calc(var(--shell-left, 0px) + 16px);
    bottom: 16px;
    z-index: 10040;
    width: min(560px, calc(100vw - 32px));
    max-height: calc(100vh - 140px);   /* refined in JS: _placeFloating() */
    overflow-y: auto;
    margin: 0;
    box-shadow: 0 12px 36px rgb(0 0 0 / 28%);
}

@media (prefers-color-scheme: dark) {
    .tp-draft-offer { background: var(--tpd-offer-bg-dark, #10203a); color: var(--tpd-offer-ink-dark, #d6e6ff); }
}

body[data-theme="dark"] .tp-draft-offer { background: var(--tpd-offer-bg-dark, #10203a); color: var(--tpd-offer-ink-dark, #d6e6ff); }

/* ---------- earlier versions ---------- */
/* The panel over php/plan-history.php. Deliberately the ☰ Sets sheet's shape
   — same slide-in, same scrim, same list rows — because it answers the same
   kind of question ("where is the thing I am looking for") and an author
   should not have to learn a second panel. */
.tpd-hist .tpd-sheet-set[aria-current="true"] {
    background: var(--tp-teacher-wash);
    border-color: var(--tp-teacher);
    font-weight: 700;
}

/* The sets a kept version has and the open lesson does not. This is the list
   an author opens the panel to read, so it is the one thing in here with a
   colour of its own. */
.tpd-histgone {
    margin: .2em 0 .6em;
    padding-left: 1.2em;
    font-size: .82em;
    color: var(--tp-note);
}

.tpd-histgone li { margin: .15em 0; }

/* A copy the server REFUSED and kept (v0.24.0.0). Marked in the list and
   explained above its contents, so it is never read as a saved version. */
.tpd-hist-flag {
    display: inline-block;
    padding: 0 .4em;
    margin-right: .25em;
    border-radius: 4px;
    font-size: .78em;
    font-weight: 700;
    color: var(--as-on-brand, #fff);
    background: var(--tpd-refused, #b54708);
}

.tpd-hist-refused { border-style: dashed; }

.tpd-hist-refused-note {
    padding: .5em .6em;
    border-left: 3px solid var(--tpd-refused, #b54708);
    background: rgb(181 71 8 / 8%);
}

.tpd-btn-go {
    margin-top: .6em;
    background: var(--tp-teacher);
    border-color: var(--tp-teacher);
    color: var(--as-on-brand, #fff);
    font-weight: 700;
}

/* ─── Kinds of word, and the pills that fill them ──────────────────────
   The lesson declares a kind once; every set whose shape names that kind
   shows the same words under it. So the pills are editable in BOTH places
   and edit the same list — the row in the overview, and the row under a
   set. A word removed under one set is removed from the kind. */
.tpd-kinds .tp-words-row,
.tpd-words-set .tp-words-row { align-items: center; }
.tpd-kinds .tpd-kindrow { position: relative; padding-right: 4.5em; }
.tpd-kind-en {
    font-size: .85em; color: var(--tp-ink-faint); font-style: italic;
    margin-right: .35em; min-width: 6em;
}
.tpd-words-set {
    margin-top: .8em; padding-top: .6em; border-top: 1px dashed var(--tp-rule);
}

/* A pill in the editor carries its own delete, shown on hover or focus —
   the same bargain the line tools make, so a row of words reads as words
   until you reach for one. */
.tpd-pill { position: relative; padding-right: .5em; }
.tpd-pill-x {
    display: none; margin-left: .3em; padding: 0 .15em; border: 0;
    background: none; color: var(--tp-ink-faint); cursor: pointer;
    font-size: 1.05em; line-height: 1;
}
.tpd-pill:hover .tpd-pill-x,
.tpd-pill:focus-within .tpd-pill-x { display: inline; }
.tpd-pill-x:hover, .tpd-pill-x:focus { color: var(--tpd-remove-hover, #C0392B); }

.tpd-pill-add {
    padding: 0 .5em; border: 1px dashed var(--tp-rule); border-radius: 999px;
    background: none; color: var(--tp-ink-faint); cursor: pointer;
    font-size: .9em; line-height: 1.6;
}
.tpd-pill-add:hover, .tpd-pill-add:focus {
    border-style: solid; border-color: var(--tp-teacher); color: var(--tp-teacher);
}

/* The picker the + opens: this lesson's Sentence Words first, a new word
   last, because a word the sentence tools already know is the one that
   keeps the rest of the app in step. */
.tpd-pick {
    position: absolute; z-index: 60; min-width: 22em; max-width: 34em;
    margin-top: .3em; padding: .7em .8em;
    border: 1px solid var(--tp-rule); border-radius: 10px;
    background: var(--tp-panel); box-shadow: 0 12px 32px rgba(0, 0, 0, .18);
}
.tpd-pick-head { margin: 0 0 .5em; font-size: .85em; color: var(--tp-ink-soft); }
.tpd-pick-row { display: flex; flex-wrap: wrap; gap: .25em; align-items: baseline; margin: .3em 0; }
.tpd-pick-new { display: flex; gap: .4em; margin-top: .6em; padding-top: .6em; border-top: 1px solid var(--tp-rule); }
.tpd-pick-new input {
    flex: 1; padding: .3em .5em; border: 1px solid var(--tp-rule); border-radius: 6px;
    background: var(--tp-panel-2); color: var(--tp-ink); font: inherit; font-size: .9em;
}

/* A group the SET names for itself carries a ✕ — the same bargain the pill
   makes, shown on hover or focus. A group a shape names has none: it is
   there because the shape has that slot. */
.tpd-kind-x {
    display: none; margin-left: .35em; padding: 0; border: 0;
    background: none; color: inherit; cursor: pointer;
    font-size: 1em; line-height: 1; opacity: .7;
}
.tp-words-row:hover .tpd-kind-x,
.tp-words-row:focus-within .tpd-kind-x { display: inline; }
.tpd-kind-x:hover, .tpd-kind-x:focus { color: var(--tpd-remove-hover, #C0392B); opacity: 1; }

/* "+ Word group" sits under the rows, quiet until it is wanted. */
.tpd-groupadd { margin-top: .2em; }

/* Reading mode strips the editing chrome, as it does for every other
   affordance on the page. */
.tpd[data-editing="off"] .tpd-pill-add,
.tpd[data-editing="off"] .tpd-groupadd,
.tpd[data-editing="off"] .tpd-kind-x,
.tpd[data-editing="off"] .tpd-pill-x { display: none; }

/* ---------- changes saved elsewhere at the same time (v0.23.0.0) ---------- */
/* bug-2026-09-27-193639: the dialog this replaces was drawn with a class whose
   only rule lived in the Test Results stylesheet — position: absolute and no
   z-index — so it sat at the top of the PAGE, under the sticky app header, and
   its buttons could not be reached. This one is fixed to the viewport, anchored
   to the bottom so it can never reach up under the header, and stacked above
   the app chrome (2000) and below the toasts (100000). */
.tp-conflict-panel {
    position: fixed;
    right: 16px;
    bottom: 16px;
    z-index: 10050;
    width: min(680px, calc(100vw - 32px));
    max-height: calc(100vh - 140px);   /* refined in JS: _placeFloating() */
    overflow-y: auto;
    padding: 16px 18px;
    border: 2px solid var(--tpd-conflict, #d97706);
    border-radius: 10px;
    background: var(--bg-primary, #fff);
    color: var(--text-primary, #1f2937);
    box-shadow: 0 12px 36px rgb(0 0 0 / 28%);
    font-size: .95rem;
    line-height: 1.45;
}
.tp-conflict-panel.collapsed {
    width: auto;
    padding: 0;
    border: 0;
    background: transparent;
    box-shadow: none;
    overflow: visible;
}
.tp-conflict-chip {
    padding: 10px 16px;
    border: 2px solid var(--tpd-conflict, #d97706);
    border-radius: 999px;
    background: var(--tpd-conflict-bg, #fff7e6);
    color: var(--tpd-conflict-ink, #7c3d00);
    font-weight: 600;
    cursor: pointer;
    box-shadow: 0 6px 18px rgb(0 0 0 / 22%);
}
.tp-conflict-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.tp-conflict-head h2 { margin: 0; font-size: 1.1rem; }
.tp-conflict-min {
    border: 1px solid currentColor; border-radius: 6px; background: transparent;
    color: inherit; padding: 4px 10px; cursor: pointer; flex-shrink: 0;
}
.tp-conflict-intro { margin: 8px 0 12px; }
.tp-conflict-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.tp-conflict-row { border: 1px solid var(--border-color, #e5e7eb); border-radius: 8px; padding: 10px 12px; }
.tp-conflict-rowhead { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
.tp-conflict-where { font-weight: 600; }
.tp-conflict-more { font-size: .85rem; opacity: .75; margin-top: 2px; }

/* The lines the two sides changed differently (v0.24.0.0). */
.tp-conflict-diff mark {
    padding: 0 .15em;
    border-radius: 2px;
    color: inherit;
    background: var(--tpd-diff-mark, #fff3c4);
}

body[data-theme="dark"] .tp-conflict-diff mark { background: var(--tpd-diff-mark-dark, #5c4a00); }

.tp-conflict-side li {
    display: -webkit-box;
    overflow: hidden;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
.tp-conflict-sides { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.tp-conflict-side {
    min-width: 0; overflow-wrap: anywhere; padding: 8px 10px; border-radius: 6px;
    background: var(--bg-secondary, #f6f7f9);
}
.tp-conflict-side.mine { outline: 2px solid var(--tpd-mine-outline, #2563eb33); }
.tp-conflict-side ul { margin: 4px 0 0; padding-left: 1.1em; }
.tp-conflict-label { font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; opacity: .75; margin-bottom: 4px; }
.tp-conflict-focus { font-weight: 600; }
.tp-conflict-none { opacity: .75; }
.tp-conflict-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.tp-conflict-foot { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; margin-top: 10px; }
.tp-outdated { border-color: var(--tpd-alarm, #dc3545); }

@media (max-width: 560px) {
    .tp-conflict-sides { grid-template-columns: 1fr; }
    .tp-conflict-panel { right: 8px; bottom: 8px; width: calc(100vw - 16px); }
}

@media (prefers-color-scheme: dark) {
    .tp-conflict-chip { background: var(--tpd-conflict-bg-dark, #3a2406); color: var(--tpd-conflict-ink-dark, #ffd9a8); }
}
body[data-theme="dark"] .tp-conflict-chip { background: var(--tpd-conflict-bg-dark, #3a2406); color: var(--tpd-conflict-ink-dark, #ffd9a8); }
