/* Lantern — a design language for the web.
 *
 * The CSS sibling of github.com/lapfelix/Lantern (UIKit). Same model, same
 * tokens, same banned list. No dependencies, no build step, no @import.
 *
 * THE MODEL
 * Everything is one sheet of material. A surface is in one of three physical
 * states: raised (a card), sunken (a well you put content into), lit (an accent
 * surface that emits). Depth comes from tone and edge, not from blur: a few
 * percent of tonal gradient, a 1px specular lip on the top edge, a hairline
 * border, a tight neutral contact shadow, and a pixel-fine grain.
 *
 * WHAT IS BANNED, AND WHY — this list is the load-bearing part.
 *   - No white gloss sweeping half a control's height. A specular lip is 1px.
 *   - No shadow tinted with the control's own colour. Contact shadows are neutral.
 *   - No glow behind anything that isn't genuinely the light source on its screen.
 *   - No saturated fills. Status hues are muted on purpose — four saturated grade
 *     keys side by side read as a toy.
 * If you find yourself widening a blur or brightening a gloss to make something
 * "pop", the fix is almost always tone or spacing instead.
 *
 * TWO TRAPS, both of which shipped once on iOS
 *   - Fade white into transparent WHITE, never `transparent`/black. Interpolating
 *     white→rgb(0 0 0/0) passes through a grey veil across the upper half of
 *     every card. Every gradient here spells out `rgb(255 255 255 / 0)`.
 *   - Build a pattern tile at the device pixel ratio. A tile sized in CSS px
 *     magnifies on a 2x/3x screen and reads as static, not grain — hence the
 *     `--ln-grain-size` overrides under `min-resolution` below.
 *
 * THEMING
 * Tokens resolve through `light-dark()`, so switching a subtree is one property:
 *   <html data-theme="dark">  ·  data-theme="light"  ·  omit for the OS setting.
 * A named palette is a second, independent attribute that composes with both
 * modes and with the flat style:
 *   <html data-ln-theme="instrument">  (see the Instrument theme section)
 *
 * YOUR OWN ACCENT
 * Everything here is neutral except the accent. Set one hue and the wash, the
 * ember and the fill gradients follow:
 *   :root { --ln-accent-light: #1478CE; --ln-accent-dark: #57ADEA; }
 * `--ln-on-accent` is the one thing worth overriding by hand — the ink over a
 * filled control has to be decided per interface style, and the default pair
 * (white in light, near-black in dark) assumes an accent that darkens for light
 * mode the way Lantern's own azure does.
 */

/* ========================================================================== *
 * Tokens
 * ========================================================================== */

