/* 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));

  /* -- 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);
  --ln-field-focus: color-mix(in srgb, var(--ln-accent) 55%, transparent);
  /* The keyboard focus ring. */
  --ln-focus: var(--ln-accent);

  /* -- 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;
}

.ln-canvas ::selection {
  background: var(--ln-accent-wash);
  color: var(--ln-ink);
}

/* ========================================================================== *
 * 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))))
    )
  );
  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;
}
.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;
}

/* Plain data shares one set of rules, without a raised surface per row. The
   table section further down refines these. */
.ln-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--ln-fs-body);
  text-align: start;
}
.ln-table th,
.ln-table td {
  padding: 12px 16px;
  border-block-end: 1px solid var(--ln-hairline-faint);
  vertical-align: top;
  text-align: start;
}
.ln-table th {
  color: var(--ln-ink-secondary);
  font-weight: 600;
  background: var(--ln-well);
}
.ln-table tbody tr:last-child td {
  border-block-end: 0;
}

/* 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-tertiary);
}
.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;
  left: var(--ln-md);
  right: 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);
}

.ln-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--ln-fs-callout);
}
.ln-table th {
  text-align: start;
  padding: var(--ln-sm) var(--ln-md);
  border-bottom: 1px solid var(--ln-hairline);
  font-size: var(--ln-fs-micro);
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--ln-ink-quiet);
}
.ln-table td {
  padding: var(--ln-sm) var(--ln-md);
  border-bottom: 1px solid var(--ln-hairline-faint);
}
.ln-table tbody tr:last-child td {
  border-bottom: 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;
}
details.ln-details > summary::before {
  content: '›';
  display: inline-block;
  color: var(--ln-ink-tertiary);
  transition: transform var(--ln-t-tap) var(--ln-ease);
}
details.ln-details[open] > summary::before {
  transform: rotate(90deg);
}
/* RTL draws the mirrored '‹', which a quarter turn clockwise points up. */
details.ln-details[open]:dir(rtl) > summary::before {
  transform: rotate(-90deg);
}

/* ========================================================================== *
 * 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;
}
.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;
}
.ln-plan[data-current],
.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.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;
}
details.ln-disclosure > summary::after {
  content: '›';
  display: inline-block;
  font-size: 1.2em;
  line-height: 1;
  transition: transform var(--ln-t-tap) var(--ln-ease);
}
details.ln-disclosure[open] > summary::after {
  transform: rotate(90deg);
}
details.ln-disclosure[open]:dir(rtl) > summary::after {
  transform: rotate(-90deg);
}

/* ========================================================================== *
 * 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);
}

/* ========================================================================== *
 * 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-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-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);
}
.ln-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Scrollbars, so a dark page does not get a light gutter. */
.ln-canvas * {
  scrollbar-color: var(--ln-ink-tertiary) transparent;
  scrollbar-width: thin;
}

/* Hallmark · pre-emit critique: P4 H5 E4 S5 R5 V4 · slop test 58/58
 * Hallmark · genre: modern-minimal · macrostructure: Workbench (home) · Long Document (guides, legal) · design-system: Lantern 0.9.1 instrument theme · designed-as-app · nav: N1b (ln-bar ln-site, phone sheet) · footer: Ft1 Mast-headed · enrichment: none (a sample meeting as the product shows it) */

/* Colour and type come from Lantern's instrument theme (data-ln-theme on <html>).
   Site tokens are layout, plus one signature (ember) for the brand mark only. */
:root{--q-signature:var(--ln-ember);--q-max:72rem;--q-gutter:clamp(var(--ln-lg),4vw,var(--ln-xxl));--q-rule:var(--ln-hairline-strong);--q-gap:clamp(var(--ln-xl),5vw,4rem);--q-section:clamp(3rem,8vw,6rem)}
html,body{overflow-x:clip}
body{margin:0;line-height:1.65}
:lang(ar) ol{list-style-type:arabic-indic}
/* The transcript lines carry their own timecodes. */
ol.q-lines{list-style:none}
[hidden]{display:none!important}
.skip:focus{position:fixed;inset-block-start:.5rem;inset-inline-start:.5rem;z-index:30}
h1,h2,h3{font-style:normal;overflow-wrap:anywhere;min-width:0}

/* Header: Lantern's site bar, content-width inside the page column. */
.q-header{padding-inline:max(var(--q-gutter),calc((100% - var(--q-max)) / 2 + var(--q-gutter)))}
.q-main,.q-footer{max-width:var(--q-max);margin-inline:auto;padding-inline:var(--q-gutter);box-sizing:border-box}
.q-brand{display:inline-flex;align-items:center;gap:var(--ln-sm);font-weight:700;font-size:1.25rem;color:inherit;text-decoration:none}
.q-mark{display:grid;place-items:center;inline-size:32px;block-size:32px;border-radius:var(--ln-r-xs);background:var(--q-signature);color:var(--ln-on-ember);font-size:1.125rem;font-weight:700;line-height:1}
.q-signin{white-space:nowrap}
@media (max-width:22.5rem){.q-header{gap:var(--ln-sm)}.q-header .ln-bar-actions{gap:var(--ln-xs)}}

