/* ============================================================
   LEXUN — the premium layer
   ------------------------------------------------------------
   This file exists because of load order, not because the rules in it
   are a different kind of rule. /css/lexun.*.css is the base and every
   page links it FIRST, so hud, deck, ctl and dash all legitimately
   override it. A handful of decisions have to hold everywhere regardless
   of which of those a page happens to load — the reading size of body
   text, the measure of a paragraph, the radius of a panel, where the one
   accent is allowed to appear — and the only honest way to make a rule
   hold last is to load it last. So this sheet is linked after every other
   stylesheet on every page, it is short on purpose, and nothing in it
   uses !important: source order does the work.

   If a rule in here duplicates one in lexun.css, that is a sign the
   component underneath should be fixed instead. The list should shrink.
   ============================================================ */

/* ---------- reading ---------------------------------------------------
   15px body text is a density decision that made sense when this was a
   dashboard and stopped making sense when it became something people read
   paragraphs on. 17px at 1.65 is the size an argument gets read at rather
   than skimmed. The measure comes down with it: 66ch is the top of the
   comfortable range, and the site had paragraphs running to 74. */
body{font-size:17px;line-height:1.65}
p,li,dd{text-wrap:pretty}
p{max-width:66ch}
h1,h2,h3,h4,h5,h6{text-wrap:balance}

/* A heading that is also a machine readout is a heading first. */
h1,h2,h3,h4,h5,h6{font-family:var(--sans)}

/* ---------- surface ---------------------------------------------------
   4px on a hairline box is the radius you get by not choosing one. The
   panel radius is now 12px (--r) and controls sit one step tighter at 8px
   (--r-s), so a control never looks softer than the panel holding it.

   The inner top highlight is the whole trick: a 1px line of 9% white
   along the top edge reads as a light source above the page, which is
   what makes a flat dark rectangle look like a physical surface rather
   than a div with a border. The ambient shadow is deliberately huge and
   nearly transparent — it separates the card from the black without ever
   being visible as a shadow. */
.card,.panel,.lx-card,.pane,.tile{
  border-radius:var(--r);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.09), 0 24px 60px -40px rgba(0,0,0,.95);
}
.lxlight .card,.lxlight .panel,.lxlight .lx-card,.lxlight .pane,.lxlight .tile{
  box-shadow:inset 0 1px 0 rgba(255,255,255,.9), 0 24px 60px -44px rgba(0,0,0,.22);
}
input,select,textarea,.btn,.btn-primary,.btn-ghost,button.primary,.btn.primary,.btn.ghost{
  border-radius:var(--r-s);
}
.chip-status,.chip,.badge,[class*="pill"]{border-radius:var(--r-full)}

/* ---------- the accent ------------------------------------------------
   One colour, three jobs: the thing you can press, the thing that is
   live, and the band the engine is uncertain across. Nothing else.
   State stays with --good/--warn/--bad, and no viewport is allowed to
   show the accent for two different reasons at once. */
:focus-visible{outline:2px solid var(--acc2);outline-offset:2px}
a:not(.btn):not(.btn-primary):not(.btn-ghost):not(.chip):not(.navbtn):hover{color:var(--acc)}
::selection{background:var(--acc);color:var(--ink-on-acc)}

/* ---------- grain -----------------------------------------------------
   A large field of #000 on an OLED phone is not black, it is a surface with
   visible banding wherever a gradient crosses it, and on an LCD it is flat in
   a way that reads as cheap. Two percent of monochrome fractal noise over the
   whole page kills the banding and gives the black a grain you feel rather
   than see.

   It lived in lexun.css until /second.html turned out to be the one page in
   the site that does not link lexun.css — it carries its own small token
   block and only ctl.css. Rather than bolt the base sheet onto that page and
   restyle it by accident, the grain moved to the one sheet every page really
   does load. Measured, not assumed: the harness checks all 35 pages for it.

   Constraints it has to meet: it must not intercept a click, must not be
   readable by assistive technology, must not be the reason a page paints
   late, and must not print. It is an ::after on the root element rather than
   an extra <div>, so it costs nothing in markup and needs no per-page edit.
   The SVG is a data: URI — part of the stylesheet, no extra request, nothing
   fetched from a third party. The 200x200 tile repeats, and feTurbulence is
   evaluated once by the rasteriser and cached as an image, so this is not a
   per-frame cost. */