:root {
  color-scheme: light dark;

  /* -- Accent ------------------------------------------------------------- */
  /* The two literals an app substitutes. Everything accent-derived below
     reads `--ln-accent`, so overriding these two is enough. */
  --ln-accent-light: #1478ce;
  --ln-accent-dark: #57adea;
  --ln-accent: light-dark(var(--ln-accent-light), var(--ln-accent-dark));
  /* Ink on a filled accent surface. */
  --ln-on-accent: light-dark(#ffffff, #07131c);
  /* A tinted wash for selected rows and quiet accent chips. Heavier in dark:
     a 10% tint that reads as a wash on paper disappears against a dark page. */
  --ln-accent-wash: light-dark(
    color-mix(in srgb, var(--ln-accent) 10%, transparent),
    color-mix(in srgb, var(--ln-accent) 16%, transparent)
  );
  /* The warm counterweight — progress, streaks, mastery. */
  --ln-ember: light-dark(#a5701f, #d8a05c);
  --ln-on-ember: light-dark(#ffffff, #1c1206);

  /* -- Page --------------------------------------------------------------- */
  /* Both modes are a vertical wash rather than a flat fill, so a card floating
     on the page has something to be lit against. */
  --ln-canvas-top: light-dark(#f5f4f1, #18181b);
  --ln-canvas-bottom: light-dark(#eae9e5, #0c0c0e);
  /* Flat stand-in, for anything that can't host a gradient. */
  --ln-canvas: light-dark(#f0efeb, #121214);

  /* -- Surfaces ----------------------------------------------------------- */
  /* A hair off white, so the lip on the top edge has something to be brighter than. */
  --ln-surface: light-dark(#fcfcfb, #1d1d21);
  /* A second surface nested inside the first (a chip inside a card). */
  --ln-surface-quiet: light-dark(#e9e7e1, #26262b);
  /* A recessed well. Barely off the page tone — a field you type into should
     read as a dip in the surface, not a dark trough cut out of it. */
  --ln-well: light-dark(#efeeea, #17171b);

  /* -- Ink ---------------------------------------------------------------- */
  --ln-ink: light-dark(#17171a, #f3f3f1);
  /* Secondary meets 4.5:1 on the canvas, a surface, a quiet surface and a well
     in both modes (5.6:1 at worst in light; it was 4.1:1 at 0.58). Tertiary
     meets 3:1, for placeholders, glyphs and disabled ink, never for body text.
     `--ln-ink-quiet` is the small-text tier (captions, micro, labels, hints):
     the same value today, named so an app that lightens secondary for large
     type does not drag its captions under AA. */
  --ln-ink-secondary: light-dark(rgb(23 23 26 / 0.68), rgb(243 243 241 / 0.6));
  --ln-ink-tertiary: light-dark(rgb(23 23 26 / 0.5), rgb(243 243 241 / 0.42));
  --ln-ink-quiet: light-dark(rgb(23 23 26 / 0.68), rgb(243 243 241 / 0.6));
  /* Placeholder text is still text, so it needs 4.5:1, which tertiary does not
     reach in the stock palette. It stays a step lighter than secondary so a
     hint never reads as a typed value. */
  --ln-ink-placeholder: light-dark(rgb(23 23 26 / 0.62), rgb(243 243 241 / 0.52));

  /* -- Status. Muted on purpose. ------------------------------------------ */
  /* These are tuned as INK — a status word, a glyph, a hairline. */
  --ln-positive: light-dark(#2e7a5a, #63be95);
  --ln-caution: light-dark(#99721e, #d1a65b);
  --ln-critical: light-dark(#af4a42, #dd7c72);
  /* And these as FILLS, which is a different job: the dark-mode hues above are
     lightened so they read as ink on a dark page, and at that lightness they
     cannot carry white. So a lit status surface starts from the light-mode hue
     in both modes, deepened toward the media recess rather than desaturated, and
     each gets its own ink. Reaching for `--ln-positive` as a background is the
     mistake this pair exists to prevent.

     Positive and critical carry white unchanged (5.2:1 and 5.4:1). Caution does
     not — `--ln-caution` is the lightest of the three and white on it measures
     4.4:1 — so its fill is darkened until it does (5.3:1). The rule is "the
     status hue, dark enough for its own ink", and only caution needs the second
     half of that. */
  --ln-positive-fill: light-dark(#2e7a5a, #2a6b50);
  --ln-caution-fill: light-dark(#8a6519, #7d5c18);
  --ln-critical-fill: light-dark(#af4a42, #963f38);
  --ln-on-positive: #ffffff;
  --ln-on-caution: #ffffff;
  --ln-on-critical: #ffffff;

  /* Grading a flash card, worst → best. Easy lands on the brand accent so the
     happy path is the app's own colour. */
  --ln-grade-again: var(--ln-critical);
  --ln-grade-hard: var(--ln-caution);
  --ln-grade-good: var(--ln-positive);
  --ln-grade-easy: var(--ln-accent);

  /* -- Interaction washes ------------------------------------------------ */
  /* Derived from the accent, so a brand hue carries into them. Hover is the
     lightest; the "now playing" word is a step heavier; a search hit is the
     one warm highlight, because a match is not a selection. */
  --ln-hover-wash: light-dark(
    color-mix(in srgb, var(--ln-accent) 7%, transparent),
    color-mix(in srgb, var(--ln-accent) 12%, transparent)
  );
  --ln-current-wash: light-dark(
    color-mix(in srgb, var(--ln-accent) 16%, transparent),
    color-mix(in srgb, var(--ln-accent) 26%, transparent)
  );
  --ln-highlight: light-dark(#fde9a8, #5a4a12);
  --ln-answer-wash: var(--ln-accent-wash);
  /* Recording. Warmer and brighter than critical: it means "on air", not
     "wrong". */
  --ln-live: light-dark(#c9372c, #ff6b5e);

  /* -- Edges -------------------------------------------------------------- */
  --ln-hairline: light-dark(rgb(23 23 26 / 0.09), rgb(255 255 255 / 0.07));
  --ln-hairline-faint: light-dark(rgb(23 23 26 / 0.055), rgb(255 255 255 / 0.045));
  /* Opaque, for the cases where a hairline is a *fill* rather than an edge — a
     divider drawn as a block, a keyline over an unknown backdrop. The
     translucent tokens above vanish there. */
  --ln-hairline-strong: color-mix(in srgb, var(--ln-ink) 14%, var(--ln-surface));
  /* The edge of a control that has no text of its own (a field, a switch
     track, a slider). A theme raises it to 3:1 without darkening every card. */
  --ln-control-edge: var(--ln-hairline);
  --ln-field-border: var(--ln-hairline-faint);
  /* The focused field's edge is its focus indicator, so it takes the ring's
     full colour: at 55% of the accent it measured 2:1 against the well. */
  --ln-field-focus: var(--ln-focus);
  /* The keyboard focus ring. */
  --ln-focus: var(--ln-accent);
  /* Selected text. Heavier than the accent wash, which is a hover tint and too
     faint to show a selection at a glance. */
  --ln-selection: light-dark(
    color-mix(in srgb, var(--ln-accent) 24%, transparent),
    color-mix(in srgb, var(--ln-accent) 36%, transparent)
  );

  /* -- Charts ------------------------------------------------------------- */
  /* No component draws a chart; these give a product's charts a fixed series
     order that follows the palette. Assign in order, never cycle. */
  --ln-series-1: var(--ln-accent);
  --ln-series-2: var(--ln-ember);
  --ln-series-3: var(--ln-positive);
  --ln-series-4: var(--ln-caution);
  --ln-series-5: var(--ln-critical);
  --ln-series-6: var(--ln-ink-secondary);
  --ln-chart-grid: var(--ln-hairline);
  --ln-chart-axis: var(--ln-ink-tertiary);

  /* -- Over media --------------------------------------------------------- */
  /* Video and artwork are their own ground: the neutral ink tokens flip with
     the interface style, so over a frame that does not they go unreadable in
     one mode. These do not flip. */
  --ln-media: #0e0e10;
  --ln-on-media: #ffffff;
  --ln-on-media-secondary: rgb(255 255 255 / 0.72);
  /* A well over artwork is invisible — the recess reads as part of the frame —
     so a track laid on media is a wash of the over-media ink instead. */
  --ln-on-media-track: rgb(255 255 255 / 0.22);
  /* A fill laid on media. A theme whose accent darkens for light mode lifts it here. */
  --ln-on-media-accent: var(--ln-accent);
  --ln-media-chip: rgb(0 0 0 / 0.55);
  /* The veil under a modal. Lighter in light mode: a near-black scrim over a
     paper page reads as the lights going out rather than as a layer above it. */
  --ln-scrim: light-dark(rgb(23 23 26 / 0.28), rgb(0 0 0 / 0.6));
  /* A translucent floating material — a bar or a panel over content it has to
     stay legible against. Paired with a backdrop-filter at the use site; on its
     own it is just a tinted wash of the page. */
  --ln-glass: color-mix(in srgb, var(--ln-canvas) 82%, transparent);

  /* -- Spacing. A 4px grid. ---------------------------------------------- */
  --ln-hair: 2px; /* between two lines of the same thought (a word and its gloss) */
  --ln-xs: 4px;
  --ln-sm: 8px; /* the default gap between siblings */
  --ln-md: 12px;
  --ln-lg: 16px; /* page margins, card padding */
  --ln-xl: 24px; /* between sections */
  --ln-xxl: 32px;

  /* -- Radius. Nothing is ever sharp. ------------------------------------ */
  --ln-r-xs: 8px; /* chips, small controls */
  --ln-r-sm: 12px; /* artwork, nested surfaces */
  --ln-r-md: 16px; /* the standard card */
  --ln-r-lg: 22px; /* a large panel, a now-playing bar */
  --ln-r-xl: 28px; /* a hero card */
  --ln-r-pill: 999px;

  /* -- Depth ------------------------------------------------------------ */
  /* Scales the material: shadows, sheen, lips, inner shadows and button gloss.
     1 is the full finish, `data-ln-style="soft"` sets 0.55 and drops the grain,
     and flat removes the material outright. */
  --ln-depth: 1;

  /* A fine monochrome dither, tiled. Real materials are not perfectly smooth,
     and a couple of percent of grain is most of the difference between a
     surface and a coloured rectangle. */
  --ln-grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='96' height='96'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='96' height='96' filter='url(%23n)' opacity='0.05'/%3E%3C/svg%3E");
  --ln-grain-size: 96px 96px;

  /* -- Motion ------------------------------------------------------------- */
  --ln-t-tap: 0.18s; /* a press-down / release. Short enough to feel mechanical. */
  --ln-t-move: 0.32s; /* something appearing or moving on screen */
  --ln-ease: cubic-bezier(0.32, 0.72, 0.24, 1);
  --ln-ease-spring: cubic-bezier(0.2, 0.9, 0.24, 1.02);

  /* -- Type --------------------------------------------------------------- */
  /* Sizes are rem, so they scale with the reader's root font size — the web's
     Dynamic Type. Never freeze a px size for body text. */
  --ln-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --ln-font-serif: ui-serif, Georgia, 'Times New Roman', serif;
  --ln-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* Arabic is the subject, not a translation of it — its own stack and, below,
     its own scale and leading. */
  --ln-font-ar:
    -apple-system, 'SF Arabic', 'Geeza Pro', 'Noto Naskh Arabic', 'Segoe UI', Tahoma, serif;

  --ln-fs-display: clamp(1.75rem, 1.35rem + 2vw, 2.125rem); /* 34 */
  --ln-fs-title: 1.375rem; /* 22 */
  --ln-fs-headline: 1.0625rem; /* 17 semibold */
  --ln-fs-body: 1.0625rem; /* 17 */
  --ln-fs-callout: 0.9375rem; /* 15 */
  --ln-fs-caption: 0.8125rem; /* 13 */
  --ln-fs-micro: 0.6875rem; /* 11 semibold — badges, timestamps, tags */

  --ln-fs-ar-hero: clamp(2rem, 1.5rem + 2.4vw, 2.375rem); /* 38 */
  --ln-fs-ar-title: 1.875rem; /* 30 */
  --ln-fs-ar-row: 1.375rem; /* 22 */
  --ln-fs-ar-body: 1.1875rem; /* 19 */
  --ln-fs-ar-caption: 1rem; /* 16 */
  --ln-lh-ar: 1.22; /* Arabic ascenders and descenders collide at Latin leading */
  --ln-lh: 1.45;
}

/* The material tokens, restated wherever the depth changes so a soft subtree
   derives from its own depth rather than the root's. */
:root,
[data-ln-style='soft'] {
  /* -- Elevation ---------------------------------------------------------- */
  /* Deliberately small: depth comes from tone and edge, and the shadow is only
     the contact with the page. Blur is 2x the UIKit shadowRadius, which is how
     the two rasterisers agree. A dark page swallows a black shadow, so dark
     mode needs a heavier one to read at all. A floating layer keeps its full
     shadow at every depth, since it is the only thing separating it. */
  --ln-e-resting: 0 1px 8px light-dark(rgb(0 0 0 / calc(0.05 * var(--ln-depth))), rgb(0 0 0 / calc(0.28 * var(--ln-depth))));
  --ln-e-raised: 0 3px 20px light-dark(rgb(0 0 0 / calc(0.075 * var(--ln-depth))), rgb(0 0 0 / calc(0.4 * var(--ln-depth))));
  --ln-e-floating: 0 9px 44px light-dark(rgb(0 0 0 / 0.12), rgb(0 0 0 / 0.52));

  /* -- Surface finish ---------------------------------------------------- */
  /* The full-height tonal wash — a few percent, never a highlight sweep. In
     dark mode light gathers on the top edge; in light mode a white sheen over a
     near-white card is invisible, so the cue is occlusion gathering at the
     bottom instead. Same physics from the other side. */
  --ln-sheen: linear-gradient(
    light-dark(rgb(255 255 255 / 0), rgb(255 255 255 / calc(0.05 * var(--ln-depth)))),
    rgb(255 255 255 / 0) 45%,
    light-dark(rgb(0 0 0 / calc(0.035 * var(--ln-depth))), rgb(0 0 0 / calc(0.05 * var(--ln-depth))))
  );
  --ln-sheen-quiet: linear-gradient(
    light-dark(rgb(255 255 255 / 0), rgb(255 255 255 / calc(0.03 * var(--ln-depth)))),
    rgb(255 255 255 / 0) 45%,
    light-dark(rgb(0 0 0 / calc(0.021 * var(--ln-depth))), rgb(0 0 0 / calc(0.03 * var(--ln-depth))))
  );
  --ln-sheen-lit: linear-gradient(
    light-dark(rgb(255 255 255 / calc(0.1 * var(--ln-depth))), rgb(255 255 255 / calc(0.05 * var(--ln-depth)))),
    rgb(255 255 255 / 0) 45%,
    light-dark(rgb(0 0 0 / calc(0.07 * var(--ln-depth))), rgb(0 0 0 / calc(0.05 * var(--ln-depth))))
  );
  /* The 1px lip where the light lands. */
  --ln-lip: light-dark(rgb(255 255 255 / calc(0.75 * var(--ln-depth))), rgb(255 255 255 / calc(0.1 * var(--ln-depth))));
  --ln-lip-lit: light-dark(rgb(255 255 255 / calc(0.34 * var(--ln-depth))), rgb(255 255 255 / calc(0.26 * var(--ln-depth))));
  /* A well's shadow falls inward. It has no lip of its own — the light lands
     on the rim above it. */
  --ln-inner: inset 0 1px 6px light-dark(rgb(0 0 0 / calc(0.1 * var(--ln-depth))), rgb(0 0 0 / calc(0.34 * var(--ln-depth))));
}

[data-theme='light'] {
  color-scheme: light;
}
[data-theme='dark'] {
  color-scheme: dark;
}

/* One device pixel per speck. A 96px tile on a 3x screen is 3px blocks. */
@media (min-resolution: 2dppx) {
  :root {
    --ln-grain-size: 48px 48px;
  }
}
@media (min-resolution: 3dppx) {
  :root {
    --ln-grain-size: 32px 32px;
  }
}

/* The one rule the web needs that iOS does not: a visible keyboard focus.
   Accent, outside the box, so it reads on every surface state. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--ln-focus);
  outline-offset: 2px;
  border-radius: inherit;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --ln-t-tap: 0.001s;
    --ln-t-move: 0.001s;
  }
}

/* ========================================================================== *
 * Canvas
 * ========================================================================== */

/* Every Lantern component is measured border-box, because several of them are
   `width: 100%` with padding and would otherwise overflow their container by
   exactly that padding — which is what happened to the gallery's rows on a page
   that had no reset of its own. Scoped to our own class prefix rather than `*`:
   nothing else in this file selects a bare element, so linking the language can
   never silently re-measure a consumer's markup. `[class^=]` plus `[class*=" "]`
   matches `ln-*` as a whole token, not as a substring of someone else's name. */
[class^='ln-'],
[class^='ln-']::before,
[class^='ln-']::after,
[class*=' ln-'],
[class*=' ln-']::before,
[class*=' ln-']::after {
  box-sizing: border-box;
}

.ln-canvas {
  min-height: 100%;
  background-color: var(--ln-canvas);
  background-image: linear-gradient(var(--ln-canvas-top), var(--ln-canvas-bottom));
  background-attachment: fixed;
  color: var(--ln-ink);
  font-family: var(--ln-font);
  font-size: var(--ln-fs-body);
  line-height: var(--ln-lh);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Browser surfaces follow the theme on every page that links the sheet, not
   only under `.ln-canvas`. Selection keeps the text's own ink; on an accent
   fill an accent tint would vanish, so a lit surface uses a wash of its ink. */
::selection {
  background: var(--ln-selection);
}
.ln-surface.ln-lit ::selection {
  background: color-mix(in srgb, var(--ln-on-accent) 30%, transparent);
}
/* `scrollbar-color` inherits, so the root covers the page; a themed subtree
   restates it so its own `light-dark()` resolves. */
:root,
[data-theme],
[data-ln-theme] {
  scrollbar-color: var(--ln-ink-tertiary) transparent;
}

/* ========================================================================== *
 * Surface — the single container primitive
 * ========================================================================== */

.ln-surface {
  position: relative;
  border-radius: var(--ln-radius, var(--ln-r-md));
  border: 1px solid var(--ln-hairline);
  background-color: var(--ln-surface);
  background-image: var(--ln-grain), var(--ln-sheen);
  background-size: var(--ln-grain-size), auto;
  background-repeat: repeat, no-repeat;
  box-shadow: var(--ln-e-raised);
  color: var(--ln-ink);
}

/* The specular lip: 1px of light caught on the top edge.
 *
 * It is an inset shadow on a full-size overlay, NOT a straight bar inset from
 * the corners. A bar has to guess how far to stop short of the arc, and on
 * anything round the guess is visibly wrong — on a circle it reads as a chord
 * floating inside the top of the shape, and on a pill its flat ends read as a
 * second, smaller shape. An inset shadow follows `border-radius: inherit`
 * exactly, so one recipe is correct for a card, a pill and a circle.
 *
 * The mask is the other half of it: a real specular highlight is brightest where
 * the surface faces the light and dies off as it turns away, so the line fades
 * out towards the sides instead of ringing the whole top edge. */
.ln-surface::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 1px 0 var(--ln-lip);
  mask-image: linear-gradient(to right, transparent, #000 22%, #000 78%, transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 22%, #000 78%, transparent);
  pointer-events: none;
  transition: opacity var(--ln-t-tap) var(--ln-ease);
}

/* A tinted panel with no depth of its own — for nesting inside another
   surface, where a second shadow would read as mud. */
.ln-surface.ln-quiet {
  background-color: var(--ln-surface-quiet);
  background-image: var(--ln-grain), var(--ln-sheen-quiet);
  border-color: var(--ln-hairline-faint);
  box-shadow: none;
}

/* A well content sits *in*. No grain, no lip; the shadow falls inward. */
.ln-surface.ln-sunken {
  background-color: var(--ln-well);
  background-image: none;
  border-color: var(--ln-hairline-faint);
  box-shadow: var(--ln-inner);
}
.ln-surface.ln-sunken::before {
  display: none;
}

/* An accent surface that emits. */
.ln-surface.ln-lit {
  background-color: var(--ln-accent);
  background-image: var(--ln-grain), var(--ln-sheen-lit);
  border-color: rgb(0 0 0 / 0.1);
  color: var(--ln-on-accent);
}
.ln-surface.ln-lit::before {
  box-shadow: inset 0 1px 0 var(--ln-lip-lit);
}
/* A lit surface sets its own ink above, but every type tier names `--ln-ink` on
   ITSELF, and a declaration on the element beats one inherited from its parent.
   So a heading dropped on a lit card kept the page's ink: near-black on the
   accent in light (3.92:1), and near-white on the lightened accent in dark
   (2.21:1) — which is exactly the failure `--ln-on-accent` exists to prevent.
   `:where()` contributes no specificity, so this lands at `.ln-surface.ln-lit`
   and a caller's own colour still wins. */
.ln-surface.ln-lit
  :where(
    .ln-display,
    .ln-title,
    .ln-headline,
    .ln-body,
    .ln-callout,
    .ln-caption,
    .ln-micro,
    .ln-section-header,
    .ln-gloss,
    .ln-meaning,
    .ln-ar
  ) {
  color: inherit;
}

.ln-flat {
  box-shadow: none;
}
.ln-resting {
  box-shadow: var(--ln-e-resting);
}
.ln-raised {
  box-shadow: var(--ln-e-raised);
}
.ln-floating {
  box-shadow: var(--ln-e-floating);
}

.ln-r-xs {
  --ln-radius: var(--ln-r-xs);
}
.ln-r-sm {
  --ln-radius: var(--ln-r-sm);
}
.ln-r-md {
  --ln-radius: var(--ln-r-md);
}
.ln-r-lg {
  --ln-radius: var(--ln-r-lg);
}
.ln-r-xl {
  --ln-radius: var(--ln-r-xl);
}

/* Padding inside a card. */
.ln-card {
  padding: var(--ln-lg);
}
.ln-card-lg {
  padding: var(--ln-xl);
}

/* A soft radial glow, behind an accent control so it looks like it is emitting
   rather than painted on. Reserved for a control that is genuinely the light
   source on its screen — a glow behind every primary button is what reads as
   amateur. */
.ln-bloom {
  position: relative;
  isolation: isolate;
}
.ln-bloom::after {
  content: '';
  position: absolute;
  inset: calc(var(--ln-bloom-spread, 22px) * -1);
  z-index: -1;
  border-radius: 50%;
  background: radial-gradient(
    closest-side,
    color-mix(in srgb, var(--ln-bloom-color, var(--ln-accent)) 45%, transparent),
    color-mix(in srgb, var(--ln-bloom-color, var(--ln-accent)) 15%, transparent) 55%,
    rgb(255 255 255 / 0) 100%
  );
  pointer-events: none;
  transition: opacity var(--ln-t-tap) var(--ln-ease);
}

/* ========================================================================== *
 * Type
 * ========================================================================== */

.ln-display {
  font-size: var(--ln-fs-display);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: -0.015em;
  color: var(--ln-ink);
  margin: 0;
}
.ln-title {
  font-size: var(--ln-fs-title);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--ln-ink);
  margin: 0;
}
.ln-headline {
  font-size: var(--ln-fs-headline);
  font-weight: 600;
  line-height: 1.3;
  color: var(--ln-ink);
  margin: 0;
}
.ln-body {
  font-size: var(--ln-fs-body);
  line-height: var(--ln-lh);
  color: var(--ln-ink);
  margin: 0;
}
.ln-callout {
  font-size: var(--ln-fs-callout);
  line-height: 1.4;
  color: var(--ln-ink);
  margin: 0;
}
.ln-caption {
  font-size: var(--ln-fs-caption);
  line-height: 1.35;
  color: var(--ln-ink-quiet);
  margin: 0;
}
/* The smallest legible tier. */
.ln-micro {
  font-size: var(--ln-fs-micro);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: 0.02em;
  color: var(--ln-ink-quiet);
  margin: 0;
}
/* A section title above a group. Sentence case, quiet — the content should
   carry the weight, not its label. */
.ln-section-header {
  font-size: var(--ln-fs-micro);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ln-ink-quiet);
  margin: 0;
}

.ln-secondary {
  color: var(--ln-ink-secondary);
}
.ln-tertiary {
  color: var(--ln-ink-tertiary);
}
.ln-quiet-ink {
  color: var(--ln-ink-quiet);
}
.ln-accent-ink {
  color: var(--ln-accent);
}

/* A gloss / literal reading. */
.ln-gloss {
  font-style: italic;
  color: var(--ln-ink-secondary);
  margin: 0;
}
/* A translation — a serif italic reads as "this is the meaning, not the words". */
.ln-meaning {
  font-family: var(--ln-font-serif);
  font-style: italic;
  color: var(--ln-ink-secondary);
  margin: 0;
}
.ln-mono {
  font-family: var(--ln-font-mono);
  font-size: var(--ln-fs-caption);
  margin: 0;
}
/* Timers, intervals, offsets — anything whose digits must not jitter. */
/* Isolated too, so "12 US$" or "00:14" keeps its own order inside Arabic text
   and never reorders the words around it. */
.ln-digits {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum';
  unicode-bidi: isolate;
  margin: 0;
}

/* Arabic. `direction` is the load-bearing half: right-aligning a block leaves
   the writing direction LTR, which mis-orders mixed content like "عندي 3 كتب".
   `unicode-bidi: isolate` keeps a run from reordering its neighbours. */
.ln-ar {
  font-family: var(--ln-font-ar);
  direction: rtl;
  unicode-bidi: isolate;
  text-align: right;
  line-height: var(--ln-lh-ar);
  color: var(--ln-ink);
  /* Like every Latin tier above: spacing is the stack's job, never the
     paragraph's. Without this a `<p class="ln-ar ln-ar-hero">` keeps the UA's
     1em margin — 38px on each side of every line, which a flex `gap` adds to
     rather than collapses with. */
  margin: 0;
}
.ln-ar-hero {
  font-size: var(--ln-fs-ar-hero);
  font-weight: 600;
}
.ln-ar-title {
  font-size: var(--ln-fs-ar-title);
  font-weight: 600;
}
.ln-ar-row {
  font-size: var(--ln-fs-ar-row);
}
.ln-ar-body {
  font-size: var(--ln-fs-ar-body);
}
.ln-ar-caption {
  font-size: var(--ln-fs-ar-caption);
}
.ln-ar.ln-center {
  text-align: center;
}
/* In a table the alignment belongs to the COLUMN and the direction to the cell.
   `.ln-ar` carries both, so an Arabic cell dropped into a table ends up flush
   right under a left-aligned `th` — which reads as a broken column, not as a
   script. The base direction is the half that matters here: it is what orders
   the content. Spelled `left` rather than `inherit` or `start`, because both of
   those resolve to the RIGHT edge once `direction: rtl` is on the cell, which is
   the whole problem. */
.ln-table .ln-ar {
  text-align: left;
}
.ln-table:dir(rtl) .ln-ar {
  text-align: right;
}

/* Prose links sit in `:where()` so they weigh one class, and skip the link-shaped
   components outright. At `.ln-prose a` (0,1,1) they outranked `.ln-btn` (0,1,0)
   and painted a button's label in accent on its own accent fill. */
a.ln-link,
.ln-prose :where(a:not(.ln-btn, .ln-icon-btn, .ln-pill, .ln-row)) {
  color: var(--ln-accent);
  text-decoration-color: color-mix(in srgb, var(--ln-accent) 40%, transparent);
  text-underline-offset: 0.2em;
}
/* A 44px hit area around a small link, without moving anything around it. */
.ln-target {
  position: relative;
}
.ln-target::after {
  content: '';
  position: absolute;
  inset-block: min(0px, calc((100% - 44px) / 2));
  inset-inline: min(0px, calc((100% - 44px) / 2));
}
a.ln-link:hover,
.ln-prose :where(a:not(.ln-btn, .ln-icon-btn, .ln-pill, .ln-row)):hover {
  text-decoration-color: var(--ln-accent);
}

/* ========================================================================== *
 * Button
 * ========================================================================== */

/* One class, six roles — so "the primary action" looks the same on every screen
   instead of being re-invented per file. A filled button is a key cut from the
   same material as everything else: a barely-there tonal gradient, a 1px
   specular lip, and a tight neutral contact shadow. Pressing it sinks it and
   dims the lip. Explicitly not: a coloured glow, or a white gloss sweeping half
   the height.

   A product can retune the finish from any ancestor without overriding rules:
   `--ln-btn-gloss` (the tonal gradient, or `none`), `--ln-btn-lip` (the 1px
   top edge colour), `--ln-btn-shadow` and `--ln-btn-border`. Each is read with
   a fallback at the button, so the default gradient still follows the
   button's own fill. */
.ln-btn {
  --ln-btn-fill: var(--ln-accent);
  --ln-btn-ink: var(--ln-on-accent);
  --ln-btn-radius: var(--ln-radius-control, var(--ln-r-md));
  /* Readable by a site, so a quiet button can hang its label on a column edge:
     `margin-inline-start: calc(var(--ln-btn-pad-inline) * -1)`. */
  --ln-btn-pad-inline: 22px;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ln-sm);
  min-height: 52px;
  padding: 14px var(--ln-btn-pad-inline);
  border: 1px solid var(--ln-btn-border, rgb(0 0 0 / 0.12));
  border-radius: var(--ln-btn-radius);
  font-family: inherit;
  font-size: var(--ln-fs-headline);
  font-weight: 600;
  line-height: 1.2;
  color: var(--ln-btn-ink);
  background-color: var(--ln-btn-fill);
  /* A few percent from top to bottom. Any more and it stops looking machined
     and starts looking moulded. */
  background-image:
    var(--ln-grain),
    var(
      --ln-btn-gloss,
      linear-gradient(
        light-dark(color-mix(in srgb, var(--ln-btn-fill), #fff calc(5.5% * var(--ln-depth))), color-mix(in srgb, var(--ln-btn-fill), #fff calc(7% * var(--ln-depth)))),
        light-dark(color-mix(in srgb, var(--ln-btn-fill), #000 calc(4.5% * var(--ln-depth))), color-mix(in srgb, var(--ln-btn-fill), #000 calc(5% * var(--ln-depth))))
      )
    );
  background-size: var(--ln-grain-size), auto;
  background-repeat: repeat, no-repeat;
  box-shadow: var(--ln-btn-shadow, var(--ln-e-raised));
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  transition:
    transform var(--ln-t-tap) var(--ln-ease),
    box-shadow var(--ln-t-tap) var(--ln-ease),
    opacity var(--ln-t-tap) var(--ln-ease);
  -webkit-tap-highlight-color: transparent;
}

/* Same recipe as the surface lip — see the note there for why it is a shadow on
   an overlay rather than a bar. A pill button is exactly the case a bar breaks. */
.ln-btn::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 1px 0 var(--ln-btn-lip, light-dark(rgb(255 255 255 / calc(0.3 * var(--ln-depth))), rgb(255 255 255 / calc(0.2 * var(--ln-depth)))));
  mask-image: linear-gradient(to right, transparent, #000 22%, #000 78%, transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 22%, #000 78%, transparent);
  pointer-events: none;
  transition: opacity var(--ln-t-tap) var(--ln-ease);
}

.ln-btn:active:not(:disabled) {
  transform: scale(0.985);
  box-shadow: var(--ln-e-resting);
}
.ln-btn:active:not(:disabled)::before {
  opacity: 0.2;
}
.ln-btn:active:not(:disabled) .ln-bloom::after,
.ln-btn.ln-bloom:active:not(:disabled)::after {
  opacity: 0.5;
}

/* Everything else: a chip, a toolbar action. */
.ln-btn.ln-secondary-btn {
  --ln-btn-fill: var(--ln-surface-quiet);
  --ln-btn-ink: var(--ln-ink);
  border-color: var(--ln-btn-border, var(--ln-hairline));
  background-image: var(
    --ln-btn-gloss,
    linear-gradient(
      light-dark(color-mix(in srgb, var(--ln-btn-fill), #fff calc(5.5% * var(--ln-depth))), color-mix(in srgb, var(--ln-btn-fill), #fff calc(7% * var(--ln-depth)))),
      light-dark(color-mix(in srgb, var(--ln-btn-fill), #000 calc(4.5% * var(--ln-depth))), color-mix(in srgb, var(--ln-btn-fill), #000 calc(5% * var(--ln-depth))))
    )
  );
  /* One gradient, so it spans the whole button. The base rule sizes two layers
     and the first size (the grain tile) would otherwise tile this gradient,
     leaving a seam on any button taller than the tile. */
  background-size: auto;
  background-repeat: no-repeat;
  box-shadow: var(--ln-btn-shadow, var(--ln-e-resting));
}

/* Reserved for the accent's warm counterpart (streaks, mastery). */
.ln-btn.ln-ember-btn {
  --ln-btn-fill: var(--ln-ember);
  --ln-btn-ink: var(--ln-on-ember);
}

/* A lit critical fill, for the one destructive action a screen is about. */
.ln-btn.ln-destructive {
  --ln-btn-fill: var(--ln-critical-fill);
  --ln-btn-ink: var(--ln-on-critical);
}

/* The other two thirds of the same pair. A status *fill* is for a button that
   commits to that status — "Mark as known", "Overwrite anyway" — not for a
   button that merely mentions it; that one is `.ln-quiet-btn` with the ink. */
.ln-btn.ln-positive {
  --ln-btn-fill: var(--ln-positive-fill);
  --ln-btn-ink: var(--ln-on-positive);
}
.ln-btn.ln-caution {
  --ln-btn-fill: var(--ln-caution-fill);
  --ln-btn-ink: var(--ln-on-caution);
}

/* Text only, no surface. */
.ln-btn.ln-quiet-btn,
.ln-btn.ln-quiet-destructive {
  --ln-btn-ink: var(--ln-accent);
  background: none;
  border-color: transparent;
  box-shadow: none;
  font-weight: 500;
}
.ln-btn.ln-quiet-btn::before,
.ln-btn.ln-quiet-destructive::before {
  display: none;
}
.ln-btn.ln-quiet-btn:hover:not(:disabled),
.ln-btn.ln-quiet-destructive:hover:not(:disabled) {
  background: color-mix(in srgb, currentColor 8%, transparent);
}
/* Text in the critical hue, for a destructive action that must not be the
   loudest thing on its screen: a lit red button invites the tap it is warning
   about, while the accent's blue makes "delete everything" read as an ordinary
   link. */
.ln-btn.ln-quiet-destructive {
  --ln-btn-ink: var(--ln-critical);
}

/* A chip in a row of chips. Under 44px it resolves to a true capsule. */
.ln-btn.ln-compact {
  --ln-btn-radius: var(--ln-r-pill);
  --ln-btn-pad-inline: 14px;
  min-height: 40px;
  padding: 9px var(--ln-btn-pad-inline);
  font-size: var(--ln-fs-callout);
  font-weight: 500;
}
.ln-btn.ln-compact.ln-squared {
  --ln-btn-radius: var(--ln-r-sm);
}
.ln-btn.ln-block {
  display: flex;
  width: 100%;
}

/* A disabled control is inert, not a faded version of an active one: dimming a
   lit fill leaves white text on a pale tint, the least legible thing on the
   screen. */
.ln-btn:disabled,
.ln-btn[aria-disabled='true'] {
  --ln-btn-fill: var(--ln-surface-quiet);
  --ln-btn-ink: var(--ln-ink-tertiary);
  background-image: none;
  border-color: var(--ln-hairline-faint);
  box-shadow: none;
  cursor: default;
  transform: none;
}
.ln-btn:disabled::before,
.ln-btn[aria-disabled='true']::before {
  display: none;
}
/* A quiet button stays text when disabled: a pill and fill there reads as hover. */
.ln-btn.ln-quiet-btn:is(:disabled, [aria-disabled='true']),
.ln-btn.ln-quiet-destructive:is(:disabled, [aria-disabled='true']) {
  background: none;
  border-color: transparent;
}

/* A circular symbol button: a bar action, a transport control. `ln-bare` for a
   button in bar chrome — a contact shadow is the contact between an object and
   the surface under it, and a bar button has no surface under it. */
.ln-icon-btn {
  --ln-btn-fill: var(--ln-surface-quiet);
  --ln-btn-ink: var(--ln-ink);
  --ln-icon-size: 38px;
  /* The icon box. The UIKit button builds its SF Symbol at `diameter * 0.42`;
     an `<svg>` or `<img>` handed to this one fills exactly that box (below). */
  --ln-icon-glyph: calc(var(--ln-icon-size) * 0.42);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--ln-icon-size);
  height: var(--ln-icon-size);
  padding: 0;
  border: 1px solid var(--ln-btn-border, var(--ln-hairline));
  border-radius: 50%;
  color: var(--ln-btn-ink);
  background-color: var(--ln-btn-fill);
  background-image: var(
    --ln-btn-gloss,
    linear-gradient(
      light-dark(color-mix(in srgb, var(--ln-btn-fill), #fff calc(5.5% * var(--ln-depth))), color-mix(in srgb, var(--ln-btn-fill), #fff calc(7% * var(--ln-depth)))),
      light-dark(color-mix(in srgb, var(--ln-btn-fill), #000 calc(4.5% * var(--ln-depth))), color-mix(in srgb, var(--ln-btn-fill), #000 calc(5% * var(--ln-depth))))
    )
  );
  box-shadow: var(--ln-btn-shadow, var(--ln-e-resting));
  font-family: inherit;
  /* A text character is not an SF Symbol. A symbol's ink fills its point size,
     but a glyph carries side bearings and ascender room inside its em box and
     draws roughly two thirds of it — at the symbol's size a `✕` is a speck in a
     38px disc. So scale the FONT until a character lands at the icon box; a real
     icon takes the box directly. `600` is the web's half of the `.semibold` the
     UIKit symbol configuration already asks for. */
  font-size: calc(var(--ln-icon-glyph) * 1.4);
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  transition:
    transform var(--ln-t-tap) var(--ln-ease),
    box-shadow var(--ln-t-tap) var(--ln-ease);
  -webkit-tap-highlight-color: transparent;
}
/* A circle is where the old straight-bar lip was worst: it sat as a visible
   chord across the top of the disc. As a shadow on a radius-inheriting overlay it
   is an arc, and the mask is tighter here because a circle turns away from the
   light much sooner than a rectangle does. */
.ln-icon-btn::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 1px 0 var(--ln-btn-lip, light-dark(rgb(255 255 255 / calc(0.42 * var(--ln-depth))), rgb(255 255 255 / calc(0.18 * var(--ln-depth)))));
  mask-image: linear-gradient(to right, transparent, #000 34%, #000 66%, transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 34%, #000 66%, transparent);
  pointer-events: none;
  transition: opacity var(--ln-t-tap) var(--ln-ease);
}
.ln-icon-btn.ln-primary-icon {
  --ln-btn-fill: var(--ln-accent);
  --ln-btn-ink: var(--ln-on-accent);
  border-color: rgb(0 0 0 / 0.12);
  box-shadow: var(--ln-e-raised);
}
.ln-icon-btn.ln-bare {
  --ln-btn-fill: transparent;
  background-image: none;
  border-color: transparent;
  box-shadow: none;
}
.ln-icon-btn.ln-bare::before {
  display: none;
}
.ln-icon-btn.ln-bare:hover:not(:disabled) {
  background-color: color-mix(in srgb, currentColor 8%, transparent);
}
.ln-icon-btn:active:not(:disabled) {
  transform: scale(0.92);
}
.ln-icon-btn:active:not(:disabled)::before {
  opacity: 0.2;
}
.ln-icon-btn:disabled {
  --ln-btn-fill: var(--ln-surface-quiet);
  --ln-btn-ink: var(--ln-ink-tertiary);
  background-image: none;
  box-shadow: none;
  cursor: default;
}
/* A supplied icon fills the icon box, so it is sized once here rather than at
   every call site — and it stays in step with `ln-sm` / `ln-lg`. */
.ln-icon-btn > svg,
.ln-icon-btn > img {
  display: block;
  width: var(--ln-icon-glyph);
  height: var(--ln-icon-glyph);
}

.ln-icon-btn.ln-lg {
  --ln-icon-size: 56px;
}
.ln-icon-btn.ln-sm {
  --ln-icon-size: 30px;
}

/* ========================================================================== *
 * Pill
 * ========================================================================== */

/* A badge. One implementation, four styles — it replaced seven hand-rolled
   capsules that agreed on nothing. */
.ln-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--ln-xs);
  padding: 3px 9px;
  border: 1px solid
    color-mix(in srgb, var(--ln-pill-color, var(--ln-ink-quiet)) 42%, transparent);
  border-radius: var(--ln-r-pill);
  background: transparent;
  color: var(--ln-pill-color, var(--ln-ink-quiet));
  font-size: var(--ln-fs-micro);
  font-weight: 600;
  line-height: 1.45;
  white-space: nowrap;
}
.ln-pill.ln-filled {
  background: var(--ln-pill-color, var(--ln-accent));
  border-color: transparent;
  color: var(--ln-on-accent);
}
.ln-pill.ln-wash {
  background: color-mix(in srgb, var(--ln-pill-color, var(--ln-accent)) 14%, transparent);
  border-color: color-mix(in srgb, var(--ln-pill-color, var(--ln-accent)) 24%, transparent);
  color: var(--ln-pill-color, var(--ln-accent));
}
/* Over video or artwork, where `filled`'s ink would flip to near-black in dark
   mode and vanish into the frame behind it. */
.ln-pill.ln-on-media {
  background: var(--ln-media-chip);
  border-color: rgb(255 255 255 / 0.16);
  color: var(--ln-on-media);
}
.ln-pill.ln-accent-pill {
  --ln-pill-color: var(--ln-accent);
}
.ln-pill.ln-positive-pill {
  --ln-pill-color: var(--ln-positive);
}
.ln-pill.ln-caution-pill {
  --ln-pill-color: var(--ln-caution);
}
.ln-pill.ln-critical-pill {
  --ln-pill-color: var(--ln-critical);
}

/* A pill you can press — a filter, a toggle chip. */
button.ln-pill,
a.ln-pill {
  min-height: 40px;
  padding: 6px 14px;
  cursor: pointer;
  font-family: inherit;
  transition:
    transform var(--ln-t-tap) var(--ln-ease),
    background-color var(--ln-t-tap) var(--ln-ease);
}
button.ln-pill:active {
  transform: scale(0.96);
}
button.ln-pill[aria-pressed='true'],
.ln-pill.ln-selected {
  background: var(--ln-accent-wash);
  border-color: color-mix(in srgb, var(--ln-accent) 34%, transparent);
  color: var(--ln-accent);
}

/* ========================================================================== *
 * Rows and groups
 * ========================================================================== */

/* A hairline between two rows of the same group. */
.ln-separator {
  height: 1px;
  border: 0;
  margin: 0;
  background: var(--ln-hairline);
}

/* A group of rows inside one raised card, hairlines between them. */
.ln-group {
  display: flex;
  flex-direction: column;
  border-radius: var(--ln-radius, var(--ln-r-md));
  overflow: hidden;
}
.ln-group > * + * {
  border-top: 1px solid var(--ln-hairline);
}

/* A tappable line in a group: an optional accent glyph, a title, a value, an
   accessory. It dims rather than sinks on press — a row is part of a card's
   surface, not an object sitting on it, so scaling it would peel it off. */
.ln-row {
  display: flex;
  align-items: center;
  gap: var(--ln-md);
  width: 100%;
  padding: var(--ln-sm) var(--ln-lg);
  border: 0;
  background: none;
  color: var(--ln-ink);
  font: inherit;
  text-align: start;
  text-decoration: none;
  transition: opacity var(--ln-t-tap) var(--ln-ease);
}
.ln-row > .ln-row-title {
  flex: 1;
  min-width: 0;
}
.ln-row > .ln-row-value {
  color: var(--ln-ink-quiet);
  font-size: var(--ln-fs-callout);
  white-space: nowrap;
}
.ln-row > .ln-row-icon {
  color: var(--ln-accent);
  flex: none;
  width: 24px;
  text-align: center;
}
.ln-row[href],
.ln-row[type],
button.ln-row {
  cursor: pointer;
}
.ln-row[href]:active,
button.ln-row:active {
  opacity: 0.55;
}
.ln-row.ln-selected {
  background: var(--ln-accent-wash);
}
/* Pushes a screen. */
/* '›' is a bidi-mirrored character, so RTL already draws it as '‹'. */
.ln-row-chevron::after {
  content: '›';
  color: var(--ln-ink-tertiary);
  font-size: 1.15em;
  line-height: 1;
}

/* A label on the leading side and its control on the trailing side. The label
   holds its width and the control yields — a label that yields instead just
   disappears, and then the row is a control with nothing saying what it does. */
.ln-control-row {
  display: flex;
  align-items: center;
  gap: var(--ln-md);
}
.ln-control-row > .ln-control-label {
  flex: none;
  color: var(--ln-ink-quiet);
  font-size: var(--ln-fs-callout);
}
.ln-control-row > :last-child {
  flex: 1;
  min-width: 0;
}
/* A segmented control never shrinks past its labels; it drops under its
   label instead of pushing the page sideways on a phone. */
.ln-control-row:has(> .ln-segmented) {
  flex-wrap: wrap;
}
.ln-control-row > .ln-segmented {
  min-width: fit-content;
}

/* ========================================================================== *
 * Fields
 * ========================================================================== */

/* Icons inherit ink and stay secondary to the visible label. */
.ln-icon {
  display: inline-block;
  width: 20px;
  height: 20px;
  flex: 0 0 20px;
  vertical-align: middle;
}
.ln-sidebar-nav {
  display: grid;
  gap: 4px;
}
/* The links carry no `ln-` class, so the border-box reset above misses them;
   without this the 44px minimum was content height and a row measured 64px. */
.ln-sidebar-nav > a,
.ln-subnav > a {
  box-sizing: border-box;
}
.ln-sidebar-nav > a {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 10px 12px;
  border-radius: var(--ln-r-xs);
  color: var(--ln-ink-secondary);
  text-decoration: none;
  font-size: var(--ln-fs-body);
}
.ln-sidebar-nav > a:hover {
  background: var(--ln-well);
  color: var(--ln-ink);
}
.ln-sidebar-nav > a[aria-current='page'] {
  background: var(--ln-accent-wash);
  color: var(--ln-accent);
  font-weight: 600;
}

/* Local navigation is a row of destinations, not a row of raised buttons. */
.ln-subnav {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 20px;
  border-block-end: 1px solid var(--ln-hairline-faint);
}
.ln-subnav > a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-block: 8px;
  color: var(--ln-ink-secondary);
  text-decoration: none;
  border-block-end: 2px solid transparent;
}
.ln-subnav > a:hover,
.ln-subnav > a[aria-current] {
  color: var(--ln-accent);
  border-block-end-color: currentColor;
}

/* Form rhythm groups labels with controls without adding another surface. */
.ln-form {
  display: grid;
  gap: 16px;
}
.ln-field {
  display: grid;
  gap: 6px;
  min-width: 0;
}
.ln-field > label {
  font-size: var(--ln-fs-body);
  font-weight: 600;
}
.ln-form-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--ln-ink-secondary);
  font-size: var(--ln-fs-micro);
}
.ln-form-divider::before,
.ln-form-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--ln-hairline-faint);
}

/* Editable text in a well. */
.ln-input,
.ln-textarea,
.ln-select {
  display: block;
  width: 100%;
  padding: var(--ln-sm) var(--ln-md);
  border: 1px solid var(--ln-field-border);
  border-radius: var(--ln-radius-control, var(--ln-r-sm));
  background: var(--ln-well);
  box-shadow: var(--ln-inner);
  color: var(--ln-ink);
  font-family: inherit;
  font-size: var(--ln-fs-body);
  line-height: var(--ln-lh);
  transition:
    border-color var(--ln-t-tap) var(--ln-ease),
    box-shadow var(--ln-t-tap) var(--ln-ease);
}
.ln-textarea {
  min-height: 72px;
  resize: vertical;
}
.ln-input::placeholder,
.ln-textarea::placeholder {
  color: var(--ln-ink-placeholder);
}
.ln-input:focus,
.ln-textarea:focus,
.ln-select:focus {
  outline: none;
  border-color: var(--ln-field-focus);
  box-shadow:
    var(--ln-inner),
    0 0 0 3px var(--ln-accent-wash);
}
.ln-input:disabled,
.ln-textarea:disabled,
.ln-select:disabled {
  color: var(--ln-ink-tertiary);
  cursor: default;
}
.ln-select {
  appearance: none;
  padding-inline-end: var(--ln-xxl);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--ln-ink-tertiary) 50%),
    linear-gradient(135deg, var(--ln-ink-tertiary) 50%, transparent 50%);
  background-position:
    right var(--ln-lg) center,
    right var(--ln-md) center;
  background-size:
    5px 5px,
    5px 5px;
  background-repeat: no-repeat;
  cursor: pointer;
}
/* The chevron is two triangles; mirrored, they trade places. */
.ln-select:dir(rtl) {
  background-position:
    left var(--ln-md) center,
    left var(--ln-lg) center;
}
.ln-field-label {
  display: block;
  margin-bottom: var(--ln-xs);
  font-size: var(--ln-fs-micro);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ln-ink-quiet);
}
.ln-field-hint {
  margin-top: var(--ln-xs);
  font-size: var(--ln-fs-caption);
  color: var(--ln-ink-quiet);
}
.ln-field-hint.ln-error {
  color: var(--ln-critical);
}

/* A toggle in the app's accent. */
.ln-switch {
  position: relative;
  display: inline-flex;
  flex: none;
  align-items: center;
  width: 51px;
  height: 31px;
  border-radius: var(--ln-r-pill);
  background: var(--ln-well);
  border: 1px solid var(--ln-control-edge);
  box-shadow: var(--ln-inner);
  cursor: pointer;
  transition:
    background-color var(--ln-t-move) var(--ln-ease),
    border-color var(--ln-t-move) var(--ln-ease);
  -webkit-tap-highlight-color: transparent;
}
.ln-switch > input {
  position: absolute;
  inset: 0;
  opacity: 0;
  margin: 0;
  cursor: pointer;
}
.ln-switch > .ln-switch-knob {
  position: absolute;
  inset-inline-start: 2px;
  width: 25px;
  height: 25px;
  border-radius: 50%;
  background: light-dark(#fff, #f3f3f1);
  box-shadow: var(--ln-e-resting);
  transition: transform var(--ln-t-move) var(--ln-ease-spring);
  pointer-events: none;
}
.ln-switch:has(> input:checked) {
  background: var(--ln-accent);
  border-color: rgb(0 0 0 / 0.12);
  box-shadow: none;
}
.ln-switch:has(> input:checked) > .ln-switch-knob {
  transform: translateX(20px);
}
.ln-switch:dir(rtl):has(> input:checked) > .ln-switch-knob {
  transform: translateX(-20px);
}
.ln-switch:has(> input:focus-visible) {
  outline: 2px solid var(--ln-focus);
  outline-offset: 2px;
}
.ln-switch:has(> input:disabled) {
  opacity: 0.5;
  cursor: default;
}

/* A row of choices sunk into the surface, the selected one raised back out of
   it. Radio labels are the no-JS form; buttons are the dynamic form used when
   a page owns the state. Both stay one component so a selector does not grow a
   page-specific visual dialect. */
.ln-segmented {
  display: inline-flex;
  align-items: stretch;
  padding: 2px;
  border: 1px solid var(--ln-hairline-faint);
  /* A capsule, matching `LanternSegmentedControl`, which sets
     `Radius.capsule(bounds.height)` on its layer. The web had this at `r-xs`. */
  border-radius: var(--ln-r-pill);
  background: var(--ln-well);
  box-shadow: var(--ln-inner);
}
/* Equal widths, not width-by-content: a two-segment control whose labels are
   "Spotlight" and "Study" should not have one segment twice the other. */
.ln-segmented > label,
.ln-segmented > button {
  position: relative;
  flex: 1;
  display: inline-flex;
  box-sizing: border-box;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: 8px 14px;
  border: 1px solid transparent;
  /* Concentric with the track: a capsule inside a capsule. */
  border-radius: var(--ln-r-pill);
  background: none;
  color: var(--ln-ink-quiet);
  font-family: inherit;
  font-size: var(--ln-fs-callout);
  cursor: pointer;
  text-align: center;
  white-space: nowrap;
  transition:
    background-color var(--ln-t-tap) var(--ln-ease),
    color var(--ln-t-tap) var(--ln-ease);
}
.ln-segmented > button:hover:not(:disabled),
.ln-segmented > label:hover {
  color: var(--ln-ink);
}
.ln-segmented input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
/* In dark a surface on a well is a step of a few percent, too close to tell
   apart, so the raised segment lifts toward the ink, takes the strong rule and
   catches the lip. The weight carries it without colour at all. */
.ln-segmented > label:has(input:checked),
.ln-segmented > button[aria-pressed='true'],
.ln-segmented > [role='tab'][aria-selected='true'] {
  background: light-dark(var(--ln-surface), color-mix(in srgb, var(--ln-surface-quiet), var(--ln-ink) 9%));
  border: 1px solid light-dark(var(--ln-hairline), var(--ln-hairline-strong));
  color: var(--ln-ink);
  font-weight: 600;
  box-shadow:
    var(--ln-e-resting),
    inset 0 1px 0 light-dark(transparent, var(--ln-lip));
}
.ln-segmented > label:has(input:focus-visible) {
  outline: 2px solid var(--ln-focus);
  outline-offset: 1px;
}
.ln-segmented > button:disabled {
  color: var(--ln-ink-tertiary);
  cursor: default;
}

/* A value track: a well with a lit fill and a machined cap. */
.ln-slider {
  appearance: none;
  width: 100%;
  height: 26px;
  background: transparent;
  cursor: pointer;
}
.ln-slider::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 3px;
  background: var(--ln-well);
  border: 1px solid var(--ln-control-edge);
  box-shadow: var(--ln-inner);
}
.ln-slider::-moz-range-track {
  height: 6px;
  border-radius: 3px;
  background: var(--ln-well);
  border: 1px solid var(--ln-control-edge);
  box-shadow: var(--ln-inner);
}
.ln-slider::-moz-range-progress {
  height: 6px;
  border-radius: 3px;
  background: var(--ln-accent);
}
/* A transport, not a setting: the part already played is lit. Gecko fills a
   range on its own (above); WebKit has no track-fill pseudo-element, so the fill
   is painted into the track and needs the value handed to it — CSS cannot read
   an input's value. Set `--ln-progress` (0…1) wherever you set `.value`. Not
   `Lantern.setProgress`: that one also writes progressbar ARIA, which would lie
   about a control the reader can drag. */
.ln-slider.ln-filled {
  --ln-progress: 0;
}
/* `no-repeat` is load-bearing. A gradient is sized to the PADDING box but
   clipped to the BORDER box, so with the default `repeat` the tile starts over
   inside the track's 1px right border — and the tile starts on the accent stop.
   The result was a blue cap welded to the right end of every filled track,
   whatever the value. */
.ln-slider.ln-filled::-webkit-slider-runnable-track {
  background: linear-gradient(
      to right,
      var(--ln-accent) 0 calc(var(--ln-progress) * 100%),
      transparent calc(var(--ln-progress) * 100%) 100%
    )
    no-repeat var(--ln-well);
}
/* A range fills from the right in RTL, so its painted fill does too. */
.ln-slider.ln-filled:dir(rtl)::-webkit-slider-runnable-track {
  background: linear-gradient(
      to left,
      var(--ln-accent) 0 calc(var(--ln-progress) * 100%),
      transparent calc(var(--ln-progress) * 100%) 100%
    )
    no-repeat var(--ln-well);
}
.ln-slider.ln-on-media {
  --ln-accent: var(--ln-on-media-accent);
}
.ln-slider.ln-on-media::-webkit-slider-runnable-track,
.ln-slider.ln-on-media::-moz-range-track {
  background-color: var(--ln-on-media-track);
  border-color: transparent;
  box-shadow: none;
}
.ln-slider::-webkit-slider-thumb {
  appearance: none;
  width: 26px;
  height: 26px;
  margin-top: -11px;
  border-radius: 50%;
  border: 1px solid var(--ln-control-edge);
  background-color: var(--ln-surface);
  background-image: linear-gradient(
    light-dark(rgb(255 255 255 / 0), rgb(255 255 255 / calc(0.08 * var(--ln-depth)))),
    light-dark(rgb(0 0 0 / calc(0.05 * var(--ln-depth))), rgb(0 0 0 / calc(0.06 * var(--ln-depth))))
  );
  box-shadow: var(--ln-e-resting);
}
.ln-slider::-moz-range-thumb {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: 1px solid var(--ln-control-edge);
  background: var(--ln-surface);
  box-shadow: var(--ln-e-resting);
}

/* A determinate bar: a well with a lit fill. */
.ln-progress {
  --ln-progress: 0;
  position: relative;
  height: 6px;
  border-radius: 3px;
  background: var(--ln-well);
  border: 1px solid var(--ln-hairline);
  box-shadow: var(--ln-inner);
  overflow: hidden;
}
.ln-progress > .ln-progress-fill {
  height: 100%;
  border-radius: 3px;
  /* Never narrower than its own height: a 2% sliver drawn into a capsule corner
     reads as a rendering glitch rather than as "just started". */
  min-width: 6px;
  width: calc(var(--ln-progress) * 100%);
  background-color: var(--ln-progress-tint, var(--ln-accent));
  background-image: linear-gradient(
    light-dark(color-mix(in srgb, var(--ln-progress-tint, var(--ln-accent)), #fff calc(5.5% * var(--ln-depth))), color-mix(in srgb, var(--ln-progress-tint, var(--ln-accent)), #fff calc(7% * var(--ln-depth)))),
    light-dark(color-mix(in srgb, var(--ln-progress-tint, var(--ln-accent)), #000 calc(4.5% * var(--ln-depth))), color-mix(in srgb, var(--ln-progress-tint, var(--ln-accent)), #000 calc(5% * var(--ln-depth))))
  );
  transition: width var(--ln-t-move) var(--ln-ease);
}
.ln-progress[data-progress='0'] > .ln-progress-fill {
  min-width: 0;
  width: 0;
}
/* Over video or artwork the well disappears and the neutral tokens go with it. */
.ln-progress.ln-on-media {
  --ln-accent: var(--ln-on-media-accent);
  background: var(--ln-on-media-track);
  border-color: transparent;
  box-shadow: none;
}

/* A determinate ring with its own reading in the middle, for a wait long enough
   that a bar would sit at the bottom of an otherwise empty screen. */
.ln-ring {
  --ln-progress: 0;
  --ln-ring-size: 96px;
  position: relative;
  width: var(--ln-ring-size);
  height: var(--ln-ring-size);
  border-radius: 50%;
  background: conic-gradient(var(--ln-accent) calc(var(--ln-progress) * 360deg), var(--ln-well) 0);
  display: grid;
  place-items: center;
}
.ln-ring::before {
  content: '';
  position: absolute;
  inset: 8px;
  border-radius: 50%;
  background: var(--ln-surface);
}
.ln-ring > .ln-ring-readout {
  position: relative;
  font-variant-numeric: tabular-nums;
  font-size: var(--ln-fs-title);
  font-weight: 600;
}

/* A colour, as a tappable chip. A transparent colour and a white one both leave
   nothing to see on a light card, so the chip is always outlined and
   transparency is drawn as a checker. */
.ln-swatch {
  position: relative;
  display: inline-grid;
  place-items: center;
  width: 56px;
  height: 32px;
  padding: 0;
  border: 1px solid var(--ln-hairline);
  border-radius: var(--ln-r-xs);
  /* A button's UA fill is grey in dark mode; the checker needs the surface under it. */
  background-color: transparent;
  /* The checker is the element's own background and the colour is a layer on top
     of it, not the reverse: an element's background paints *below* its ::before,
     so a checker in the pseudo-element would cover every opaque colour instead
     of showing through a transparent one. */
  background-image:
    linear-gradient(
      45deg,
      rgb(0 0 0 / 0.14) 25%,
      transparent 25%,
      transparent 75%,
      rgb(0 0 0 / 0.14) 75%
    ),
    linear-gradient(
      45deg,
      rgb(0 0 0 / 0.14) 25%,
      transparent 25%,
      transparent 75%,
      rgb(0 0 0 / 0.14) 75%
    );
  background-size: 8px 8px;
  background-position:
    0 0,
    4px 4px;
  cursor: pointer;
  font-size: var(--ln-fs-micro);
  color: var(--ln-ink-quiet);
  transition: transform var(--ln-t-tap) var(--ln-ease);
  overflow: hidden;
}
.ln-swatch::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--ln-swatch-color, transparent);
}
/* Above the colour layer, so the "None" label survives an opaque swatch. */
.ln-swatch > * {
  position: relative;
}
.ln-swatch:active {
  transform: scale(0.95);
}

/* ========================================================================== *
 * States — empty, loading, failure
 * ========================================================================== */

/* One vocabulary for the three states every list can be in, so that every
   failure in the app reads the same. */
.ln-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--ln-sm);
  padding: var(--ln-xxl) var(--ln-lg);
  text-align: center;
  color: var(--ln-ink-quiet);
}
.ln-state > .ln-state-glyph {
  font-size: 2rem;
  line-height: 1;
  color: var(--ln-ink-tertiary);
}
.ln-state > .ln-state-title {
  font-size: var(--ln-fs-headline);
  font-weight: 600;
  color: var(--ln-ink);
}
.ln-state > .ln-state-detail {
  font-size: var(--ln-fs-callout);
  max-width: 34ch;
}
.ln-state.ln-failure > .ln-state-glyph {
  color: var(--ln-caution);
}

.ln-spinner {
  display: inline-block;
  width: var(--ln-spinner-size, 18px);
  height: var(--ln-spinner-size, 18px);
  border: 2px solid color-mix(in srgb, currentColor 22%, transparent);
  border-top-color: currentColor;
  border-radius: 50%;
  animation: ln-spin 0.7s linear infinite;
}
@keyframes ln-spin {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .ln-spinner {
    animation-duration: 1.6s;
  }
}

/* A shape standing in for content that is on its way. */
.ln-skeleton {
  border-radius: var(--ln-r-xs);
  background: linear-gradient(
    90deg,
    var(--ln-surface-quiet) 25%,
    color-mix(in srgb, var(--ln-surface-quiet), var(--ln-ink) 5%) 37%,
    var(--ln-surface-quiet) 63%
  );
  background-size: 400% 100%;
  animation: ln-shimmer 1.4s ease-in-out infinite;
  color: transparent;
  user-select: none;
}
@keyframes ln-shimmer {
  from {
    background-position: 100% 0;
  }
  to {
    background-position: 0 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .ln-skeleton {
    animation: none;
  }
}

/* ========================================================================== *
 * Over media
 * ========================================================================== */

/* A media tile: the dark recess fill, a continuous radius, clipped content, and
   — the load-bearing part — a hairline edge. Without it a tile disappears in
   dark mode: `--ln-media` is a hair off `--ln-canvas` by design, so a black
   rectangle on a black page has nothing to be an edge against, and a thumbnail
   that has not loaded reads as a hole in the screen rather than as a frame
   waiting for a frame. */
.ln-media-tile {
  background: var(--ln-media);
  border: 1px solid var(--ln-hairline);
  border-radius: var(--ln-radius, var(--ln-r-sm));
  overflow: hidden;
}
.ln-media-tile > img,
.ln-media-tile > video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The gradient that lets text sit over media. Both stops are transparent black
   rather than one being `transparent`, for the same reason a card's sheen fades
   white into white. */
.ln-scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(rgb(0 0 0 / 0), rgb(0 0 0 / 0.33) 55%, rgb(0 0 0 / 0.78));
}
.ln-on-media-ink {
  color: var(--ln-on-media);
}
.ln-on-media-ink-secondary {
  color: var(--ln-on-media-secondary);
}

/* ========================================================================== *
 * Chrome — bars, dialogs, sheets, toasts
 * ========================================================================== */

/* Scrolled-to-top is the page wash with no edge, so a card scrolling under the
   bar is the only edge on screen; scrolled-away gains the hairline the rest of
   the app separates things with. Toggle `data-scrolled` from a scroll listener. */
.ln-bar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: var(--ln-md);
  padding: var(--ln-md) var(--ln-lg);
  background: var(--ln-glass);
  backdrop-filter: saturate(140%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--ln-t-move) var(--ln-ease);
}
.ln-bar[data-scrolled='true'] {
  border-bottom-color: var(--ln-hairline);
}
.ln-bar > .ln-bar-title {
  flex: 1;
  min-width: 0;
  font-size: var(--ln-fs-headline);
  font-weight: 600;
}

/* A native <dialog>, in the language. */
dialog.ln-dialog {
  max-width: min(560px, calc(100vw - var(--ln-xl) * 2));
  max-height: min(80vh, 720px);
  padding: 0;
  border: 1px solid var(--ln-hairline);
  border-radius: var(--ln-r-lg);
  background-color: var(--ln-surface);
  background-image: var(--ln-grain), var(--ln-sheen);
  background-size: var(--ln-grain-size), auto;
  box-shadow: var(--ln-e-floating);
  color: var(--ln-ink);
  overflow: hidden;
}
dialog.ln-dialog::backdrop {
  background: var(--ln-scrim);
  backdrop-filter: blur(3px);
}
dialog.ln-dialog[open] {
  animation: ln-dialog-in var(--ln-t-move) var(--ln-ease-spring);
}
@keyframes ln-dialog-in {
  from {
    opacity: 0;
    transform: translateY(12px) scale(0.985);
  }
}
.ln-dialog-head,
.ln-dialog-foot {
  display: flex;
  align-items: center;
  gap: var(--ln-sm);
  padding: var(--ln-md) var(--ln-lg);
}
.ln-dialog-head {
  border-bottom: 1px solid var(--ln-hairline);
  font-size: var(--ln-fs-headline);
  font-weight: 600;
}
.ln-dialog-head > :first-child {
  flex: 1;
}
.ln-dialog-body {
  padding: var(--ln-lg);
  overflow-y: auto;
}
.ln-dialog-foot {
  border-top: 1px solid var(--ln-hairline);
  justify-content: flex-end;
}

/* A sheet: the same surface, docked to an edge. Bottom on narrow screens,
   side on wide ones — the web's equivalent of a drawer and a rail being the
   same component in two places. */
dialog.ln-sheet {
  max-width: none;
  width: min(420px, 100vw);
  max-height: 100vh;
  height: 100vh;
  margin: 0;
  margin-inline-start: auto;
  border-radius: 0;
  border-start-start-radius: var(--ln-r-lg);
  border-end-start-radius: var(--ln-r-lg);
}
dialog.ln-sheet[open] {
  animation: ln-sheet-in var(--ln-t-move) var(--ln-ease);
}
@keyframes ln-sheet-in {
  from {
    transform: translateX(var(--ln-sheet-from, 24px));
    opacity: 0;
  }
}
dialog.ln-sheet:dir(rtl) {
  --ln-sheet-from: -24px;
}
@media (max-width: 560px) {
  /* `fit-content`, not `auto`: a modal dialog is inset on every side, so an
     auto height stretched the sheet to its max-height whatever it held. */
  dialog.ln-sheet {
    width: 100vw;
    height: fit-content;
    max-height: 88vh;
    margin: auto 0 0;
    border-radius: var(--ln-r-lg) var(--ln-r-lg) 0 0;
  }
  dialog.ln-sheet[open] {
    animation: ln-sheet-up var(--ln-t-move) var(--ln-ease);
  }
  @keyframes ln-sheet-up {
    from {
      transform: translateY(24px);
      opacity: 0;
    }
  }
}
/* The grab handle a bottom sheet needs to look draggable. */
.ln-sheet-grip {
  width: 36px;
  height: 4px;
  margin: var(--ln-sm) auto 0;
  border-radius: var(--ln-r-pill);
  background: var(--ln-ink-tertiary);
}

/* A transient message over the content. A toast states something and leaves. */
/* `--ln-toast-offset` lifts the stack above a sticky bottom bar. */
/* Spans the viewport rather than sitting at `left: 50%`, which left a toast
   half the screen to wrap in. It takes no pointer events itself. */
.ln-toast-host {
  position: fixed;
  inset-inline: 0;
  bottom: calc(var(--ln-xl) + var(--ln-toast-offset, 0px));
  z-index: 100;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--ln-sm);
  padding-inline: var(--ln-md);
  pointer-events: none;
}
.ln-toast {
  max-width: min(88vw, 420px);
  padding: 10px 18px;
  border: 1px solid var(--ln-hairline);
  border-radius: 18px;
  background-color: var(--ln-surface);
  background-image: var(--ln-grain), var(--ln-sheen);
  background-size: var(--ln-grain-size), auto;
  box-shadow: var(--ln-e-floating);
  color: var(--ln-ink);
  font-size: var(--ln-fs-callout);
  text-align: center;
  animation: ln-toast-in var(--ln-t-move) var(--ln-ease-spring);
}
.ln-toast.ln-leaving {
  animation: ln-toast-out 0.24s var(--ln-ease) forwards;
}
.ln-toast.ln-positive-toast {
  color: var(--ln-positive);
}
.ln-toast.ln-warning-toast {
  color: var(--ln-caution);
}
.ln-toast.ln-critical-toast {
  color: light-dark(color-mix(in srgb, var(--ln-critical), var(--ln-ink) 20%), var(--ln-critical));
}
/* A toast that waits for the reader: it carries an action and a close button,
   so it takes pointer events the host itself refuses. For payment and quota
   errors, which must not vanish before they are read. */
.ln-toast.ln-persistent {
  display: flex;
  align-items: center;
  gap: var(--ln-sm);
  padding-block: 6px;
  padding-inline: 18px 6px;
  text-align: start;
  pointer-events: auto;
}
.ln-toast.ln-persistent > .ln-toast-message {
  flex: 1;
  min-width: 0;
}
.ln-toast.ln-persistent > .ln-btn {
  flex: none;
  min-height: 44px;
}
.ln-toast.ln-persistent > .ln-icon-btn {
  --ln-icon-size: 44px;
  --ln-icon-glyph: 13px;
  flex: none;
  color: var(--ln-ink-quiet);
}
@keyframes ln-toast-in {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
}
@keyframes ln-toast-out {
  to {
    opacity: 0;
    transform: translateY(8px);
  }
}

/* A snackbar is an offer, not a statement: it carries up to two actions, so it
   waits long enough to read and stays reachable. A floating raised surface with
   ink on it, not a dark bar — a bar in its own palette is a second material on
   the page. */
.ln-snackbar {
  position: fixed;
  inset-inline: var(--ln-md);
  bottom: var(--ln-md);
  z-index: 100;
  max-width: 520px;
  margin: 0 auto;
  padding: var(--ln-lg);
  border: 1px solid var(--ln-hairline);
  border-radius: var(--ln-r-md);
  background-color: var(--ln-surface);
  background-image: var(--ln-grain), var(--ln-sheen);
  background-size: var(--ln-grain-size), auto;
  box-shadow: var(--ln-e-floating);
  animation: ln-toast-in var(--ln-t-move) var(--ln-ease-spring);
}
.ln-snackbar > .ln-snackbar-message {
  font-size: var(--ln-fs-callout);
  color: var(--ln-ink);
}
/* Stacked, not side by side: a term, an arrow, its replacement and two verbs
   never fit one line at a large text size. */
.ln-snackbar > .ln-snackbar-actions {
  display: flex;
  gap: var(--ln-sm);
  margin-top: var(--ln-sm);
}

/* A banner that stays: a persistent, dismissible notice at the top of a view. */
.ln-banner {
  display: flex;
  align-items: center;
  gap: var(--ln-sm);
  padding: var(--ln-sm) var(--ln-md);
  border: 1px solid color-mix(in srgb, var(--ln-banner-color, var(--ln-caution)) 30%, transparent);
  border-radius: var(--ln-r-sm);
  background: color-mix(in srgb, var(--ln-banner-color, var(--ln-caution)) 12%, transparent);
  color: var(--ln-banner-color, var(--ln-caution));
  font-size: var(--ln-fs-callout);
}
.ln-banner.ln-positive-banner {
  --ln-banner-color: var(--ln-positive);
}
/* The critical hue on its own wash measures 4.0:1 in light; 30% of ink
   brings it past 4.5:1 without turning it brown. */
.ln-banner.ln-critical-banner {
  --ln-banner-color: var(--ln-critical);
  color: color-mix(in srgb, var(--ln-critical), var(--ln-ink) 30%);
}
/* Grey text in a grey wash read as a disabled field (3.65:1). Neutral is ink
   on the quiet surface, edged like one. */
.ln-banner.ln-neutral-banner {
  --ln-banner-color: var(--ln-ink-secondary);
  background: var(--ln-surface-quiet);
  border-color: var(--ln-hairline-strong);
  color: var(--ln-ink);
}
.ln-banner > :first-child {
  flex: 1;
}

/* A tooltip on hover/focus, from a `data-ln-tip` attribute. Web-only: there is
   no hover on iOS, so the language gains this one rather than porting it. */
[data-ln-tip] {
  position: relative;
}
[data-ln-tip]:hover::after,
[data-ln-tip]:focus-visible::after {
  content: attr(data-ln-tip);
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 30;
  padding: 4px 8px;
  border: 1px solid var(--ln-hairline);
  border-radius: var(--ln-r-xs);
  background: var(--ln-surface);
  box-shadow: var(--ln-e-resting);
  color: var(--ln-ink);
  font-size: var(--ln-fs-micro);
  font-weight: 500;
  white-space: nowrap;
  pointer-events: none;
}

/* ========================================================================== *
 * Lists, tables, prose — web surfaces iOS has no equivalent for
 * ========================================================================== */

/* A list with no background of its own: the page wash is already behind it. */
.ln-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.ln-list > li + li {
  border-top: 1px solid var(--ln-hairline);
}

/* Plain data without a raised surface per row: quiet headers on a well,
   hairline row separators. */
.ln-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--ln-fs-callout);
  text-align: start;
}
.ln-table th,
.ln-table td {
  padding: var(--ln-sm) var(--ln-md);
  vertical-align: top;
  text-align: start;
}
.ln-table th {
  border-block-end: 1px solid var(--ln-hairline);
  background: var(--ln-well);
  font-size: var(--ln-fs-micro);
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--ln-ink-quiet);
}
.ln-table td {
  border-block-end: 1px solid var(--ln-hairline-faint);
}
.ln-table tbody tr:last-child td {
  border-block-end: 0;
}
/* Wide content scrolls inside its own box; the page never scrolls sideways. */
.ln-scroll-x {
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

.ln-prose {
  color: var(--ln-ink);
  line-height: 1.6;
}
.ln-prose h1 {
  font-size: var(--ln-fs-display);
  font-weight: 700;
  line-height: 1.15;
  margin: 0 0 var(--ln-lg);
}
/* Headings lead tighter than the 1.6 body they sit in. */
.ln-prose h2 {
  font-size: var(--ln-fs-title);
  font-weight: 600;
  line-height: 1.25;
  margin: var(--ln-xl) 0 var(--ln-sm);
}
.ln-prose h3 {
  font-size: var(--ln-fs-headline);
  font-weight: 600;
  line-height: 1.25;
  margin: var(--ln-lg) 0 var(--ln-xs);
}
.ln-prose p {
  margin: 0 0 var(--ln-md);
}
.ln-prose code {
  font-family: var(--ln-font-mono);
  font-size: 0.9em;
  padding: 1px 5px;
  border-radius: 5px;
  background: var(--ln-well);
  border: 1px solid var(--ln-hairline-faint);
}
.ln-prose pre {
  padding: var(--ln-md);
  border-radius: var(--ln-r-sm);
  background: var(--ln-well);
  border: 1px solid var(--ln-hairline-faint);
  box-shadow: var(--ln-inner);
  overflow-x: auto;
}
.ln-prose pre code {
  padding: 0;
  border: 0;
  background: none;
}

/* A search hit. */
mark.ln-mark,
.ln-prose mark {
  padding: 0 1px;
  border-radius: 3px;
  background: var(--ln-highlight);
  color: inherit;
}

/* A collapsible section. The marker is ours: the system triangle is a fifth
   grey nobody chose. */
details.ln-details > summary {
  display: flex;
  align-items: center;
  gap: var(--ln-sm);
  padding: var(--ln-sm) 0;
  cursor: pointer;
  list-style: none;
  font-size: var(--ln-fs-callout);
  font-weight: 600;
  color: var(--ln-ink);
}
details.ln-details > summary::-webkit-details-marker {
  display: none;
}
/* A drawn chevron rather than a '›' glyph: a glyph's size, weight and bidi
   mirroring depend on the font, and a native marker points the wrong way in
   RTL in some engines. Two edges of a box, turned to point at the inline end
   while closed and down while open, so it mirrors with the direction.
   `.ln-chevron` is the same mark as an element, for a site's own summary or
   row: inside an open `<details>` summary it turns down too. */
.ln-chevron,
details.ln-details > summary::before,
details.ln-disclosure > summary::after {
  content: '';
  display: inline-block;
  flex: none;
  width: 0.42em;
  height: 0.42em;
  margin-inline: 0.12em 0.2em;
  border-style: solid;
  border-color: currentColor;
  border-width: 0 0.11em 0.11em 0;
  border-radius: 0 0 1px 0;
  transform: translateX(-0.06em) rotate(-45deg);
  transition: transform var(--ln-t-tap) var(--ln-ease);
}
.ln-chevron:dir(rtl),
details.ln-details:dir(rtl) > summary::before,
details.ln-disclosure:dir(rtl) > summary::after {
  transform: translateX(0.06em) rotate(135deg);
}
details[open] > summary .ln-chevron,
details.ln-details[open] > summary::before,
details.ln-disclosure[open] > summary::after {
  transform: translateY(-0.12em) rotate(45deg);
}
details.ln-details > summary::before {
  color: var(--ln-ink-tertiary);
}

/* ========================================================================== *
 * Product components — uploads, choices, quotas, plans, onboarding
 * ========================================================================== */

/* A drop zone: a well you put files into. Dashed because it is an invitation,
   not a surface; solid and lit once something is actually over it. */
.ln-drop {
  --ln-drop-radius: var(--ln-r-xl);
  --ln-drop-pad: clamp(48px, 9vw, 88px);
  --ln-drop-pad-compact: var(--ln-lg);
  --ln-drop-border: color-mix(in srgb, var(--ln-accent) 42%, var(--ln-hairline-strong));
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--ln-sm);
  padding: var(--ln-drop-pad) var(--ln-lg);
  border: 1.5px dashed var(--ln-drop-border);
  border-radius: var(--ln-drop-radius);
  background: var(--ln-well);
  box-shadow: var(--ln-inner);
  color: var(--ln-ink);
  text-align: center;
  transition:
    border-color var(--ln-t-tap) var(--ln-ease),
    background-color var(--ln-t-tap) var(--ln-ease),
    box-shadow var(--ln-t-tap) var(--ln-ease);
}
.ln-drop[data-over] {
  border-style: solid;
  border-color: var(--ln-accent);
  background: color-mix(in srgb, var(--ln-accent) 9%, var(--ln-well));
  box-shadow:
    var(--ln-inner),
    0 0 0 4px var(--ln-accent-wash);
}
.ln-drop[data-reject] {
  border-color: var(--ln-critical);
  background: color-mix(in srgb, var(--ln-critical) 7%, var(--ln-well));
}
.ln-drop:has(input:focus-visible) {
  outline: 2px solid var(--ln-focus);
  outline-offset: 3px;
}
.ln-drop[aria-disabled='true'] {
  opacity: 0.55;
  pointer-events: none;
}
.ln-drop > .ln-drop-glyph {
  width: 40px;
  height: 40px;
  color: var(--ln-accent);
}
.ln-drop > .ln-drop-title {
  margin: 0;
  font-size: var(--ln-fs-headline);
  font-weight: 600;
}
.ln-drop > .ln-drop-hint {
  margin: 0;
  max-width: 44ch;
  font-size: var(--ln-fs-callout);
  color: var(--ln-ink-quiet);
}
.ln-drop > .ln-drop-meta {
  margin: 0;
  font-size: var(--ln-fs-caption);
  color: var(--ln-ink-quiet);
}
/* Once files are listed the zone steps aside into one row. */
.ln-drop.ln-compact {
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: var(--ln-sm) var(--ln-md);
  padding: var(--ln-drop-pad-compact);
  border-radius: var(--ln-r-md);
  text-align: start;
}
.ln-drop.ln-compact > .ln-drop-glyph {
  width: 24px;
  height: 24px;
}
.ln-drop.ln-compact > .ln-drop-title {
  flex: 1;
  min-width: 12ch;
  font-size: var(--ln-fs-callout);
}

/* A full-window target while a file is dragged anywhere over the page: a
   drop works everywhere, so the feedback covers everything. Holds a
   `.ln-drop-title` and an optional `.ln-drop-hint`. */
.ln-drop-overlay {
  position: fixed;
  inset: 12px;
  z-index: 90;
  display: grid;
  place-content: center;
  gap: var(--ln-xs);
  padding: var(--ln-xl);
  border: 2px dashed var(--ln-accent);
  border-radius: var(--ln-r-md);
  background: color-mix(in srgb, var(--ln-canvas) 94%, transparent);
  text-align: center;
  pointer-events: none;
}
.ln-drop-overlay[hidden] {
  display: none;
}
.ln-drop-overlay > * {
  margin: 0;
}
.ln-drop-overlay > .ln-drop-title {
  color: var(--ln-accent);
  font-size: var(--ln-fs-title);
  font-weight: 600;
}
.ln-drop-overlay > .ln-drop-hint {
  color: var(--ln-ink-secondary);
}

/* A file or job in a list: name, status, optional progress, meta, actions.
   The status carries the state in words as well as colour. */
.ln-file-list {
  list-style: none;
  margin: 0;
  padding: 0;
}
/* A page rule like `:lang(ar) ol { list-style-type: arabic-indic }` outranks a
   bare class, but an item's own declaration always beats an inherited one. */
:is(.ln-steps, .ln-file-list, .ln-meter-list) > li {
  list-style: none;
}
.ln-file-list > li + li {
  border-block-start: 1px solid var(--ln-hairline);
}
.ln-file-row {
  --ln-file-tint: var(--ln-ink-quiet);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--ln-xs) var(--ln-md);
  padding: var(--ln-md) var(--ln-lg);
}
.ln-file-row[data-state='uploading'],
.ln-file-row[data-state='running'] {
  --ln-file-tint: var(--ln-accent);
}
.ln-file-row[data-state='done'],
.ln-file-row[data-state='ready'] {
  --ln-file-tint: var(--ln-positive);
}
.ln-file-row[data-state='failed'] {
  --ln-file-tint: light-dark(color-mix(in srgb, var(--ln-critical), var(--ln-ink) 20%), var(--ln-critical));
}
.ln-file-row > * {
  grid-column: 1;
  min-width: 0;
}
.ln-file-row > .ln-file-name {
  overflow: hidden;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* A Latin name on an Arabic page keeps its own start, so it loses its end
     to the ellipsis rather than its beginning; it still aligns with the row. */
  unicode-bidi: plaintext;
}
/* Aligned with the row, not with the name's own direction; `match-parent`
   resolves against the name in Chrome. */
.ln-file-row:dir(ltr) > .ln-file-name {
  text-align: left;
}
.ln-file-row:dir(rtl) > .ln-file-name {
  text-align: right;
}
.ln-file-row > .ln-file-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--ln-file-tint);
  font-size: var(--ln-fs-callout);
  font-weight: 500;
}
.ln-file-row > .ln-file-status::before {
  content: '';
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
}
.ln-file-row:is([data-state='waiting'], [data-state='queued']) > .ln-file-status::before {
  background: none;
  box-shadow: inset 0 0 0 1.5px currentColor;
}
.ln-file-row:is([data-state='uploading'], [data-state='running']) > .ln-file-status::before {
  animation: ln-pulse 1.4s var(--ln-ease) infinite;
}
.ln-file-row > .ln-file-reason {
  font-size: var(--ln-fs-caption);
  color: var(--ln-ink-quiet);
}
.ln-file-row > .ln-file-meta {
  font-size: var(--ln-fs-caption);
  color: var(--ln-ink-quiet);
}
.ln-file-row > .ln-progress {
  grid-column: 1 / -1;
  --ln-progress-tint: var(--ln-file-tint);
}
.ln-file-row:is([data-state='waiting'], [data-state='queued']) > .ln-progress {
  --ln-progress-tint: var(--ln-ink-tertiary);
}
.ln-file-row > .ln-file-actions {
  grid-column: 2;
  grid-row: 1 / span 2;
  display: flex;
  align-items: center;
  gap: var(--ln-xs);
}
.ln-file-row.ln-compact {
  padding: var(--ln-sm) var(--ln-md);
}
/* On a phone the actions take their own row, kept on one line at the end. */
@media (max-width: 640px) {
  .ln-file-row > .ln-file-actions {
    grid-column: 1 / -1;
    grid-row: auto;
    justify-content: flex-end;
    flex-wrap: nowrap;
  }
}
@keyframes ln-pulse {
  50% {
    opacity: 0.35;
  }
}
@media (prefers-reduced-motion: reduce) {
  .ln-file-row > .ln-file-status::before {
    animation: none !important;
  }
}

/* A file picked but not yet sent: a chip with its name and a remove button. */
.ln-file-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--ln-sm);
  max-width: 100%;
  min-height: 40px;
  padding-block: 4px;
  padding-inline: var(--ln-md) 4px;
  border: 1px solid var(--ln-hairline);
  border-radius: var(--ln-r-pill);
  background: var(--ln-surface-quiet);
  font-size: var(--ln-fs-callout);
}
.ln-file-chip > .ln-file-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  unicode-bidi: plaintext;
}
.ln-file-chip > .ln-file-meta {
  flex: none;
  color: var(--ln-ink-quiet);
  font-size: var(--ln-fs-caption);
}
.ln-file-chip > .ln-icon-btn {
  --ln-icon-size: 32px;
  flex: none;
}

/* A choice as a card: a radio or checkbox, a title and a line of detail. The
   whole card is the label, so the hit area is the card. */
.ln-choice-group {
  display: grid;
  gap: var(--ln-sm);
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}
.ln-choice-group > legend {
  float: inline-start;
  width: 100%;
  margin-block-end: var(--ln-sm);
  padding: 0;
  font-size: var(--ln-fs-micro);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ln-ink-quiet);
}
/* Flex rather than an auto-fit grid: the floated legend is an item too, and a
   spanning item keeps auto-fit from collapsing the empty tracks. */
.ln-choice-group.ln-columns {
  display: flex;
  flex-wrap: wrap;
}
.ln-choice-group.ln-columns > .ln-choice {
  flex: 1 1 220px;
}
.ln-choice {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: var(--ln-xs) var(--ln-md);
  min-height: 44px;
  padding: var(--ln-md) var(--ln-lg);
  border: 1px solid var(--ln-hairline);
  border-radius: var(--ln-r-md);
  background: var(--ln-surface);
  box-shadow: var(--ln-e-resting);
  color: var(--ln-ink);
  cursor: pointer;
  transition:
    border-color var(--ln-t-tap) var(--ln-ease),
    background-color var(--ln-t-tap) var(--ln-ease);
  -webkit-tap-highlight-color: transparent;
}
.ln-choice:hover {
  border-color: var(--ln-hairline-strong);
}
.ln-choice > input {
  width: 20px;
  height: 20px;
  margin: 2px 0 0;
  accent-color: var(--ln-accent);
  cursor: pointer;
}
.ln-choice > input:focus-visible {
  outline: none;
}
.ln-choice > .ln-choice-body {
  display: grid;
  gap: var(--ln-hair);
  min-width: 0;
}
.ln-choice .ln-choice-title {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ln-xs) var(--ln-sm);
  font-weight: 600;
}
.ln-choice .ln-choice-detail {
  font-size: var(--ln-fs-callout);
  color: var(--ln-ink-quiet);
}
.ln-choice:has(> input:checked) {
  border-color: color-mix(in srgb, var(--ln-accent) 60%, transparent);
  background: color-mix(in srgb, var(--ln-accent) 7%, var(--ln-surface));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--ln-accent) 60%, transparent);
}
.ln-choice:has(> input:focus-visible) {
  outline: 2px solid var(--ln-focus);
  outline-offset: 2px;
}
.ln-choice:has(> input:disabled) {
  opacity: 0.55;
  cursor: default;
  box-shadow: none;
}
/* An action card: the same shape as a button or link, for "ways in" on a
   first run. No input; the title takes the accent. */
a.ln-choice,
button.ln-choice {
  grid-template-columns: minmax(0, 1fr) auto;
  font: inherit;
  text-align: start;
  text-decoration: none;
}
a.ln-choice:hover,
button.ln-choice:hover {
  border-color: var(--ln-accent);
}
:is(a, button).ln-choice .ln-choice-title {
  color: var(--ln-accent);
}
:is(a, button).ln-choice > .ln-choice-body {
  grid-column: 1;
}

/* An offer: the same card with a price at the end and room for a badge. */
.ln-choice.ln-offer {
  align-items: center;
  padding-block: var(--ln-lg);
}
.ln-choice.ln-offer > input {
  margin: 0;
}
.ln-choice > .ln-choice-price {
  align-self: center;
  font-size: var(--ln-fs-title);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  unicode-bidi: isolate;
  white-space: nowrap;
}
.ln-choice .ln-choice-badge {
  font-weight: 600;
}
@media (max-width: 480px) {
  .ln-choice > .ln-choice-price {
    grid-column: 2;
    justify-self: start;
    font-size: var(--ln-fs-headline);
  }
}

/* A quota or allowance: a label, a reading and a bar. `--ln-meter` is the
   used share of the cap (0…1). With `data-over`, `--ln-meter-need` (the
   request over the cap, above 1) widens the scale and the part past the cap is
   drawn hatched in the critical hue. `Lantern.setMeter` sets all of it. */
.ln-meter {
  --ln-meter: 0;
  --ln-meter-scale: 1;
  --ln-meter-tint: var(--ln-accent);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: var(--ln-xs) var(--ln-md);
}
.ln-meter[data-over] {
  --ln-meter-scale: max(1, var(--ln-meter-need, 1));
}
.ln-meter[data-tone='caution'] {
  --ln-meter-tint: var(--ln-caution);
}
.ln-meter[data-tone='critical'] {
  --ln-meter-tint: var(--ln-critical);
}
.ln-meter > .ln-meter-label {
  font-size: var(--ln-fs-callout);
  font-weight: 600;
}
.ln-meter > .ln-meter-value {
  font-size: var(--ln-fs-callout);
  color: var(--ln-ink-quiet);
  font-variant-numeric: tabular-nums;
  unicode-bidi: isolate;
  white-space: nowrap;
}
.ln-meter > .ln-meter-bar {
  position: relative;
  grid-column: 1 / -1;
  height: 8px;
  border: 1px solid var(--ln-hairline);
  border-radius: 4px;
  background: var(--ln-well);
  box-shadow: var(--ln-inner);
  overflow: hidden;
}
.ln-meter-bar > .ln-meter-fill {
  display: block;
  height: 100%;
  width: calc(min(1, var(--ln-meter)) / var(--ln-meter-scale) * 100%);
  border-radius: 4px;
  background: var(--ln-meter-tint);
  transition: width var(--ln-t-move) var(--ln-ease);
}
.ln-meter-bar > .ln-meter-over {
  display: none;
}
.ln-meter[data-over] .ln-meter-over {
  display: block;
  position: absolute;
  inset-block: 0;
  inset-inline-start: calc(100% / var(--ln-meter-scale));
  inset-inline-end: 0;
  border-inline-start: 2px solid var(--ln-surface);
  background: repeating-linear-gradient(
    135deg,
    color-mix(in srgb, var(--ln-critical) 70%, transparent) 0 3px,
    color-mix(in srgb, var(--ln-critical) 22%, transparent) 3px 6px
  );
}
.ln-meter > .ln-meter-text {
  grid-column: 1 / -1;
  margin: 0;
  font-size: var(--ln-fs-caption);
  color: var(--ln-ink-quiet);
}

/* Recording: a live dot beside a running time, and an input level. */
.ln-live {
  display: inline-flex;
  align-items: center;
  gap: var(--ln-sm);
  color: var(--ln-live);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
/* The dot also stands alone, beside a label in the page's own ink. */
.ln-live-dot {
  display: inline-block;
  flex: none;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--ln-live);
  animation: ln-pulse 1.2s ease-in-out infinite;
}
.ln-live > :not(.ln-live-dot) {
  unicode-bidi: isolate;
}
.ln-level {
  --ln-level: 0;
  position: relative;
  height: 8px;
  border-radius: var(--ln-r-pill);
  background: color-mix(in srgb, var(--ln-ink) 12%, transparent);
  overflow: hidden;
}
.ln-level::before {
  content: '';
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: calc(var(--ln-level) * 100%);
  border-radius: inherit;
  background: var(--ln-positive);
  transition: width 0.1s linear;
}
@media (prefers-reduced-motion: reduce) {
  .ln-live-dot {
    animation: none;
  }
}

/* A site header: brand, destinations, then actions at the inline end. Below
   720px, or wherever the links don't fit, the destinations move into a sheet
   behind a menu button (`Lantern.barMenu`), so the row never wraps. */
.ln-bar.ln-site {
  gap: var(--ln-lg);
  min-height: 64px;
  padding-block: var(--ln-sm);
}
.ln-bar > .ln-bar-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--ln-sm);
  min-height: 44px;
  color: var(--ln-ink);
  font-size: var(--ln-fs-headline);
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
}
.ln-bar > .ln-bar-nav {
  display: flex;
  align-items: center;
  gap: 2px;
  min-width: 0;
}
.ln-bar-nav > a,
.ln-sheet-nav > a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--ln-md);
  border-radius: var(--ln-r-pill);
  color: var(--ln-ink-quiet);
  font-size: var(--ln-fs-callout);
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition:
    background-color var(--ln-t-tap) var(--ln-ease),
    color var(--ln-t-tap) var(--ln-ease);
}
.ln-bar-nav > a:hover,
.ln-sheet-nav > a:hover {
  color: var(--ln-ink);
  background: color-mix(in srgb, var(--ln-ink) 6%, transparent);
}
.ln-bar-nav > a[aria-current],
.ln-sheet-nav > a[aria-current] {
  color: var(--ln-accent);
  background: var(--ln-accent-wash);
  font-weight: 600;
}
.ln-bar-brand > .ln-bar-brand-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ln-bar-brand > :is(svg, img) {
  flex: none;
}
/* The one primary destination, filled, so the phone sheet carries it with the
   other links. In the sheet it is an ordinary row. */