/* Workbench: a short hero, then the product itself (a sample meeting as Qarar shows it). */
.q-hero{max-width:44rem;padding-block:clamp(var(--ln-xxl),6vw,4.5rem) clamp(3rem,8vw,6rem)}
.q-hero h1{margin:0 0 var(--ln-lg);font-size:clamp(2.125rem,5.2vw,3.25rem);line-height:1.12;letter-spacing:-.025em}
:lang(ar) .q-hero h1{line-height:1.35;letter-spacing:0}
.q-lede{margin:0 0 var(--ln-xl);font-size:1.125rem;color:var(--ln-ink-secondary)}
.q-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ln-md);margin-block-end:var(--ln-lg)}
.q-actions .ln-btn{white-space:nowrap}
.q-hero .ln-caption{margin:0;color:var(--ln-ink-quiet)}

.q-meeting{padding-block:var(--ln-xl) 0;border-block-start:1px solid var(--q-rule)}
.q-meeting-head{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:var(--ln-sm) var(--ln-xl);margin-block-end:var(--ln-xl)}
.q-meeting-head h2{margin:0;font-size:var(--ln-fs-title)}
.q-meeting-meta{margin:0}
.q-meeting-body{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--ln-xl) var(--q-gap);align-items:start}
@media (min-width:60rem){.q-meeting-body{grid-template-columns:minmax(0,7fr) minmax(0,5fr)}}
.q-meeting h3{margin:0 0 var(--ln-lg);font-size:var(--ln-fs-headline)}
.q-lines{display:grid;gap:var(--ln-lg);margin:0;padding:0}
.q-line-head{display:flex;align-items:baseline;gap:var(--ln-sm);margin:0 0 var(--ln-xs)}
.q-who{font-weight:600;color:var(--ln-ink-secondary)}
.q-who.q-named{color:var(--ln-accent)}
.q-time{font-family:var(--ln-font-mono);font-size:var(--ln-fs-caption);font-variant-numeric:tabular-nums;color:var(--ln-ink-quiet);unicode-bidi:isolate}
.q-line{margin:0;font-size:1.1875rem;line-height:1.8}
.q-fixed{text-decoration:underline dotted var(--ln-accent);text-underline-offset:.35em}
.q-fix-note{margin:var(--ln-xs) 0 0;font-size:var(--ln-fs-caption);color:var(--ln-ink-quiet)}
.q-notes{padding:var(--ln-xl);border-radius:var(--ln-r-lg)}
.q-notes h4{margin:var(--ln-xl) 0 var(--ln-sm);font-size:var(--ln-fs-callout);font-weight:600;color:var(--ln-ink-quiet)}
.q-notes h3+h4{margin-block-start:0}
.q-summary,.q-tasks{display:grid;gap:var(--ln-sm);margin:0;padding:0;list-style:none}
.q-summary li,.q-decision,.q-task{margin:0;line-height:1.7}
.q-decision{font-weight:600}
.q-notes .ln-meta{margin:var(--ln-xs) 0 0}
.q-tasks li+li{padding-block-start:var(--ln-sm);border-block-start:1px solid var(--ln-hairline)}
.q-meeting-caption{max-width:46rem;margin:var(--ln-xl) 0 0}

.q-section{padding-block:var(--q-section) 0}
.q-section h2{margin:0 0 var(--ln-lg);font-size:clamp(1.5rem,3.2vw,2.125rem);line-height:1.25;letter-spacing:-.015em}
:lang(ar) .q-section h2{letter-spacing:0}
.q-facts-text{max-width:46rem}
.q-facts-text p{margin:0 0 var(--ln-lg);color:var(--ln-ink-secondary)}
.q-facts-text strong{color:var(--ln-ink);font-weight:600}