html::after{
  content:"";position:fixed;inset:0;z-index:2147483000;pointer-events:none;
  opacity:.02;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size:200px 200px;
}
@media print{html::after{display:none}}

/* ---------- motion ----------------------------------------------------
   Two durations and one curve, restated here so a component sheet cannot
   quietly introduce a third. Anything that is a response to a person
   finishes inside 600ms; ambient loops are exempt and are handled by the
   reduced-motion block in lexun.css. */
@media (prefers-reduced-motion:no-preference){
  .btn,.btn-primary,.btn-ghost,.btn.primary,.btn.ghost,.chip,.navbtn,input,select,textarea{
    transition-duration:var(--fast);
    transition-timing-function:var(--ease);
  }
}

/* ---------- carousel pagination ---------------------------------------
   A card clipped at the right edge with no indicator does not read as a
   carousel, it reads as a broken grid. The eight per cent peek and the snap
   points were already right; what was missing was the answer to "how many"
   and "where am I". These dots supply both.

   They are inserted by carousel.js only when a container genuinely scrolls,
   so nothing here is tied to a breakpoint — the strip is present on a phone
   and absent on the desktop grid because the measurement says so, not
   because a media query guessed.

   The button is 24x24 including its padding, which is the WCAG 2.2 target
   size (2.5.8); the visible dot is 8px because a 24px dot would be a
   different design. Position is carried by aria-current, and by scale as
   well as colour, so it never depends on hue alone. */
.cdots{display:flex;align-items:center;gap:0;margin:0 0 10px;padding:0;flex-wrap:wrap}
.cdots[hidden]{display:none}
.cdots>button{
  appearance:none;-webkit-appearance:none;background:none;border:0;margin:0;
  width:24px;height:24px;padding:8px;line-height:0;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;
}
.cdots>button::before{
  content:"";display:block;width:8px;height:8px;border-radius:50%;
  background:var(--line2,rgba(255,255,255,.34));
}
.cdots>button[aria-current="true"]::before{background:var(--acc);transform:scale(1.25)}
@media(hover:hover){ .cdots>button:hover::before{background:var(--acc-hi,var(--acc))} }
.cdots>button:focus-visible{outline:2px solid var(--acc2);outline-offset:1px;border-radius:50%}
@media (prefers-reduced-motion:no-preference){
  .cdots>button::before{transition:background var(--fast) var(--ease),transform var(--fast) var(--ease)}
}

/* ── the shared announcer, and the panels tabs.js hides ────────────────────
   Both of these belong here rather than on a page, because tabs.js is loaded
   on all thirty-five pages and second.html is the one page that does not link
   lexun.css. A rule that must hold everywhere has exactly one home.

   .lxsr is what the announcer tabs.js appends to <body> wears. Without it
   that paragraph is not a screen-reader-only status region, it is a visible
   line of text at the foot of the page saying "Region focus: Europe".
   clip-path as well as clip because clip is deprecated, and white-space
   because a collapsed 1px box otherwise wraps its text to one character per
   line, which some screen readers read out letter by letter. */
.lxsr{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;
}

/* [hidden] is a user-agent rule at the very bottom of the cascade, so any
   author display declaration beats it — and several of this site's panels
   inherit display:grid or display:flex from a layout rule. tabs.js hides a
   panel by setting the hidden property, so the sheet that ships alongside it
   has to make that property mean something. */