.ln-bar-nav > a.ln-bar-cta,
.ln-bar-nav > a.ln-bar-cta[aria-current] {
  margin-inline-start: var(--ln-xs);
  background: var(--ln-accent);
  color: var(--ln-on-accent);
  font-weight: 600;
}
.ln-bar-nav > a.ln-bar-cta:hover {
  background: color-mix(in srgb, var(--ln-accent), var(--ln-ink) 12%);
  color: var(--ln-on-accent);
}
.ln-bar-nav > a.ln-bar-cta:active {
  background: color-mix(in srgb, var(--ln-accent), var(--ln-ink) 22%);
}
/* A secondary item in the nav, such as the language switch: set apart by a
   hairline so it doesn't read as another destination. */
.ln-bar-nav > .ln-bar-aside {
  position: relative;
  margin-inline-start: var(--ln-md);
  font-weight: 400;
}
.ln-bar-nav > .ln-bar-aside:not(.ln-bar-aside + .ln-bar-aside)::before {
  content: '';
  position: absolute;
  inset-inline-start: calc(var(--ln-md) / -2 - 0.5px);
  top: 50%;
  width: 1px;
  height: 1.1em;
  translate: 0 -50%;
  background: var(--ln-hairline-strong);
}
.ln-bar > .ln-bar-actions {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--ln-sm);
  margin-inline-start: auto;
}
.ln-bar > .ln-bar-menu {
  display: none;
  flex: none;
  --ln-icon-size: 44px;
}
/* An icon action in the bar (a theme toggle) is bar chrome like the menu
   button beside it: flat, the same weight, with no contact shadow, because
   nothing sits under it. Drawn at 40px; the hit area stays 44px. Zero extra
   specificity beyond `.ln-icon-btn.ln-bare`, so a site rule still wins. */
