/*
 * Injected into every page, so every rule is scoped to our own <llr-w> tag.
 * Colours come from CSS custom properties written on <html> by the reader,
 * which makes a settings change repaint the page without touching the DOM.
 */

llr-w {
  --llr-fill: transparent;
  cursor: pointer;
  border-radius: 3px;
  /* Keep the highlight intact when a word wraps across two lines. */
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  transition: box-shadow 120ms ease;
}

/* Fill mode (default). Known and Ignored are deliberately unmarked. */
llr-w[data-s='0'] { --llr-fill: var(--llr-c-new); }
llr-w[data-s='1'] { --llr-fill: var(--llr-c-1); }
llr-w[data-s='2'] { --llr-fill: var(--llr-c-2); }
llr-w[data-s='3'] { --llr-fill: var(--llr-c-3); }
llr-w[data-s='4'] { --llr-fill: var(--llr-c-4); }
llr-w[data-s='5'],
llr-w[data-s='6'],
llr-w[data-s='7'] { background-color: transparent; }

/*
 * The fill starts a pixel in from the word's leading edge.
 *
 * Chinese and Japanese put no space between words, so two highlighted words
 * side by side used to read as one long one -- and where the segmenter cut a
 * phrase is exactly what a reader needs to see, to spot the split that needs
 * fixing. Drawn as a gradient rather than a flat colour, so the gap is a
 * hairline of the page itself between neighbours, whatever colour the page is,
 * and not one character moves to make room for it.
 */
llr-w:is([data-s='0'], [data-s='1'], [data-s='2'], [data-s='3'], [data-s='4']) {
  background-image: linear-gradient(to right, transparent 1px, var(--llr-fill) 1px);
}
llr-w[data-llr-dir='ttb']:is([data-s='0'], [data-s='1'], [data-s='2'], [data-s='3'], [data-s='4']) {
  background-image: linear-gradient(to bottom, transparent 1px, var(--llr-fill) 1px);
}

/*
 * Levels 3 and 4 add a second cue: a line under the word.
 *
 * One pale yellow cannot carry four steps on white -- the whole range from the
 * page to level 1 is about thirty units of colour difference, and four fills
 * in it sit six or seven apart, which side by side is visible and scattered
 * across a page is not. So the last two levels change shape rather than only
 * getting paler: level 3 keeps a light fill and gains the line, level 4 loses
 * the fill and keeps only the line. The line is the learning colour made a
 * quarter darker (--llr-c-line), which holds 3:1 or better against white, book
 * paper and a dark page.
 */
llr-w[data-s='3'] { box-shadow: inset 0 -0.12em 0 var(--llr-c-line); }
llr-w[data-s='4'] { --llr-fill: transparent; box-shadow: inset 0 -0.12em 0 var(--llr-c-line); }
/* 直排: the line sits on the right of the glyph, where the underline modes put theirs. */
llr-w[data-llr-dir='ttb']:is([data-s='3'], [data-s='4']) { box-shadow: inset -0.12em 0 0 var(--llr-c-line); }
/* With readings printed down the right, the line moves to the left, as the underline mode's does below. */
html[data-llr-fx] llr-w[data-llr-dir='ttb']:is([data-s='3'], [data-s='4']) { box-shadow: inset 0.12em 0 0 var(--llr-c-line); }
/*
 * Or, with levels drawn as fills (levelStyle 'fade', Settings → Appearance), no
 * line at all: level 3 keeps its light fill and level 4 takes its own fainter
 * one, so the four levels are four steps of one fill. For a reader the line
 * distracts or misleads, say one new to a script. SlukaCC's subtitles too, at
 * their own strength for the dark box. The underline mode draws every level as
 * a line anyway, so it is left as it is.
 */
html[data-llr-fade]:not([data-llr-underline]) llr-w:is([data-s='3'], [data-s='4']) { box-shadow: none; }
html[data-llr-fade]:not([data-llr-underline]) llr-w[data-s='4'] { --llr-fill: var(--llr-c-4); }
html[data-llr-fade]:not([data-llr-underline]) .llr-cc llr-w[data-s='4'] { --llr-fill: var(--llr-cc-4, var(--llr-c-4)); }

/*
 * New here, but already known in the other script — 开始 while 開始 is known.
 * Written as an extra attribute on the new status rather than as a status of
 * its own, because that is what it is: the word is still new in the script
 * being read, and only its colour says otherwise. The dotted line is there so
 * the colour is not the only thing saying it: this green and the learning
 * amber sit close together for a red-green colour-blind reader.
 */
llr-w[data-s='0'][data-tw] {
  --llr-fill: var(--llr-c-twin);
  text-decoration: underline dotted 2px var(--llr-c-twin-line, currentColor);
  text-underline-offset: 0.18em;
}

/* Underline mode: gentler over photos and busy layouts. */
html[data-llr-underline] llr-w {
  background-color: transparent !important;
  background-image: none !important;
  text-decoration: none;
}
html[data-llr-underline] llr-w[data-s='0'] { box-shadow: inset 0 -0.16em 0 var(--llr-c-new); }
html[data-llr-underline] llr-w[data-s='1'] { box-shadow: inset 0 -0.16em 0 var(--llr-c-1); }
html[data-llr-underline] llr-w[data-s='2'] { box-shadow: inset 0 -0.16em 0 var(--llr-c-2); }
html[data-llr-underline] llr-w[data-s='3'] { box-shadow: inset 0 -0.16em 0 var(--llr-c-3); }
html[data-llr-underline] llr-w[data-s='4'] { box-shadow: inset 0 -0.16em 0 var(--llr-c-4); }
html[data-llr-underline] llr-w[data-s='0'][data-tw] { box-shadow: inset 0 -0.16em 0 var(--llr-c-twin); }

/* 直排: the mark sits on the right of the glyph, the traditional side. */
html[data-llr-underline] llr-w[data-llr-dir='ttb'][data-s='0'] { box-shadow: inset -0.16em 0 0 var(--llr-c-new); }
html[data-llr-underline] llr-w[data-llr-dir='ttb'][data-s='1'] { box-shadow: inset -0.16em 0 0 var(--llr-c-1); }
html[data-llr-underline] llr-w[data-llr-dir='ttb'][data-s='2'] { box-shadow: inset -0.16em 0 0 var(--llr-c-2); }
html[data-llr-underline] llr-w[data-llr-dir='ttb'][data-s='3'] { box-shadow: inset -0.16em 0 0 var(--llr-c-3); }
html[data-llr-underline] llr-w[data-llr-dir='ttb'][data-s='4'] { box-shadow: inset -0.16em 0 0 var(--llr-c-4); }
html[data-llr-underline] llr-w[data-llr-dir='ttb'][data-s='0'][data-tw] { box-shadow: inset -0.16em 0 0 var(--llr-c-twin); }

/*
 * Names and places, while they are new (specialWords, Settings → Appearance):
 * filled in SlukaCC's special-words colour instead of the new words' blue, so
 * a page's Madrid and María are not taken for vocabulary to learn. The engine
 * marks them on every page (data-sp, reader.js specialWord); only this switch
 * draws them here. SlukaCC's subtitles and transcript go by their own switch
 * (cc.specialWords, further down), so they are left out.
 */
html[data-llr-special] llr-w[data-s='0'][data-sp]:not(:is(.llr-cc, .llr-cc-transcript) *) { --llr-fill: var(--llr-c-special); }
html[data-llr-special][data-llr-underline] llr-w[data-s='0'][data-sp]:not(:is(.llr-cc, .llr-cc-transcript) *) {
  box-shadow: inset 0 -0.16em 0 var(--llr-c-special);
}
html[data-llr-special][data-llr-underline] llr-w[data-llr-dir='ttb'][data-s='0'][data-sp]:not(:is(.llr-cc, .llr-cc-transcript) *) {
  box-shadow: inset -0.16em 0 0 var(--llr-c-special);
}

/*
 * Known words, while Colour known words is on (colorKnown, Settings →
 * Appearance): their letters in the known colour, no fill and no line, so a
 * page of words you know still reads as a page. Known through the other script
 * (7) is known too. Pages and books only: SlukaCC has a known colour of its own
 * (cc.colours.known). Highlights off takes it away with the rest.
 */
html[data-llr-known]:not([data-llr-off]) llr-w:is([data-s='5'], [data-s='7']):not(:is(.llr-cc, .llr-cc-transcript) *) {
  color: var(--llr-c-known);
}

/*
 * Rarer new words, past the reach of Colour new words for pages (faintWords,
 * Settings → Reading; reader.js pageRareWord): no fill and no line, their
 * letters in the rare colour (Appearance), so the commonest new words are the
 * ones that stand out. Still opened by a click. Pages and books only: SlukaCC
 * has its own reach (data-rare, further down). Highlights off takes it away.
 */
html:not([data-llr-off]) llr-w[data-s='0'][data-page-rare]:not(:is(.llr-cc, .llr-cc-transcript) *) {
  --llr-fill: transparent;
  background-image: none;
  box-shadow: none;
  text-decoration: none;
  color: var(--llr-c-rare);
}

llr-w:hover {
  outline: 2px solid var(--llr-c-outline);
  outline-offset: 1px;
}

llr-w[data-llr-sel] {
  outline: 2px solid var(--llr-c-outline);
  outline-offset: 1px;
}

/* The other word of a German verb the clause split, with the one hovered: `sieht` and `aus` (reader.js markPair). */
llr-w[data-llr-pair] {
  outline: 2px solid var(--llr-c-outline);
  outline-offset: 1px;
}

/* Shift+D's word (content/sentence-jump.js): the commonest frequent word of the
   line or sentence it went to, ringed in gold (Oskar, 2026-10-17) -- over its
   level's colour and the outline a pointer or an open word gives, and with the
   highlights off too: the key asked for it. */