[role="tabpanel"][hidden],[data-lxpanel][hidden]{display:none!important}


/* ── the command surface ──────────────────────────────────────────────────
   The instrument's mouthpiece: every place a person types a decision — the
   hero Analyse box, the workspace question, the assistant, the Earth's fly
   search. One treatment across all of them: glass depth with a hairline top
   light, a caret in the accent, and a focus state that arrives as a ring of
   the one colour this site reserves for interactive affordances. */
.cmdrow input,#homeQ,#askIn,#flyQ,#fStatement{
  background:linear-gradient(180deg,rgba(255,255,255,.075),rgba(255,255,255,.035));
  border:1px solid rgba(255,255,255,.16);
  border-radius:12px;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.07);
  caret-color:var(--acc,#5b9dff);
  transition:border-color .18s cubic-bezier(.22,.61,.36,1),box-shadow .18s cubic-bezier(.22,.61,.36,1);
}
.cmdrow input:hover,#homeQ:hover,#askIn:hover,#flyQ:hover,#fStatement:hover{border-color:rgba(255,255,255,.28)}
.cmdrow input:focus,#homeQ:focus,#askIn:focus,#flyQ:focus,#fStatement:focus,
.cmdrow input:focus-visible,#homeQ:focus-visible,#askIn:focus-visible,#flyQ:focus-visible,#fStatement:focus-visible{
  outline:none;border-color:var(--acc,#5b9dff);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.07),0 0 0 3px rgba(91,157,255,.20),0 6px 30px -12px rgba(91,157,255,.35);
}
.cmdrow input::placeholder,#homeQ::placeholder,#askIn::placeholder,#flyQ::placeholder,#fStatement::placeholder{color:rgba(174,174,174,.66)}

/* the Analyse button: it lifts to meet the hand, presses under it, and its
   arrow leans into the journey — 180ms, compositor-only, nothing over 420ms */
.cmdrow .btn.primary{
  border-radius:12px;
  transition:transform .18s cubic-bezier(.22,.61,.36,1),box-shadow .18s cubic-bezier(.22,.61,.36,1),background .18s cubic-bezier(.22,.61,.36,1);
}
.cmdrow .btn.primary:hover{
  transform:translateY(-1px);
  box-shadow:0 8px 28px -10px rgba(91,157,255,.55),0 2px 8px -2px rgba(0,0,0,.6);
}
.cmdrow .btn.primary:active{transform:translateY(1px);box-shadow:none}
.cmdrow .btn.primary .ar{display:inline-block;transition:transform .18s cubic-bezier(.22,.61,.36,1)}
.cmdrow .btn.primary:hover .ar{transform:translateX(3px)}

/* The three decisions the entrance offers. These were pills, and a pill is
   the right shape for an example someone else typed; they are the first real
   choice on the site now, so they are cards. Each still carries, once the
   engine is warm, the band it would return — computed live in this browser
   from the same frozen models, which is why that number may wear the accent:
   it is a probability band, the one figure colour belongs to.

   The selector keeps the id, because the id is what made the pill radius win
   over the card radius when this changed shape — leaving it and adding a
   class rule elsewhere would have been two rules fighting over one button. */
#heroEx button[data-q],.cmdex button{
  border-radius:12px;padding:13px 14px;min-height:0;text-align:left;
  background:linear-gradient(180deg,rgba(255,255,255,.055),rgba(255,255,255,.02));
  border:1px solid rgba(168,204,255,.24);
  transition:border-color .18s cubic-bezier(.22,.61,.36,1),transform .18s cubic-bezier(.22,.61,.36,1),background .18s cubic-bezier(.22,.61,.36,1);
}
#heroEx button[data-q]:hover,.cmdex button:hover{border-color:var(--acc,#5b9dff);transform:translateY(-1px);background:rgba(255,255,255,.085)}
#heroEx button[data-q]:active,.cmdex button:active{transform:translateY(0)}
/* the live band sits under the description rather than beside the title */
#heroEx button .bnd{display:inline-block;margin-top:7px}
#heroEx button .bnd,.cmdex button .bnd{
  font-family:var(--mono,ui-monospace,monospace);font-size:11px;font-variant-numeric:tabular-nums;
  color:var(--acc,#5b9dff);margin-left:7px;opacity:0;transition:opacity .42s cubic-bezier(.22,.61,.36,1);
}
#heroEx button .bnd.on,.cmdex button .bnd.on{opacity:1}