:where(.ln-bar > .ln-bar-actions) > .ln-icon-btn:not(.ln-primary-icon) {
  --ln-icon-size: 40px;
  --ln-btn-fill: transparent;
  background-image: none;
  border-color: transparent;
  box-shadow: none;
}
:where(.ln-bar > .ln-bar-actions) > .ln-icon-btn:not(.ln-primary-icon)::before {
  display: none;
}
:where(.ln-bar > .ln-bar-actions) > .ln-icon-btn:not(.ln-primary-icon, .ln-bloom)::after {
  content: '';
  position: absolute;
  inset: -2px;
}
:where(.ln-bar > .ln-bar-actions) > .ln-icon-btn:not(.ln-primary-icon, :disabled):hover {
  background-color: color-mix(in srgb, currentColor 8%, transparent);
}
.ln-sheet-nav {
  display: grid;
  gap: 2px;
}
.ln-sheet-nav > a {
  min-height: 48px;
  padding-inline: var(--ln-lg);
  border-radius: var(--ln-r-sm);
  font-size: var(--ln-fs-body);
}
.ln-sheet-nav > .ln-separator {
  margin: var(--ln-sm) 0;
}
/* Bar actions carried into the sheet by `Lantern.barMenu`. */
.ln-sheet-nav > .ln-sheet-actions {
  display: grid;
  gap: var(--ln-sm);
}
.ln-sheet-actions[hidden],
.ln-sheet-actions > [hidden] {
  display: none;
}
@media (max-width: 720px) {
  .ln-bar.ln-site > .ln-bar-nav {
    display: none;
  }
  .ln-bar.ln-site > .ln-bar-menu {
    display: inline-flex;
  }
  /* Only once `Lantern.barMenu` has copied them into the sheet. */
  .ln-bar.ln-site[data-ln-bar-menu] > .ln-bar-actions [data-ln-sheet] {
    display: none;
  }
}
/* Set by `Lantern.barMenu` when the links don't fit at a wider width. */
.ln-bar.ln-site[data-ln-bar-collapsed] > .ln-bar-nav {
  display: none;
}
.ln-bar.ln-site[data-ln-bar-collapsed] > .ln-bar-menu {
  display: inline-flex;
}
.ln-bar.ln-site[data-ln-bar-collapsed][data-ln-bar-menu] > .ln-bar-actions [data-ln-sheet] {
  display: none;
}
/* A 320px phone: brand, one action, theme and the menu button have to fit. */
@media (max-width: 400px) {
  .ln-bar.ln-site {
    gap: var(--ln-xs);
    padding-inline: var(--ln-md);
  }
  .ln-bar.ln-site > .ln-bar-actions {
    gap: var(--ln-xs);
  }
  /* The brand gives way last, and only here: its name ellipsises rather than
     pushing the menu button out of the bar. */
  .ln-bar.ln-site > .ln-bar-brand {
    min-width: 0;
    font-size: var(--ln-fs-callout);
  }
}