.q-section-head{max-width:40rem;margin-block-end:var(--ln-xl)}
.q-section-head p{margin:0;color:var(--ln-ink-secondary)}
/* One, two or four plans a row, never three over one. */
.q-pricing .ln-plan-grid,.q-plan-cards{grid-template-columns:minmax(0,1fr)}
@media (min-width:40rem){.q-pricing .ln-plan-grid,.q-plan-cards{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (min-width:64rem){.q-pricing .ln-plan-grid{grid-template-columns:repeat(4,minmax(0,1fr))}}
.q-pricing .ln-plan-name{font-size:var(--ln-fs-title)}
.q-foot{margin-block-start:var(--ln-lg)}
.q-plan-facts{display:grid;gap:var(--ln-sm);margin:0;font-size:var(--ln-fs-callout)}
.q-plan-facts dt{color:var(--ln-ink-quiet);font-size:var(--ln-fs-caption);font-weight:600}
.q-plan-facts dd{margin:0}
.ln-plan>.q-plan-action{margin-block-start:auto}
.q-none{color:var(--ln-ink-quiet)}

/* Hallmark · pre-emit critique: P4 H4 E4 S5 R5 V4 · pricing: one ruled ln-table (plans as columns),
   ln-plan cards below 60rem, Lantern tokens only, no badge, no invented numbers. */
.q-pricing-hero{padding-block-end:var(--ln-xxl)}
.q-pricing-hero .ln-caption{margin-block-start:var(--ln-md)}
.q-plans{display:grid;gap:var(--ln-xl)}
.q-cadence{justify-self:start}
.q-plan-wide{display:none}
@media (min-width:60rem){.q-plan-wide{display:block}.q-plan-cards{display:none}}
.q-plan-table{table-layout:fixed}
.q-plan-table :is(th,td){padding:var(--ln-md);vertical-align:top}
.q-plan-table thead :is(th,td){background:none;border-block-end:1px solid var(--q-rule)}
.q-plan-table th[scope="col"]{font-size:var(--ln-fs-title);font-weight:600;letter-spacing:0;text-transform:none;color:var(--ln-ink)}
.q-plan-table th[scope="row"]{inline-size:11rem;background:none;font-size:var(--ln-fs-caption);letter-spacing:0;text-transform:none;color:var(--ln-ink-quiet)}
.q-plan-table thead td:first-child{inline-size:11rem}
.q-plan-table td{color:var(--ln-ink-secondary)}
.q-plan-table .ln-plan-price{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--ln-xs) var(--ln-sm);margin:0}
.q-plan-table .ln-plan-period,.q-plan-table .ln-plan-detail{font-size:var(--ln-fs-caption);color:var(--ln-ink-quiet)}
.q-plan-table .ln-plan-detail{margin:var(--ln-xs) 0 0}
.q-price-row td{padding-block:var(--ln-lg)}
.q-action-row td{padding-block-start:var(--ln-lg)}
.q-every-plan{max-width:46rem;margin:0;color:var(--ln-ink-secondary)}
.q-rules{max-width:46rem}
.q-rules p{margin:0 0 var(--ln-lg);color:var(--ln-ink-secondary)}
.q-compare-table{margin-block-start:var(--ln-sm)}
/* ln-stack-sm is also a flex utility class; above Lantern's 640px stacking the table stays a table. */
@media (min-width:640.02px){.q-compare-table{display:table}}
.q-compare-table th[scope="col"]{text-transform:none;letter-spacing:0}
.q-compare-foot,.q-compare-sources{max-width:46rem;margin:var(--ln-lg) 0 0}
.q-compare-sources{margin-block-start:var(--ln-sm)}
.ln-btn[aria-disabled="true"]{opacity:.65;cursor:default;pointer-events:none}

.q-faq{max-width:46rem}
.q-faq-list{display:grid}
.q-faq-list summary{font-weight:600}
.q-faq-list p{margin-block:0 var(--ln-lg);color:var(--ln-ink-secondary)}

/* Long Document pages: one readable column, a closing action on a rule. */
.q-prose{max-width:46rem;padding-block:clamp(var(--ln-xxl),6vw,4rem) var(--ln-xl)}
.q-prose h1{margin-block-start:0;font-size:clamp(1.875rem,4.6vw,2.75rem);line-height:1.2;letter-spacing:-.02em}
:lang(ar) .q-prose h1{line-height:1.4;letter-spacing:0}
.q-prose h2{margin-block-start:var(--ln-xxl)}
.q-facts li{margin-block-end:var(--ln-md)}
.q-sources{font-size:var(--ln-fs-callout)}
.q-sources li{overflow-wrap:anywhere}
.q-prose sup a{display:inline-block;min-inline-size:24px;min-block-size:24px;text-align:center}
.q-cta{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--ln-lg) var(--ln-xl);margin-block:var(--ln-xxl) 0;padding-block:var(--ln-xl);border-block:1px solid var(--q-rule)}
.q-cta h2{margin:0 0 var(--ln-xs);font-size:var(--ln-fs-title)}
.q-cta p{margin:0;color:var(--ln-ink-secondary)}
.q-cta .ln-btn{white-space:nowrap}
.q-pending{margin-block:var(--ln-xl);padding:var(--ln-xl)}
.q-pending h2{margin-block-start:0}
.q-table-wrap{overflow-x:auto}
.q-ours th,.q-ours td{font-weight:700}

