/* Map Lexe's palette onto Zensical's design tokens.
 *
 * Every rule in the theme's stylesheet reads its colors through these custom
 * properties rather than hardcoding them, so redefining them here recolors the
 * whole site. `extra_css` is linked after the theme's stylesheet, which is what
 * lets a plain `:root` block win the cascade.
 *
 * Note: these are unscoped, so they would also flatten a second color scheme.
 * Adding a `theme.palette` with a dark toggle means scoping them under
 * `[data-md-color-scheme="default"]` first - the scheme's own rules carry the
 * same specificity and load earlier, so an unscoped `:root` beats them. */
:root {
  --md-default-bg-color: var(--lx-background);
  --md-default-bg-color--light: color-mix(
    in srgb,
    var(--lx-background) 70%,
    transparent
  );
  --md-default-bg-color--lighter: color-mix(
    in srgb,
    var(--lx-background) 30%,
    transparent
  );
  --md-default-bg-color--lightest: color-mix(
    in srgb,
    var(--lx-background) 12%,
    transparent
  );

  /* The two darkest steps are the app's own text colors. The lighter two stay
     translucent at the theme's original alphas, since it also draws borders
     and subtle fills with them, which must composite over code blocks too. */
  --md-default-fg-color: var(--lx-foreground);
  --md-default-fg-color--light: var(--lx-fg-secondary);
  --md-default-fg-color--lighter: color-mix(
    in srgb,
    var(--lx-foreground) 32%,
    transparent
  );
  --md-default-fg-color--lightest: color-mix(
    in srgb,
    var(--lx-foreground) 5%,
    transparent
  );

  /* The theme derives no transparent variant from the accent - that is a
     separate literal, and left alone it tints the active nav item indigo. */
  --md-primary-fg-color: var(--lx-accent);
  --md-accent-fg-color: var(--lx-accent);
  --md-accent-fg-color--transparent: color-mix(
    in srgb,
    var(--lx-accent) 10%,
    transparent
  );
  --md-typeset-a-color: var(--lx-accent);

  /* Code sits inset against the page rather than raised, since the page
     background is already a light grey rather than white. The two derived
     variants back the copy-button chrome, and would otherwise stay a
     near-white translucency floating over the grey. */
  --md-code-bg-color: var(--lx-grey-850);
  --md-code-fg-color: var(--lx-grey-300);
  --md-code-bg-color--light: color-mix(
    in srgb,
    var(--lx-grey-850) 70%,
    transparent
  );
  --md-code-bg-color--lighter: color-mix(
    in srgb,
    var(--lx-grey-850) 30%,
    transparent
  );

  /* Derived, not a palette value: the step just off the body color that
     headings sit at. */
  --lx-ink-soft: color-mix(
    in srgb,
    var(--lx-fg-secondary) 25%,
    var(--lx-foreground)
  );
}

/* The theme reserves the scrollbar's gutter on every page, which leaves an
   empty bar down the right edge of pages too short to scroll. The wide
   layout keeps the page still as the scrollbar comes and goes instead. */
html {
  scrollbar-gutter: auto;
}

/* Headings below the page title sit just off the body color - enough to
   part them from it, without the washed look a full step down gave h2 - and
   a step below the theme's bold, which reads as heavy at these sizes. */
.md-typeset :is(h2, h3, h4, h5, h6) {
  color: var(--lx-ink-soft);
  font-weight: 600;
}

/* Bold carries its emphasis by weight alone, inheriting the body color, and
   sits a step below the browser's bold - at this size full weight reads as
   heavy rather than emphatic. Listed by container so bold inside a heading
   keeps the heading's own weight. */
.md-typeset :is(p, li, dd, td) strong {
  font-weight: 600;
}

/* Reduce spacing after H1 */
.md-content h1 {
  margin-bottom: 0.5rem !important;
}

/* Nudged right so the wordmark lines up with the sidebar below it, to within
   the sidebar's own few px of inset. The right margin is the breathing room the
   wordmark used to carry inside its own viewBox, before it was cropped. */
.md-header__button.md-logo {
  margin-left: 0.56rem;
  margin-right: 1.27rem;
  /* A step darker than the nav links, so the mark reads as the brand rather
     than another item in the bar, and carries its side of the bar against the
     search field opposite, which is also full foreground. */
  color: var(--lx-foreground);
}

/* The wordmark is the brand mark rather than a nav target, so it sits out the
   theme's hover fade. Its focus ring is untouched. */