/* A figure worth a glance: label, value, an optional trend and note. Layout
   only; put it in a card, or several in `.ln-stat-grid`. */
.ln-stat {
  display: grid;
  gap: var(--ln-xs);
  align-content: start;
  min-width: 0;
}
.ln-stat > .ln-stat-label {
  font-size: var(--ln-fs-caption);
  font-weight: 600;
  color: var(--ln-ink-quiet);
}
.ln-stat > .ln-stat-value {
  font-size: var(--ln-fs-display);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  unicode-bidi: isolate;
}
.ln-stat.ln-quiet > .ln-stat-value {
  font-size: var(--ln-fs-title);
  font-weight: 600;
}
/* Trend arrows point up and down in both directions: they mean more and
   less, not forward and back. */
.ln-stat-delta {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  justify-self: start;
  font-size: var(--ln-fs-caption);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  unicode-bidi: isolate;
  color: var(--ln-ink-quiet);
}
.ln-stat-delta[data-trend='up'] {
  color: var(--ln-positive);
}
.ln-stat-delta[data-trend='down'] {
  color: var(--ln-critical);
}
.ln-stat-delta[data-trend='up']::before {
  content: '↑';
}
.ln-stat-delta[data-trend='down']::before {
  content: '↓';
}
.ln-stat-delta[data-trend='flat']::before {
  content: '–';
}
/* For a figure where less is better: the arrow stays, the verdict flips. */
.ln-stat-delta[data-good='down'][data-trend='down'] {
  color: var(--ln-positive);
}
.ln-stat-delta[data-good='down'][data-trend='up'] {
  color: var(--ln-critical);
}
.ln-stat > .ln-stat-note {
  font-size: var(--ln-fs-caption);
  color: var(--ln-ink-quiet);
}
.ln-stat > .ln-stat-spark {
  display: block;
  width: 100%;
  height: 32px;
  margin-block-start: var(--ln-xs);
}
.ln-stat-grid {
  --ln-stat-gap: var(--ln-md);
  display: grid;
  gap: var(--ln-stat-gap);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr));
}
/* Label / value pairs on one line: "Today · 7 days · 30 days". */
.ln-stat-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ln-md) var(--ln-xl);
  margin: 0;
}
.ln-stat-row > div {
  display: grid;
  gap: var(--ln-hair);
}
.ln-stat-row dt {
  font-size: var(--ln-fs-caption);
  color: var(--ln-ink-quiet);
}
.ln-stat-row dd {
  margin: 0;
  font-size: var(--ln-fs-headline);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  unicode-bidi: isolate;
}

/* Labeled comparison bars, one per row. `data-ours` marks the row the page is
   about; the rest recede. Each row sets its own `--ln-meter` (0…1). */
.ln-meter-list {
  display: grid;
  gap: var(--ln-md);
  margin: 0;
  padding: 0;
  list-style: none;
}
.ln-meter-list > .ln-meter {
  --ln-meter-tint: color-mix(in srgb, var(--ln-ink) 34%, transparent);
}
.ln-meter-list > .ln-meter > .ln-meter-label {
  font-weight: 500;
}
.ln-meter-list > .ln-meter[data-ours] {
  --ln-meter-tint: var(--ln-accent);
}
.ln-meter-list > .ln-meter[data-ours] > .ln-meter-label,
.ln-meter-list > .ln-meter[data-ours] > .ln-meter-value {
  color: var(--ln-ink);
  font-weight: 700;
}
.ln-meter > .ln-meter-sub {
  grid-column: 1 / -1;
  font-size: var(--ln-fs-caption);
  color: var(--ln-ink-quiet);
}
.ln-meter-caption {
  margin: var(--ln-sm) 0 0;
  font-size: var(--ln-fs-caption);
  color: var(--ln-ink-quiet);
}

/* A plan or price card. Put it on a surface: `<article class="ln-surface
   ln-plan">`. The action sits at the bottom so a row of plans aligns. */
.ln-plan {
  display: flex;
  flex-direction: column;
  gap: var(--ln-md);
  padding: var(--ln-xl);
  border-radius: var(--ln-r-lg);
}
.ln-plan > .ln-plan-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--ln-sm);
}
.ln-plan .ln-plan-name {
  margin: 0;
  font-size: var(--ln-fs-headline);
  font-weight: 600;
}
.ln-plan > .ln-plan-price {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--ln-xs) var(--ln-sm);
  margin: 0;
}
.ln-plan-price > :first-child {
  font-size: var(--ln-fs-display);
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  unicode-bidi: isolate;
}
.ln-plan-price > .ln-plan-period {
  font-size: var(--ln-fs-callout);
  color: var(--ln-ink-quiet);
}
.ln-plan > .ln-plan-detail {
  margin: 0;
  font-size: var(--ln-fs-callout);
  color: var(--ln-ink-quiet);
}
.ln-plan > .ln-plan-limits {
  display: grid;
  gap: var(--ln-sm);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--ln-fs-callout);
}
.ln-plan-limits > li {
  display: flex;
  gap: var(--ln-sm);
}
.ln-plan-limits > li::before {
  content: '✓';
  flex: none;
  color: var(--ln-positive);
  font-weight: 700;
}
.ln-plan > :last-child:is(.ln-btn, a, button, form) {
  margin-block-start: auto;
}
/* Recommended is the offer, so it takes the accent edge. Current is a fact,
   not an offer: a positive hairline and no extra weight, so a row with both
   never shows two identical highlighted cards. */
.ln-plan[data-recommended] {
  border-color: color-mix(in srgb, var(--ln-accent) 55%, transparent);
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--ln-accent) 55%, transparent),
    var(--ln-e-raised);
}
.ln-plan[data-current]:not([data-recommended]) {
  border-color: color-mix(in srgb, var(--ln-positive) 55%, transparent);
}
.ln-plan.ln-quiet {
  box-shadow: none;
}
.ln-plan-grid {
  display: grid;
  gap: var(--ln-lg);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  align-items: stretch;
}

/* Onboarding or a flow's steps, each todo, current or done. The number is a
   counter, so it follows `data-ln-numerals="arab"`. */
.ln-steps {
  display: grid;
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: ln-step;
}
.ln-steps > li {
  position: relative;
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  column-gap: var(--ln-md);
  padding-block-end: var(--ln-lg);
  counter-increment: ln-step;
}
.ln-steps > li:last-child {
  padding-block-end: 0;
}
.ln-steps > li::before {
  content: counter(ln-step);
  grid-row: 1 / span 2;
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 1.5px solid var(--ln-hairline-strong);
  border-radius: 50%;
  background: var(--ln-surface);
  color: var(--ln-ink-quiet);
  font-size: var(--ln-fs-caption);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
[data-ln-numerals='arab'] .ln-steps > li::before {
  content: counter(ln-step, arabic-indic);
}
/* The rail between two markers. */
.ln-steps > li:not(:last-child)::after {
  content: '';
  position: absolute;
  inset-block: 32px 4px;
  inset-inline-start: 13px;
  width: 2px;
  border-radius: 1px;
  background: var(--ln-hairline-strong);
}
.ln-steps > li > .ln-step-title {
  align-self: center;
  min-height: 28px;
  display: flex;
  align-items: center;
  font-weight: 600;
  color: var(--ln-ink);
}
.ln-steps > li > .ln-step-detail {
  grid-column: 2;
  font-size: var(--ln-fs-callout);
  color: var(--ln-ink-quiet);
}
.ln-steps > li[data-state='current']::before {
  border-color: var(--ln-accent);
  background: var(--ln-accent);
  color: var(--ln-on-accent);
}
.ln-steps > li[data-state='done']::before {
  content: '✓';
  border-color: var(--ln-positive-fill);
  background: var(--ln-positive-fill);
  color: var(--ln-on-positive);
}
.ln-steps > li[data-state='done']:not(:last-child)::after {
  background: var(--ln-positive-fill);
}
/* Done is the check and a quieter title, never a strike: struck reads as
   cancelled, and the title is often still a link back to that step. */
.ln-steps > li[data-state='done'] > .ln-step-title {
  color: var(--ln-ink-quiet);
  font-weight: 500;
}
/* Inline: "1 Choose file › 2 Direction › 3 Pay" on one wrapping line. */
.ln-steps.ln-inline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ln-sm) var(--ln-md);
}
.ln-steps.ln-inline > li {
  display: inline-flex;
  align-items: center;
  gap: var(--ln-sm);
  padding: 0;
}
.ln-steps.ln-inline > li::before {
  width: 22px;
  height: 22px;
  font-size: var(--ln-fs-micro);
}
.ln-steps.ln-inline > li:not(:last-child)::after {
  content: '›';
  position: static;
  width: auto;
  background: none;
  color: var(--ln-ink-tertiary);
}
.ln-steps.ln-inline > li > .ln-step-title {
  min-height: 0;
  font-size: var(--ln-fs-callout);
}
.ln-steps.ln-inline > li > .ln-step-detail {
  display: none;
}

/* A privacy or analytics choice: title, body, the current state in words,
   and actions. In place by default; `ln-docked` pins it to the bottom edge
   and `ln-floating` to the bottom inline-end corner. */
.ln-consent {
  display: grid;
  gap: var(--ln-sm);
  padding: var(--ln-lg);
}
.ln-consent > .ln-consent-title {
  margin: 0;
  font-size: var(--ln-fs-headline);
  font-weight: 600;
}
.ln-consent > .ln-consent-body {
  margin: 0;
  font-size: var(--ln-fs-callout);
  color: var(--ln-ink-quiet);
}
.ln-consent-state {
  display: inline-flex;
  align-items: center;
  gap: var(--ln-sm);
  margin: 0;
  font-size: var(--ln-fs-callout);
  font-weight: 600;
}
.ln-consent-state::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ln-ink-tertiary);
}
.ln-consent-state[data-state='on']::before {
  background: var(--ln-positive);
}
.ln-consent > .ln-consent-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ln-sm);
  margin-block-start: var(--ln-xs);
}
.ln-consent > .ln-consent-close {
  position: absolute;
  inset-block-start: var(--ln-xs);
  inset-inline-end: var(--ln-xs);
  --ln-icon-size: 44px;
  --ln-icon-glyph: 13px;
}
.ln-consent:not([data-dismissible]) > .ln-consent-close {
  display: none;
}
.ln-consent[data-dismissible] {
  padding-inline-end: 52px;
}
.ln-consent.ln-docked,
.ln-consent.ln-floating {
  position: fixed;
  z-index: 80;
  box-shadow: var(--ln-e-floating);
}
.ln-consent.ln-docked {
  inset-inline: 0;
  bottom: 0;
  border-radius: var(--ln-r-lg) var(--ln-r-lg) 0 0;
  border-block-end: 0;
}
.ln-consent.ln-floating {
  inset-inline-end: var(--ln-lg);
  bottom: var(--ln-lg);
  width: min(380px, calc(100vw - var(--ln-lg) * 2));
}
.ln-consent[hidden] {
  display: none;
}

/* Code with a copy button. The code stays LTR; the header follows the page. */
.ln-code {
  display: flex;
  flex-direction: column;
  margin: 0;
  min-width: 0;
  border: 1px solid var(--ln-hairline-faint);
  border-radius: var(--ln-r-sm);
  background: var(--ln-well);
  box-shadow: var(--ln-inner);
  overflow: hidden;
}
.ln-code > .ln-code-head {
  display: flex;
  align-items: center;
  gap: var(--ln-sm);
  min-height: 44px;
  padding-inline: var(--ln-md) var(--ln-xs);
  border-block-end: 1px solid var(--ln-hairline-faint);
}
.ln-code-head > .ln-code-lang {
  flex: 1;
  font-size: var(--ln-fs-micro);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ln-ink-quiet);
}
.ln-code-head > .ln-segmented {
  margin-inline-end: auto;
}
.ln-code-head > .ln-segmented > button {
  min-height: 32px;
  padding: 4px 12px;
  font-size: var(--ln-fs-caption);
}
.ln-code-head > .ln-code-copy {
  min-height: 36px;
}
/* Panels switched by `Lantern.tabs` from a `.ln-segmented[role=tablist]` in the
   head. A panel that isn't a `pre` is rendered markup, padded like the code. */
.ln-code > :is(pre, [role='tabpanel']) {
  flex: 1 1 auto;
  min-height: 0;
}
.ln-code > [role='tabpanel']:not(pre) {
  padding: var(--ln-md);
  overflow: auto;
}
/* The UA gives `code` its own `monospace`, which is Courier in Chrome. */
.ln-code pre > code {
  font: inherit;
}
.ln-code > pre {
  margin: 0;
  padding: var(--ln-md);
  overflow-x: auto;
  direction: ltr;
  text-align: left;
  font-family: var(--ln-font-mono);
  font-size: var(--ln-fs-caption);
  line-height: 1.55;
  tab-size: 2;
}
.ln-code > pre[hidden] {
  display: none;
}

/* ========================================================================== *
 * Extensions — progress, tables, disclosure
 * ========================================================================== */

/* A label over a bar: text at the start, the reading at the end. */
.ln-progress-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--ln-md);
  margin-block-end: var(--ln-xs);
  font-size: var(--ln-fs-callout);
}
.ln-progress-label > :last-child:not(:first-child) {
  color: var(--ln-ink-quiet);
  font-variant-numeric: tabular-nums;
  unicode-bidi: isolate;
}
/* Work of unknown length: a sheen that travels along the track. Under reduced
   motion it holds still as a faint full bar. */
.ln-progress.ln-indeterminate > .ln-progress-fill {
  width: 38%;
  min-width: 0;
  animation: ln-indeterminate 1.3s var(--ln-ease) infinite;
}
.ln-progress.ln-indeterminate:dir(rtl) > .ln-progress-fill {
  animation-name: ln-indeterminate-rtl;
}
@keyframes ln-indeterminate {
  from {
    transform: translateX(-105%);
  }
  to {
    transform: translateX(270%);
  }
}
@keyframes ln-indeterminate-rtl {
  from {
    transform: translateX(105%);
  }
  to {
    transform: translateX(-270%);
  }
}
/* Partial data ("recorded since…"): hatched rather than solid. */
.ln-progress.ln-partial > .ln-progress-fill,
.ln-legend-swatch.ln-partial {
  background-color: transparent;
  background-image: repeating-linear-gradient(
    135deg,
    var(--ln-progress-tint, var(--ln-accent)) 0 3px,
    color-mix(in srgb, var(--ln-progress-tint, var(--ln-accent)) 35%, transparent) 3px 6px
  );
}
.ln-legend-swatch {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 3px;
  background: var(--ln-progress-tint, var(--ln-accent));
  vertical-align: -1px;
}
/* Stages (upload → processing): `--ln-steps` equal stages, divided by a notch
   in the track. */
.ln-progress.ln-staged {
  --ln-steps: 2;
  height: 8px;
}
.ln-progress.ln-staged::after {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    to right,
    transparent 0 calc(100% / var(--ln-steps) - 1px),
    var(--ln-surface) calc(100% / var(--ln-steps) - 1px) calc(100% / var(--ln-steps) + 1px)
  );
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .ln-progress.ln-indeterminate > .ln-progress-fill {
    width: 100%;
    opacity: 0.35;
    animation: none;
  }
}

/* Tables: numeric columns, sticky header, density, sorting, a pager, and
   rows that stack into cards on a phone. */
.ln-table :is(th, td)[data-num] {
  text-align: end;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.ln-table.ln-sticky-head thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--ln-surface);
  box-shadow: inset 0 -1px 0 var(--ln-hairline);
}
.ln-table.ln-dense :is(th, td) {
  padding: var(--ln-xs) var(--ln-sm);
}
.ln-table th[aria-sort] > button {
  display: inline-flex;
  align-items: center;
  gap: var(--ln-xs);
  min-height: 32px;
  margin: -6px -4px;
  padding: 6px 4px;
  border: 0;
  border-radius: var(--ln-r-xs);
  background: none;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
}
.ln-table th[aria-sort] > button:hover {
  color: var(--ln-ink);
}
.ln-table th[aria-sort] > button::after {
  content: '↕';
  color: var(--ln-ink-tertiary);
  font-size: 1.1em;
}
.ln-table th[aria-sort='ascending'] > button::after {
  content: '↑';
  color: var(--ln-accent);
}
.ln-table th[aria-sort='descending'] > button::after {
  content: '↓';
  color: var(--ln-accent);
}
.ln-table-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--ln-sm) var(--ln-md);
  padding: var(--ln-sm) var(--ln-md);
  border-block-start: 1px solid var(--ln-hairline-faint);
  font-size: var(--ln-fs-caption);
  color: var(--ln-ink-quiet);
}
.ln-pager {
  display: inline-flex;
  align-items: center;
  gap: var(--ln-sm);
}
.ln-pager > .ln-pager-range {
  font-variant-numeric: tabular-nums;
  unicode-bidi: isolate;
}
.ln-pager > button {
  --ln-icon-size: 40px;
}
@media (max-width: 640px) {
  .ln-table.ln-stack-sm,
  .ln-table.ln-stack-sm tbody,
  .ln-table.ln-stack-sm tr,
  .ln-table.ln-stack-sm td {
    display: block;
  }
  .ln-table.ln-stack-sm thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }
  .ln-table.ln-stack-sm tr {
    padding-block: var(--ln-sm);
    border-block-end: 1px solid var(--ln-hairline);
  }
  .ln-table.ln-stack-sm tbody tr:last-child {
    border-block-end: 0;
  }
  .ln-table.ln-stack-sm td {
    display: grid;
    grid-template-columns: minmax(7em, 38%) minmax(0, 1fr);
    gap: var(--ln-md);
    padding: var(--ln-xs) var(--ln-md);
    border: 0;
    text-align: start;
  }
  .ln-table.ln-stack-sm td::before {
    content: attr(data-label);
    color: var(--ln-ink-quiet);
    font-size: var(--ln-fs-caption);
    font-weight: 600;
  }
  .ln-table.ln-stack-sm td[data-num] {
    white-space: normal;
  }
}

/* Disclosure variants. `ln-card` gives it its own surface; `ln-bordered` is a
   hairline-ruled list for FAQs. Summaries keep a 44px target. */
details.ln-details > summary {
  min-height: 44px;
}
details.ln-details.ln-card {
  padding-block: var(--ln-xs);
  border: 1px solid var(--ln-hairline);
  border-radius: var(--ln-r-md);
  background: var(--ln-surface);
  box-shadow: var(--ln-e-resting);
}
details.ln-details.ln-bordered {
  border-block: 1px solid var(--ln-hairline);
}
details.ln-details.ln-bordered + details.ln-details.ln-bordered {
  border-block-start: 0;
}
/* An inline disclosure: a quiet link-like summary with a chevron after it that
   turns down on open. */
details.ln-disclosure > summary {
  display: inline-flex;
  align-items: center;
  gap: var(--ln-xs);
  min-height: 44px;
  color: var(--ln-accent);
  font-size: var(--ln-fs-callout);
  font-weight: 500;
  list-style: none;
  cursor: pointer;
}
details.ln-disclosure > summary::-webkit-details-marker {
  display: none;
}

/* ========================================================================== *
 * Controls and chrome — tabs, menus, checks, meta, banners
 * ========================================================================== */

/* A segmented control can be a tab list: the selected tab takes the pressed
   look. `.ln-tabs` is the underlined form for page-level tab sets.
   `Lantern.tabs` wires roving focus and arrow keys, mirrored in RTL. */
.ln-tabs {
  display: flex;
  gap: var(--ln-lg);
  overflow-x: auto;
  border-block-end: 1px solid var(--ln-hairline);
  scrollbar-width: none;
}
.ln-tabs > [role='tab'] {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-block-end: -1px;
  padding: 0 var(--ln-xs);
  border: 0;
  border-block-end: 2px solid transparent;
  background: none;
  color: var(--ln-ink-quiet);
  font: inherit;
  font-size: var(--ln-fs-callout);
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
}
.ln-tabs > [role='tab']:hover {
  color: var(--ln-ink);
}
.ln-tabs > [role='tab'][aria-selected='true'] {
  color: var(--ln-accent);
  border-block-end-color: currentColor;
  font-weight: 600;
}
[role='tabpanel'][hidden] {
  display: none;
}

/* A menu, usually a `[popover]` anchored by `Lantern.menu`. A bottom sheet on
   a phone. */
.ln-menu {
  min-width: 200px;
  max-width: min(320px, calc(100vw - var(--ln-lg) * 2));
  margin: 0;
  padding: var(--ln-xs);
  border: 1px solid var(--ln-hairline);
  border-radius: var(--ln-r-sm);
  background-color: var(--ln-surface);
  box-shadow: var(--ln-e-floating);
  color: var(--ln-ink);
}
.ln-menu:not([popover])[hidden] {
  display: none;
}
.ln-menu:not([popover]) {
  position: fixed;
  z-index: 60;
}
.ln-menu > .ln-separator {
  margin: var(--ln-xs) 0;
}
.ln-menu-item {
  display: flex;
  align-items: center;
  gap: var(--ln-sm);
  width: 100%;
  min-height: 40px;
  padding: 8px 12px;
  border: 0;
  border-radius: var(--ln-r-xs);
  background: none;
  color: inherit;
  font: inherit;
  font-size: var(--ln-fs-callout);
  text-align: start;
  text-decoration: none;
  cursor: pointer;
}
.ln-menu-item:hover,
.ln-menu-item:focus-visible {
  background: var(--ln-hover-wash);
}
/* Keyboard focus is a ring, not only the hover wash: arrowing through a menu
   under a resting pointer would otherwise show two identical washes. Inset, so
   the menu's padding never clips it. */
.ln-menu-item:focus-visible {
  outline: 2px solid var(--ln-focus);
  outline-offset: -2px;
}
.ln-menu-item.ln-destructive {
  color: var(--ln-critical);
}
@media (max-width: 600px) {
  .ln-menu {
    position: fixed;
    inset: auto 0 0 !important;
    width: 100%;
    max-width: none;
    padding: var(--ln-sm) var(--ln-sm) calc(var(--ln-sm) + env(safe-area-inset-bottom));
    border-radius: var(--ln-r-lg) var(--ln-r-lg) 0 0;
  }
  .ln-menu-item {
    min-height: 48px;
    font-size: var(--ln-fs-body);
  }
}

/* A dialog that is centred on a wide screen and a bottom sheet on a phone —
   what `Lantern.confirm` builds. The primary action sits at the inline end. */
@media (max-width: 600px) {
  dialog.ln-dialog.ln-dock {
    width: 100vw;
    max-width: none;
    max-height: 88vh;
    margin: auto 0 0;
    border-radius: var(--ln-r-lg) var(--ln-r-lg) 0 0;
    padding-block-end: env(safe-area-inset-bottom);
  }
  dialog.ln-dialog.ln-dock[open] {
    animation-name: ln-sheet-up;
  }
  dialog.ln-dialog.ln-dock .ln-dialog-foot > .ln-btn {
    flex: 1;
  }
}
@keyframes ln-sheet-up {
  from {
    transform: translateY(24px);
    opacity: 0;
  }
}

/* A checkbox or radio with its label, and an optional hint under it. The box
   is drawn at 20px; the label row keeps a 44px target. */
.ln-check,
.ln-radio {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr);
  align-items: start;
  column-gap: var(--ln-md);
  min-height: 44px;
  padding-block: 10px;
  cursor: pointer;
}
.ln-check > input,
.ln-radio > input {
  width: 20px;
  height: 20px;
  margin: 1px 0 0;
  accent-color: var(--ln-accent);
  cursor: pointer;
}
.ln-check > .ln-check-label,
.ln-radio > .ln-check-label {
  display: grid;
  gap: var(--ln-hair);
}
.ln-check-hint {
  font-size: var(--ln-fs-caption);
  color: var(--ln-ink-quiet);
}
.ln-check:has(> input:disabled),
.ln-radio:has(> input:disabled) {
  opacity: 0.55;
  cursor: default;
}