.q-bench{padding-block-end:var(--ln-xl)}
.q-spec{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--ln-xl) var(--q-gap);padding-block-start:var(--ln-xl);border-block-start:1px solid var(--q-rule)}
@media (min-width:60rem){.q-spec{grid-template-columns:repeat(3,minmax(0,1fr))}}
.q-spec h2{margin:0 0 var(--ln-md);font-size:var(--ln-fs-title)}
.q-spec p{margin:0;color:var(--ln-ink-secondary)}
.q-spec-list{display:grid;gap:var(--ln-sm);margin:0;padding-inline-start:var(--ln-xl);color:var(--ln-ink-secondary)}
.q-spec-list li::marker{color:var(--ln-ink-quiet);font-variant-numeric:tabular-nums}

/* Ft1: wordmark and one sentence, two short link groups, the credit on the rule. */
.q-footer{display:grid;grid-template-columns:minmax(0,1fr);gap:var(--ln-xl);width:min(calc(100% - 2 * var(--q-gutter)),calc(var(--q-max) - 2 * var(--q-gutter)));margin-block-start:clamp(3rem,7vw,5rem);padding:var(--ln-xxl) 0 var(--ln-xl);border-block-start:1px solid var(--q-rule)}
@media (min-width:48rem){.q-footer{grid-template-columns:minmax(0,5fr) minmax(0,6fr)}}
.q-footer-mast p{margin:var(--ln-sm) 0 0;color:var(--ln-ink-secondary);max-width:24rem}
.q-footer-links{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--ln-xl)}
.q-footer-title{margin:0 0 var(--ln-sm);font-size:var(--ln-fs-caption);font-weight:600;color:var(--ln-ink-quiet)}
.q-footer ul{margin:0;padding:0;list-style:none;display:grid}
.q-footer-links a{display:inline-flex;align-items:center;min-block-size:36px;color:var(--ln-ink);text-decoration:none;white-space:nowrap}
.q-footer-links a:hover{text-decoration:underline;text-underline-offset:.2em}
.q-footer-links a[aria-current]{color:var(--ln-accent)}
.q-footer-rule{grid-column:1/-1;margin:0;padding-block-start:var(--ln-lg);border-block-start:1px solid var(--ln-hairline);font-size:var(--ln-fs-caption)}
.q-powered{font-weight:600;color:var(--ln-ink-secondary);text-decoration:none}
.q-powered:hover{color:var(--ln-ink);text-decoration:underline}
.q-footer a:focus-visible,.q-brand:focus-visible{outline:2px solid var(--ln-accent);outline-offset:2px;border-radius:var(--ln-r-xs)}

/* Minutes template */
.q-tool{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,22rem),1fr));gap:var(--ln-xl);align-items:start;padding-block-end:var(--ln-xxl)}
.q-tool-intro{padding-block-end:var(--ln-lg)}
.q-tool-form{display:grid;gap:var(--ln-md);padding:var(--ln-lg)}
.q-tool-form h2,.q-tool-preview h2{margin:0 0 var(--ln-sm);font-size:var(--ln-fs-title)}
.q-tool-preview{position:sticky;inset-block-start:calc(64px + var(--ln-lg))}
.q-tool-actions{display:flex;flex-wrap:wrap;gap:var(--ln-sm);margin-block:var(--ln-sm)}
.q-tool-actions .ln-btn{white-space:nowrap}
.q-minutes{margin:0;padding:var(--ln-lg);min-block-size:20rem;white-space:pre-wrap;overflow-wrap:anywhere;font-family:inherit;font-size:.9375rem;line-height:1.7}
.q-after-tool.q-prose{padding-block-start:var(--ln-xl)}
.q-signup{display:grid;gap:var(--ln-md);max-width:36rem;padding:var(--ln-lg)}
.q-signup h2{margin:0}
.q-consent{display:flex;gap:var(--ln-sm);align-items:flex-start;font-size:var(--ln-fs-callout)}
.q-consent input{margin-block-start:var(--ln-xs)}
.q-hp{position:absolute;inline-size:1px;block-size:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
.q-subscribed,.q-signup-error{display:none;max-width:36rem;padding:var(--ln-lg)}
.q-subscribed:target,.q-signup-error:target{display:block}
@media (max-width:48rem){.q-tool-preview{position:static}}
@media print{
  .q-header,.q-footer,.q-tool-intro,.q-tool-form,.q-tool-actions,#minutes-status,.q-after-tool,.q-tool-preview h2{display:none!important}
  .q-tool{display:block}
  .q-minutes{border:0;box-shadow:none;background:none;padding:0}
}
/* Plex Mono has no Arabic-Indic digits; Arabic pages keep timecodes in the sans. */
html:lang(ar) .q-time{font-family:inherit}