llr-w[data-llr-top] {
  outline: 0.12em solid var(--llr-c-top, #e0a100) !important;
  outline-offset: 0.08em;
}

/* The sentence a reading jump went to (sentence-jump.js), marked for a moment
   so the eye finds it once the page has scrolled. */
::highlight(sluka-jump) {
  background-color: rgba(240, 170, 0, 0.22);
}

/* The characters the panel's expansion arrows currently cover, and the words a
   drag takes (reader.js, paintPick). A highlight rather than an outline because
   it can end halfway into a neighbouring word, which no per-element rule can
   draw. Sluka's own mark instead of the browser's blue selection (Oskar,
   2026-10-12): one soft band over the whole run, gaps and all, with a solid
   line under it, so several words read as one thing and keep their own text
   colour and levels' fills. Its own colour, orange by default (Settings ->
   Appearance; reader.js applyColors), with a thick line (Oskar, 2026-10-20). */
::highlight(llr-expand) {
  background-color: var(--llr-c-pick);
  text-decoration: underline 5px var(--llr-c-pick-line);
  text-underline-offset: 0.2em;
}

/* While a drag is under way: no outline jumping from word to word under the
   pointer, which is only passing over them. */
html[data-llr-drag] llr-w:hover { outline: none; }

/* The browser's own selection, hidden while Sluka draws the run it will open in
   its place (reader.js, paintPick). Still there to copy. */
html[data-llr-pick] ::selection { background-color: transparent; }

/* Reading aloud (content/narrator.js): what is being read is underlined in
   green and nothing more, so a paragraph keeps its own look and its words their
   blue and amber (Oskar's call, 2026-10-03). The word being said is marked over
   it in solid green, that one word only, and moves on with the voice: a wash
   was lost among the blue of the new words around it. */
::highlight(sluka-narration) {
  text-decoration: underline 2px rgba(16, 185, 129, 0.85);
  text-underline-offset: 0.22em;
}
::highlight(sluka-narration-word) {
  color: #fff;
  background-color: rgb(16, 150, 105);
  text-decoration: none;
}

/*
 * Highlights off (Settings, or shift+H): the colour goes and nothing else. The
 * words are still there to be clicked, so the pointer and the outline under it
 * stay -- in Chinese they are all that shows where the word a click will open
 * begins and ends -- and so does the outline on the word that is open.
 */
html[data-llr-off] llr-w {
  background-color: transparent !important;
  background-image: none !important;
  box-shadow: none !important;
  text-decoration: none !important;
}

/*
 * Readings printed over the characters.
 *
 * The reading never enters the DOM as text: it rides on `data-r` and this
 * stylesheet draws it as generated content. That is the whole mechanism, and
 * it is what keeps `textContent`, `innerText`, a drag-selection, a clipboard
 * copy and every character offset the reader counts -- the expansion arrows,
 * the caption landing, the sentence sent for an AI check -- seeing exactly the
 * string the page wrote. Switching the highlighter off restores the page's own
 * text rather than baking pinyin into it, for the same reason.
 *
 * Two mechanisms draw it from the same markup, because one engine cannot:
 *
 *   'ruby'   the browser's own ruby layout. Blink has had the ruby display
 *            values since Chrome 121, which covers the desktop, the Android
 *            WebView floor and everything above it. It reserves space, it eats
 *            into existing leading rather than always adding to it, and in 直排
 *            it puts the reading down the right of the glyph by itself.
 *   'stack'  a two-line inline-block, for an engine with no CSS ruby -- which
 *            is every shipping WebKit, so every iPhone.
 *
 * Which one is chosen is measured at boot rather than asked for by name, in
 * ruby-paint.js: an engine that parses `display: ruby` without laying it out
 * would print the readings across the line above with nothing reserving space
 * for them, and that is worse than printing nothing.
 *
 * html[data-llr-fx] is the on switch as well as the choice. It and the scope
 * attribute are written and removed together: leaving the mechanism behind
 * with no scope reserves an empty band above every line with no reading in
 * sight.
 */
@property --llr-pull {
  syntax: '<length>';
  inherits: true;
  initial-value: 0px;
}

html[data-llr-fx='ruby'] llr-r[data-r] { display: ruby; }

/*
 * The stack mode's own leading recovery. Native ruby consumes the leading a
 * line already has before it adds any; a plain two-line stack cannot, and would
 * charge a fixed ~10px to every annotated line however loosely the page is set.
 * Pulling the annotation up by the slack in the line-height matches the two to
 * within a couple of pixels, and on a book page (line-height 1.9) that is the
 * difference between 3px and 10px a line.
 *
 * The custom property is registered above so `lh` and `em` resolve against
 * <llr-r> itself. Unregistered, `var()` resolves at the point of use -- inside
 * the pseudo-element -- and silently computes to nothing. An engine missing any
 * of `@property`, `lh` or `min()` drops the declaration and gets the slightly
 * looser layout, which cannot overlap.
 */
html[data-llr-fx='stack'] llr-r[data-r] {
  display: inline-block;
  text-align: center;
  line-height: 1.15;
  vertical-align: baseline;
  --llr-pull: min(0px, calc(0.62em - (1lh - 1em)));
  margin-block-start: var(--llr-pull);
}

/*
 * Every annotated segment carries an annotation whatever the scope -- a
 * zero-width one when its word is out of scope. The line above the text is then
 * the same height whether a reading is showing or not, so grading a word known
 * takes its reading off the page without the paragraph, or anything below it,
 * moving. Only that one word's width changes.
 */
html[data-llr-fx='ruby'] llr-r[data-r]::after,
html[data-llr-fx='stack'] llr-r[data-r]::before {
  content: '\200B';
  font: 0.55em/1 system-ui, 'Segoe UI', 'Hiragino Sans', 'Microsoft JhengHei', sans-serif;
  /* The page's own colour is the only one legible on the page's own
     background, and it is what survives the books viewer multiplying a word
     against the paper -- multiply can only darken. Opacity keeps the reading
     subordinate to the text it annotates. */
  color: currentColor;
  opacity: 0.72;
  -webkit-user-select: none;
  user-select: none;
}

/* What lifts the annotation onto its own line. Without these two the reading
   is drawn inline beside the character, which is the shape the boot probe
   exists to refuse — so it is worth its own rule rather than a line inside the
   block above, where it was once lost. */
html[data-llr-fx='ruby'] llr-r[data-r]::after { display: ruby-text; }
html[data-llr-fx='stack'] llr-r[data-r]::before { display: block; }

/* Which words get one. A pure attribute gate, so a change of scope repaints
   without touching the DOM -- the same contract the colours already keep. */
html[data-llr-fx='ruby'][data-llr-ruby='all'] llr-r[data-r]::after,
html[data-llr-fx='stack'][data-llr-ruby='all'] llr-r[data-r]::before { content: attr(data-r); }

html[data-llr-fx='ruby'][data-llr-ruby='new'] llr-w[data-s='0'] llr-r[data-r]::after,
html[data-llr-fx='stack'][data-llr-ruby='new'] llr-w[data-s='0'] llr-r[data-r]::before { content: attr(data-r); }

/* Level 4 is left out: a word that far along is one the reader should be
   reading without the crutch. */
html[data-llr-fx='ruby'][data-llr-ruby='learning']
  llr-w:is([data-s='0'], [data-s='1'], [data-s='2'], [data-s='3'])
  llr-r[data-r]::after,
html[data-llr-fx='stack'][data-llr-ruby='learning']
  llr-w:is([data-s='0'], [data-s='1'], [data-s='2'], [data-s='3'])
  llr-r[data-r]::before { content: attr(data-r); }

/*
 * Korean reads a word as it is said before the next one (ko.js `rubyOf`), and
 * the next one sometimes takes this word's last sound: 밥을 is ba·beul. When
 * that next word draws no reading, this one shows the sound it has with nothing
 * after it (data-ro), so 밥 beside a bare 을 says bap rather than a ba that
 * nothing on the page finishes. Scope by scope: the word in it, the next out.
 */
html[data-llr-fx='ruby'][data-llr-ruby='new'] llr-w[data-s='0']:has(+ llr-w:not([data-s='0'])) llr-r[data-ro]::after,
html[data-llr-fx='stack'][data-llr-ruby='new'] llr-w[data-s='0']:has(+ llr-w:not([data-s='0'])) llr-r[data-ro]::before { content: attr(data-ro); }
html[data-llr-fx='ruby'][data-llr-ruby='learning']
  llr-w:is([data-s='0'], [data-s='1'], [data-s='2'], [data-s='3']):has(+ llr-w:not([data-s='0'], [data-s='1'], [data-s='2'], [data-s='3']))
  llr-r[data-ro]::after,
html[data-llr-fx='stack'][data-llr-ruby='learning']
  llr-w:is([data-s='0'], [data-s='1'], [data-s='2'], [data-s='3']):has(+ llr-w:not([data-s='0'], [data-s='1'], [data-s='2'], [data-s='3']))
  llr-r[data-ro]::before { content: attr(data-ro); }

/*
 * 直排 puts the reading on the right of the glyph, which is the traditional
 * side -- and the side the status underline already claims. With readings on,
 * the mark moves to the left so the two are not drawn on top of each other.
 */
html[data-llr-fx][data-llr-underline] llr-w[data-llr-dir='ttb'][data-s='0'] { box-shadow: inset 0.16em 0 0 var(--llr-c-new); }
html[data-llr-fx][data-llr-underline] llr-w[data-llr-dir='ttb'][data-s='1'] { box-shadow: inset 0.16em 0 0 var(--llr-c-1); }
html[data-llr-fx][data-llr-underline] llr-w[data-llr-dir='ttb'][data-s='2'] { box-shadow: inset 0.16em 0 0 var(--llr-c-2); }
html[data-llr-fx][data-llr-underline] llr-w[data-llr-dir='ttb'][data-s='3'] { box-shadow: inset 0.16em 0 0 var(--llr-c-3); }
html[data-llr-fx][data-llr-underline] llr-w[data-llr-dir='ttb'][data-s='4'] { box-shadow: inset 0.16em 0 0 var(--llr-c-4); }
html[data-llr-fx][data-llr-underline] llr-w[data-llr-dir='ttb'][data-s='0'][data-tw] { box-shadow: inset 0.16em 0 0 var(--llr-c-twin); }
html[data-llr-fx][data-llr-special][data-llr-underline] llr-w[data-llr-dir='ttb'][data-s='0'][data-sp]:not(:is(.llr-cc, .llr-cc-transcript) *) {
  box-shadow: inset 0.16em 0 0 var(--llr-c-special);
}

/*
 * A page that already prints its own furigana -- NHK News Web Easy, a graded
 * reader, most Japanese EPUBs -- gets no second copy. The painter refuses these
 * words as well, so this is belt: a rule matching a word inside the page's own
 * <ruby> still pays for the annotation line's height even when it draws
 * nothing, and the painter never creating the segment costs nothing at all.
 */
ruby llr-r[data-r] { display: inline !important; }
ruby llr-r[data-r]::after,
ruby llr-r[data-r]::before { content: none !important; }

/*
 * YouTube gets readings in its subtitles only (applyRuby, data-llr-ruby-cc).
 * Everywhere else on its pages a line is cut to a fixed height -- a title
 * beside a thumbnail to two lines, a channel name to one -- and a line raised
 * by its readings pushed the last one out of sight, under the channel name.
 * Off there band and all, as inside <ruby> above: an empty annotation still
 * raises its line.
 */
html[data-llr-ruby-cc] llr-r[data-r]:not(:is(.llr-cc, .llr-cc-transcript, .ytp-caption-window-container, .caption-window) llr-r) {
  display: inline !important;
}
html[data-llr-ruby-cc] llr-r[data-r]:not(:is(.llr-cc, .llr-cc-transcript, .ytp-caption-window-container, .caption-window) llr-r)::after,
html[data-llr-ruby-cc] llr-r[data-r]:not(:is(.llr-cc, .llr-cc-transcript, .ytp-caption-window-container, .caption-window) llr-r)::before {
  content: none !important;
}

/*
 * Meanings over the words in books and Sluka's reader (settings.readWordTr;
 * gloss-paint.js writes each word's meaning into data-gloss), as SlukaCC's
 * over a subtitle. Only inside the containers marked data-llr-glossed: the
 * book's pages and the reader's article, never a site's own page, whose
 * layout a taller line would break.
 *
 * The meaning is the word's ::before, set absolutely in small type centred
 * over it, so the word's box -- what its colour fills, the outline of the
 * word open -- stays the word's, and the text flows as it did: the words
 * never move for their meanings. The room over each line is leading: every
 * word of the page, shown a meaning or not, gets the same taller line, so the
 * lines stay evenly spaced and grading a word known takes its meaning off
 * without the paragraph moving up. Important, as a book's own stylesheet that
 * sets every line's height with !important put each meaning on the line above.
 * With readings over the characters (data-llr-fx), or the book's own in a
 * paragraph with <ruby> (data-llr-gl-rt, gloss-paint.js), every meaning of the
 * line stands over the readings' row, never on it.
 *
 * A meaning wider than its word is slid along its line out of its
 * neighbours' way (--llr-gl-shift) and, on a line too crowded for that, cut
 * to a width with "…" (--llr-gl-max): gloss-paint.js placeLine.
 *
 * Generated content, never page text: selecting, copying and read aloud see
 * the text as it was written. A press on it is a press on its word, since
 * half of each line is meanings.
 */
html[data-llr-gloss] [data-llr-glossed] llr-w {
  line-height: 2.6 !important;
  /* A word broken over two lines has no one place to stand its meaning over. */
  white-space: nowrap;
}
html[data-llr-gloss][data-llr-fx] [data-llr-glossed] llr-w,
html[data-llr-gloss] [data-llr-glossed] [data-llr-gl-rt] llr-w { line-height: 3.7 !important; }
/* Only a word with a meaning is the box its meaning is placed in: thousands of positioned words made every
   grade on a long page lay it all out again for longer. */
html[data-llr-gloss] [data-llr-glossed] llr-w[data-gloss] { position: relative; }
html[data-llr-gloss] [data-llr-glossed] llr-w[data-gloss]::before {
  content: none;
  position: absolute;
  left: 50%;
  bottom: 100%;
  transform: translateX(calc(-50% + var(--llr-gl-shift, 0px)));
  max-width: var(--llr-gl-max, none);
  overflow: hidden;
  text-overflow: ellipsis;
  /* gloss-paint.js measures each meaning in this type: change both together. */
  font: 450 max(10px, 0.5em)/1.1 system-ui, 'Segoe UI', Roboto, sans-serif;
  letter-spacing: 0;
  text-indent: 0;
  text-transform: none;
  white-space: nowrap;
  /* Its own direction, by its own first letter: an English meaning over an
     Arabic word is not turned round. */
  unicode-bidi: plaintext;
  /* The page's own colour, as the readings take it: the one legible on the
     paper, and what survives a book multiplying its words against it. */
  color: currentColor;
  opacity: 0.72;
  -webkit-user-select: none;
  user-select: none;
}
/* Over the reading: 1.3 of the meaning's em is a little over the reading's 0.55 of the word's. */
html[data-llr-gloss][data-llr-fx] [data-llr-glossed] llr-w[data-gloss]::before,
html[data-llr-gloss] [data-llr-glossed] [data-llr-gl-rt] llr-w[data-gloss]::before { bottom: calc(100% + 1.3em); }
/* Which words show theirs: a pure attribute gate, as SlukaCC's, so grading
   a word known takes its meaning away without a repaint. */
html[data-llr-gloss='all'] [data-llr-glossed] llr-w[data-gloss]::before,
html[data-llr-gloss='learning'] [data-llr-glossed] llr-w[data-gloss]:is([data-s='0'], [data-s='1'], [data-s='2'], [data-s='3'], [data-s='4'])::before {
  content: attr(data-gloss);
}
/* Blurred (readWordTr 'blur') until the word is open, or pointed at with a
   mouse: a finger's :hover stays on the last word tapped after its panel has
   closed, which left that meaning sharp. */
html[data-llr-gloss-blur] [data-llr-glossed] llr-w:not([data-llr-hov], [data-llr-sel])::before { filter: blur(0.25em); }
@media (hover: hover) {
  html[data-llr-gloss-blur] [data-llr-glossed] llr-w:hover::before { filter: none; }
}
/* Not over a book laid out top to bottom, whose lines are columns, nor over a
   printed PDF page's invisible text layer: neither gets a meaning
   (gloss-paint.js), nor the taller line. */
html[data-llr-gloss] [data-llr-glossed] :is([data-vertical], .textLayer) llr-w,
html[data-llr-gloss] [data-llr-glossed] llr-w[data-llr-dir='ttb'] { position: static; line-height: inherit !important; white-space: inherit; }

/*
 * A haodoo-style character table rebuilt as real CSS 直排, so the highlighter
 * can wrap whole words instead of one glyph per cell.
 */
.llr-vert {
  writing-mode: vertical-rl;
  overflow: auto;
  box-sizing: border-box;
  line-height: 1.9;
  letter-spacing: 0.08em;
  text-orientation: mixed;
}

/*
 * Streaming captions are painted with pointer-events: none so they don't
 * steal clicks from the player. Words inside them have to be hoverable.
 */
.ytp-caption-window-container,
.caption-window,
.ytp-caption-segment,
.player-timedtext,
.player-timedtext-text-container,
[data-uia="player-timedtext"],
.llr-caption-root {
  pointer-events: auto !important;
}

/* SlukaCC toggle, injected into the player's own control bar. */
.llr-ap-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 36px;
  padding: 0 10px;
  margin: 0 4px;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: #eee;
  font: 600 12px/1 system-ui, "Segoe UI", sans-serif;
  letter-spacing: 0.02em;
  cursor: pointer;
  vertical-align: top;
}
.llr-ap-btn:hover { background: rgba(255, 255, 255, 0.12); }
.llr-ap-btn[aria-pressed="true"] { color: #fff; }
.llr-ap-btn[aria-pressed="true"] .llr-ap-mark { color: #f2b418; }
.llr-ap-btn .llr-ap-mark { font-size: 11px; }
.ytp-right-controls .llr-ap-btn {
  height: 100%;
  background: transparent;
}
.watch-video--bottom-controls-container .llr-ap-btn,
[data-uia="controls-standard"] .llr-ap-btn {
  height: 32px;
  background: rgba(0, 0, 0, 0.45);
}

/* ======================================================================
 * SlukaCC: Sluka's own subtitles inside the video player (cc-overlay.js).
 *
 * Interface text is in fixed pixels, like YouTube's own controls; the lines
 * themselves are sized from the player's width (--llr-cc-w, measured), so they
 * grow into theatre mode and full screen instead of staying one size.
 * ====================================================================== */

/* YouTube's own captions, while Sluka's are loading or showing. Hidden rather
   than switched off: the player's own caption request carries the token every
   other track is fetched with. */
#movie_player[data-llr-cc-on] .ytp-caption-window-container {
  display: none !important;
}
/* Netflix's own subtitles, the same way (cc-site-netflix.js puts the
   attribute on its player view). */
.watch-video--player-view[data-llr-cc-on] .player-timedtext {
  display: none !important;
}
/* YouTube's phone site (cc-site-youtube-phone.js), in the app's YouTube tab:
   the attribute is on the box that holds the picture and the controls. */
#player-container-id[data-llr-cc-on] .ytp-caption-window-container {
  display: none !important;
}
/* Any other player's (cc-site-generic.js): the caption box it draws itself,
   found on screen, and anything the browser draws from a text track the
   player switches back on. Hidden, not removed: the box is still read. */