/* A metadata line: parts divided by a hairline, never by "·", which next to
   Arabic-Indic digits reads as a zero. */
.ln-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ln-hair) 1.2em;
  margin: 0;
  font-size: var(--ln-fs-caption);
  color: var(--ln-ink-quiet);
  /* The separator hangs in the gap before its item, so on a wrapped line's
     first item it falls outside the box and is clipped. The clip margin
     leaves room for a focus ring on a link at the edge. */
  overflow-x: clip;
  overflow-clip-margin: 4px;
}
.ln-meta > * {
  position: relative;
  unicode-bidi: isolate;
}
.ln-meta > * + *::before {
  content: '';
  position: absolute;
  inset-inline-start: calc(-0.6em - 0.5px);
  top: 50%;
  width: 1px;
  height: 0.9em;
  translate: 0 -50%;
  background: currentColor;
  opacity: 0.45;
}

/* States gain an action row, a dashed card form, and a loading form. */
.ln-state > .ln-state-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--ln-sm);
  margin-block-start: var(--ln-xs);
}
.ln-state.ln-state-card {
  border: 1.5px dashed var(--ln-hairline-strong);
  border-radius: var(--ln-r-md);
  background: var(--ln-well);
}
.ln-state.ln-loading > .ln-state-glyph {
  font-size: 0;
}
:lang(ar) .ln-state > .ln-state-detail {
  max-width: 42ch;
}

/* A sticky banner at the foot of the page, for a failure that must stay in
   view. Dismiss with a `.ln-icon-btn` inside it. */
.ln-banner.ln-page-banner {
  position: sticky;
  bottom: var(--ln-md);
  z-index: 40;
  margin-inline: auto;
  width: min(100%, 640px);
  padding-inline-end: var(--ln-xs);
  border-radius: var(--ln-r-md);
  box-shadow: var(--ln-e-floating);
  background-color: var(--ln-surface);
  background-image: linear-gradient(
    color-mix(in srgb, var(--ln-banner-color, var(--ln-caution)) 12%, transparent),
    color-mix(in srgb, var(--ln-banner-color, var(--ln-caution)) 12%, transparent)
  );
}
.ln-banner > .ln-icon-btn {
  --ln-icon-size: 40px;
  --ln-icon-glyph: 13px;
  flex: none;
  color: inherit;
}

/* Skeleton lines for text (`--ln-lines`) and a player bar. RTL shimmers from
   the right. */
.ln-skeleton:dir(rtl) {
  animation-direction: reverse;
}
/* Use with `.ln-skeleton`: the shimmer shows through a mask of `--ln-lines`
   text lines, the last one shorter, on the reading side. */
.ln-skeleton.ln-skeleton-text {
  --ln-lines: 3;
  --ln-skeleton-line: 14px;
  --ln-skeleton-gap: 12px;
  --ln-skeleton-last: 62%;
  height: calc(var(--ln-lines) * (var(--ln-skeleton-line) + var(--ln-skeleton-gap)) - var(--ln-skeleton-gap));
  border-radius: 0;
  --ln-skeleton-mask:
    repeating-linear-gradient(
      #000 0 var(--ln-skeleton-line),
      transparent var(--ln-skeleton-line) calc(var(--ln-skeleton-line) + var(--ln-skeleton-gap))
    ),
    linear-gradient(#000, #000);
  mask-image: var(--ln-skeleton-mask);
  -webkit-mask-image: var(--ln-skeleton-mask);
  mask-size:
    100% calc(100% - var(--ln-skeleton-line)),
    var(--ln-skeleton-last) var(--ln-skeleton-line);
  -webkit-mask-size:
    100% calc(100% - var(--ln-skeleton-line)),
    var(--ln-skeleton-last) var(--ln-skeleton-line);
  mask-position:
    0 0,
    left bottom;
  -webkit-mask-position:
    0 0,
    left bottom;
  mask-repeat: no-repeat;
  -webkit-mask-repeat: no-repeat;
}
/* For pages whose CSP refuses style attributes. */
.ln-skeleton-text[data-lines='1'] {
  --ln-lines: 1;
}
.ln-skeleton-text[data-lines='2'] {
  --ln-lines: 2;
}
.ln-skeleton-text[data-lines='4'] {
  --ln-lines: 4;
}
.ln-skeleton-text[data-lines='5'] {
  --ln-lines: 5;
}
.ln-skeleton-text[data-lines='6'] {
  --ln-lines: 6;
}
.ln-skeleton.ln-skeleton-text:dir(rtl) {
  mask-position:
    0 0,
    right bottom;
  -webkit-mask-position:
    0 0,
    right bottom;
}
/* The height of a native audio player, so the page does not move when it lands. */
.ln-skeleton.ln-skeleton-player {
  height: 54px;
  border-radius: var(--ln-r-pill);
}

/* The Auto / Light / Dark choice, built by `Lantern.themeSwitch`. */
.ln-theme-switch > button {
  min-width: 44px;
  padding-inline: 10px;
}
.ln-theme-switch svg {
  width: 18px;
  height: 18px;
}

/* An echo of a native date field in the page's own locale: a `<input
   type=date>` shows mm/dd/yyyy on an Arabic page. `Lantern.echoDates`. */
.ln-field-echo {
  display: block;
  min-height: 1.35em;
  margin-top: var(--ln-xs);
  font-size: var(--ln-fs-caption);
  color: var(--ln-ink-quiet);
}

/* ========================================================================== *
 * Touch targets and forced colours
 * ========================================================================== */

/* Under a finger, every compact control reaches 44px without being drawn any
   larger: an invisible hit area centred on it, as `.ln-target` does for a
   link. Skips the elements whose `::after` already has a job. */
@media (pointer: coarse) {
  :is(.ln-btn, .ln-icon-btn, button.ln-pill, a.ln-pill, .ln-segmented > label, .ln-segmented > button):not(.ln-bloom, [data-ln-tip])::after {
    content: '';
    position: absolute;
    inset-block: min(0px, calc((100% - 44px) / 2));
    inset-inline: min(0px, calc((100% - 44px) / 2));
  }
  button.ln-pill,
  a.ln-pill {
    position: relative;
  }
}

/* Windows High Contrast and other forced palettes drop backgrounds and box
   shadows, which is where several controls keep their state. Restate those
   states in system colours. */
@media (forced-colors: active) {
  .ln-switch:has(> input:checked) {
    background: Highlight;
    forced-color-adjust: none;
  }
  .ln-switch > .ln-switch-knob {
    background: CanvasText;
    forced-color-adjust: none;
  }
  .ln-switch:has(> input:checked) > .ln-switch-knob {
    background: HighlightText;
  }
  .ln-segmented > label:has(input:checked),
  .ln-segmented > button[aria-pressed='true'],
  .ln-segmented > [role='tab'][aria-selected='true'],
  button.ln-pill[aria-pressed='true'],
  .ln-pill.ln-selected {
    border-color: Highlight;
    outline: 2px solid Highlight;
    outline-offset: -2px;
  }
  .ln-bar-nav > a[aria-current],
  .ln-sheet-nav > a[aria-current],
  .ln-sidebar-nav > a[aria-current='page'],
  .ln-row.ln-selected {
    outline: 2px solid Highlight;
    outline-offset: -2px;
  }
  .ln-progress-fill,
  .ln-meter-fill,
  .ln-live-dot,
  .ln-level::before,
  .ln-file-status::before,
  .ln-consent-state::before {
    background: Highlight;
    forced-color-adjust: none;
  }
  .ln-ring {
    background: conic-gradient(Highlight calc(var(--ln-progress) * 360deg), Canvas 0);
    border: 1px solid CanvasText;
    forced-color-adjust: none;
  }
  .ln-ring::before {
    background: Canvas;
  }
  .ln-spinner {
    border-top-color: Highlight;
  }
  /* A swatch is the colour itself; a forced palette would erase its content. */
  .ln-swatch {
    forced-color-adjust: none;
  }
  /* A quiet button has no edge, so in a forced palette it would be bare text. */
  .ln-btn.ln-quiet-btn,
  .ln-btn.ln-quiet-destructive,
  .ln-icon-btn.ln-bare {
    border-color: ButtonText;
  }
}

/* ========================================================================== *
 * Arabic interface scale — automatic under `lang="ar"`, or `.ln-ar-ui`
 * ========================================================================== */

/* Latin micro sizes are too small for Arabic UI text (11px micro, 13px
   caption), Arabic needs more leading, and a label must never be smaller than
   the hint under it. */
:root:lang(ar),
.ln-ar-ui {
  --ln-fs-micro: 0.8125rem;
  --ln-fs-caption: 0.875rem;
  --ln-fs-callout: 1rem;
}
/* One class of weight, so it lifts the micro label but never shrinks a label
   a layout has already set larger (`.ln-field > label`). */
:where(:lang(ar), .ln-ar-ui) .ln-field-label {
  font-size: 0.875rem;
}
:lang(ar) :is(.ln-body, .ln-field-hint, .ln-caption),
.ln-ar-ui :is(.ln-body, .ln-field-hint, .ln-caption) {
  line-height: 1.7;
}
/* Tracking breaks Arabic joins, whatever the size. */
:lang(ar) :is(.ln-micro, .ln-section-header, .ln-field-label, .ln-pill, .ln-code-lang, .ln-choice-group > legend, .ln-table th) {
  letter-spacing: 0;
  text-transform: none;
}

/* ========================================================================== *
 * Flat style — `data-ln-style="flat"` on the root or a subtree
 * ========================================================================== */

/* The same tokens without the material: no grain, sheen, lip or inner shadow,
   and only a contact shadow on floating things. For a product that wants the
   language's colour and type in a plainer finish. `--ln-radius-control` sets
   the corner of buttons and fields in either style. */
[data-ln-style='flat'] {
  --ln-grain: none;
  --ln-sheen: none;
  --ln-sheen-quiet: none;
  --ln-sheen-lit: none;
  --ln-lip: transparent;
  --ln-lip-lit: transparent;
  --ln-inner: none;
  --ln-e-resting: none;
  --ln-e-raised: 0 1px 2px light-dark(rgb(0 0 0 / 0.06), rgb(0 0 0 / 0.3));
  --ln-btn-gloss: none;
  --ln-btn-lip: transparent;
  --ln-btn-shadow: none;
  --ln-radius-control: var(--ln-r-xs);
}
[data-ln-style='flat'] .ln-progress > .ln-progress-fill {
  background-image: none;
}

/* Between the two: the finish at a little over half strength and no grain.
   Buttons keep their lip and gloss, cards their contact shadow, floating layers
   their full shadow. Controls take the flat style's tighter corner. Retune
   with `--ln-depth` (0 to 1) on the same element. */
[data-ln-style='soft'] {
  --ln-depth: 0.55;
  --ln-grain: none;
  --ln-radius-control: var(--ln-r-xs);
}

/* ========================================================================== *
 * Instrument theme — `data-ln-theme="instrument"` on the root or a subtree
 * ========================================================================== */

/* Hallmark · macrostructure: none (design-system theme; each site keeps its own) · scope: Lantern token theme
 * theme: custom · vibe: "precise technical instrument, follows Lantern" · paper: oklch(96.6% 0.005 255) · accent: oklch(45% 0.140 258)
 * display: IBM Plex Sans / IBM Plex Sans Arabic 600–700 · body: the same family 400 · mono: IBM Plex Mono · axes: light / geometric-sans / cool
 * studied: no · context: explicit · v1.1.0
 */

/* Cool neutral paper, one ink-blue accent and crisp opaque rules, set in IBM
   Plex. Tokens only: every component, the flat style, the Arabic scale and RTL
   work unchanged. The fonts ship beside this file in `fonts/` and only download
   when a page renders with the theme on. Plex Sans and Plex Sans Arabic are one
   CSS family; `unicode-range` picks the file per character, so a mixed line
   never changes voice.
   To adopt it: serve `fonts/` beside wherever this file is served (the URLs are
   relative), then set the attribute. `--ln-control-edge` and
   `--ln-field-border` carry the 3:1 edge of fields, switches and sliders.
   Charts use `--ln-series-1` … `-6` in order, never cycled. The gallery's
   Palette switch, or `?palette=instrument`, shows every component under it. */
/* The variable Latin file is declared once per Arabic weight, not as one
   100–700 range: WebKit and Blink pick faces by weight before script, so a
   range face beside an exact Arabic 400 lost Latin text to the fallback. The
   four rules share one URL and download once. */
@font-face {
  font-family: 'Lantern Instrument Sans';
  src: url('fonts/plex-sans-latin.woff2') format('woff2');
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Lantern Instrument Sans';
  src: url('fonts/plex-sans-latin.woff2') format('woff2');
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Lantern Instrument Sans';
  src: url('fonts/plex-sans-latin.woff2') format('woff2');
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Lantern Instrument Sans';
  src: url('fonts/plex-sans-latin.woff2') format('woff2');
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Lantern Instrument Sans';
  src: url('fonts/plex-sans-latin-italic.woff2') format('woff2');
  font-style: italic;
  font-weight: 100 700;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Lantern Instrument Sans';
  src: url('fonts/plex-sans-arabic-400.woff2') format('woff2');
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC;
}
@font-face {
  font-family: 'Lantern Instrument Sans';
  src: url('fonts/plex-sans-arabic-500.woff2') format('woff2');
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC;
}
@font-face {
  font-family: 'Lantern Instrument Sans';
  src: url('fonts/plex-sans-arabic-600.woff2') format('woff2');
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC;
}
@font-face {
  font-family: 'Lantern Instrument Sans';
  src: url('fonts/plex-sans-arabic-700.woff2') format('woff2');
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC;
}
@font-face {
  font-family: 'Lantern Instrument Mono';
  src: url('fonts/plex-mono-latin-400.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Lantern Instrument Mono';
  src: url('fonts/plex-mono-latin-600.woff2') format('woff2');
  font-weight: 600;
  font-display: swap;
}

[data-ln-theme='instrument'] {
  /* Accent: ink-blue. White on it is 7.4:1; as a ring it clears 3:1 on every surface. */
  --ln-accent-light: oklch(45% 0.14 258);
  --ln-accent-dark: oklch(75% 0.11 250);
  --ln-accent: light-dark(var(--ln-accent-light), var(--ln-accent-dark));
  --ln-on-accent: light-dark(oklch(98.6% 0.005 255), oklch(18% 0.03 258));
  --ln-focus: light-dark(oklch(50% 0.18 258), oklch(78% 0.105 250));
  --ln-ember: light-dark(oklch(52% 0.12 50), oklch(76% 0.11 58));
  --ln-on-ember: light-dark(oklch(98.6% 0.005 255), oklch(20% 0.03 60));

  /* Paper. Chroma never drops to zero; hue 255 keeps it cool without reading blue. */
  --ln-canvas-top: light-dark(oklch(97.6% 0.005 255), oklch(19.5% 0.01 258));
  --ln-canvas-bottom: light-dark(oklch(95.4% 0.005 255), oklch(14.8% 0.009 258));
  --ln-canvas: light-dark(oklch(96.6% 0.005 255), oklch(17% 0.01 258));
  --ln-surface: light-dark(oklch(98.9% 0.005 255), oklch(21.5% 0.011 258));
  --ln-surface-quiet: light-dark(oklch(93.2% 0.006 255), oklch(26% 0.012 258));
  --ln-well: light-dark(oklch(95.2% 0.005 255), oklch(15.8% 0.009 258));

  /* Ink. Opaque, and every tier, tertiary included, clears 4.5:1 on every paper level. */
  --ln-ink: light-dark(oklch(21% 0.02 260), oklch(95.5% 0.005 255));
  --ln-ink-secondary: light-dark(oklch(42% 0.02 258), oklch(78% 0.012 255));
  --ln-ink-quiet: light-dark(oklch(46% 0.018 258), oklch(74% 0.012 255));
  --ln-ink-tertiary: light-dark(oklch(51% 0.016 258), oklch(66% 0.012 255));
  --ln-ink-placeholder: var(--ln-ink-tertiary);

  --ln-positive: light-dark(oklch(46.5% 0.095 162), oklch(76% 0.11 162));
  --ln-caution: light-dark(oklch(50% 0.1 75), oklch(80% 0.11 80));
  --ln-critical: light-dark(oklch(50% 0.15 27), oklch(74% 0.12 25));
  --ln-positive-fill: light-dark(oklch(48% 0.095 162), oklch(44% 0.085 162));
  --ln-caution-fill: light-dark(oklch(48% 0.095 72), oklch(45% 0.09 72));
  --ln-critical-fill: light-dark(oklch(50% 0.15 27), oklch(46% 0.14 27));
  --ln-on-positive: oklch(98.6% 0.005 255);
  --ln-on-caution: oklch(98.6% 0.005 255);
  --ln-on-critical: oklch(98.6% 0.005 255);
  --ln-live: light-dark(oklch(54% 0.19 28), oklch(72% 0.17 27));
  --ln-highlight: light-dark(oklch(92% 0.075 95), oklch(40% 0.07 95));

  /* Rules. Opaque hairlines, and a 3:1 edge for controls with no text of their own. */
  --ln-hairline: light-dark(oklch(88% 0.007 255), oklch(31% 0.012 258));
  --ln-hairline-faint: light-dark(oklch(91.5% 0.006 255), oklch(27% 0.011 258));
  --ln-hairline-strong: light-dark(oklch(83% 0.009 255), oklch(37% 0.012 258));
  --ln-control-edge: light-dark(oklch(60% 0.014 256), oklch(55% 0.014 256));
  --ln-field-border: var(--ln-control-edge);
  --ln-field-focus: var(--ln-focus);
  --ln-scrim: light-dark(oklch(21% 0.02 260 / 0.3), oklch(8% 0.01 258 / 0.62));

  /* Charts: validated for colour-vision separation as adjacent pairs, in this order. */
  --ln-series-1: light-dark(oklch(52% 0.15 258), oklch(66% 0.13 255));
  --ln-series-2: light-dark(oklch(64% 0.15 48), oklch(66% 0.14 48));
  --ln-series-3: light-dark(oklch(58.5% 0.103 180), oklch(62% 0.105 182));
  --ln-series-4: light-dark(oklch(66% 0.13 82), oklch(64% 0.12 85));
  --ln-series-5: light-dark(oklch(56% 0.17 350), oklch(64% 0.15 355));
  --ln-series-6: light-dark(oklch(46% 0.15 295), oklch(54% 0.15 295));
  --ln-chart-grid: light-dark(oklch(90% 0.006 255), oklch(29% 0.011 258));
  --ln-chart-axis: var(--ln-control-edge);
  --ln-on-media-accent: oklch(75% 0.11 250);

  /* Everything :root derives from the tokens above, restated so a themed
     subtree derives from its own values rather than inheriting the root's. */
  --ln-accent-wash: light-dark(
    color-mix(in srgb, var(--ln-accent) 10%, transparent),
    color-mix(in srgb, var(--ln-accent) 16%, transparent)
  );
  --ln-hover-wash: light-dark(
    color-mix(in srgb, var(--ln-accent) 7%, transparent),
    color-mix(in srgb, var(--ln-accent) 12%, transparent)
  );
  --ln-current-wash: light-dark(
    color-mix(in srgb, var(--ln-accent) 16%, transparent),
    color-mix(in srgb, var(--ln-accent) 26%, transparent)
  );
  --ln-answer-wash: var(--ln-accent-wash);
  --ln-selection: light-dark(
    color-mix(in srgb, var(--ln-accent) 24%, transparent),
    color-mix(in srgb, var(--ln-accent) 36%, transparent)
  );
  --ln-grade-again: var(--ln-critical);
  --ln-grade-hard: var(--ln-caution);
  --ln-grade-good: var(--ln-positive);
  --ln-grade-easy: var(--ln-accent);
  --ln-glass: color-mix(in srgb, var(--ln-canvas) 82%, transparent);

  /* A step tighter than stock. Nothing is sharp. */
  --ln-r-xs: 6px;
  --ln-r-sm: 9px;
  --ln-r-md: 12px;
  --ln-r-lg: 16px;
  --ln-r-xl: 20px;

  /* Type. No serif in this theme: `.ln-meaning` sets in the sans italic. */
  --ln-font: 'Lantern Instrument Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --ln-font-ar: 'Lantern Instrument Sans', -apple-system, 'SF Arabic', 'Geeza Pro', 'Noto Naskh Arabic', 'Segoe UI', Tahoma, sans-serif;
  --ln-font-serif: var(--ln-font);
  /* Plex Mono has no Arabic-Indic digits; the theme's sans draws them (and any
     Arabic in code) instead of an unrelated system face. */
  --ln-font-mono: 'Lantern Instrument Mono', 'Lantern Instrument Sans', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* Plex Arabic's ascenders meet the line above at Lantern's 1.22. */
  --ln-lh-ar: 1.4;
  /* So a themed subtree sets in the theme's face, not the one it inherits. */
  font-family: var(--ln-font);
}
/* Code, keys and samples set in Plex Mono, not the UA's monospace. Zero
   specificity, so any site rule wins; the size makes up for the UA's smaller
   monospace default, which stops applying once the family is named. */
[data-ln-theme='instrument'] :where(code, kbd, samp) {
  font-family: var(--ln-font-mono);
  font-size: 0.9em;
}

/* ========================================================================== *
 * Layout helpers
 * ========================================================================== */

/* Each size is a complete stack, not a modifier that needs `.ln-stack` beside
   it — a gap with no flex container is a silent no-op, and it was one. */
.ln-stack,
.ln-stack-sm,
.ln-stack-md,
.ln-stack-xl {
  display: flex;
  flex-direction: column;
}
.ln-stack {
  gap: var(--ln-lg);
}
.ln-stack-sm {
  gap: var(--ln-sm);
}
.ln-stack-md {
  gap: var(--ln-md);
}
.ln-stack-xl {
  gap: var(--ln-xl);
}
.ln-hstack {
  display: flex;
  align-items: center;
  gap: var(--ln-sm);
}
.ln-hstack-wrap {
  flex-wrap: wrap;
}
.ln-spacer {
  flex: 1;
}
/* Page margins for a scrolling reading surface. */
.ln-page {
  max-width: var(--ln-page-width, 860px);
  margin: 0 auto;
  padding: var(--ln-xl) var(--ln-lg);
}
/* A page shell that keeps the footer at the foot of the window on a short
   page: put it on `body` (or a wrapper), and `main` or `.ln-shell-main` takes
   the spare height. Opt-in, because a flex column changes how children's
   margins behave. */
.ln-shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}
.ln-shell > :is(main, .ln-shell-main) {
  flex: 1 0 auto;
}
.ln-shell > * {
  min-width: 0;
}
/* Auto margins in a flex column shrink an item to its content; a page column
   should still fill up to its max-width. */
.ln-shell > .ln-page {
  width: 100%;
}
.ln-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Thin scrollbars inside the canvas; their colour is set at the root. */
.ln-canvas * {
  scrollbar-width: thin;
}

/* Hallmark · genre: modern-minimal · macrostructure: Workbench (the live tool, then the file it makes, then the measured evidence) · design-system: Lantern instrument theme · designed-as-app
 * pages: app = Workbench · pricing = Split Studio · guides hub = Index-First · articles = Long Document · help = Conversational FAQ
 * nav: Lantern ln-bar.ln-site (N1b, phone sheet) · footer: Ft2 inline single line · enrichment: none (a real SRT excerpt, real benchmark rows)
 * pre-emit critique: P4 H4 E4 S5 R5 V4 · contrast: pass (40–41) · mobile: pass (34, 49, 50–57) */

/* Colour and type come from Lantern's instrument theme (data-ln-theme on <html>).
   The site's one signature is teal, for the brand mark only. */
:root{
  --site-signature:var(--ln-series-3);
  /* Site rhythm, derived from Lantern's scale: the proof section gets more air than the rest. */
  --site-section:var(--ln-xxl);
  --site-section-wide:calc(var(--ln-xxl) * 2);
}

*{box-sizing:border-box}
html{height:100%}
html,body{overflow-x:clip}
body{margin:0;min-height:100%;display:flex;flex-direction:column}
h1,h2,h3,p{margin-top:0}
h1,h2,h3{overflow-wrap:anywhere;min-width:0;text-wrap:balance}
a{color:inherit;text-decoration:none}
a:hover{text-decoration:underline;text-underline-offset:4px}
svg{fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;width:28px;height:28px}
button svg{pointer-events:none}
[hidden]{display:none!important}
.skip:focus{position:fixed;inset-inline-start:var(--ln-lg);top:var(--ln-lg);width:auto;height:auto;clip-path:none;padding:var(--ln-md);background:var(--ln-surface);border-radius:var(--ln-r-xs);z-index:60}

.site-header,main,footer{width:min(920px,calc(100% - 32px));margin-inline:auto}
main{flex:1}
/* The header scrolls away with the page and lines up with the content column,
   so it needs no glass of its own over the canvas. */
.site-header{position:relative;padding-inline:0;background:none;backdrop-filter:none}
.site-header a:hover{text-decoration:none}
.brand-mark{flex:none;background:var(--site-signature);color:var(--ln-on-accent);display:grid;place-items:center;width:32px;height:32px;border-radius:var(--ln-r-xs)}
.brand-mark svg{width:20px;height:20px}
/* A flat icon button: the header's one control should not look like a raised disc. */
#theme{--ln-icon-size:40px;--ln-btn-fill:transparent;--ln-btn-border:transparent;--ln-btn-shadow:none;--ln-btn-gloss:none}
#theme::before{display:none}
@media (hover:hover){#theme:hover{--ln-btn-fill:var(--ln-hover-wash, var(--ln-accent-wash))}}
#theme svg{width:22px;height:22px;stroke-width:1.8}
#theme .theme-half{fill:currentColor;stroke:none}

/* ---- the app ---- */

.app{padding-block:var(--ln-lg) var(--ln-xl)}
.checkout-return{margin:0 0 var(--ln-md)}

/* The top of the page is the tool: a short title, then Lantern's drop zone.
   The whole zone opens the picker (app.js); its button is the keyboard control. */
.app-title{margin:0 0 var(--ln-md);max-width:28ch}
.drop{--ln-drop-pad:clamp(40px,7vw,72px);cursor:pointer;min-height:clamp(260px,38vh,340px);justify-content:center}
.drop .choose-file{min-width:min(100%,15rem);margin-block:var(--ln-xs)}
.drop-title{text-wrap:balance}
.drop-over-label{display:none}
.drop[data-over] .drop-idle{display:none}
.drop[data-over] .drop-over-label{display:inline;color:var(--ln-accent)}
.drop-meta{max-width:76ch;line-height:1.6;text-wrap:balance}
.drop-meta strong{color:var(--ln-ink-secondary);font-weight:600;white-space:nowrap}
.drop-meta-part{white-space:nowrap}
.drop-meta-part:last-child{white-space:normal}
/* Files held over the page: the arrow rises to meet them. */
@media (prefers-reduced-motion:no-preference){
  .drop>.ln-drop-glyph{transition:transform var(--ln-t-move) var(--ln-ease)}
  .drop[data-over]>.ln-drop-glyph{transform:translateY(-4px)}
}
/* Once files are listed the zone is one row: drop more, or choose more. */
.drop.ln-compact{min-height:0;cursor:default}
.drop.ln-compact .drop-meta{display:none}
.drop.ln-compact .choose-file{margin-block:0}
.app:has(.drop.ln-compact) .drop-refill{display:none}
/* Touch screens cannot drag a file in: the button is the whole action. */
@media (hover:none) and (pointer:coarse){
  .drop .drop-title{display:none}
  .drop:not(.ln-compact) .choose-file{width:100%;max-width:22rem;min-height:56px;font-size:var(--ln-fs-headline)}
  .drop.ln-compact .choose-file{flex:1}
}
@media (max-width:640px){.drop{--ln-drop-pad:var(--ln-xl);min-height:0}}
/* Under the zone, quietly: the refill, and the way back into an existing minute bank. */
.drop-after{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:0 var(--ln-lg);margin-block-start:var(--ln-sm)}
.drop-refill{margin:0;color:var(--ln-ink-quiet)}
.drop-refill strong{color:var(--ln-ink-secondary);font-weight:600}
.drop-refill a{color:var(--ln-accent);text-decoration:underline;text-decoration-color:color-mix(in srgb,var(--ln-accent) 40%,transparent);text-underline-offset:.2em;white-space:nowrap}
.drop-refill a:hover{text-decoration-color:currentColor}

/* What you get: the file itself beside the facts a visitor needs to act. */
.app-notes{margin:0 0 var(--ln-xl);padding-block:var(--ln-xl) 0;border-block-start:1px solid var(--ln-hairline)}
.tagline{margin:0 0 var(--ln-sm);max-width:34ch;color:var(--ln-ink);font-size:var(--ln-fs-title);font-weight:600;line-height:1.3}
.deliverable{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--ln-lg) var(--ln-xl);align-items:start}
.srt-sample{margin:0;min-width:0}
/* A language page's example: the file you bring, then the SRT you get, one under the other. */
.srt-pair{display:grid;gap:var(--ln-md);min-width:0}
.srt-pair .srt-sample figcaption.srt-label{margin:0 0 var(--ln-xs);color:var(--ln-ink-secondary);font-size:var(--ln-fs-caption);font-weight:600;max-width:none}
.srt-pair-note{margin:0;max-width:52ch}
.deliverable.no-sample{grid-template-columns:minmax(0,1fr);max-width:44rem}
/* SRT syntax as it sits in the file: numbers and timecodes in mono, each
   subtitle line in its own direction. */
.srt-file{margin:0;padding:var(--ln-lg);overflow-x:auto;border:1px solid var(--ln-hairline);border-radius:var(--ln-r-md);background:var(--ln-well);font:400 var(--ln-fs-callout)/1.55 var(--ln-font-mono);color:var(--ln-ink-secondary);text-align:left;white-space:pre}
.srt-file code{font:inherit}
.srt-index{color:var(--ln-ink-quiet)}
.srt-time{color:var(--ln-ink-quiet);font-variant-numeric:tabular-nums}
.srt-text{display:inline-block;width:100%;text-align:right;color:var(--ln-ink);font-family:var(--ln-font-ar);font-size:var(--ln-fs-body);unicode-bidi:isolate;white-space:normal}
.srt-sample figcaption{margin-block-start:var(--ln-sm);color:var(--ln-ink-quiet);max-width:52ch}
.facts{margin:0;border-block-start:1px solid var(--ln-hairline)}
.facts>div{display:grid;grid-template-columns:7.5rem minmax(0,1fr);gap:var(--ln-md);padding-block:var(--ln-md);border-block-end:1px solid var(--ln-hairline)}
.facts dt{color:var(--ln-ink-quiet);font-size:var(--ln-fs-caption);font-weight:600;line-height:1.6}
.facts dd{margin:0;color:var(--ln-ink-secondary);font-size:var(--ln-fs-callout);line-height:1.5}
.facts>.price-reassurance dd{color:var(--ln-ink)}
.notes-links,.notes-guides{display:flex;flex-wrap:wrap;gap:var(--ln-xs) var(--ln-lg);margin:var(--ln-md) 0 0;color:var(--ln-ink-quiet)}
.notes-guides>span{color:var(--ln-ink-quiet)}
.notes-links a,.notes-guides a{color:var(--ln-ink-secondary);text-decoration:underline;text-decoration-color:var(--ln-hairline-strong);text-underline-offset:.25em;white-space:nowrap}
.notes-links a:hover,.notes-guides a:hover{color:var(--ln-ink);text-decoration-color:currentColor}
.notes-links a:focus-visible,.notes-guides a:focus-visible{outline:2px solid var(--ln-focus);outline-offset:2px;border-radius:var(--ln-r-xs)}
.notes-links a:active,.notes-guides a:active{color:var(--ln-accent)}
@media (max-width:760px){.deliverable{grid-template-columns:minmax(0,1fr)}}
@media (max-width:400px){.srt-file{padding:var(--ln-md);font-size:var(--ln-fs-caption)}}
@media (max-width:640px){.tagline{font-size:var(--ln-fs-headline)}.facts>div{grid-template-columns:minmax(0,1fr);gap:var(--ln-xs)}}
.editor-retention{margin:calc(var(--ln-sm) * -1) 0 var(--ln-md);color:var(--ln-ink-quiet)}

.workflow-controls{max-width:560px;min-inline-size:0;margin:var(--ln-lg) 0 0;padding:var(--ln-lg)}
.workflow-controls .ln-control-label{display:grid;gap:var(--ln-xs);min-width:0;margin-block:var(--ln-sm)}
.workflow-controls .ln-select{min-height:44px}
.direction-cards{display:grid;gap:var(--ln-sm);margin-top:var(--ln-sm)}
.direction-card{display:flex;align-items:flex-start;gap:var(--ln-sm);min-inline-size:0;padding:var(--ln-md);border:1px solid var(--ln-hairline);border-radius:var(--ln-r-md);cursor:pointer;transition:border-color var(--ln-t-tap) var(--ln-ease),background var(--ln-t-tap) var(--ln-ease)}
@media (hover:hover){.direction-card:hover{border-color:var(--ln-ink-tertiary)}}
.direction-card:active{background:var(--ln-hover-wash, var(--ln-accent-wash))}
.direction-card:has(input:checked){border-color:var(--ln-accent);background:var(--ln-accent-wash)}
.direction-card:has(input:focus-visible){outline:2px solid var(--ln-focus);outline-offset:2px}
.direction-card:has(input:disabled){opacity:.55;cursor:not-allowed}
.direction-card input{accent-color:var(--ln-accent);margin-top:var(--ln-xs)}
.direction-card span{display:grid;gap:var(--ln-xs);min-width:0;overflow-wrap:anywhere}
.direction-card strong,.direction-card small{overflow-wrap:anywhere}
.direction-card small{color:var(--ln-ink-secondary);font-size:var(--ln-fs-caption);line-height:1.45}
.translation-faq{margin:var(--ln-lg) 0 0;border-block:1px solid var(--ln-hairline)}
.translation-faq>summary{display:flex;align-items:center;min-height:48px;cursor:pointer;list-style-position:inside}
.translation-faq>summary::marker{color:var(--ln-accent)}
.translation-faq-title{font-weight:600}
.translation-faq[open]>summary{padding-block-end:var(--ln-sm)}
.translation-faq-list{padding-block-end:var(--ln-sm)}
.translation-faq .ln-details{padding-block:var(--ln-sm);border-block-end:1px solid var(--ln-hairline)}
.translation-faq .ln-details:first-of-type{border-block-start:1px solid var(--ln-hairline)}
.translation-faq summary{display:flex;align-items:center;min-height:44px;font-weight:600;cursor:pointer}
.translation-faq p{margin:var(--ln-sm) 0 0}
.translation-seo{margin-block:0}
.language-catalog{max-width:860px}
.language-catalog-list,.language-target-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 var(--ln-lg);padding:0;list-style:none}
.language-catalog-list li,.language-target-list li{display:grid;grid-template-columns:auto 1fr;align-items:baseline;gap:var(--ln-sm);padding-block:var(--ln-sm);border-block-end:1px solid var(--ln-hairline)}
.language-choice{display:grid;gap:var(--ln-xs);min-width:0}
.language-choice:hover{text-decoration:none}
.language-choice:hover .language-native{color:var(--ln-accent)}
.language-choice:focus-visible{outline:2px solid var(--ln-focus);outline-offset:2px;border-radius:var(--ln-r-xs)}
.language-target-list li{grid-template-columns:1fr auto}
.language-rank{color:var(--ln-ink-quiet);font-variant-numeric:tabular-nums}
.language-native{font-weight:600;justify-self:start}
.language-english{justify-self:start}
.language-english{color:var(--ln-ink-quiet);font-size:var(--ln-fs-caption)}
@media (max-width:640px){.language-catalog-list,.language-target-list{grid-template-columns:1fr}.language-catalog-list li,.language-target-list li{gap:var(--ln-xs)}}
@media (max-width:640px){.workflow-controls{padding:var(--ln-md)}.direction-card{padding:var(--ln-sm) var(--ln-md)}}