.md-header__button.md-logo:hover {
  opacity: 1;
}

/* Nudged up because the mark is taller than the text beside it, so centering
   its box leaves its baseline low and it reads as hanging. */
.md-header__button.md-logo svg {
  transform: translateY(-2px);
}

/* Hide logo in mobile/medium drawer */
@media screen and (max-width: 76.1875em) {
  .md-nav__button.md-logo {
    display: none;
  }
}

/* Constrain content width for readability */
.md-content {
  max-width: 60em;
}

/* Medium screens: center content when sidebar is hidden */
@media screen and (min-width: 40em) and (max-width: 76.1875em) {
  .md-content {
    margin: 0 auto;
  }
}

/* Tables default to the page background, which leaves them reading as loose
   rules rather than a block. Lift the body just off the page - short of white,
   which stood out more than the content it holds - and settle the header
   below it. */
.md-typeset table:not([class]) {
  background-color: var(--lx-grey-950);
  border-color: var(--lx-grey-850);
}

/* The same tint the sidebar gives its active item. Mixed against the page
   background rather than layered as a translucency, so the lighter table body
   underneath doesn't wash it out. The label itself stays at the body color and
   one weight below bold, leaving the fill to mark the row off. */
.md-typeset table:not([class]) th {
  color: var(--lx-foreground);
  font-weight: 600;
  background-color: color-mix(
    in srgb,
    var(--lx-accent) 10%,
    var(--lx-background)
  );
}

/* Screenshots and screen recordings share the app's background with the page,
   so without a surface of their own they read as borderless. Keying on "a
   paragraph holding nothing but the media" is what keeps this out of the
   markdown: authors write a plain `![alt](src)` or `<video>` and the panel
   follows (the `lexe_www.media` extension gives a standalone video the
   paragraph Markdown gives an image). Video is matched without `:only-child`,
   since video extensions inject their controls as a sibling of the video and
   would otherwise drop the panel for anyone running one. */
.md-typeset p:has(> img:only-child),
.md-typeset p:has(> video) {
  display: flex;
  justify-content: center;
  margin: 2em 0;
  padding: 1rem;
  border: 1px solid var(--lx-grey-850);
  border-radius: 1.2rem;
  background-color: var(--lx-grey-950);
}

.md-typeset p:has(> img:only-child) > img,
.md-typeset p:has(> video) > video {
  display: block;
  border-radius: 0.6rem;
}

/* External link icon for nav items linking outside the site */
.md-nav__link[href^="http"]::after {
  content: " ↗";
  font-size: 0.8em;
}

/* On a page too short to track, the table of contents holds its resting state
   rather than reporting a position it cannot know (see `javascripts/extra.js`
   for when this applies). */
.lx-toc-untracked .md-sidebar--secondary .md-nav__link--passed,
.lx-toc-untracked .md-sidebar--secondary .md-nav__link--passed code,
.lx-toc-untracked .md-sidebar--secondary .md-nav__link--active,
.lx-toc-untracked .md-sidebar--secondary .md-nav__link--active code {
  color: inherit;
  font-weight: inherit;
}

/* The table-of-contents button flips to a solid accent pill with a white icon
   on hover, which is a lot of emphasis for a disclosure control. The fill the
   sidebar gives the current page instead, mixed against the page rather than
   layered as a translucency, since the button floats over content. */
.md-sidebar--secondary .md-sidebar-button:is(:focus, :hover) {
  background-color: color-mix(in srgb, var(--lx-accent) 10%, var(--lx-background));
  color: var(--md-default-fg-color);
}

/* Sidebar scrollbars - including the table-of-contents panel the sidebar
   collapses into below the breakpoint - sit at 32% of the foreground and turn
   accent on hover, which for a panel you are touching is most of the time.
   One light grey in every state instead: the bar is incidental. */
.md-sidebar__scrollwrap,
.md-sidebar__scrollwrap:is(:focus-within, :hover) {
  scrollbar-color: var(--lx-grey-750) transparent;
}

/* Same again for engines still on the prefixed scrollbar pseudo-elements,
   which is how the theme styles this. */
.md-sidebar__scrollwrap::-webkit-scrollbar-thumb,
.md-sidebar__scrollwrap:is(:focus-within, :hover)::-webkit-scrollbar-thumb,
.md-sidebar__scrollwrap:is(:focus-within, :hover)::-webkit-scrollbar-thumb:hover {
  background-color: var(--lx-grey-750);
}