[data-llr-cc-theirs] {
  visibility: hidden !important;
}
video[data-llr-cc-hide]::cue {
  color: transparent !important;
  background: transparent !important;
  text-shadow: none !important;
}

/* The box rests at its saved height, or just clear of YouTube's control bar
   (--llr-cc-lift, measured) when that is higher -- whether or not the bar is
   showing. Lifting it only while the bar showed made it jump the moment the
   pointer reached it, and the pointer then sat on the seek bar instead of on
   the line it was aimed at. A box dragged higher stays where it was put. */
.llr-cc {
  --llr-cc-k: 1;
  --llr-cc-kt: 1;
  --llr-cc-s: 0.88;
  position: absolute;
  left: 50%;
  bottom: max(calc(var(--llr-cc-pos, 8) * 1%), var(--llr-cc-lift, 0px));
  z-index: 38;
  width: max-content;
  max-width: 88%;
  transform: translateX(-50%);
  /* The box and the buttons take the pointer; the space around them is the
     video's. Overrides the caption rule near the top of this file. */
  pointer-events: none !important;
  font-size: calc((13px + var(--llr-cc-w, 854px) * 0.0165) * var(--llr-cc-k) * var(--llr-cc-s));
  text-align: center;
  color: #fff;
  transition: bottom 0.2s cubic-bezier(0.2, 0, 0, 1), left 0.2s cubic-bezier(0.2, 0, 0, 1);
}
/* CJK gets the full size: a character carries a word's worth of meaning. */
.llr-cc[data-script='cjk'] { --llr-cc-s: 1; }
/* Each line on one row wherever the picture has room for it (the owner's
   call, 2026-10-04): it wraps only past 88% of the player, in every script,
   and one that only just runs over is drawn a little smaller instead
   (cc-overlay.js, _fit). A reading measure of 26em for anything with spaces
   used to put an Italian or a Vietnamese line onto two rows at about 50
   characters, with half the player still free. */
.llr-cc[data-size='s'] { --llr-cc-k: 0.82; }
.llr-cc[data-size='l'] { --llr-cc-k: 1.2; }
.llr-cc[data-size='xl'] { --llr-cc-k: 1.42; }
/* The translation's own size (cc.trSize), on the same steps. The box's text is
   the subtitle's size, so the translation undoes that and applies its own. */
.llr-cc[data-tr-size='s'] { --llr-cc-kt: 0.82; }
.llr-cc[data-tr-size='l'] { --llr-cc-kt: 1.2; }
.llr-cc[data-tr-size='xl'] { --llr-cc-kt: 1.42; }
/* Put lower than YouTube's bar (data-low): it rests there while the bar is
   hidden, and rises clear of it while the bar shows -- the video paused, or
   the pointer moving over the picture. Not while the pointer is on the
   subtitles themselves: the bar fading out must not slide a word out from
   under the pointer reading it -- one under a clear layer included
   (data-llr-hov, reader.js wordUnderLayer). */
#movie_player.ytp-autohide .llr-cc[data-low]:not(:hover, :has(llr-w[data-llr-hov])),
.llr-cc[data-low][data-bar-hidden]:not(:hover, :has(llr-w[data-llr-hov])) {
  bottom: calc(var(--llr-cc-pos, 8) * 1%);
}
/* Being moved, it follows the grip and nothing else, down past the bar too;
   from the keyboard, it shows where the arrows are putting it. */
.llr-cc[data-drag],
.llr-cc[data-low]:has(.llr-cc-grip:focus-visible) {
  bottom: calc(var(--llr-cc-pos, 8) * 1%);
}
.llr-cc[data-drag] { transition: none; }
/* SlukaCC off on a video it has subtitles for: only the chip row, to say so. */
.llr-cc[data-idle] :is(.llr-cc-box, .llr-cc-btns) { display: none; }

/* YouTube's phone player, in full screen: the subtitles are drawn over its
   control layer and rest low on the picture, where its own captions sit.
   While its controls show -- a tap in the middle brings them up, for a few
   seconds while the video plays and for as long as it is paused by hand --
   the subtitles make way: dimmed but still readable, and letting every touch
   through to play, the seek bar and full screen under them. The two cannot
   both be touched at once, and the player's own controls come first; a tap
   on the picture beside its buttons puts them away again, and so does a
   touch on one of SlukaCC's sides (data-llr-quiet below). Not while SlukaCC's
   own menu is open: the menu is about the subtitles. See
   cc-site-youtube-phone.js. */
#player-container-id > .llr-cc { transition: bottom 0.2s cubic-bezier(0.2, 0, 0, 1), opacity 0.15s ease; }
/* And they rise clear of its seek bar meanwhile (the site's liftBar), then rest
   low again: dimmed but where they were, they lay across the bar. */
#player-container-id > .llr-cc[data-bar-hidden]:not([data-below]) { bottom: calc(var(--llr-cc-pos, 8) * 1%); }
/* Full screen, nothing on the picture is the browser's to pan, and a swipe
   down on the right is SlukaCC's: a busy frame let the browser take one for a
   pan mid-move and cancel it (cc-touch.js, which stops a touch's default only
   once the page has heard of it). */
#player-container-id[data-llr-cc-on]:fullscreen { touch-action: none; }
#player-container-id:not([data-llr-cc-menu]) > .llr-cc:not([data-bar-hidden]):not([data-below]) { opacity: 0.5; }
#player-container-id:not([data-llr-cc-menu]) > .llr-cc:not([data-bar-hidden]):not([data-below]),
#player-container-id:not([data-llr-cc-menu]) > .llr-cc:not([data-bar-hidden]):not([data-below]) * {
  pointer-events: none !important;
}
/* YouTube's "Includes paid promotion" label, which SlukaCC's button in the
   top row lies over on a phone held upright (cc-site-youtube-phone.js,
   buttonSlot): still shown, but its link takes no touch while the button is
   there. The button fades out with YouTube's controls, the label stays, and
   a tap meant for the button as they went -- they go two seconds after they
   came, while the video plays -- opened YouTube's help page over the video. */
#player-container-id:has(.llr-cc-ctl) .ytmPaidContentOverlayLink {
  pointer-events: none !important;
}
/* The controls the player raises by itself after a play-on of SlukaCC's own,
   kept out of sight for the moment it takes to put them away (the site's
   play): they are not the viewer's. And in full screen the controls a touch
   on one of the sides put away, for as long as the video stays stopped (the
   site's hideControls). Out of reach as well as out of sight, every part of
   them -- their seek bar and SlukaCC's own button are in their layer too: a
   finger on one of the sides that met an unseen button of theirs would bring
   them back rather than step, and a tap beside an open word would reach them
   rather than close the word (reader.js, onPicturePress). */
#player-container-id[data-llr-quiet] #player-control-overlay {
  opacity: 0 !important;
  transition: none !important;
}
#player-container-id[data-llr-quiet] #player-control-overlay,
#player-container-id[data-llr-quiet] #player-control-overlay * {
  pointer-events: none !important;
}
/* And the subtitles come to full strength, and down to rest, at once: the
   word popup is placed against the line as the finger lifts, a moment after
   the touch that put the controls away, and must find it where it rests. */
#player-container-id[data-llr-quiet] > .llr-cc { transition: none; }

/* Upright on the phone (the site's lineBelow): the subtitles under the
   picture, over the top of the page and the width of the screen. The picture
   is 232 px tall and YouTube's controls take its top, middle and bottom rows;
   under it, nothing of theirs ever lands on a word. A band of the player's own
   black, so the picture and its line read as one player, holding its height
   between lines so the page under it never flickers into view -- and no lower
   than a line and its translation even before the first one. The chips stay
   just over it, at the foot of the picture. */
#player-container-id > .llr-cc[data-below] {
  top: 100%;
  bottom: auto;
  left: 0;
  width: 100%;
  max-width: none;
  transform: none;
  transition: none;
}
.llr-cc[data-below] .llr-cc-box {
  min-height: calc(1.42em + max(0.62em * var(--llr-cc-kt) / var(--llr-cc-k), 13px * var(--llr-cc-kt)) * 1.36 + 0.5em);
  padding: 0.22em 0.7em 0.3em;
  border-radius: 0;
  /* Solid whatever the background setting says: there is no picture under the
     band to see through to, only the page. And so no edge round the letters. */
  --llr-cc-bg: 1;
  background: #000;
}
.llr-cc[data-below][data-empty] .llr-cc-box {
  opacity: 1;
  visibility: visible;
}
.llr-cc[data-below][data-empty] .llr-cc-box > * { visibility: hidden; }
/* With the menu's ⋯ at the band's right end (cc-ui.css), the line keeps clear
   of its 48 px on both sides, so a tap meant for a word or the translation
   never meets it, and the line stays centred under the picture. */
.llr-cc[data-below]:has(> .llr-cc-bandmenu) .llr-cc-box { padding-left: 48px; padding-right: 48px; }
.llr-cc[data-below] .llr-cc-bar { box-sizing: border-box; padding: 0 10px 6px; }
/* And out of the way while YouTube's controls show: upright they sit on its
   seek bar and time, and an Undo there took the touches meant for the bar; on
   the picture the line lets every touch through to the controls then, and an
   Undo drawn over ⏮ was YouTube's previous video (found 2026-10-01). */
#player-container-id > .llr-cc:not([data-bar-hidden]) .llr-cc-bar { visibility: hidden; }
/* Its translation at a size a phone can read: 0.62 of a Latin line 412 px
   wide is under 11 px. That floor moves with the translation's own size. */
.llr-cc[data-below] .llr-cc-line[data-cc='tr'] {
  margin-top: 0.08em;
  font-size: max(calc(0.62em * var(--llr-cc-kt) / var(--llr-cc-k) * var(--llr-cc-squeeze, 1)), calc(13px * var(--llr-cc-kt)));
}
/* The band is the one box here (the owner's call, 2026-10-04): the lines and
   notes in it have none of their own, as they have on the picture, and each
   takes the band's whole width -- a tap that uncovers a blurred line lands
   anywhere across it. */