.files{margin-top:var(--ln-lg)}
.row-price{color:var(--ln-ink-secondary);font-size:var(--ln-fs-callout);padding-inline-end:var(--ln-xs)}
.row-track{margin-top:var(--ln-xs)}
.ln-file-row>.row-downloads{grid-column:1/-1;display:flex;flex-wrap:wrap;align-items:center;gap:var(--ln-xs);margin-top:var(--ln-xs)}
.row-downloads-label{flex-basis:100%;color:var(--ln-ink-secondary);font-size:var(--ln-fs-caption);font-weight:600}
/* Under 640px there's no room for the downloads to sit in a row: the
   translation is the one the visitor came for, so it gets its own full-width
   row; the source and bilingual files are secondary, so they share a row as
   equal quiet buttons rather than wrapping wherever they fit. */
@media (max-width:640px){
  .ln-file-row>.row-downloads{display:grid;grid-template-columns:1fr 1fr}
  .row-downloads-label{grid-column:1/-1}
  .ln-file-row>.row-downloads>.ln-btn:not(.ln-quiet-btn){grid-column:1/-1;width:100%}
  .ln-file-row>.row-downloads>.ln-btn.ln-quiet-btn{width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
}

/* A name's own script picks the isolate's direction (so an Arabic recording
   still reads right-to-left), but the pair as a whole stays left-to-right so
   an appended `.en.srt` (or any upload's `.ext`) never jumps in front of it.
   The extension is its own flex item so it is never the part that's cut. */
.ln-file-row>.ln-file-name{display:flex;overflow:hidden;white-space:nowrap;unicode-bidi:isolate;direction:ltr}
.ln-file-row:dir(ltr)>.ln-file-name{justify-content:flex-start}
.ln-file-row:dir(rtl)>.ln-file-name{justify-content:flex-end}
.ln-file-name-base{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;flex:0 1 auto}
.ln-file-name-ext{flex:0 0 auto;white-space:nowrap}

.paybar{
  --ln-radius:var(--ln-r-lg);
  margin-top:var(--ln-lg);padding:var(--ln-lg);
  display:grid;gap:var(--ln-md);
}
.paybar-head{display:flex;align-items:baseline;justify-content:space-between;gap:var(--ln-lg)}
.paybar-head .ln-caption{color:var(--ln-ink-secondary)}
.paybar-note{color:var(--ln-ink-quiet);margin:0;text-align:center}
/* The balance is good news, so it carries the accent rather than the muted note colour. */
.paybar-balance{margin:calc(var(--ln-md) * -1) 0 0;text-align:end;color:var(--ln-accent);font-weight:530}
#pay-express:not([hidden]):not(:empty){margin-bottom:var(--ln-xs)}
/* Where the batch is: three inline steps and one line on what to do now. */
.stages{display:grid;gap:var(--ln-xs);margin:var(--ln-lg) 0 var(--ln-md)}
.stage-detail{margin:0;max-width:70ch;color:var(--ln-ink-secondary);line-height:1.5}
/* The itemized quote: label and figure on one line, the charge set apart. */
.quote-rows{margin:0;display:grid}
.quote-rows>div{display:flex;align-items:baseline;justify-content:space-between;gap:var(--ln-md);padding-block:var(--ln-xs);border-block-end:1px solid var(--ln-hairline)}
.quote-rows>div:last-child{border-block-end:0}
.quote-rows dt{color:var(--ln-ink-secondary);font-size:var(--ln-fs-callout)}
.quote-rows dd{margin:0;color:var(--ln-ink);font-size:var(--ln-fs-callout);font-weight:530;text-align:end;white-space:nowrap}
.quote-rows .quote-charge dt,.quote-rows .quote-charge dd{color:var(--ln-ink);font-weight:650}
.quote-notes{margin:var(--ln-sm) 0 0;color:var(--ln-ink-quiet);line-height:1.5}
.quote-notes a{color:var(--ln-accent);text-decoration:underline;text-underline-offset:.2em}
.first-purchase{margin-top:var(--ln-xl);padding-top:var(--ln-xl);border-top:1px solid var(--ln-hairline);max-width:70ch}
.first-purchase h2{margin:0 0 var(--ln-sm);font-size:var(--ln-fs-title);line-height:1.25}
.first-purchase p{margin:0 0 var(--ln-sm)}
.first-purchase .ln-body{color:var(--ln-ink-secondary)}
.first-purchase .ln-caption{color:var(--ln-ink-quiet)}
.first-purchase a{color:var(--ln-accent);text-decoration:underline;text-underline-offset:.2em}
.quote-example{max-width:26rem;margin-block:var(--ln-md);padding:var(--ln-sm) var(--ln-lg)}

/* ---- why: the benchmark comparison, as a claim beside its proof ---- */

.section{padding-block:var(--site-section)}
.why{border-top:1px solid var(--ln-hairline);padding-block:var(--site-section-wide) var(--site-section)}
.why-grid{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);grid-template-areas:"claim proof" "dialects proof";grid-template-rows:auto 1fr;gap:0 calc(var(--ln-xl) * 2);align-items:start}
.why-claim{grid-area:claim}
.why-grid>.wer{grid-area:proof}
.why-grid>.dialect-list{grid-area:dialects}
.why-claim h2{margin-bottom:var(--ln-md);font-size:var(--ln-fs-display);line-height:1.15}
.why-claim .ln-body{color:var(--ln-ink-secondary);max-width:46ch}
.why-meaning{color:var(--ln-ink-quiet);margin:var(--ln-lg) 0 0;padding-block-start:var(--ln-md);border-block-start:1px solid var(--ln-hairline);max-width:46ch}
@media (max-width:800px){.why-grid{grid-template-columns:minmax(0,1fr);grid-template-areas:"claim" "proof" "dialects";grid-template-rows:auto;row-gap:var(--ln-lg)}.why-claim h2{font-size:var(--ln-fs-title)}}

/* The dialect groups as a plain list: a fact about coverage, not decoration. */
.dialect-list{margin:var(--ln-lg) 0 0;padding:0;list-style:none;border-block-start:1px solid var(--ln-hairline)}
.dialect{display:flex;align-items:baseline;gap:var(--ln-sm);padding-block:var(--ln-xs);border-block-end:1px solid var(--ln-hairline);color:var(--ln-ink-secondary);font-size:var(--ln-fs-caption);line-height:1.45}
.dialect-name{unicode-bidi:isolate;min-width:0}

.wer{margin:0;padding:var(--ln-lg)}
.wer figcaption h3{margin-bottom:var(--ln-xs)}
.wer figcaption p{margin:0;color:var(--ln-ink-secondary)}
/* The scale direction is a footnote to the metric, not a second metric. */
.wer figcaption p span{display:block;color:var(--ln-ink-quiet)}
.wer-rows{margin-top:var(--ln-lg)}
/* Engine names stay Latin on the Arabic page; an isolate keeps each in one
   piece while it still aligns with the row. */
.wer-row>.ln-meter-label{unicode-bidi:isolate}
.wer-caption{color:var(--ln-ink-quiet);font-size:var(--ln-fs-caption);font-weight:400;line-height:1.5;margin:var(--ln-lg) 0 0;padding-block-start:var(--ln-md);border-block-start:1px solid var(--ln-hairline)}

/* ---- pricing: the price on one side, what it means on the other ---- */

.pricing{border-top:1px solid var(--ln-hairline)}
.pricing-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);grid-template-areas:"lead pack" "free pack";gap:0 calc(var(--ln-xl) * 2);align-items:start}
.pricing-lead{grid-area:lead}
.pricing-grid>.slider{grid-area:pack}
.pricing-grid>.free-tier{grid-area:free}
.pricing h1,.pricing h2{margin-bottom:var(--ln-sm)}
.pricing h1{font-size:var(--ln-fs-display);line-height:1.15}
.pricing .ln-body{color:var(--ln-ink-secondary);max-width:46ch}
.pricing .ln-table{margin-block:0}
.slider{margin:0;padding:var(--ln-xl) var(--ln-lg) var(--ln-lg);display:grid;gap:var(--ln-lg)}
.slider-readout{display:grid;gap:var(--ln-xs);justify-items:start;text-align:start}
.slider-length{color:var(--ln-ink-secondary);font-weight:530}
.slider-amount{color:var(--ln-accent);line-height:1.05;font-size:calc(var(--ln-fs-display) * 1.4)}
.slider-alt{display:block;color:var(--ln-ink-quiet)}
.slider-note{color:var(--ln-ink-quiet);margin:0}
.slider-readout .slider-note{margin-block-start:var(--ln-sm);padding-block-start:var(--ln-md);border-block-start:1px solid var(--ln-hairline);justify-self:stretch}
.pricing-cta{justify-self:start;white-space:nowrap}
.free-tier{display:grid;gap:var(--ln-xs);margin-block-start:var(--ln-xl);padding-block-start:var(--ln-md);border-block-start:1px solid var(--ln-hairline);max-width:46ch}
.free-tier-title{color:var(--ln-ink);font-weight:600}
.pricing-foot{margin:var(--ln-xl) 0 0;max-width:70ch;color:var(--ln-ink-quiet)}
@media (max-width:760px){.pricing-grid{grid-template-columns:minmax(0,1fr);grid-template-areas:"lead" "pack" "free";row-gap:var(--ln-lg)}.free-tier{margin-block-start:0}.pricing-cta{justify-self:stretch}}
.price-table{margin-block:var(--ln-md)}
.price-table summary{color:var(--ln-ink-secondary);font-size:var(--ln-fs-caption)}
.bank-access{max-width:680px;margin-block:var(--ln-lg);margin-inline:0;padding:var(--ln-lg);display:grid;gap:var(--ln-sm)}
.pricing .bank-access{max-width:560px;margin:var(--ln-xl) 0 0}
.bank-access h2{margin:0;font-size:var(--ln-fs-title)}
.bank-access>p{margin:0}
/* The inline restore link opens into the form across the column. */
.bank-access-inline{display:block;margin:0;padding:0;max-width:none}
.bank-access-inline[open]{flex-basis:100%}
.bank-access-body{display:grid;gap:var(--ln-sm);max-width:560px;padding-block:var(--ln-xs) var(--ln-md)}
.bank-access-body>p{margin:0}
/* The field and its button share one height. */
.bank-access-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:stretch;gap:var(--ln-sm)}
.bank-access-row .ln-input{min-width:0;width:100%;min-height:52px}
.bank-access-row .ln-btn{white-space:nowrap}
.bank-access-status{padding-block-start:var(--ln-xs)}

/* ---- guides: an index, not a wall of cards ---- */

.guide-index{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 var(--ln-xl);margin:0;padding:0;list-style:none;border-block-start:1px solid var(--ln-hairline)}
.guide-index>li{border-block-end:1px solid var(--ln-hairline)}
.guide-index-link{display:block;padding-block:var(--ln-md) var(--ln-lg);border-radius:var(--ln-r-xs)}
.guide-index-link:hover{text-decoration:none}
.guide-index-title{margin:0 0 var(--ln-xs);color:var(--ln-ink);font-size:var(--ln-fs-headline);font-weight:600;line-height:1.3;text-decoration:underline;text-decoration-color:transparent;text-underline-offset:.2em;transition:color var(--ln-t-tap) var(--ln-ease),text-decoration-color var(--ln-t-tap) var(--ln-ease)}
.guide-index-link:hover .guide-index-title{color:var(--ln-accent);text-decoration-color:currentColor}
.guide-index-link:focus-visible{outline:2px solid var(--ln-focus);outline-offset:2px}
.guide-index-link:active .guide-index-title{color:var(--ln-ink-secondary)}
.guide-index-text{margin:0;color:var(--ln-ink-secondary);font-size:var(--ln-fs-callout);line-height:1.5;max-width:52ch}
@media (max-width:640px){.guide-index{grid-template-columns:minmax(0,1fr)}}

.ln-prose{max-width:70ch;margin-block:var(--ln-xl) var(--ln-xxl)}
.ln-prose p{color:var(--ln-ink-secondary);margin-bottom:var(--ln-lg)}
.ln-prose .ln-details{margin-bottom:var(--ln-sm)}
.help-page .ln-details.ln-bordered{margin-bottom:0}
.help-page>section{margin-block-start:var(--ln-xxl)}
.guide-links{margin-block:var(--ln-xl)}
.guide-links ul{padding-inline-start:var(--ln-lg)}

/* ---- guide hub and article pages ---- */