/* ── the scrub: sensitivity you can hold ──────────────────────────────────
   The slider takes the accent because it is an interactive affordance; the
   result beside it is a probability band, the other thing the accent is for.
   The row is honest by construction — the word "modelled" travels with the
   number, in the machine voice, because a scrubbed run is an experiment. */
.adrv .d .scrub{grid-column:1/-1;display:flex;gap:10px;align-items:center;margin:1px 0 5px}
.adrv .d .scrub input[type=range]{flex:1;min-width:0;accent-color:var(--acc,#5b9dff);min-height:24px;cursor:ew-resize}
.adrv .d .scrub .sout{font-family:var(--mono,ui-monospace,monospace);font-size:11px;font-variant-numeric:tabular-nums;color:var(--acc,#5b9dff);min-width:13ch;text-align:right;white-space:nowrap}
.adrv .snote{margin:6px 0 0;font-size:11.5px;color:var(--dim,#919191);letter-spacing:.02em}

/* the workspace scrub rows share the homepage geometry */
#sensScrub{display:grid;gap:4px;margin-top:10px}
#sensScrub .d{display:grid;grid-template-columns:1fr;gap:2px;font-size:12.5px;color:var(--mut,#aeaeae)}

/* the workspace fan and the preview badge */
#wsFan{display:block;width:100%;height:120px}
#livePrev{color:var(--acc,#5b9dff);font-variant-numeric:tabular-nums}


/* ============================================================
   THE INSTRUMENT SURFACE
   ------------------------------------------------------------
   The identity at the top of lexun.css is "dark instrument: hairline grid,
   weight-flat type, hierarchy from size and colour only". The grid was in
   the description and not on the screen. This section puts it there, and
   does the two other things a surface needs to stop reading as a flat black
   div and start reading as a machined panel: an edge that catches light, and
   a body that is not one uniform value.

   Every rule here is scoped `html:not(#_)` for one reason. Forty-three pages
   set `body{background:var(--bg)}` in an inline <style>, and an inline style
   block is parsed after this linked sheet -- so at equal specificity the
   shorthand wins and resets background-image to none. The extra :not() takes
   the specificity above it for the one longhand each rule needs, and touches
   nothing else. It is the same device system.css already uses.

   Nothing here changes a text colour, a size or a measure. Contrast is
   asserted across forty-five pages by gates/axe-sweep.mjs and this section
   is deliberately written so that it cannot move a single one of those
   numbers: it only ever adds a background-image or a shadow.
   ============================================================ */

/* THE FIELD.
   A 64px grid at 3% of a pale blue: on black it is at the threshold of
   visible, which is the point -- it should register as depth and texture
   rather than as a pattern anybody looks at. It scrolls with the page,
   because a fixed grid under a scrolling page reads as the page sliding over
   glass, and this is meant to be the surface the page is ON. */
/* PAINTED ONCE, NOT ON EVERY SCROLLED PIXEL.
   As a background-image on <body> this grid was repainted for every frame of
   every scroll, on a page several thousand pixels tall. On its own that is
   cheap; stacked with the panel gradients, the figure glow and a blurred
   sticky header it was a measured part of a p90 frame time of 50ms.
   As a fixed, pointer-transparent layer the compositor keeps it as one
   texture and scrolling costs nothing. Same grid, same 3% of a pale blue. */
/* ── THE GUARD HAD TO NAME THE CLASS THAT IS ACTUALLY SET ────────────────
   These six rules exclude themselves from the homepage's light chapter, and
   for six releases they did not, because they tested for `.lxlight` -- which is
   a class on the SECTION. The class the script puts on <html> when that
   chapter owns the viewport is `.lxpaper`. So the guard never fired, and
   because `:not(#_)` raises the specificity above anything the light theme can
   write, the dark rules won every time.

   What that looked like on the deployed site: scroll to the public-record
   chapter and the header painted `rgba(7,8,11,.965)` -- near black -- under
   text the light theme had already coloured `#111114`, also near black. The
   navigation was invisible for the height of an entire chapter. It was never
   seen in a sweep because the contrast gates photograph pages at the top,
   where the chapter is not in the viewport and the class is not set.

   Both classes are named now, and platform/tests.mjs asserts that every
   `:not(#_)` guard in this sheet names `.lxpaper`, so the next one cannot ship
   half-written. */
html:not(#_):not(.lxlight):not(.lxpaper)::before{
  content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;
  background-color:var(--bg,#000);
  background-image:
    linear-gradient(rgba(126,174,255,.030) 1px,transparent 1px),
    linear-gradient(90deg,rgba(126,174,255,.030) 1px,transparent 1px);
  background-size:64px 64px;
}

/* THE PANELS.
   Two things, both barely there. A hairline of light along the top edge,
   which is what tells the eye a surface has a thickness and is facing up
   into a light; and a vertical fall from a hair above the panel colour to a
   hair below it, which stops a large card reading as a hole cut in the page.
   Cards that already paint their own gradient keep it -- these are added as
   a background-image layer and any component setting its own image wins. */
html:not(#_):not(.lxlight):not(.lxpaper) :is(.card,.tcard,.rcard,.box,.hairbox,.vtile,.anat,.pcard){
  background-image:
    linear-gradient(rgba(150,192,255,.055),rgba(150,192,255,0) 34%),
    linear-gradient(rgba(255,255,255,.014),rgba(0,0,0,.10));
  box-shadow:inset 0 1px 0 rgba(178,210,255,.085);
}

/* ── THE CARD DECKS, GIVEN A SURFACE ──────────────────────────────────────
   The three decision cards and the three plan cards were a hairline, eleven
   pixels of padding and a thirteen-and-a-half-pixel title: the shape of a card
   without any of the things that make one. The gradient above already gave
   them a face; what they had no version of was DEPTH -- no shadow, so nothing
   to sit on; no lift on hover, so a card that is a link looked exactly like a
   card that is not; and a title small enough that the eyebrow above it
   competed with it.

   The treatment is the one the entrance decision cards wear, so a visitor
   meets the same object twice rather than two dialects of it: a shadow under a
   graded face, an accent rule down the left edge that is dark until the card is
   pointed at, and a title that outweighs its label. Cards that are not links
   get the surface and not the interaction, because promising an action a card
   does not have is worse than a flat card. */
html:not(#_):not(.lxlight):not(.lxpaper) :is(.tcard,.rcard){
  position:relative;overflow:hidden;
  border-radius:12px;
  padding:clamp(14px,1.7vw,18px);
  box-shadow:inset 0 1px 0 rgba(178,210,255,.085),
             0 1px 2px rgba(0,0,0,.42),
             0 12px 30px -20px rgba(0,0,0,.92);
  transition:border-color .18s cubic-bezier(.22,.61,.36,1),
             box-shadow .18s cubic-bezier(.22,.61,.36,1),
             transform .18s cubic-bezier(.22,.61,.36,1);
}
html:not(#_):not(.lxlight):not(.lxpaper) :is(.tcard,.rcard)::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:2px;
  background:linear-gradient(180deg,var(--acc,#5b9dff),rgba(91,157,255,.12));
  opacity:.22;transition:opacity .18s cubic-bezier(.22,.61,.36,1);
}
html:not(#_):not(.lxlight):not(.lxpaper) :is(.tcard,.rcard) :is(b,.rt){
  font-size:15px;line-height:1.3;margin-bottom:6px;color:#f4f8ff;
}
html:not(#_):not(.lxlight):not(.lxpaper) :is(.tcard,.rcard) .rk{
  font-size:11px;color:var(--acc,#5b9dff);
}
html:not(#_):not(.lxlight):not(.lxpaper) :is(.tcard,.rcard) p{
  font-size:13.5px;line-height:1.55;
}
/* only the ones that actually go somewhere behave like it */
@media(hover:hover){
  html:not(#_):not(.lxlight):not(.lxpaper) a.rcard:hover{
    transform:translateY(-2px);border-color:var(--acc-line,rgba(91,157,255,.42));
    box-shadow:inset 0 1px 0 rgba(198,224,255,.14),
               0 2px 5px rgba(0,0,0,.45),
               0 22px 46px -24px rgba(0,0,0,.95);
  }
  html:not(#_):not(.lxlight):not(.lxpaper) a.rcard:hover::before,
  html:not(#_):not(.lxlight):not(.lxpaper) .tcard:hover::before{opacity:1}
}
html:not(#_):not(.lxlight):not(.lxpaper) a.rcard:focus-visible::before{opacity:1}
@media(prefers-reduced-motion:reduce){
  html:not(#_):not(.lxlight):not(.lxpaper) :is(.tcard,.rcard),
  html:not(#_):not(.lxlight):not(.lxpaper) :is(.tcard,.rcard)::before{transition:none}
  html:not(#_):not(.lxlight):not(.lxpaper) a.rcard:hover{transform:none}
}

/* THE LIVE INDICATOR.
   A dot that means "this is computing, here, now" should look lit rather
   than printed. The accent is already the atmosphere blue the globe paints;
   this gives it the halo that colour implies. */
html:not(#_):not(.lxlight):not(.lxpaper) :is(.vlive,.chip-status,.ab.live) i,
html:not(#_):not(.lxlight):not(.lxpaper) .vlive i{
  box-shadow:0 0 0 2px rgba(91,157,255,.16),0 0 10px 1px rgba(91,157,255,.55);
}

/* THE READOUTS.
   Every figure the engine produced already carries .num, which is how the
   audit tells machine output from a label. Giving that one class a faint
   glow means the numbers on this site light up and the words do not -- which
   is exactly the distinction the whole product rests on, made visible
   without a word of new copy. Kept well under a pixel of blur so it never
   costs legibility. */
/* THE GLOW IS FOR THE FIGURE YOU LOOK AT, NOT EVERY DIGIT ON THE PAGE.
   .num marks every engine-produced figure, and on the homepage that is well
   over a hundred elements — each one a text-shadow the browser repaints as
   the page moves. The effect only ever read on the large figures anyway, so
   it is scoped to those: the headline probability, the breakpoint, the stat
   line under the entrance and the tiles. Cheap, and it actually looks more
   deliberate for being rarer. */
html:not(#_):not(.lxlight):not(.lxpaper) :is(.vbig .num,.hbkbig,.hodeltabig,.lxfstat .num,
  .kv .cell .v.num,.vfacts .v.num,h1 .num,h2 .num,.big .num){
  text-shadow:0 0 14px rgba(126,174,255,.22);
}

/* THE HORIZON.
   A single accent hairline under the fixed header, brightest at the centre
   and gone at both edges, so the chrome sits on a lit edge rather than a
   drawn box. */
/* A STICKY BAR WITH A BACKDROP FILTER RE-BLURS THE PAGE UNDER IT EVERY FRAME.
   Measured on its own, removing the header's blur took the homepage's p90
   frame time from 50ms to 33ms — it was the single most expensive thing on
   the page. On a site this dark the frost was doing almost nothing a nearly
   opaque bar does not: the content behind it is near-black either way. The
   spread glow goes with it; a sticky element's shadow is repainted on every
   scrolled pixel too, and one hairline says the same thing. */
html:not(#_):not(.lxlight):not(.lxpaper) :is(.lxhead,header.nav,.lxnav-top){
  box-shadow:0 1px 0 rgba(126,174,255,.10);
  -webkit-backdrop-filter:none;backdrop-filter:none;
  background:rgba(7,8,11,.965);
}

/* Reduced motion changes nothing here: none of it moves. It is stated so the
   next person does not have to check. */

/* ============================================================
   ARRIVAL
   ------------------------------------------------------------
   The motion half of the instrument surface, paired with /reveal.js.
   Content enters from a little below and a little out of focus and settles;
   members of a group follow one another by a beat.

   The hidden state is scoped to html.lxjs — the class the document sets in
   its own head — so a page whose scripts never run shows every word rather
   than a column of empty boxes. That is the whole reason this is not just
   `[data-rv="out"] { opacity: 0 }`.

   The transition is on the element, not the state, so a block that is marked
   done before the first frame (anything already on screen at load) settles
   without a flash.
   ============================================================ */
/* Durations sit under the site's own 600ms ceiling for anything that responds
   to a person -- and a scroll reveal responds to a person. The first pass used
   720ms on the transform and audit/premium.mjs caught it on eight elements
   across five pages, which is exactly what that rule is for. */
/* OPACITY AND TRANSFORM ONLY.
   The blur was the prettier half of this effect and by far the most expensive:
   opacity and transform are handed to the compositor, a filter forces a repaint
   of the element on every frame it animates, and forty of them arriving while
   the page scrolls is what the owner was seeing as stutter. Removing it took
   the p90 frame time down measurably on its own. The rise and the fade do the
   work; nobody misses six pixels of blur they never consciously saw. */
/* A FADE, AND NOTHING ELSE.
   Rise-and-fade on forty elements measured at 27% of frames over budget while
   scrolling, against 19% with no reveal at all — and the owner reported the
   page as glitchy the day after it shipped. A transform on a block that is
   also being scrolled asks the compositor to move the same pixels twice; a
   fade asks it to change one number. The count came down with it: section
   heads and major blocks arrive, individual cards no longer do, because forty
   things arriving separately is not an effect, it is noise. */
html.lxjs [data-rv]{
  transition:opacity 380ms cubic-bezier(.22,.61,.28,1);
  transition-delay:var(--rv-d,0ms);
}
html.lxjs [data-rv="out"]{opacity:0}
html.lxjs [data-rv="in"]{opacity:1}

@media (prefers-reduced-motion:reduce){
  html.lxjs [data-rv]{transition:none!important;opacity:1!important}
}

/* WHAT IS NOT ON SCREEN IS NOT RENDERED.
   The homepage is several thousand pixels tall and the browser was laying out
   and painting all of it on every frame. contain-intrinsic-size gives each
   section a placeholder height so the scrollbar and anchor links stay honest
   while its contents are skipped. The entrance is excluded: it is on screen at
   load and must never be deferred. */
html:not(#_) .slide:not(.lxopen){
  content-visibility:auto;
  contain-intrinsic-size:auto 1200px;
}

/* THE READING LINE.
   A one-pixel accent rule across the top that fills as the page is read. It
   is the only element on the site that reports the reader's own position, and
   it is decoration: aria-hidden, no number, nothing to misread. */
#lxread{position:fixed;top:0;left:0;height:2px;width:100%;z-index:1000;pointer-events:none;
  transform-origin:0 50%;transform:scaleX(var(--lxread,0));
  background:linear-gradient(90deg,rgba(91,157,255,0),rgba(126,190,255,.95) 45%,rgba(190,232,255,1));
  box-shadow:0 0 12px rgba(91,157,255,.55);
  transition:transform 120ms linear}
@media (prefers-reduced-motion:reduce){#lxread{transition:none}}

/* ============================================================
   THE TYPE
   ------------------------------------------------------------
   The scale, the tracking and the measure were already decided in
   lexun.css and are not touched here. What was missing is everything
   below the size of a token: how the faces are actually set.

   Both families are variable fonts, and both were being rendered with
   optical sizing off, standard figures in running text, no contextual
   alternates, and browser-default underlines. Those are the differences
   between type that is styled and type that is set.

   Nothing here changes a colour, a size or a measure, so no contrast
   number and no width in the responsive ladder can move.
   ============================================================ */

html{
  font-optical-sizing:auto;
  /* kern and liga are on by default; calt is what makes a variable face
     use its contextual pairs, and it is off unless asked for. */
  font-feature-settings:"kern" 1,"liga" 1,"calt" 1;
  text-rendering:optimizeLegibility;
}

/* FIGURES.
   Proportional figures in a sentence, tabular everywhere a number is a
   measurement -- so a column of them lines up and a live value that changes
   does not shift the words beside it. .num is the site's own marker for a
   figure the engine produced, which makes it exactly the right hook. */
body{font-variant-numeric:proportional-nums}
.num,.mono,code,kbd,samp,pre,table td,table th,time,
input[type="number"],output{font-variant-numeric:tabular-nums lining-nums}

/* DISPLAY TYPE.
   A large heading set at the same tracking as a paragraph reads loose, and
   the scale already carries per-step values -- they were simply never
   applied to the elements. h1 and h2 are display sizes on this site. */
h1{letter-spacing:var(--ls-4xl);line-height:var(--lh-display)}
h2{letter-spacing:var(--ls-3xl);line-height:var(--lh-heading)}
h3{letter-spacing:var(--ls-2xl);line-height:var(--lh-heading)}
h4,h5,h6{letter-spacing:var(--ls-lg);line-height:var(--lh-heading)}

/* THE LABELS.
   Every eyebrow, tag and column head on the site is uppercase at 10-12px.
   At that size a capital needs air, and the cap-height sits high in the box,
   so the optical baseline needs help the metrics do not give it. */
.stag,.eyebrow,.kicker,.chmark,.engk,.k,.kk,.ab,.lbl,.chip-status,caption,
th,.mhead,.brand .desc{
  letter-spacing:var(--ls-caps);
  font-feature-settings:"kern" 1,"liga" 1,"calt" 1,"case" 1;
}

/* LINKS.
   The browser's default underline sits on the baseline and cuts every
   descender it meets. One pixel, dropped clear of the letterforms, and
   thickening rather than moving on hover: the detail that most reliably
   separates a set page from a styled one. */
p a,li a,dd a,.notefoot a,.ssub a,.lxfine a{
  text-decoration-line:underline;
  text-decoration-thickness:1px;
  text-underline-offset:.18em;
  text-decoration-skip-ink:auto;
  transition:text-decoration-thickness 120ms ease-out,text-underline-offset 120ms ease-out;
}
@media (hover:hover){
  p a:hover,li a:hover,dd a:hover,.notefoot a:hover,.ssub a:hover,.lxfine a:hover{
    text-decoration-thickness:2px;text-underline-offset:.2em;
  }
}

/* PARAGRAPHS.
   Hanging punctuation pulls an opening quote or bullet into the margin so the
   text edge stays straight; supported in Safari, ignored elsewhere, wrong
   nowhere. */
p,li,dd,blockquote{hanging-punctuation:first last}
/* a single word alone on the last line of a paragraph */
p{text-wrap:pretty}

/* SELECTION.
   The accent, at a weight that stays readable while selected. */
::selection{background:rgba(91,157,255,.30);color:#fff}
::-moz-selection{background:rgba(91,157,255,.30);color:#fff}