.llr-cc[data-below] .llr-cc-box { align-items: stretch; gap: 0; }
.llr-cc[data-below] .llr-cc-line {
  width: auto;
  padding: 0;
  border-radius: 0;
  background: none;
}
.llr-cc[data-below] .llr-cc-note {
  padding: 0.1em 0;
  border-radius: 0;
  background: none;
}
/* YouTube's page pushed down by the band (cc-overlay.js sets the height): laid
   over it, the band hid the video's title and its "more", the way to the
   description, for as long as SlukaCC was on. */
html[data-llr-cc-band] ytm-watch .watch-below-the-player { padding-top: var(--llr-cc-band, 0px); }

/* SlukaCC's menu ends above the subtitles, which keep their place and their
   width while it is open (cc-controls.js). In a player too small for it to end
   above the chips too, it reaches down over their row, and the line buttons
   beside the line step aside meanwhile rather than come up under it. */
[data-llr-cc-menu-low] > .llr-cc .llr-cc-btns { visibility: hidden; }
/* Smaller still, the chips step aside as well, rather than lie under the menu
   that takes their room; and with its first view open, the line closes up to
   its subtitle row, which keeps its place at the foot of the box and is never
   covered: the translation row steps aside too (the owner's call,
   2026-10-03). Back the moment the menu closes or goes to another view. */
[data-llr-cc-menu-bare] > .llr-cc .llr-cc-bar { visibility: hidden; }
[data-llr-cc-menu-tight] > .llr-cc :is(.llr-cc-line[data-cc='tr'], .llr-cc-trnote) { display: none; }
/* Full screen with the phone turned, the menu a sheet down the right of the
   picture (cc-controls.js, _placeSheet): the line moves into the room left of
   it, centred there and wrapping if it must, none of it under the sheet. */
[data-llr-cc-menu-side] > .llr-cc {
  left: calc(var(--llr-cc-side) / 2);
  max-width: calc(var(--llr-cc-side) - 16px);
}

/* No line of the video's during an ad. */
#movie_player.ad-showing .llr-cc,
#movie_player.ad-interrupting .llr-cc,
.llr-cc[data-ad] {
  display: none !important;
}

/* YouTube's corner overlays (.ytp-overlays-container, z-index 59: the
   channel's watermark, a timely action, a product badge) lay over a line long
   enough to reach the bottom-right corner of a windowed player, and took the
   pointer there: the words under them, seen through them, showed a hand and
   took no click (the owner's report, 2026-10-18). The line goes over those
   corners, still under the control bar (59) and every menu (67 and up): the
   overlays come down to 58, which keeps them where they were among YouTube's
   own layers -- over the top bar (58, before them), under the control bar --
   and the line goes to 58 after them, with the clip bar (cc-clip-bar.js)
   after it, over the line as before. Not in full screen, where those corners
   hold buttons to press (like, share) and the line was never reported under
   them: there everything stays as it was. The player is marked while the line
   is in it (data-llr-cc-here, cc-overlay.js mount): a :has(> .llr-cc) rule had every
   change in the player restyle the whole of it, the lag 0.6.6 brought to every
   key and hover (2026-10-19). */
#movie_player:not(.ytp-fullscreen) > :is(.llr-cc, .llr-cc-clip) { z-index: 58; }
#movie_player:not(.ytp-fullscreen)[data-llr-cc-here] > .ytp-overlays-container { z-index: 58; }

/* What holds the lines, with no background of its own: the subtitle and the
   translation each sit in a box of their own, centred one above the other,
   each just bigger than its own text (the owner's call, 2026-10-04). One box
   round both was as wide as the wider of the two, and a line that wrapped
   kept the box at the full width allowed -- a Vietnamese line balanced over
   two rows of 300 px sat in a box of 600. The holder is still what the pointer
   is on, between and beside the boxes too: the whole block is the line, for
   the line buttons, for the way out of a word's popup (reader.js, leaveAreas)
   and for a click beside a word, which must not reach the player. */
.llr-cc-box {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.14em;
  /* As each box thins out (cc.background, below), a dark edge round the
     letters takes over keeping them readable on a bright picture: none from
     60% up, growing to its full strength with no box at all. Everything in the
     holder inherits it. The tight shadow twice over, or the thin translation
     line barely showed on a white frame. */
  --llr-cc-edge: clamp(0, 1 - var(--llr-cc-bg, 0.84) / 0.6, 1);
  /* By name as well, for a blurred subtitle's words to take back (below). */
  --llr-cc-edges:
    0 0 0.05em rgb(0 0 0 / var(--llr-cc-edge)),
    0 0 0.05em rgb(0 0 0 / var(--llr-cc-edge)),
    0 0.03em 0.12em rgb(0 0 0 / calc(var(--llr-cc-edge) * 0.9)),
    0 0 0.35em rgb(0 0 0 / calc(var(--llr-cc-edge) * 0.6));
  text-shadow: var(--llr-cc-edges);
  pointer-events: auto;
  transition: opacity 0.15s ease, visibility 0s linear 0s;
}
/* Between lines: nothing on the picture. Faded out, not snapped, and kept in
   place so the next line does not jump in from somewhere else. */
.llr-cc[data-empty] .llr-cc-box {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.15s ease, visibility 0s linear 0.15s;
}

/* Each line's box and each note's, as solid as the viewer set it
   (cc.background, cc-overlay.js): 84% unless the menu's slider moved it. As
   wide as the text it holds: a line on one row is that by itself, and one
   that wraps is brought in to its widest row (--llr-cc-fit, cc-overlay.js's
   _fit) -- the browser leaves a box that wraps at the whole width it had. */
.llr-cc-line,
.llr-cc-note {
  box-sizing: border-box;
  max-width: 100%;
  background: rgba(8, 8, 7, var(--llr-cc-bg, 0.84));
}
/* Rows of even length: a line that wraps never leaves one character or one
   word alone on its last row. */
.llr-cc-line {
  display: block;
  width: var(--llr-cc-fit, auto);
  text-wrap: balance;
}
.llr-cc-line > :is(.llr-cc-txt, .llr-cc-tr-text) {
  transition: filter 0.15s ease, opacity 0.15s ease;
}
.llr-cc-line[data-cc='target'] {
  padding: 0.12em 0.55em 0.18em;
  border-radius: 0.32em;
  /* A little smaller where that keeps the line on one row (_fit). */
  font-size: calc(1em * var(--llr-cc-squeeze, 1));
  font-family: var(--llr-cc-lead,) system-ui, 'Segoe UI', Roboto, sans-serif;
  font-weight: 500;
  line-height: 1.34;
  letter-spacing: 0.01em;
  -webkit-user-select: text;
  user-select: text;
}
.llr-cc-line[data-cc='target']:is(:lang(zh), :lang(yue)) {
  font-family: var(--llr-cc-lead,) 'Noto Sans TC', 'PingFang TC', 'Microsoft JhengHei', system-ui, sans-serif;
  letter-spacing: 0.03em;
  line-height: 1.42;
}
.llr-cc-line[data-cc='target']:is(:lang(zh-Hans), :lang(zh-CN), :lang(zh-SG)) {
  font-family: var(--llr-cc-lead,) 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', system-ui, sans-serif;
}
.llr-cc-line[data-cc='target']:lang(ja) {
  font-family: var(--llr-cc-lead,) 'Noto Sans JP', 'Hiragino Sans', 'Yu Gothic UI', Meiryo, system-ui, sans-serif;
  line-height: 1.42;
}
.llr-cc-line[data-cc='target']:lang(ko) {
  font-family: var(--llr-cc-lead,) 'Noto Sans KR', 'Apple SD Gothic Neo', 'Malgun Gothic', system-ui, sans-serif;
  line-height: 1.42;
}
/* Readings on: the row of pinyin (or kana) above the characters gets some
   room of its own, rather than starting against the box's top edge. Chrome
   lets a first line's readings overflow upward, out of the row, so the room
   has to be over the row: in the subtitle's own box, 0.2em more padding (the
   margin this was when one box held both lines, and readings ran out of the
   top of the subtitle's box once it had one). In the band, still the margin. */
html[data-llr-fx] .llr-cc-line[data-cc='target']:has(llr-r[data-r]) { padding-top: 0.32em; }
html[data-llr-fx] .llr-cc[data-below] .llr-cc-line[data-cc='target']:has(llr-r[data-r]) {
  padding-top: 0;
  margin-top: 0.2em;
}
/* At a stop, over the covered subtitle: the box to type what was heard into
   (cc.dictation, cc-dictation.js). Smaller than the line, and solid whatever
   the line's background is set to: it is written in, not read through. */
.llr-cc-dict {
  box-sizing: border-box;
  width: min(100%, 18em);
  /* Centred over the line in the phone's band under the video too, where the box's rows stretch. */
  align-self: center;
}
.llr-cc-dict[hidden] { display: none; }
.llr-cc-dict-in {
  box-sizing: border-box;
  width: 100%;
  margin: 0;
  padding: 0.32em 0.6em;
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 0.32em;
  background: rgba(8, 8, 7, 0.92);
  color: #fff;
  font: 500 max(14px, 0.72em)/1.3 var(--llr-cc-lead,) system-ui, 'Segoe UI', Roboto, sans-serif;
  text-shadow: none;
  outline: none;
  pointer-events: auto;
}
.llr-cc-dict-in:focus { border-color: rgba(255, 255, 255, 0.7); }
.llr-cc-dict-in::placeholder { color: rgba(255, 255, 255, 0.5); opacity: 1; }
.llr-cc-line[data-cc='tr'] {
  padding: 0.14em 0.7em 0.18em;
  border-radius: 0.45em;
  font: 450 calc(0.62em * var(--llr-cc-kt) / var(--llr-cc-k) * var(--llr-cc-squeeze, 1))/1.36 var(--llr-cc-lead,) system-ui, 'Segoe UI', Roboto, sans-serif;
  color: #e8e4dc;
  letter-spacing: 0.005em;
}

/* The font, for both lines (cc.font; cc-overlay.js puts it on the box as
   data-font). Sans is the faces above, as they always were. The others go in
   front of those as --llr-cc-lead, and the faces behind still draw whatever
   the ones in front lack. Serif is the book reader's, and a Chinese, Japanese
   or Korean subtitle gets its own script's serif -- Song, Mincho, Myeongjo --
   rather than serif letters beside sans characters. Monospace leaves those
   characters in the line's own faces: they are one width already. Each ends
   on the generic name, for the phone, which knows its faces only by that.
   The viewer's own font goes in front the same way (--llr-cc-own, the name
   typed in the menu). The menu writes each choice in its own face, from the
   same lists (cc-ui.css). */
.llr-cc,
.llr-cc-menu {
  --llr-cc-serif: 'Iowan Old Style', 'Palatino Linotype', Palatino, 'Book Antiqua', Georgia;
  --llr-cc-mono: Consolas, 'Cascadia Mono', 'SF Mono', Menlo, 'Roboto Mono';
}
.llr-cc[data-font='serif'] { --llr-cc-lead: var(--llr-cc-serif), serif,; }
.llr-cc[data-font='serif'] .llr-cc-line[data-cc='target']:is(:lang(zh), :lang(yue)) {
  --llr-cc-lead: 'Noto Serif TC', 'Source Han Serif TC', 'Songti TC', PMingLiU, MingLiU, var(--llr-cc-serif), serif,;
}
.llr-cc[data-font='serif'] .llr-cc-line[data-cc='target']:is(:lang(zh-Hans), :lang(zh-CN), :lang(zh-SG)) {
  --llr-cc-lead: 'Noto Serif SC', 'Source Han Serif SC', 'Songti SC', SimSun, var(--llr-cc-serif), serif,;
}
.llr-cc[data-font='serif'] .llr-cc-line[data-cc='target']:lang(ja) {
  --llr-cc-lead: 'Noto Serif JP', 'Source Han Serif', 'Hiragino Mincho ProN', 'Yu Mincho', YuMincho, 'MS Mincho', var(--llr-cc-serif), serif,;
}
.llr-cc[data-font='serif'] .llr-cc-line[data-cc='target']:lang(ko) {
  --llr-cc-lead: 'Noto Serif KR', 'Source Han Serif K', 'Nanum Myeongjo', AppleMyungjo, Batang, var(--llr-cc-serif), serif,;
}
.llr-cc[data-font='mono'] { --llr-cc-lead: var(--llr-cc-mono), monospace,; }
.llr-cc[data-font='mono'] .llr-cc-line[data-cc='target']:is(:lang(zh), :lang(yue), :lang(ja), :lang(ko)) { --llr-cc-lead: var(--llr-cc-mono),; }
.llr-cc[data-font='own'] { --llr-cc-lead: var(--llr-cc-own),; }