.guide-hub{margin-block:var(--ln-xl) var(--ln-xxl)}
.guide-hub>h1{margin-bottom:var(--ln-md);font-size:var(--ln-fs-display);line-height:1.15;max-width:24ch}
.guide-hub>h2{margin-block:var(--ln-xxl) var(--ln-md);font-size:var(--ln-fs-title);font-weight:600}
.guide-hub .guide-cta{margin-block-start:var(--ln-xxl)}
.guide-home{margin:var(--ln-lg) 0 0}
.guide-article{max-width:70ch}
.guide-article h1{max-width:30ch}
.guide-intro{font-size:var(--ln-fs-title);line-height:1.45;color:var(--ln-ink)!important;max-width:62ch}
.guide-article h2{margin-block-start:var(--ln-xxl)}
.guide-article ol,.guide-article ul{margin-block:var(--ln-md) var(--ln-lg);padding-inline-start:1.35em}
.guide-article li+li{margin-block-start:var(--ln-sm)}
.guide-article .ln-scroll-x{margin-block:var(--ln-lg);overflow-x:auto}
.guide-article .ln-table{min-width:38rem;margin:0}
.guide-article .ln-table caption{padding:var(--ln-sm) 0;color:var(--ln-ink-quiet);caption-side:bottom;text-align:start;font-size:var(--ln-fs-caption)}
.guide-article .ln-table th,.guide-article .ln-table td{vertical-align:top;white-space:normal}
.guide-article pre{margin-block:var(--ln-lg);padding:var(--ln-lg);overflow-x:auto;border:1px solid var(--ln-hairline);border-radius:var(--ln-r-md);background:var(--ln-well);direction:ltr;text-align:left;font:500 var(--ln-fs-callout)/1.6 var(--ln-font-mono)}
/* A list to tick off: an empty box in place of each bullet. */
.guide-article .guide-checklist{list-style:none;padding-inline-start:0}
.guide-checklist li{position:relative;padding-inline-start:1.9em}
.guide-checklist li::before{content:"";position:absolute;inset-inline-start:0;top:.32em;width:1em;height:1em;border:1.5px solid var(--ln-ink-quiet);border-radius:var(--ln-r-xs)}
.guide-article blockquote{margin:var(--ln-lg) 0;padding:var(--ln-sm) var(--ln-lg);border:1px solid var(--ln-hairline);border-radius:var(--ln-r-md);background:var(--ln-well)}
.guide-article blockquote p{margin:0}
/* A closing action: a hairline well, never a side stripe. */
.guide-cta{margin-block:var(--ln-xxl);padding:var(--ln-xl);border:1px solid var(--ln-hairline);background:var(--ln-well);border-radius:var(--ln-r-md)}
.guide-cta h2{margin:0 0 var(--ln-sm)}
.guide-cta p{margin-bottom:var(--ln-lg);max-width:56ch}
.guide-cta .ln-btn{display:inline-flex;white-space:nowrap}
.guide-sources{margin-block:var(--ln-xxl) var(--ln-xl);padding-block-start:var(--ln-lg);border-block-start:1px solid var(--ln-hairline)}
.guide-sources h2{margin-block-start:0}
.guide-sources ul{margin-bottom:0}
.guide-sources a{overflow-wrap:anywhere}
.guide-faq{margin-block:var(--ln-xxl) var(--ln-xl)}
.guide-faq h2{margin-block-start:0}
.guide-faq .ln-details{padding-block:var(--ln-sm);border-block-end:1px solid var(--ln-hairline)}
.guide-faq .ln-details:first-of-type{border-block-start:1px solid var(--ln-hairline)}
.guide-faq summary{font-weight:600}
.dialect-landing .guide-links{padding-block-start:var(--ln-lg);border-block-start:1px solid var(--ln-hairline)}

/* /arabic-srt/: the tool first, then the steps and the sample file it describes. */
.srt-guide h2{margin-block-start:var(--ln-xxl)}
.srt-guide ul:not([class]),.srt-guide ol:not([class]){padding-inline-start:1.35em}
.srt-guide :is(ul,ol):not([class])>li+li{margin-block-start:var(--ln-xs)}
/* Hear, review, export: the steps, then a chooser of needs, each a link with one line under it. */
.srt-workflow .ln-steps{margin-block-start:var(--ln-md)}
.srt-workflow .ln-step-detail a,.srt-choices a{color:var(--ln-accent);text-decoration:underline;text-underline-offset:.2em}
.srt-choices{list-style:none;margin:var(--ln-md) 0 0;padding:0;display:grid;gap:var(--ln-sm);grid-template-columns:repeat(auto-fill,minmax(15rem,1fr))}
.srt-choices li{display:grid;align-content:start;gap:2px;margin:0;padding:var(--ln-sm) var(--ln-md);border:1px solid var(--ln-hairline);border-radius:var(--ln-r-md)}
.srt-choices a{font-weight:600}
.srt-choices span{color:var(--ln-ink-secondary);font-size:var(--ln-fs-callout)}
.srt-choices span a{font-weight:inherit}
/* The same cards inside a guide, where list rules would otherwise indent them. */
.guide-article .srt-choices{padding-inline-start:0;margin-block:var(--ln-md) var(--ln-lg)}
.guide-article .srt-choices li+li{margin-block-start:0}
.guide-provenance{margin-block:var(--ln-xxl) 0;padding-block-start:var(--ln-md);border-block-start:1px solid var(--ln-hairline);color:var(--ln-ink-quiet)}
.guide-provenance a{color:inherit;text-decoration:underline}
.srt-guide .srt-sample{margin-block:var(--ln-lg) var(--ln-md)}
.srt-guide .srt-download{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ln-sm) var(--ln-md)}
.srt-download .ln-caption{overflow-wrap:anywhere}
.srt-guide p code,.srt-guide li code{font-family:var(--ln-font-mono);font-size:.9em;unicode-bidi:isolate;white-space:nowrap}
.srt-check{margin-block:0 var(--ln-lg);padding:var(--ln-md) var(--ln-lg);overflow-x:auto;border:1px solid var(--ln-hairline);border-radius:var(--ln-r-md);background:var(--ln-well);text-align:left;font:500 var(--ln-fs-caption)/1.6 var(--ln-font-mono);white-space:pre-wrap;overflow-wrap:anywhere}
.srt-check code{font:inherit}

@media (max-width:640px){
  .bank-access-row{grid-template-columns:1fr}
  .guide-intro{font-size:var(--ln-fs-headline)}
  .guide-cta{padding:var(--ln-lg)}
  .guide-cta .ln-btn{display:flex;justify-content:center}
  .guide-article .ln-table{min-width:32rem}
}

footer{display:flex;flex-wrap:wrap;justify-content:space-between;gap:var(--ln-md);margin-top:var(--ln-xl);border-top:1px solid var(--ln-hairline);padding-block:var(--ln-lg) var(--ln-xl);color:var(--ln-ink-quiet);font-size:var(--ln-fs-caption)}
footer>div{display:flex;flex-wrap:wrap;gap:var(--ln-xs) var(--ln-md)}
footer a,footer button{white-space:nowrap}
.analytics-consent{position:fixed;inset-inline:var(--ln-lg);bottom:var(--ln-lg);z-index:40;width:min(560px,calc(100% - 32px));margin-inline:auto;display:grid;gap:var(--ln-md);padding:var(--ln-lg)}
.analytics-consent p{margin:var(--ln-xs) 0 0;color:var(--ln-ink-secondary)}
.analytics-consent:focus{outline:none}
.analytics-consent a{color:var(--ln-accent);text-decoration:underline;text-decoration-color:color-mix(in srgb,var(--ln-accent) 50%,transparent);text-underline-offset:.2em}
.analytics-consent a:focus-visible{outline:2px solid var(--ln-focus);outline-offset:2px;border-radius:2px}
.analytics-consent .analytics-state{margin-top:var(--ln-sm);color:var(--ln-ink);font-weight:600}
.analytics-state::before{content:'';display:inline-block;width:.55em;height:.55em;margin-inline-end:.45em;border-radius:50%;background:var(--ln-accent)}
.analytics-state[data-state=off]::before{background:var(--ln-ink-tertiary)}
.analytics-settings{padding:0;border:0;background:none;color:var(--ln-accent);font:inherit;cursor:pointer}
.analytics-settings:hover{text-decoration:underline;text-underline-offset:.2em}
.analytics-settings:focus-visible{outline:2px solid var(--ln-focus);outline-offset:2px;border-radius:2px}
.analytics-settings:active{color:var(--ln-ink)}
.analytics-consent-actions{display:flex;flex-wrap:wrap;gap:var(--ln-sm);justify-content:flex-end}
.analytics-consent-actions .ln-btn{white-space:nowrap}
@media(max-width:480px){.analytics-consent-actions .ln-btn{flex:1 1 auto}}

/* ---- editor ---- */

/* The editor takes over the page; the marketing sections below it would
   otherwise scroll into view under the cue list. */
body.editing .section,body.editing .app-notes{display:none}
.editor{padding-block:var(--ln-lg) var(--ln-xxl)}
/* An empty <video> still paints native controls over the placeholder. */
#player:not([src]){display:none}
/* A laptop screen keeps the player and the first cues in view together. */
@media (min-width:641px){.preview:not(.empty){aspect-ratio:auto;height:min(52vh,518px)}.preview:not(.empty) video{position:absolute;inset:0}}
.editor-top{display:flex;align-items:center;gap:var(--ln-md);margin-bottom:var(--ln-lg)}
/* The back button's label sits on the column edge, not its padding box. */
#editor-back{--ln-btn-pad-inline:var(--ln-sm);margin-inline-start:calc(var(--ln-btn-pad-inline) * -1);white-space:nowrap}
.editor-top h2{flex:1;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Same treatment as the file list rows: the name's own script picks the
   isolate's direction, but the pair as a whole stays left-to-right so an
   appended `.ext` never jumps in front of it or borrows its digit shapes. */
.editor-title-name{unicode-bidi:isolate;direction:ltr}
.editor-title-ext{white-space:nowrap}
.editor-top .ln-caption{color:var(--ln-ink-quiet);white-space:nowrap}
.preview{position:relative;background:var(--ln-media);border-radius:var(--ln-r-md);overflow:hidden;aspect-ratio:16/9;display:grid;place-items:center}
.preview video{width:100%;height:100%;object-fit:contain;background:var(--ln-media)}
#preview-placeholder{position:absolute;inset:0;display:grid;place-items:center;align-content:center;gap:var(--ln-md);padding:var(--ln-lg);text-align:center;color:var(--ln-on-media-secondary)}
#preview-placeholder p{margin:0;max-width:34ch}
#caption-preview{position:absolute;inset-inline:0;bottom:var(--ln-lg);text-align:center;color:var(--ln-on-media);font-size:var(--ln-fs-title);text-shadow:0 2px 8px var(--ln-media);padding-inline:var(--ln-lg);white-space:pre-line}
.editor-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ln-sm);margin-block:var(--ln-lg)}
.editor-toolbar .ln-btn{white-space:nowrap}
.editor-toolbar .speed{margin-inline-start:auto;display:flex;align-items:center;gap:var(--ln-sm);margin-block:0;white-space:nowrap}
.cues{display:grid;gap:var(--ln-sm)}
.cue-row{display:flex;gap:var(--ln-md);padding:var(--ln-md);align-items:start}
.cue-row.active{outline:2px solid var(--ln-accent);outline-offset:-2px}
.cue-index{flex-shrink:0;font-variant-numeric:tabular-nums}
.cue-timing{display:grid;gap:var(--ln-xs);flex-shrink:0;width:120px}
.cue-timing .ln-input{width:100%}
.cue-timing .ln-field-label{white-space:nowrap}
.cue-body{flex:1;min-width:0;display:grid;gap:var(--ln-xs)}
.cue-body .ln-textarea{min-height:64px;width:100%}
.cue-actions{display:flex;gap:var(--ln-sm)}

@media (max-width:640px){
  .site-header,main,footer{width:calc(100% - 32px)}
  .cue-row{flex-wrap:wrap}
  .cue-timing{width:100%;grid-template-columns:1fr 1fr;gap:var(--ln-sm)}
  .editor-toolbar .speed{margin-inline-start:0;width:100%}
}

/* ---- free daily minutes ---- */

.free-reassurance{color:var(--ln-accent);font-weight:530}
.facts>.free-reassurance dd{color:var(--ln-ink)}
.free-offer{display:grid;gap:var(--ln-sm);padding-block-start:var(--ln-md);border-block-start:1px solid var(--ln-hairline)}
.free-offer>p{margin:0;color:var(--ln-ink-secondary)}
.free-offer-title{font-weight:600}
.free-offer .ln-meter{margin-block-start:var(--ln-xs)}
.free-turnstile:empty{display:none}
/* The note wraps under the title rather than squeezing it into a narrow column. */
.ln-banner.payment-success{flex-wrap:wrap;margin-top:var(--ln-md)}
.ln-banner.payment-success>*{flex:1 1 24ch}

/* ---- pay bar ---- */

.paybar.free-first #pay-total{color:var(--ln-accent)}
.paybar-head strong{white-space:nowrap}

/* ---- translation direction ---- */

/* A floated legend sits inside the card instead of straddling its border. */
.workflow-controls>legend{float:inline-start;width:100%;padding:0;margin-block-end:var(--ln-sm)}
.workflow-controls>legend~*{clear:both}
.workflow-controls.collapsed>*:not(legend):not(.translation-summary){display:none}
.workflow-controls.collapsed{padding-block:var(--ln-md)}
.translation-summary{margin:0;color:var(--ln-ink-secondary);clear:both}
.workflow-controls #translation-continue{width:100%;margin-block-start:var(--ln-sm)}

/* ---- editor ---- */

#editor-title:focus{outline:none}
#editor-title{overflow-wrap:anywhere}
.preview.empty{aspect-ratio:auto;display:block}
.preview.empty #preview-placeholder{position:static;width:100%;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;text-align:start;padding:var(--ln-md) var(--ln-lg)}
.cue-head{display:flex;flex-direction:column;align-items:center;gap:var(--ln-xs);flex-shrink:0}
.cue-edited{font-size:var(--ln-fs-micro)}

@media (max-width:640px){
  .editor{padding-block-start:var(--ln-sm)}
  .editor-top{flex-wrap:wrap;gap:var(--ln-xs) var(--ln-md);margin-bottom:var(--ln-md)}
  .editor-top h2{order:3;flex-basis:100%;white-space:normal;font-size:var(--ln-fs-headline);display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2}
  .editor-top .ln-caption{margin-inline-start:auto}
  .preview:not(.empty){aspect-ratio:auto;max-height:36vh}
  .editor-toolbar{margin-block:var(--ln-md)}
  /* One cue: number and times on one line, then the text, then small actions.
     The head column has one width on every row, so the timing fields line up. */
  .cue-row{display:grid;grid-template-columns:56px minmax(0,1fr);gap:var(--ln-xs) var(--ln-sm);padding:var(--ln-sm)}
  .cue-head{grid-row:1}
  .cue-timing{grid-column:2;grid-row:1;width:auto;grid-template-columns:1fr 1fr;gap:var(--ln-xs)}
  .cue-timing .ln-field-label{font-size:var(--ln-fs-micro)}
  .cue-timing .ln-input{min-height:36px;padding-block:var(--ln-xs)}
  .cue-body{grid-column:1/-1}
  .cue-body .ln-textarea{min-height:48px}
}
/* Playback speed only matters once a video is attached. */
.preview.empty~.editor-toolbar .speed{display:none}
.cue-head{width:64px}
@media (max-width:640px){
  .cue-head{width:auto}
  .editor-toolbar .ln-btn{padding-inline:var(--ln-md)}
  .editor-toolbar .editor-download{margin-inline-start:auto}
  .preview.empty #preview-placeholder{padding:var(--ln-sm) var(--ln-md);gap:var(--ln-sm)}
}
/* Under 360px the primary action takes its own row rather than wrapping its label. */
@media (max-width:359px){.editor-toolbar .editor-download{flex-basis:100%;margin-inline-start:0}.editor-download #download{flex:1}}
/* A Latin name keeps its own direction, so it truncates at its end, but still lines up with the Arabic page. */
[dir=rtl] #editor-title{text-align:right}

/* The file format is one small choice beside the downloads, not a button per format. */
.row-format.ln-select{display:inline-block;width:auto;min-height:36px;padding-block:4px;padding-inline-start:var(--ln-sm);font-size:var(--ln-fs-caption);font-weight:600}
.ln-file-row>.row-downloads>.row-downloads-head{flex-basis:100%;display:flex;align-items:center;gap:var(--ln-sm)}
.row-downloads-head>.row-downloads-label{flex:0 0 auto}
.row-download,.editor-download{display:inline-flex;align-items:center;gap:var(--ln-xs)}
@media (max-width:640px){.ln-file-row>.row-downloads>.row-downloads-head{grid-column:1/-1}}
.cue-speaker{font-size:var(--ln-fs-micro);white-space:nowrap}
.cue-warnings{margin:var(--ln-xs) 0 0;padding-inline-start:var(--ln-lg);color:var(--ln-caution);font-size:var(--ln-fs-caption)}
.cue-warnings:empty{display:none}
.editor-top .editor-warnings{white-space:nowrap}

/* ---- what the visitor receives, and the section for repeat professional users ---- */
.srt-text-ltr{text-align:left;font-family:var(--ln-font)}
.pro{border-top:1px solid var(--ln-hairline)}
.pro h2{margin:0 0 var(--ln-lg);max-width:30ch;font-size:var(--ln-fs-title);line-height:1.25}
.pro-list{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--ln-lg) var(--ln-xl)}
.pro-list li{padding-block-start:var(--ln-md);border-block-start:2px solid var(--ln-accent)}
.pro-list h3{margin:0 0 var(--ln-xs)}
.pro-list p{margin:0;color:var(--ln-ink-secondary);line-height:1.5}
.pro-link{margin:var(--ln-lg) 0 0}
.pro-link a{color:var(--ln-accent);font-weight:500;text-decoration:underline;text-underline-offset:.2em}
@media (max-width:960px){.pro-list{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:560px){.pro-list{grid-template-columns:minmax(0,1fr)}}

/* ---- translation market landing ---- */
.ln-prose .market-direction{margin-block:var(--ln-lg);padding-block:var(--ln-sm);border-block:1px solid var(--ln-hairline);color:var(--ln-ink);font-weight:600}

/* ---- file rows on a phone: name and actions on one line, status and duration on the next ---- */
@media (max-width:640px){
  .ln-file-row:not([data-state=done])>.ln-file-actions{grid-column:2;grid-row:1}
  body[data-workflow=arabic] .ln-file-row:not([data-state=uploading])>.ln-file-meta{grid-column:2;grid-row:2;justify-self:end;margin:0}
  .ln-file-row{padding:var(--ln-sm) var(--ln-md)}
}
.duplicate-note{margin:var(--ln-sm) 0 0;color:var(--ln-ink-secondary)}


.market-links{display:flex;flex-wrap:wrap;gap:var(--ln-sm) var(--ln-xl)}
.bank-access .ln-field-label{font-size:var(--ln-fs-caption)}
.notes-lede{margin:0 0 var(--ln-lg);max-width:60ch;color:var(--ln-ink-secondary)}
/* One short line between a page's title and its drop zone, for pages whose search intent needs it. */
.app-lede{margin:0 0 var(--ln-lg);max-width:62ch;color:var(--ln-ink-secondary)}
.app-lede a{color:var(--ln-accent);text-decoration:underline;text-underline-offset:.2em}

/* /tools/arabic-srt-checker/. Lantern supplies the components (drop zone, banner, table, pills,
   disclosures); these rules only place them, and are scoped to the checker's own classes. */

.srt-checker{padding-block:var(--ln-lg) var(--ln-xl)}
.checker-lede{max-width:62ch;margin-bottom:var(--ln-lg);color:var(--ln-ink-secondary)}

/* The drop zone: the zone opens the picker, its button is the keyboard control. */
.checker-drop{--ln-drop-pad:clamp(32px,6vw,56px);justify-content:center;min-height:clamp(200px,30vh,260px);cursor:pointer}
.checker-drop .ln-btn{min-width:min(100%,15rem);margin-block:var(--ln-xs)}
.checker-drop .ln-drop-title{text-wrap:balance}
.checker-drop .ln-drop-meta{max-width:60ch;line-height:1.6;text-wrap:balance}
.checker-over{display:none}
.checker-drop[data-over] .checker-idle{display:none}
.checker-drop[data-over] .checker-over{display:inline;color:var(--ln-accent)}
.checker-drop.ln-compact{min-height:0;cursor:default}
.checker-drop.ln-compact .ln-drop-meta{display:none}
.checker-drop.ln-compact .ln-btn{margin-block:0}
@media (max-width:640px){.checker-drop{--ln-drop-pad:var(--ln-xl);min-height:0}}

.checker-notice{margin:var(--ln-md) 0 0}

/* Paste and limits fold away under the zone as quiet disclosures. */
.checker-tools{display:flex;flex-wrap:wrap;gap:0 var(--ln-xl);margin-block-start:var(--ln-xs)}
.checker-more{display:block}
.checker-more[open]{flex-basis:100%}
.checker-more-body{display:grid;gap:var(--ln-sm);max-width:44rem;padding-block:var(--ln-xs) var(--ln-md)}
.checker-more-body>p{margin:0}
.checker-text{min-height:9rem;font:400 var(--ln-fs-callout)/1.5 var(--ln-font-mono);text-align:left}
.checker-row{display:flex;flex-wrap:wrap;gap:var(--ln-sm)}
.checker-limit-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(13rem,1fr));gap:var(--ln-md)}
.checker-field .ln-input{max-width:8rem}
.checker-field .ln-field-hint{font-size:var(--ln-fs-caption)}

/* Results. */
.checker-results{margin-block-start:var(--ln-xl);scroll-margin-block-start:var(--ln-lg)}
.checker-results-title{margin:0 0 var(--ln-md);font-size:var(--ln-fs-title);font-weight:600}
.checker-results-title:focus{outline:none}
.checker-banner{align-items:flex-start}
.checker-banner-title{display:block;font-size:var(--ln-fs-body)}
.checker-banner-detail{margin:var(--ln-xs) 0 0;max-width:68ch}
.checker-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(9.5rem,1fr));gap:var(--ln-md) var(--ln-lg);margin:var(--ln-lg) 0 0;padding:0}
.checker-facts>div{min-width:0}
.checker-facts>.checker-fact-wide{grid-column:span 2}
.checker-facts bdi{white-space:nowrap}
.checker-facts dt{color:var(--ln-ink-quiet);font-size:var(--ln-fs-caption)}
.checker-facts dd{margin:2px 0 0;font-weight:600;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}
.checker-actions{display:flex;flex-wrap:wrap;gap:var(--ln-sm);margin-block:var(--ln-lg) var(--ln-xs)}
.checker-report-note{margin:0;max-width:60ch}

.checker-section{margin-block-start:var(--ln-xl)}
.checker-section-title{display:flex;align-items:center;gap:var(--ln-sm);margin:0;font-size:var(--ln-fs-headline);font-weight:600}
.checker-section-intro{margin:var(--ln-xs) 0 var(--ln-sm);max-width:62ch}
.checker-group{border-block-start:1px solid var(--ln-hairline)}
.checker-section>.checker-group:last-child{border-block-end:1px solid var(--ln-hairline)}
.checker-group-summary{gap:var(--ln-sm)}
.checker-group-title{min-width:0;text-wrap:balance}
.checker-explain{max-width:68ch;margin:0 0 var(--ln-sm)}
.checker-table{font-size:var(--ln-fs-callout)}
.checker-table th,.checker-table td{padding:var(--ln-sm)}
.checker-table th{text-transform:none;letter-spacing:0;font-size:var(--ln-fs-caption)}
/* Same column widths in every group. */
@media (min-width:641px){.checker-table{table-layout:fixed}.checker-table th:nth-child(1){width:6rem}.checker-table th:nth-child(2){width:9.5rem}}
/* Phone: one block per finding, each cell labelled. Lantern's ln-stack-sm is not used because its class
   name also matches the generic flex-column stack utility, which breaks a table on wide screens. */
@media (max-width:640px){
.checker-table,.checker-table tbody,.checker-table tr{display:block}
.checker-table thead{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
.checker-table tr{padding-block:var(--ln-sm);border-block-end:1px solid var(--ln-hairline)}
.checker-table tbody tr:last-child{border-block-end:0}
.checker-table td{display:grid;grid-template-columns:minmax(5em,30%) minmax(0,1fr);gap:var(--ln-md);padding:var(--ln-xs) var(--ln-sm);border:0}
.checker-table td::before{content:attr(data-label);color:var(--ln-ink-quiet);font-size:var(--ln-fs-caption);font-weight:600}
}
.checker-cue{white-space:nowrap;font-weight:600;font-variant-numeric:tabular-nums}
.checker-line{display:block;color:var(--ln-ink-quiet);font-size:var(--ln-fs-caption);font-weight:400}
.checker-time{white-space:nowrap;font-family:var(--ln-font-mono);font-size:var(--ln-fs-caption);font-variant-numeric:tabular-nums}
.checker-raw{display:block;width:fit-content;max-width:100%;margin-block-start:2px;color:var(--ln-ink-quiet);font:400 var(--ln-fs-caption)/1.4 var(--ln-font-mono);overflow-wrap:anywhere;text-align:start}
.checker-shown{margin:var(--ln-xs) 0 0}
.checker-show-more{margin-block-end:var(--ln-sm)}
@media (max-width:640px){.checker-actions .ln-btn{flex:1 1 100%}}

/* The explainer under the tool. */
.checker-article{max-width:70ch}
.checker-article h2{margin-block-start:var(--ln-xxl)}
.checker-article h3{margin-block:var(--ln-xl) var(--ln-sm);font-size:var(--ln-fs-headline);font-weight:600}
.checker-article ul{margin-block:var(--ln-md) var(--ln-lg);padding-inline-start:1.35em}
.checker-article li+li{margin-block-start:var(--ln-sm)}
.checker-term>summary{text-wrap:balance}
.checker-term>p{margin:0 0 var(--ln-md);color:var(--ln-ink-secondary);line-height:1.55}
.checker-pointer{margin-block-start:var(--ln-xl)}
.checker-pointer a{color:var(--ln-accent);text-decoration:underline;text-underline-offset:.2em}

.wer-row[data-rank="0"]{--ln-meter:0.1533}
.wer-row[data-rank="1"]{--ln-meter:0.1667}
.wer-row[data-rank="2"]{--ln-meter:0.2100}
.wer-row[data-rank="3"]{--ln-meter:0.2467}
.wer-row[data-rank="4"]{--ln-meter:0.2467}
.wer-row[data-rank="5"]{--ln-meter:0.5667}
.wer-row[data-rank="6"]{--ln-meter:0.8367}