/* The sidebars sit a step below the content they navigate, which at full
   strength they compete with. Everything in them takes this color, list
   headings included, which their weight already sets apart. Hovering lifts
   an entry to the foreground or accent, and the current page keeps its
   accent. */
.md-sidebar {
  color: var(--lx-fg-secondary);
}

/* "On this page", a step below the theme's bold, which reads as heavy at
   this size. */
.md-nav--secondary .md-nav__title {
  font-weight: 600;
}

/* Table of contents entries the reader has scrolled past, which the theme
   draws a step below full strength. That step now has to come below the
   sidebar's own color. */
.md-sidebar .md-nav__link--passed,
.md-sidebar .md-nav__link--passed code {
  color: var(--lx-grey-500);
}

/* The current section counts as passed too, but keeps the sidebar's color
   rather than the theme's accent, marked out by its weight alone. */
.md-sidebar--secondary .md-nav__link--active,
.md-sidebar--secondary .md-nav__link--active code {
  color: inherit;
}

/* Cross-site nav in the header (see `partials/header.html`). Sizes are given
   in rem with their pixel value alongside, since 1rem is 20px here. */
.lx-header__nav {
  display: flex;
  gap: 1.5rem; /* 30px */
  font-size: 0.7rem; /* 14px */
  line-height: 1rem; /* 20px */
  font-weight: 500;
  white-space: nowrap;
}

/* The two flex-1 groups either side of the nav. `display: contents` keeps them
   out of the layout below the breakpoint, where the nav is hidden anyway. */
.lx-header__start,
.lx-header__end {
  display: contents;
}

/* The bar's height once pinned, and the headroom above and below its row at
   rest. Below the breakpoint this is the theme's own 48px bar, which never
   clamps; the wide layout overrides both. */
:root {
  --lx-header-height-pinned: 2.4rem;
  --lx-header-headroom: 0px;
}

@media screen and (min-width: 76.25em) {
  /* Equal flex on both neighbors - the title on the left, search on the
     right - is what centers the nav: their differing content widths then
     can't drag the middle off-center. */
  .lx-header__start,
  .lx-header__end {
    display: flex;
    flex: 1 1 0;
    align-items: center;
    min-width: 0;
  }

  .lx-header__end {
    justify-content: flex-end;
  }

  /* The wordmark has to sit inside the flex-1 group, not beside it, or its
     width offsets the nav from center. The title keeps the theme's
     `flex-grow`, which is the only thing giving it width: its topics are
     absolutely positioned, so it has no intrinsic size to fall back on. */
  .md-header__title {
    min-width: 0;
    /* Long page titles ellipsize about one nav link and its gap short of the
       nav, rather than running up against it. */
    margin-right: 4em;
  }

  /* At rest, a 60px row centered in 80px with a 32px wordmark, matching the
     lexe.app header. Of the 10px above and below the row, only the top is the
     bar's: it sticks at minus that headroom, so the headroom scrolls away and
     the row clamps to the top of the viewport. The box never changes size, so
     the page keeps its height and nothing below moves as it pins. */
  :root {
    --lx-header-height-pinned: 3rem;
    --lx-header-headroom: 0.5rem;
    /* The gap the content keeps below the bar's headroom. */
    --lx-main-gap: 0.5rem;
  }

  .md-header {
    top: calc(-1 * var(--lx-header-headroom));
  }

  .md-header__inner {
    height: calc(var(--lx-header-height-pinned) + var(--lx-header-headroom));
    padding-top: var(--lx-header-headroom);
  }

  /* The wordmark is cropped to its glyphs, so this is their height, not a
     padded box's. */
  .md-header__button.md-logo svg {
    height: 0.89rem;
  }

  /* A fixed 1280px grid with a 24px inset. Pinned in px because the theme
     scales its root font size at wide viewports (125% -> 137.5% -> 150%) and
     `.md-grid`'s 61rem scales with it, which would widen the bar as the window
     grows - by over 100px at 2200px. */
  .md-grid {
    max-width: 1280px;
  }

  .md-header__inner,
  .md-main__inner {
    padding-left: 24px;
    padding-right: 24px;
  }

  /* Laid out at the full viewport width whether or not the page scrolls, so
     a scrollbar overlays the right edge rather than narrowing the page and
     shifting the centered grid. The 24px inset keeps content clear of it,
     and the theme's `overflow-x: hidden` on `html` clips the width it
     covers. Only up here: the narrow layout runs closer to the edge. */
  html {
    width: 100vw;
  }

  /* Tighten the gap under the header - one lever that moves the content and
     both sidebars together, so they keep starting on the same line. Only up
     here: the compact header the hamburger comes with sits closer to the
     text, and the theme's own spacing is what that layout wants. The bar's
     headroom below the row is carried here rather than by the bar, so the
     pinned bar is the row alone. */
  .md-main__inner {
    margin-top: calc(var(--lx-header-headroom) + var(--lx-main-gap));
  }

  /* Fill the grid while centering the capped prose. Horizontal flex shrinking
     preserves the theme's side margins when the sidebars leave less room. */
  .md-content {
    max-width: none;
    display: flex;
    justify-content: center;
    align-items: flex-start;
  }

  .md-content__inner {
    min-width: 0;
    max-width: 28rem;
    width: 100%;
  }
}