/* Blur: unreadable until hovered. The words' colours show through it
   (cc.blurColours, on by default): a smudge of blue where a new word is, of
   amber where one is being learned, so the ear knows where in the line the
   word to listen for is -- Language Reactor's way, and the owner's call.
   Switched off, nothing leaks through: no colours, no underlines. Readings
   never do, nor the outline of a word the arrows have reached.

   Unreadable, but plainly there: at full strength the smudge's brightest part
   measured 2.1-3.0:1 against the box over four real frames, where at 0.62 it
   was 1.6-1.8:1 and a first-time viewer saw a dark band with nothing in it.
   Only the text is blurred; the hint after it ("E to peek") stays sharp.

   The pointer uncovers the subtitle a word at a time: only the word it is on,
   or the word whose panel is open, so the one coloured smudge in a line can be
   read without the sentence round it (the owner's call, 2026-10-09). A filter
   on the line would cover its words whatever is set on them, so each word
   carries its own, and the text between the words -- spaces, punctuation,
   numbers -- is drawn as nothing but its shadow, blurred as far (a shadow's
   radius is twice the blur's). The translation is still uncovered whole. */
.llr-cc-line[data-v='blur'] { cursor: help; }
.llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt {
  -webkit-text-fill-color: transparent;
  text-shadow: 0 0 0.52em currentcolor;
}
.llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt llr-w {
  -webkit-text-fill-color: currentcolor;
  text-shadow: var(--llr-cc-edges);
  filter: blur(0.26em);
  transition: filter 0.15s ease;
}
.llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt llr-w:is(:hover, [data-llr-hov], [data-llr-sel]) { filter: none; }
.llr-cc-line[data-cc='tr'][data-v='blur'] > .llr-cc-tr-text { filter: blur(0.3em); }
.llr-cc-line[data-cc='tr'][data-v='blur']:hover > .llr-cc-tr-text { filter: none; }
.llr-cc-line[data-v='blur'] llr-w:not(:hover, [data-llr-hov], [data-llr-sel]) { outline: none !important; }
html[data-llr-cc-plainblur] .llr-cc-line[data-v='blur'] llr-w:not(:hover, [data-llr-hov], [data-llr-sel]) {
  background-color: transparent !important;
  background-image: none !important;
  box-shadow: none !important;
  text-decoration: none !important;
  color: inherit !important;
}
.llr-cc-line[data-v='blur'] llr-w:not(:hover, [data-llr-hov], [data-llr-sel]) llr-r[data-r]::before,
.llr-cc-line[data-v='blur'] llr-w:not(:hover, [data-llr-hov], [data-llr-sel]) llr-r[data-r]::after {
  visibility: hidden !important;
}
/* A touch screen has no hover to read a blur with: a tap leaves the browser's
   :hover on whatever it landed on until the next tap lands elsewhere, and
   every later line in the same place came up uncovered, the setting still
   saying Blur. There a blurred line stays blurred whatever :hover says, and a
   tap on it uncovers that line for as long as it is on screen, as E does
   (cc-overlay.js). The same for everything else hover shows: a word's
   outline, and the line buttons (below). A word opened by a step -- a side
   tap, or a controller's D-pad -- is sharp alone, as on a computer. */
@media (hover: none) {
  .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt llr-w:is(:hover, [data-llr-hov]):not([data-llr-sel]) { filter: blur(0.26em); }
  .llr-cc-line[data-cc='tr'][data-v='blur']:hover > .llr-cc-tr-text { filter: blur(0.3em); }
  .llr-cc-line[data-v='blur'] llr-w:not([data-llr-sel]) { outline: none !important; }
  html[data-llr-cc-plainblur] .llr-cc-line[data-v='blur'] llr-w:not([data-llr-sel]) {
    background-color: transparent !important;
    background-image: none !important;
    box-shadow: none !important;
    text-decoration: none !important;
    color: inherit !important;
  }
  .llr-cc-line[data-v='blur'] llr-w:not([data-llr-sel]) llr-r[data-r]::before,
  .llr-cc-line[data-v='blur'] llr-w:not([data-llr-sel]) llr-r[data-r]::after {
    visibility: hidden !important;
  }
  .llr-cc llr-w:hover:not([data-llr-sel]) { outline: none; }
}
/* A+D, with the pointer resting on the subtitles (data-cover, cc-overlay.js):
   the line heard again covered, whatever :hover says, as on a touch screen --
   until the pointer leaves the box and comes back (the owner's call,
   2026-10-04). A word the arrows open is sharp in it all the same. The hint
   after it stays, the line being covered. */
.llr-cc[data-cover] .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt llr-w:is(:hover, [data-llr-hov]):not([data-llr-sel]) { filter: blur(0.26em); }
.llr-cc[data-cover] .llr-cc-line[data-cc='tr'][data-v='blur']:hover > .llr-cc-tr-text { filter: blur(0.3em); }
.llr-cc[data-cover] .llr-cc-line[data-v='blur'] llr-w:not([data-llr-sel]) { outline: none !important; }
html[data-llr-cc-plainblur] .llr-cc[data-cover] .llr-cc-line[data-v='blur'] llr-w:not([data-llr-sel]) {
  background-color: transparent !important;
  background-image: none !important;
  box-shadow: none !important;
  text-decoration: none !important;
  color: inherit !important;
}
.llr-cc[data-cover] .llr-cc-line[data-v='blur'] llr-w:not([data-llr-sel]) llr-r[data-r]::before,
.llr-cc[data-cover] .llr-cc-line[data-v='blur'] llr-w:not([data-llr-sel]) llr-r[data-r]::after {
  visibility: hidden !important;
}
.llr-cc[data-cover] .llr-cc-line[data-v='blur']:hover > .llr-cc-hint { opacity: 1; }

/* Shift+D's ringed word (content.css above, sentence-jump.js) on the subtitles:
   brighter gold on the dark box, and kept through a blur, which takes every
   other outline away. Blurred, the word is a smudge drawn as the text between
   the words is -- its text's own shadow, blurred as far -- and not by a filter,
   which would blur the ring with it: the ring stays sharp round a word that is
   still unreadable. Uncovered as any word is, by the pointer, the arrows or E. */