.lx-header__nav-link,
.lx-header__repo {
  color: var(--md-default-fg-color--light);
  transition: color 150ms ease;
}

/* The repo icon tints like the links rather than taking the theme's hover
   fade for header buttons. */
.lx-header__nav-link:hover,
.lx-header__nav-link:focus-visible,
.lx-header__repo:hover,
.lx-header__repo:focus-visible {
  color: var(--md-accent-fg-color);
  opacity: 1;
}

/* The theme's usual icon size, up from the 1rem it gives header icons. */
.md-header .lx-header__repo svg {
  width: 1.2rem;
  height: 1.2rem;
}

/* The site or page you are already on. Weight rather than color, which in
   this bar belongs to hover. */
.lx-header__nav-link--active {
  font-weight: 700;
}

/* Every link holds its bold width, so which item is current cannot move the
   rest of the bar. The copy is clipped to zero height and hidden from
   assistive tech, leaving only its width. */
.lx-header__nav-link {
  display: inline-block;
  text-align: center;
}

.lx-header__nav-link::after {
  content: attr(data-label);
  display: block;
  height: 0;
  overflow: hidden;
  visibility: hidden;
  font-weight: 700;
}

/* The bar has no room for these once the title and search collapse onto it.
   Note this leaves blog.lexe.app with no cross-site nav at all below the
   breakpoint, as its drawer lists only post titles. */
@media screen and (max-width: 76.1875em) {
  .lx-header__nav {
    display: none;
  }
}

/* The header's underline is absent at the top of the page and draws out from
   the center once scrolled. A pseudo-element rather than the theme's own
   always-on `box-shadow`, which has no center to grow from. `lx-scrolled` is
   toggled on the document root in `javascripts/extra.js`.

   It clamps on its own rather than riding the bar: at rest it marks the
   bottom of the full bar, headroom included, and it scrolls with the page
   until it meets the bottom of the pinned row. That takes a sticky element
   outside the header, so it is the first item of the column below it, with
   its margins netting to zero so it occupies no space. */
.md-header--shadow {
  box-shadow: none;
}

.md-container::before {
  content: "";
  display: block;
  position: sticky;
  top: calc(var(--lx-header-height-pinned) - 0.05rem);
  z-index: 4;
  height: 0.05rem;
  margin-top: calc(var(--lx-header-headroom) - 0.05rem);
  margin-bottom: calc(-1 * var(--lx-header-headroom));
  background-color: var(--md-default-fg-color--lightest);
  transform: scaleX(0);
  transition: transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1);
}

.lx-scrolled .md-container::before {
  transform: scaleX(1);
}

@media (prefers-reduced-motion: reduce) {
  .md-container::before {
    transition: none;
  }
}

/* The site name reads as a subtitle to the wordmark, so it sits a step off
   the foreground. Clickable to return home; see `javascripts/extra.js`. */
.md-header__title {
  color: var(--lx-grey-300);
  cursor: pointer;
}

/* A step below the theme's bold, which reads as heavy at this size. */
.md-header__title .md-header__topic:first-child {
  font-weight: 600;
  pointer-events: auto;
}

/* Make drawer title clickable (extends logo link over "Blog" text) */
.md-nav__title[for="__drawer"] {
  position: relative;
}

.md-nav__title[for="__drawer"] .md-nav__button.md-logo::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
}

/* The footer strip takes the sidebar's highlight - the same fill the nav gives
   the current page - in place of the theme's `--md-default-fg-color--lightest`,
   a neutral grey that reads as unrelated to the rest of the palette. */
.md-footer-meta {
  background-color: var(--md-accent-fg-color--transparent);
}