.llr-cc { --llr-c-top: #ffc61a; }
.llr-cc .llr-cc-line > .llr-cc-txt llr-w[data-llr-top],
.llr-cc[data-cover] .llr-cc-line > .llr-cc-txt llr-w[data-llr-top] {
  outline: 0.1em solid var(--llr-c-top) !important;
  outline-offset: 0.06em;
}
.llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt llr-w[data-llr-top]:not([data-llr-sel]),
.llr-cc[data-cover] .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt llr-w[data-llr-top]:not([data-llr-sel]) {
  filter: none;
  -webkit-text-fill-color: transparent;
  text-shadow: 0 0 0.52em currentcolor;
}
@media (hover: hover) {
  .llr-cc:not([data-cover]) .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt llr-w[data-llr-top]:is(:hover, [data-llr-hov]) {
    -webkit-text-fill-color: currentcolor;
    text-shadow: var(--llr-cc-edges);
  }
}

/* Hide. The subtitle line stays in the page, invisible and out of the way,
   so it is already wrapped and coloured the instant E brings it back. */
.llr-cc-line[data-cc='target'][data-v='hide'] {
  position: absolute;
  inset: 0 0 auto 0;
  visibility: hidden;
  pointer-events: none;
}
.llr-cc-line[data-cc='tr'][data-v='hide'] { display: none; }

/* What a hidden line leaves behind, so the box never looks empty for no
   reason -- and a translation that failed, so it never just vanishes. */
.llr-cc-note {
  padding: 0.1em 0.9em;
  border-radius: 0.6em;
  font: 500 max(12px, 0.5em)/1.7 system-ui, 'Segoe UI', Roboto, sans-serif;
  color: #b5b3ad;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.llr-cc-note[hidden] { display: none; }

/* A load that failed, on a touch screen (cc-overlay.js): one line where the
   subtitles would be -- in the band under the picture, upright -- saying so,
   and a tap on it tries again. Shown though SlukaCC has no line to show. */
.llr-cc[data-failed] .llr-cc-box {
  display: flex;
  flex-direction: column;
  justify-content: center;
  opacity: 1;
  visibility: visible;
}
.llr-cc[data-failed] .llr-cc-box > :not(.llr-cc-fail) { display: none; }
.llr-cc[data-failed] .llr-cc-box > .llr-cc-note.llr-cc-fail {
  display: block;
  box-sizing: border-box;
  margin: 0;
  padding: 0.9em 1.1em;
  border: 0;
  color: #f2f0eb;
  text-align: center;
  white-space: normal;
  cursor: pointer;
  pointer-events: auto;
  visibility: visible;
  -webkit-tap-highlight-color: transparent;
}
/* In the band under the picture, the band's whole width, on its black. */
.llr-cc[data-below][data-failed] .llr-cc-box > .llr-cc-note.llr-cc-fail {
  width: 100%;
  padding: 0.9em 0;
  background: none;
}
.llr-cc-fail::before {
  content: '';
  display: inline-block;
  width: 7px;
  height: 7px;
  margin-right: 8px;
  border-radius: 50%;
  background: #ff8a78;
  vertical-align: 0.1em;
}

/* The quiet key after a blurred line, on the one E uncovers next. The same
   size as the hidden-line marker whichever line it follows (0.5em of the
   box's text; the translation's own text is 0.62em) -- and like it, never
   under 12 px: in a 1024-px window that was 9.7 px. Just outside the line's
   own box, in a small one of its own, so the box is only as wide as its text:
   inside it, the hint left an empty stretch at the end of the line while the
   pointer uncovered it (the owner's call, 2026-10-06). In the band under the
   picture, where each line is the band's width, it stays at the line's end. */
.llr-cc-line { position: relative; }
.llr-cc-hint {
  display: none;
  position: absolute;
  left: 100%;
  top: 0;
  bottom: 0;
  align-items: center;
  column-gap: 0.3em;
  margin-left: 6px;
  padding: 0 0.45em;
  /* As tall as its line, and as round: the line's radius, already in px. */
  border-radius: inherit;
  background: rgba(8, 8, 7, var(--llr-cc-bg, 0.84));
  font: 500 max(12px, 0.5em)/1 system-ui, 'Segoe UI', Roboto, sans-serif;
  color: #b5b3ad;
  letter-spacing: 0.02em;
  white-space: nowrap;
  transition: opacity 0.15s ease;
}
.llr-cc-line[data-cc='tr'] > .llr-cc-hint { font-size: max(12px, calc(0.806em * var(--llr-cc-k) / var(--llr-cc-kt))); }
.llr-cc-line[data-hint] > .llr-cc-hint:not([hidden]) { display: inline-flex; }
.llr-cc[data-below] .llr-cc-line[data-hint] > .llr-cc-hint:not([hidden]) {
  display: inline;
  position: static;
  margin-left: 0.7em;
  padding: 0;
  background: none;
  vertical-align: 0.2em;
}
/* Out of sight by opacity, not visibility: still under the pointer, so a
   pointer on the hint keeps the line uncovered. Hidden by visibility, it
   stopped taking the hover, the line covered itself again, the hint came
   back, and so on, many times a second. The subtitle's stays: the pointer
   uncovers no more than a word of it. */
.llr-cc-line[data-cc='tr'][data-v='blur']:hover > .llr-cc-hint { opacity: 0; }

.llr-cc-note kbd,
.llr-cc-hint kbd {
  display: inline-block;
  min-width: 1.5em;
  padding: 0 0.35em;
  border: 1px solid #7c7b74;
  border-bottom-width: 2px;
  border-radius: 0.3em;
  background: #2a2a28;
  color: #f2f0eb;
  font: 600 max(12px, 0.92em)/1.45 system-ui, 'Segoe UI', Roboto, sans-serif;
  text-align: center;
}

/* Word colours on the subtitle background. The same scheme as on a page, with
   the fills strengthened for a dark box over a moving picture (the alphas are
   worked out from the reader's own in reader.js applyColors), and the level 3
   and 4 line in the learning colour itself, which is what stands out on dark. */
.llr-cc llr-w { border-radius: 0.12em; }
.llr-cc llr-w[data-s='0'] { --llr-fill: var(--llr-cc-new, var(--llr-c-new)); }
.llr-cc llr-w[data-s='1'] { --llr-fill: var(--llr-cc-1, var(--llr-c-1)); }
.llr-cc llr-w[data-s='2'] { --llr-fill: var(--llr-cc-2, var(--llr-c-2)); }
.llr-cc llr-w[data-s='3'] { --llr-fill: var(--llr-cc-3, var(--llr-c-3)); }
.llr-cc llr-w[data-s='0'][data-tw] { --llr-fill: var(--llr-cc-twin, var(--llr-c-twin)); }
.llr-cc llr-w:is([data-s='3'], [data-s='4']) {
  box-shadow: inset 0 -0.1em 0 var(--llr-cc-line, var(--llr-c-line));
}
.llr-cc llr-w:hover,
.llr-cc llr-w[data-llr-hov],
.llr-cc llr-w[data-llr-pair],
.llr-cc llr-w[data-llr-sel] {
  outline: 0.08em solid #fff;
  outline-offset: 0.02em;
}

/*
 * Only the commonest new words coloured (cc.focusWords): every other new word
 * is drawn faint, still there to read and to click, out of the way of the
 * words worth hunting now. The engine marks them on any page (data-rare);
 * only SlukaCC's subtitles and transcript draw it.
 */
:is(.llr-cc, .llr-cc-transcript) llr-w[data-s='0'][data-rare] {
  --llr-fill: transparent;
  background-image: none;
  box-shadow: none;
  text-decoration: none;
  color: rgba(232, 228, 220, 0.45);
}

/*
 * SlukaCC's own colours (cc.colours, `own` on): the word itself in the colour,
 * as Language Reactor draws it, rather than the reader's fills -- known, new
 * and learning each in one of the viewer's choosing, and the faint new words
 * too. The subtitles and the transcript only; pages keep the reader's palette.
 * Known in the other script (Chinese and Cantonese only) has a colour of its
 * own, pink unless chosen, and so do the words marked Ignored, green as the
 * known ones unless chosen. After the underline mode's rules, which these
 * match in weight.
 *
 * The learning levels as the page has them, 1 strongest and 4 faintest: level
 * 1 in the learning colour itself, and each level after it a fifth of the way
 * further toward the subtitle's white. Not all the way: a quarter a step left
 * level 4 of the default orange too near the white beside it on a moving
 * picture, and two fifths of the colour still reads as tinted. Mixed in Oklab,
 * where equal parts look like equal steps. Here once, for the words and for
 * the menu's sample of them (cc-ui.css).
 */
html {
  --llr-cc-own-learning-2: color-mix(in oklab, var(--llr-cc-own-learning) 80%, #fff);
  --llr-cc-own-learning-3: color-mix(in oklab, var(--llr-cc-own-learning) 60%, #fff);
  --llr-cc-own-learning-4: color-mix(in oklab, var(--llr-cc-own-learning) 40%, #fff);
}
html[data-llr-cc-own] :is(.llr-cc, .llr-cc-transcript) llr-w {
  --llr-fill: transparent;
  background-image: none;
  box-shadow: none;
  text-decoration: none;
  color: var(--llr-cc-own-known);
}
html[data-llr-cc-own] :is(.llr-cc, .llr-cc-transcript) llr-w[data-s='0'] { color: var(--llr-cc-own-new); }
html[data-llr-cc-own] :is(.llr-cc, .llr-cc-transcript) llr-w[data-s='6'] { color: var(--llr-cc-own-ignored, var(--llr-cc-own-known)); }
html[data-llr-cc-own] :is(.llr-cc, .llr-cc-transcript) llr-w[data-s='1'] { color: var(--llr-cc-own-learning); }
html[data-llr-cc-own] :is(.llr-cc, .llr-cc-transcript) llr-w[data-s='2'] { color: var(--llr-cc-own-learning-2); }
html[data-llr-cc-own] :is(.llr-cc, .llr-cc-transcript) llr-w[data-s='3'] { color: var(--llr-cc-own-learning-3); }
html[data-llr-cc-own] :is(.llr-cc, .llr-cc-transcript) llr-w[data-s='4'] { color: var(--llr-cc-own-learning-4); }
html[data-llr-cc-own] :is(.llr-cc, .llr-cc-transcript) llr-w[data-s='0'][data-tw] { color: var(--llr-cc-own-twin); }
html[data-llr-cc-own] :is(.llr-cc, .llr-cc-transcript) llr-w[data-s='0'][data-rare] { color: var(--llr-cc-own-rare); }

/*
 * SlukaCC's special words (cc.specialWords): a name or a place, while it is
 * new, as text in a colour of its own over either palette -- marked by the
 * engine (data-sp, reader.js specialWord). And numbers, which are no word's,
 * as a highlight over the digits (reader.js markNumbers). A plain blur and
 * highlights off take both away like the rest.
 */
html[data-llr-cc-special] :is(.llr-cc, .llr-cc-transcript) llr-w[data-s='0'][data-sp] {
  --llr-fill: transparent;
  background-image: none;
  box-shadow: none;
  text-decoration: none;
  color: var(--llr-cc-own-special);
}
html:not([data-llr-off]) :is(.llr-cc-txt, .llr-cc-row-tg)::highlight(llr-cc-num),
html:not([data-llr-off]) :is(.llr-cc-txt, .llr-cc-row-tg) *::highlight(llr-cc-num) {
  color: var(--llr-cc-own-special);
}
/* Under a blur a highlight would paint its digits sharp over the smudge, so
   they are drawn as the text round them is (above): a shadow in their colour,
   or the line's under a plain blur. Being no word's, the pointer never
   uncovers them (the tokenizers keep digits out of words); E does, with the
   line. */
html:not([data-llr-off]) .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt::highlight(llr-cc-num),
html:not([data-llr-off]) .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt *::highlight(llr-cc-num) {
  color: transparent;
  text-shadow: 0 0 0.52em var(--llr-cc-own-special);
}
html[data-llr-cc-plainblur] .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt::highlight(llr-cc-num),
html[data-llr-cc-plainblur] .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt *::highlight(llr-cc-num) {
  text-shadow: none;
}

/*
 * A word still being learned inside a known or ignored phrase: grado, level
 * 1, in `grado superior`, Ignored (reader.js markPhraseParts). The phrase is
 * one word and draws nothing, so its learning words are drawn as highlights
 * over their characters, in the colours they have on their own. A highlight
 * can draw no box-shadow, so the line of levels 3 and 4 is an underline here.
 * Here, after the rules for SlukaCC and its own colours, to stack as they do:
 * the page's fills, then SlukaCC's, the underline mode over both, SlukaCC's
 * own colours over that. Highlights off takes them all away.
 */
html:not([data-llr-off]) :is(llr-w, llr-w *)::highlight(llr-part-1) { background-color: var(--llr-c-1); }
html:not([data-llr-off]) :is(llr-w, llr-w *)::highlight(llr-part-2) { background-color: var(--llr-c-2); }
html:not([data-llr-off]) :is(llr-w, llr-w *)::highlight(llr-part-3) {
  background-color: var(--llr-c-3);
  text-decoration: underline 0.12em var(--llr-c-line);
}
html:not([data-llr-off]) :is(llr-w, llr-w *)::highlight(llr-part-4) { text-decoration: underline 0.12em var(--llr-c-line); }

html:not([data-llr-off], [data-llr-underline]) .llr-cc :is(llr-w, llr-w *)::highlight(llr-part-1) { background-color: var(--llr-cc-1, var(--llr-c-1)); }
html:not([data-llr-off], [data-llr-underline]) .llr-cc :is(llr-w, llr-w *)::highlight(llr-part-2) { background-color: var(--llr-cc-2, var(--llr-c-2)); }
html:not([data-llr-off], [data-llr-underline]) .llr-cc :is(llr-w, llr-w *)::highlight(llr-part-3) {
  background-color: var(--llr-cc-3, var(--llr-c-3));
  text-decoration: underline 0.1em var(--llr-cc-line, var(--llr-c-line));
}
html:not([data-llr-off], [data-llr-underline]) .llr-cc :is(llr-w, llr-w *)::highlight(llr-part-4) {
  text-decoration: underline 0.1em var(--llr-cc-line, var(--llr-c-line));
}
/* Levels as fills (levelStyle 'fade'): the parts too, without the line. */
html[data-llr-fade]:not([data-llr-off], [data-llr-underline]) :is(llr-w, llr-w *)::highlight(llr-part-3) { text-decoration: none; }
html[data-llr-fade]:not([data-llr-off], [data-llr-underline]) :is(llr-w, llr-w *)::highlight(llr-part-4) {
  background-color: var(--llr-c-4);
  text-decoration: none;
}
html[data-llr-fade]:not([data-llr-off], [data-llr-underline]) .llr-cc :is(llr-w, llr-w *)::highlight(llr-part-3) { text-decoration: none; }
html[data-llr-fade]:not([data-llr-off], [data-llr-underline]) .llr-cc :is(llr-w, llr-w *)::highlight(llr-part-4) {
  background-color: var(--llr-cc-4, var(--llr-c-4));
  text-decoration: none;
}

html[data-llr-underline]:not([data-llr-off]) :is(llr-w, llr-w *)::highlight(llr-part-1) {
  background-color: transparent;
  text-decoration: underline 0.16em var(--llr-c-1);
}
html[data-llr-underline]:not([data-llr-off]) :is(llr-w, llr-w *)::highlight(llr-part-2) {
  background-color: transparent;
  text-decoration: underline 0.16em var(--llr-c-2);
}
html[data-llr-underline]:not([data-llr-off]) :is(llr-w, llr-w *)::highlight(llr-part-3) {
  background-color: transparent;
  text-decoration: underline 0.16em var(--llr-c-3);
}
html[data-llr-underline]:not([data-llr-off]) :is(llr-w, llr-w *)::highlight(llr-part-4) {
  background-color: transparent;
  text-decoration: underline 0.16em var(--llr-c-4);
}

html[data-llr-cc-own]:not([data-llr-off]) :is(.llr-cc, .llr-cc-transcript) :is(llr-w, llr-w *)::highlight(llr-part-1) {
  background-color: transparent;
  text-decoration: none;
  color: var(--llr-cc-own-learning);
}
html[data-llr-cc-own]:not([data-llr-off]) :is(.llr-cc, .llr-cc-transcript) :is(llr-w, llr-w *)::highlight(llr-part-2) {
  background-color: transparent;
  text-decoration: none;
  color: var(--llr-cc-own-learning-2);
}
html[data-llr-cc-own]:not([data-llr-off]) :is(.llr-cc, .llr-cc-transcript) :is(llr-w, llr-w *)::highlight(llr-part-3) {
  background-color: transparent;
  text-decoration: none;
  color: var(--llr-cc-own-learning-3);
}
html[data-llr-cc-own]:not([data-llr-off]) :is(.llr-cc, .llr-cc-transcript) :is(llr-w, llr-w *)::highlight(llr-part-4) {
  background-color: transparent;
  text-decoration: none;
  color: var(--llr-cc-own-learning-4);
}

/*
 * The word being said (cc.spokenWord; content/cc-spoken.js): a soft light tint
 * behind it and nothing else. The word keeps its own colour -- known, new or
 * the viewer's own -- so what it is still reads while it lights up, and the
 * dark box under the subtitle keeps the text on it legible.
 * A highlight's background is drawn over the word's own fill, so the tint
 * shows over a coloured word as over a plain one. Under a blur the word's
 * filter smudges the tint with it: where the voice is without giving the word
 * away. A hidden line stays hidden.
 */
::highlight(sluka-spoken) {
  background-color: rgba(255, 255, 255, 0.3);
}
.llr-cc-line[data-cc='target'][data-v='hide'] > .llr-cc-txt::highlight(sluka-spoken),
.llr-cc-line[data-cc='target'][data-v='hide'] > .llr-cc-txt *::highlight(sluka-spoken) {
  background-color: transparent;
}

/* Highlights off (Shift+H) takes these away as well: the words read plain. */
html[data-llr-off] :is(.llr-cc, .llr-cc-transcript) llr-w { color: inherit !important; }

/*
 * Meanings over the words (cc.wordTr; cc-overlay.js paintWordTr writes
 * each word's meaning into data-gloss). Every word of the subtitle becomes a
 * small column: its meaning in small type centred over it, then the word (the
 * owner's call, 2026-10-10: over, not under).
 *
 * The meaning is the column's ::before, a block of no height and no width of
 * its own (contain: inline-size) whose text stands up out of it (flex-end).
 * So the word's box -- what its colour fills, the line under a level 3 or 4
 * word, the outline of the word open -- stays the word's and nothing more,
 * and the meaning stands over it, in room the top margin keeps on every word
 * alike: a word graded out of the scope loses its meaning and the line keeps
 * its height. Readings over a Chinese or Japanese word are part of the
 * word's box, so its meaning stands over them, never on them. A meaning
 * wider than its word spills out to both sides, into side margins
 * paintWordTr sizes to fit it, so it never runs into the next word's -- and a
 * fill never grows past its characters, which in Chinese is all that shows
 * where a word ends. The baseline is the word's (baseline-source: last, the
 * meaning being first), so commas and full stops between the words sit where
 * they always did.
 *
 * The fill is held to the bottom of the box: with readings over a word the
 * box grows upwards, and the fill stays on the characters.
 *
 * Generated content, never page text, as the readings are: selecting, copying
 * and the AI check read the line as it was written.
 */
.llr-cc[data-word-tr] .llr-cc-txt llr-w {
  --llr-cc-gf: max(12px, 0.46em);
  display: inline-block;
  baseline-source: last;
  line-height: 1.24;
  margin-inline: var(--llr-cc-room, 0);
  margin-top: calc(var(--llr-cc-gf) * 1.5);
  background-size: 100% 1.24em;
  background-position: 0 100%;
  background-repeat: no-repeat;
  /* The other script's dotted line, as a border: a text decoration would be
     drawn under the meaning as well. Transparent elsewhere, so every word's
     column is the same height. */
  border-bottom: 0.08em dotted transparent;
}
/* In a line with readings, a word that has none -- katakana, a Latin name --
   is given the readings' row as well (as tall as theirs), so its meaning
   stands in the same row as the rest, not lower. Only while readings are
   drawn (data-llr-fx), when every annotated word has its row, shown or not.
   The line says it has readings, and its words read that: one rule
   `.llr-cc-txt:has(llr-r[data-r]) llr-w` had every class change anywhere on
   the page restyle every word, the transcript's too (measured 2026-10-24). */
html[data-llr-fx] .llr-cc[data-word-tr] .llr-cc-txt:has(llr-r[data-r]) { --llr-cc-ruby-row: 0.5em; }
html[data-llr-fx] .llr-cc[data-word-tr] .llr-cc-txt llr-w:not(:has(llr-r[data-r])) { padding-top: var(--llr-cc-ruby-row, 0); }
.llr-cc[data-word-tr] .llr-cc-txt llr-w::before {
  content: '';
  display: flex;
  justify-content: center;
  align-items: flex-end;
  contain: inline-size;
  height: 0;
  position: relative;
  top: -0.24em;
  font: 450 var(--llr-cc-gf)/1.18 var(--llr-cc-lead,) system-ui, 'Segoe UI', Roboto, sans-serif;
  letter-spacing: 0;
  white-space: nowrap;
  /* Its own direction, by its own first letter: an English meaning under an
     Arabic word is not turned round. */
  unicode-bidi: plaintext;
  color: rgba(232, 228, 220, 0.86);
  -webkit-user-select: none;
  user-select: none;
}
/* Which words show theirs: a pure attribute gate, as the readings' scope is,
   so grading a word known takes its meaning away without a repaint. */
.llr-cc[data-word-tr='all'] .llr-cc-txt llr-w[data-gloss]::before,
.llr-cc[data-word-tr='learning'] .llr-cc-txt llr-w[data-gloss]:is([data-s='0'], [data-s='1'], [data-s='2'], [data-s='3'], [data-s='4'])::before {
  content: attr(data-gloss);
}
.llr-cc[data-word-tr] .llr-cc-txt llr-w[data-s='0'][data-tw] {
  text-decoration: none;
  border-bottom-color: var(--llr-c-twin-line, currentColor);
}
html:is([data-llr-off], [data-llr-cc-own]) .llr-cc[data-word-tr] .llr-cc-txt llr-w[data-tw],
.llr-cc[data-word-tr] .llr-cc-txt llr-w[data-tw][data-rare] {
  border-bottom-color: transparent;
}
/* Blurred on their own (cc.wordTr 'blur', until E uncovers them): each
   meaning a smudge, and the word pointed at shows its own -- its panel is
   opening on it anyway. On a touch screen a swipe down on the left uncovers
   them, with the rest of the line. */
.llr-cc[data-word-blur] .llr-cc-txt llr-w::before {
  /* The subtitle's own strength for its size: unreadable, and plainly there. */
  filter: blur(0.28em);
  transition: filter 0.15s ease;
}
@media (hover: hover) {
  .llr-cc[data-word-blur] .llr-cc-txt llr-w:is(:hover, [data-llr-hov])::before { filter: none; }
}
/* A blurred line gives nothing away under it either, as its readings don't;
   a word pointed at shows its meaning with it, and E's peek all of them, as
   their own setting has them. They are never ahead of their word. */
.llr-cc[data-word-tr] .llr-cc-line[data-v='blur'] llr-w:not(:hover, [data-llr-hov], [data-llr-sel])::before { visibility: hidden; }
html[data-llr-cc-plainblur] .llr-cc[data-word-tr] .llr-cc-line[data-v='blur'] llr-w:not(:hover, [data-llr-hov], [data-llr-sel]) { border-bottom-color: transparent; }
@media (hover: none) {
  .llr-cc[data-word-tr] .llr-cc-line[data-v='blur'] llr-w:not([data-llr-sel])::before { visibility: hidden; }
  html[data-llr-cc-plainblur] .llr-cc[data-word-tr] .llr-cc-line[data-v='blur'] llr-w:not([data-llr-sel]) { border-bottom-color: transparent; }
}
/* Under A+D's cover too (data-cover): the line stays covered under the pointer, and so do its meanings. */
.llr-cc[data-cover][data-word-tr] .llr-cc-line[data-v='blur'] llr-w:not([data-llr-sel])::before { visibility: hidden; }
html[data-llr-cc-plainblur] .llr-cc[data-cover][data-word-tr] .llr-cc-line[data-v='blur'] llr-w:not([data-llr-sel]) { border-bottom-color: transparent; }
/* Unless the words with a meaning stay sharp (cc.meantSharp, data-word-sharp;
   on by default -- the owner's call, 2026-10-10): under a blurred subtitle,
   with the meanings on Show (on Blur the words stay blurred with the line,
   the owner, 2026-10-10), the words the scope gives a meaning are readable, their meaning and
   readings over them, and every other word a smudge -- the words to listen
   for in plain sight, the known ones covered. The same gate as the meaning's
   own, so a word graded known blurs with its meaning gone. On a touch screen
   and under A+D's cover as well: nothing here waits for the pointer. A plain
   blur keeps them plain, as the line round them. */
.llr-cc[data-word-sharp][data-word-tr='all'] .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt llr-w[data-gloss],
.llr-cc[data-word-sharp][data-word-tr='learning'] .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt llr-w[data-gloss]:is([data-s='0'], [data-s='1'], [data-s='2'], [data-s='3'], [data-s='4']) {
  filter: none;
}
.llr-cc[data-word-sharp][data-word-tr='all'] .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt llr-w[data-gloss]::before,
.llr-cc[data-word-sharp][data-word-tr='learning'] .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt llr-w[data-gloss]:is([data-s='0'], [data-s='1'], [data-s='2'], [data-s='3'], [data-s='4'])::before {
  visibility: visible;
}
.llr-cc[data-word-sharp][data-word-tr='all'] .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt llr-w[data-gloss] llr-r[data-r]::before,
.llr-cc[data-word-sharp][data-word-tr='all'] .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt llr-w[data-gloss] llr-r[data-r]::after,
.llr-cc[data-word-sharp][data-word-tr='learning'] .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt llr-w[data-gloss]:is([data-s='0'], [data-s='1'], [data-s='2'], [data-s='3'], [data-s='4']) llr-r[data-r]::before,
.llr-cc[data-word-sharp][data-word-tr='learning'] .llr-cc-line[data-cc='target'][data-v='blur'] > .llr-cc-txt llr-w[data-gloss]:is([data-s='0'], [data-s='1'], [data-s='2'], [data-s='3'], [data-s='4']) llr-r[data-r]::after {
  visibility: visible !important;
}

/*
 * The light theme (cc.theme, data-light; cc-overlay.js): black text on white
 * boxes, every colour above that was drawn for a black box swapped for its
 * match on white, as solid as the background slider says. Inside the box only:
 * the chips and buttons round it stay dark, on the picture as before. A thin
 * box gets a white edge round the letters rather than the black one.
 *
 * The viewer's own word colours (cc.colours) keep their hue, but one too pale
 * to read on white -- the default green and orange -- is darkened to a
 * lightness of 0.55 in Oklch; a colour already that dark is left as chosen.
 * Taken from the page's values on the holder and redefined on the box under
 * it, so every rule above that colours a word picks the darker one up, and
 * the transcript keeps the chosen ones. The learning levels fade toward the
 * black text instead of the white.
 */
.llr-cc[data-light] {
  --llr-c-top: #c98a00;
  --llr-cc-src-known: var(--llr-cc-own-known);
  --llr-cc-src-new: var(--llr-cc-own-new);
  --llr-cc-src-learning: var(--llr-cc-own-learning);
  --llr-cc-src-ignored: var(--llr-cc-own-ignored, var(--llr-cc-own-known));
  --llr-cc-src-twin: var(--llr-cc-own-twin);
  --llr-cc-src-rare: var(--llr-cc-own-rare);
  --llr-cc-src-special: var(--llr-cc-own-special);
}
.llr-cc[data-light] .llr-cc-box {
  --llr-cc-own-known: oklch(from var(--llr-cc-src-known) min(l, 0.55) c h);
  --llr-cc-own-new: oklch(from var(--llr-cc-src-new) min(l, 0.55) c h);
  --llr-cc-own-learning: oklch(from var(--llr-cc-src-learning) min(l, 0.55) c h);
  --llr-cc-own-ignored: oklch(from var(--llr-cc-src-ignored) min(l, 0.55) c h);
  --llr-cc-own-twin: oklch(from var(--llr-cc-src-twin) min(l, 0.55) c h);
  --llr-cc-own-rare: oklch(from var(--llr-cc-src-rare) min(l, 0.55) c h);
  --llr-cc-own-special: oklch(from var(--llr-cc-src-special) min(l, 0.55) c h);
  --llr-cc-own-learning-2: color-mix(in oklab, var(--llr-cc-own-learning) 80%, #111);
  --llr-cc-own-learning-3: color-mix(in oklab, var(--llr-cc-own-learning) 60%, #111);
  --llr-cc-own-learning-4: color-mix(in oklab, var(--llr-cc-own-learning) 40%, #111);
  color: #111;
  --llr-cc-edges:
    0 0 0.05em rgb(255 255 255 / var(--llr-cc-edge)),
    0 0 0.05em rgb(255 255 255 / var(--llr-cc-edge)),
    0 0.03em 0.12em rgb(255 255 255 / calc(var(--llr-cc-edge) * 0.9)),
    0 0 0.35em rgb(255 255 255 / calc(var(--llr-cc-edge) * 0.6));
}
.llr-cc[data-light]:not([data-below]) :is(.llr-cc-line, .llr-cc-note, .llr-cc-hint) {
  background: rgba(255, 255, 255, var(--llr-cc-bg, 0.84));
}
.llr-cc[data-light][data-below] .llr-cc-box { background: #fff; }
.llr-cc[data-light] .llr-cc-line[data-cc='tr'] { color: #33312c; }
.llr-cc[data-light] :is(.llr-cc-note, .llr-cc-hint) { color: #5c5a54; }
.llr-cc[data-light][data-failed] .llr-cc-box > .llr-cc-note.llr-cc-fail { color: #1a1a18; }
.llr-cc[data-light] :is(.llr-cc-note, .llr-cc-hint) kbd {
  border-color: #a9a79f;
  background: #f1efea;
  color: #1a1a18;
}
.llr-cc[data-light] llr-w[data-s='0'][data-rare] { color: rgba(26, 26, 24, 0.45); }
.llr-cc[data-light][data-word-tr] .llr-cc-txt llr-w::before { color: rgba(26, 26, 24, 0.8); }
.llr-cc[data-light] llr-w:is(:hover, [data-llr-hov], [data-llr-pair], [data-llr-sel]) { outline-color: #111; }
.llr-cc[data-light] .llr-cc-txt::highlight(sluka-spoken),
.llr-cc[data-light] .llr-cc-txt *::highlight(sluka-spoken) {
  background-color: rgba(0, 0, 0, 0.14);
}

/* The row above the box: the chips -- auto-pause and the loop while they are
   on, and what the last key did. */
.llr-cc-bar {
  position: absolute;
  left: 50%;
  bottom: 100%;
  display: flex;
  align-items: flex-end;
  gap: 8px;
  width: 100%;
  min-width: max-content;
  padding-bottom: 6px;
  transform: translateX(-50%);
  pointer-events: none;
  font: 600 12px/1 system-ui, 'Segoe UI', Roboto, sans-serif;
  text-align: left;
}
.llr-cc[data-empty] .llr-cc-btns { visibility: hidden; }
/* Read in the transcript instead (cc.videoSubs, cc-overlay.js): the lines and
   their buttons gone from the picture, whatever else they would show. Opacity
   too: the meanings and readings of sharp words under a blur are made visible
   again inside a hidden box, and opacity reaches them where visibility does not. */
.llr-cc[data-subs-off] .llr-cc-box,
.llr-cc[data-subs-off] .llr-cc-btns {
  visibility: hidden !important;
  opacity: 0 !important;
  pointer-events: none !important;
}
.llr-cc-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 26px;
  padding: 0 11px;
  border-radius: 13px;
  background: rgba(8, 8, 7, 0.84);
  color: #fff;
  font: 600 12px/1 system-ui, 'Segoe UI', Roboto, sans-serif;
  white-space: nowrap;
}
.llr-cc-chip[hidden] { display: none; }
.llr-cc-chip[data-pop] { animation: llr-cc-pop 0.16s cubic-bezier(0.2, 0, 0, 1); }
@keyframes llr-cc-pop {
  from { opacity: 0.2; transform: translateY(3px); }
}
/* On a touch screen the chips say things in words -- "swipe down on the right
   to play on" -- and in Russian or German one such sentence is wider than a
   phone's picture (386 and 388 px against 360), so a chip that never wraps ran
   off both of its edges, the bar being as wide as its widest chip. Here the bar
   is as wide as its chips (and as the line, so a short chip still starts at the
   line's left edge), but never wider than the player less 8 px a side;
   chips that do not fit side by side take a row each, and one that is longer
   than a row wraps onto a second line, growing upward from the line. A
   pointer's chips are short and keep their one row. */
@media (hover: none) {
  .llr-cc-bar {
    flex-wrap: wrap;
    gap: 4px 8px;
    width: max-content;
    min-width: 100%;
    max-width: calc(var(--llr-cc-w, 100vw) - 16px);
  }
  .llr-cc-chip {
    height: auto;
    min-height: 16px;
    padding-top: 5px;
    padding-bottom: 5px;
    line-height: 1.25;
    text-align: center;
    white-space: normal;
  }
  .llr-cc-chip::before,
  .llr-cc-chip > svg { flex: none; }
}
/* A picked word's audio clip, shaped before its card keeps it (cc-clip-bar.js):
   at the top of the picture -- the word panel opens over the line, and covered
   the bar's play button when it sat there -- in the chips' dark glass, the clip
   lit in Sluka's amber and the subtitle's own times a faint band under it. */
.llr-cc-clip {
  position: absolute;
  left: 50%;
  top: 12px;
  z-index: 39;
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: min(620px, calc(100% - 24px));
  box-sizing: border-box;
  padding: 8px 10px;
  border-radius: 12px;
  background: rgba(8, 8, 7, 0.88);
  color: #fff;
  font: 600 12px/1.3 system-ui, 'Segoe UI', Roboto, sans-serif;
  text-align: left;
  transform: translateX(-50%);
  pointer-events: auto;
}
.llr-cc-clip[hidden],
.llr-cc-clip [hidden] { display: none; }
.llr-cc-clip-row { display: flex; align-items: center; gap: 10px; }
.llr-cc-clip button {
  flex: none;
  border: 0;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
  font: inherit;
  cursor: pointer;
}
.llr-cc-clip button:hover:not(:disabled) { background: rgba(255, 255, 255, 0.24); }
.llr-cc-clip button:disabled { opacity: 0.4; cursor: default; }
.llr-cc-clip-play { width: 32px; height: 32px; font-size: 13px; }
/* Reset, Cancel and Done: small, side by side at the bar's end. */
.llr-cc-clip-tools { display: flex; flex: none; gap: 4px; }
.llr-cc-clip-tool { height: 22px; padding: 0 7px; font-size: 11px !important; border-radius: 6px !important; }
.llr-cc-clip-tool[data-cancel]:hover:not(:disabled) { background: rgba(255, 110, 90, 0.4); }
/* The waveform over a ruler of the video's time (cc-clip-bar.js RULER_H, 14px): the edges and the clip span the waveform only. */
.llr-cc-clip-wave {
  position: relative;
  flex: 1 1 auto;
  min-width: 120px;
  height: 54px;
  cursor: ew-resize;
  touch-action: none;
}
.llr-cc-clip-wave canvas { display: block; width: 100%; height: 100%; }
.llr-cc-clip-line,
.llr-cc-clip-sel { position: absolute; pointer-events: none; }
.llr-cc-clip-line { top: 39px; height: 2px; border-radius: 1px; background: rgba(255, 255, 255, 0.5); }
.llr-cc-clip-sel { top: 0; bottom: 14px; border-radius: 4px; background: rgba(242, 180, 24, 0.14); }
.llr-cc-clip-edge {
  position: absolute;
  top: -3px;
  bottom: 12px;
  width: 4px;
  margin-left: -2px;
  border-radius: 2px;
  background: #f2b418;
  outline: none;
}
.llr-cc-clip-edge::before { content: ''; position: absolute; inset: 0 -6px; }
.llr-cc-clip-edge:focus-visible { box-shadow: 0 0 0 2px #fff; }
/* A controller's mark (cc-clip-bar.js padFocus), as the keyboard's ring; an edge taken hold of to move, wider and white. */
.llr-cc-clip [data-pad-focus] { outline: 2px solid #fff; outline-offset: 2px; }
.llr-cc-clip-edge[data-pad-focus] { outline: none; box-shadow: 0 0 0 2px #fff; }
.llr-cc-clip-edge[data-pad-grab] { width: 6px; margin-left: -3px; background: #fff; box-shadow: 0 0 0 2px #f2b418; }
.llr-cc-clip-says { flex: 0 1 120px; color: rgba(255, 255, 255, 0.82); font-weight: 500; }
.llr-cc-clip-says[data-error] { color: #ffb3a1; }
/* The lines before, of and after the word: −1 and +1 take theirs into the clip and the sentence. */
/* The lines and, at their end, the ✎ that opens the card's sentence to change. */
.llr-cc-clip-body { display: flex; align-items: flex-start; gap: 6px; padding-top: 6px; border-top: 1px solid rgba(255, 255, 255, 0.12); }
.llr-cc-clip-lines { display: flex; flex: 1; flex-direction: column; gap: 2px; min-width: 0; }
.llr-cc-clip .llr-cc-clip-pen { width: 28px; height: 28px; font-size: 15px; line-height: 1; }
.llr-cc-clip .llr-cc-clip-pen[aria-pressed='true'],
.llr-cc-clip .llr-cc-clip-pen[data-own] { background: #f2b418; color: #000; }
/* The ✎'s box: the sentence, its translation, Cancel and Save. */
.llr-cc-clip-edit { display: flex; flex-direction: column; gap: 6px; padding-top: 6px; border-top: 1px solid rgba(255, 255, 255, 0.12); }
.llr-cc-clip-field { display: flex; flex-direction: column; gap: 3px; }
.llr-cc-clip-fname { color: rgba(255, 255, 255, 0.6); font-size: 11px; }
.llr-cc-clip-edit textarea {
  box-sizing: border-box;
  width: 100%;
  min-height: 40px;
  padding: 5px 7px;
  border: 1px solid rgba(255, 255, 255, 0.24);
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
  font: 500 13px/1.35 system-ui, 'Segoe UI', Roboto, sans-serif;
  resize: vertical;
}
.llr-cc-clip-edit textarea:focus { border-color: #f2b418; outline: none; }
.llr-cc-clip-ekeys { display: flex; justify-content: flex-end; gap: 4px; }
.llr-cc-clip .llr-cc-clip-ln {
  display: flex;
  align-items: baseline;
  gap: 8px;
  width: 100%;
  padding: 3px 6px;
  border-radius: 6px;
  background: transparent;
  color: rgba(255, 255, 255, 0.5);
  font-size: 13px;
  font-weight: 500;
  text-align: left;
}
.llr-cc-clip .llr-cc-clip-ln[data-side='own'],
.llr-cc-clip .llr-cc-clip-ln[aria-pressed='true'] { color: #fff; }
.llr-cc-clip-n {
  flex: none;
  min-width: 22px;
  padding: 1px 4px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.12);
  font-size: 10px;
  font-weight: 700;
  text-align: center;
}
.llr-cc-clip-ln[aria-pressed='true'] .llr-cc-clip-n { background: #f2b418; color: #000; }
/* No line before or after (the first or last line): no row, over the display above. */
.llr-cc-clip .llr-cc-clip-ln[hidden] { display: none; }
.llr-cc-clip-tx { display: -webkit-box; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
.llr-cc-clip-tx mark { border-radius: 3px; background: rgba(242, 180, 24, 0.35); color: inherit; }
.llr-cc-ap { padding-left: 9px; }
.llr-cc-ap::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #f7a800;
}
.llr-cc-ap[data-waiting] { box-shadow: inset 0 0 0 1.5px #f7a800; }
/* A word holding the video at the line's end: auto-pause's waiting ring,
   without its dot -- no mode is on, the video is only waiting for the viewer. */
.llr-cc-hold { box-shadow: inset 0 0 0 1.5px #f7a800; }
/* A load that failed: the salmon of the SlukaCC button's own error mark. */
.llr-cc-say[data-error] { padding-left: 9px; }
.llr-cc-say[data-error]::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #ff8a78;
}
/* A mode that is on, like auto-pause: the learning amber marks it. */
.llr-cc-loopchip { padding-left: 8px; }
.llr-cc-loopchip svg {
  width: 14px;
  height: 14px;
  fill: #f7a800;
}
/* Prev, replay, next, loop, the spoken-word microphone and the grip in a
   column just left of the line, its foot level with the line's, so it follows
   the line's left edge from sentence to sentence and never reaches down to
   YouTube's bar (the owner's calls, 2026-10-06; on the left 2026-10-13). Always there, faint enough not to catch the eye, and at
   full strength once the pointer comes near (data-near, cc-overlay.js) --
   only then does it take the pointer; until then a click there is the
   video's. Small, 22 px buttons 3 px apart, so the column stays not much
   taller than the line (the owner's call, 2026-10-06). On the left it is
   clear of the "E to peek" that ends a line on the right. */
.llr-cc-btns {
  position: absolute;
  right: 100%;
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  margin-right: 8px;
  padding: 2px;
  border-radius: 8px;
  /* At rest no panel at all, and the icons a faint ghost with a dark edge,
     so they show alike on a dark picture and a bright one: faded as one, the
     black panel showed on a bright picture as an empty grey bar. */
  background: transparent;
  transition: background-color 0.15s ease;
}
.llr-cc-btns > * {
  opacity: 0.1;
  filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.7));
  transition: opacity 0.15s ease;
}
.llr-cc:is(:hover, :focus-within, [data-drag], :has(llr-w[data-llr-hov])) .llr-cc-bar { pointer-events: auto; }
.llr-cc:is([data-near], :focus-within, [data-drag]) .llr-cc-btns {
  background-color: rgba(8, 8, 7, 0.84);
  pointer-events: auto;
}
.llr-cc:is([data-near], :focus-within, [data-drag]) .llr-cc-btns > * {
  opacity: 1;
  filter: none;
}
/* While SlukaCC's menu is open the column steps aside: the menu is about the
   subtitles' look, and ending just above the line, it came down onto the
   column's head. */
[data-llr-cc-menu] > .llr-cc .llr-cc-btns { visibility: hidden; }
/* A touch screen has no line buttons: hovering shows them, which a finger
   cannot do, only leave behind -- a tap on a word kept them up line after
   line, keyboard letters and all, and taking the touches meant for the
   picture under them (cc-touch.js's zones). The chips take none either, but
   for the grade chip's Undo. */
@media (hover: none) {
  .llr-cc-btns { display: none; }
  .llr-cc:is(:hover, :focus-within) .llr-cc-bar { pointer-events: none; }
}
/* A chip with something to press in it (Undo, after a grade on a touch
   screen): taller, and the button's reach wider than its face, a thumb's. */
.llr-cc-chip:has(> .llr-cc-undo) {
  height: 36px;
  padding-right: 4px;
  border-radius: 18px;
  font-size: 13px;
}
.llr-cc-undo {
  position: relative;
  margin-left: 4px;
  padding: 0 13px;
  height: 28px;
  border: 0;
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.18);
  color: #fff;
  font: 700 13px/1 system-ui, 'Segoe UI', Roboto, sans-serif;
  pointer-events: auto;
  cursor: pointer;
}
.llr-cc-undo::before {
  content: '';
  position: absolute;
  inset: -10px -8px;
}
.llr-cc-undo:focus-visible { outline: 2px solid #6b9bff; outline-offset: 1px; }
.llr-cc-lb {
  position: relative;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: #fff;
  cursor: pointer;
}
/* The microphone, where the spoken-word highlight can't be had. */
.llr-cc-lb[hidden] { display: none; }
.llr-cc-lb:hover {
  background: rgba(255, 255, 255, 0.22);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
}
.llr-cc-lb svg {
  width: 13px;
  height: 13px;
  fill: currentColor;
}
/* The key, in the corner, only on the button pointed at (the owner's call,
   2026-10-06): the icon moves up and left of it to make the room. Every icon
   sits centred otherwise, all five on one line. 9 px in the small column; it
   was 12, and 9.5 before that. */
.llr-cc-lb-key {
  position: absolute;
  right: 2px;
  bottom: 1px;
  font: 700 9px/1 system-ui, 'Segoe UI', Roboto, sans-serif;
  color: #c9c6bf;
  opacity: 0;
}
.llr-cc-lb:is(:hover, :focus-visible) > .llr-cc-lb-key { opacity: 1; }
.llr-cc-lb:is(:hover, :focus-visible):has(> .llr-cc-lb-key:not(:empty)) svg { transform: translate(-2px, -2px); }
.llr-cc-lb[aria-pressed='true'] {
  background: #f2f0eb;
  color: #111110;
}
.llr-cc-lb[aria-pressed='true'] .llr-cc-lb-key { color: #5a5852; }
.llr-cc-grip {
  display: grid;
  place-items: center;
  width: 22px;
  height: 16px;
  border-radius: 5px;
  cursor: ns-resize;
  touch-action: none;
}
.llr-cc-grip:hover { background: rgba(255, 255, 255, 0.18); }
/* Three dots by two, the usual handle: two dotted rules read as "more". */
.llr-cc-grip i {
  width: 12px;
  height: 7px;
  background: radial-gradient(circle, #b5b3ad 1px, transparent 1.3px) 0 0 / 4px 3.5px;
}
.llr-cc-lb:focus-visible,
.llr-cc-grip:focus-visible {
  outline: 2px solid #6b9bff;
  outline-offset: 1px;
}
