/* Pysae brand theme, aligned with web.pysae.com.
 *
 * Brand values come from the site's own CSS custom properties:
 *   --primary   #00b871   --secondary #0a4b4d   --soft-bg #e6faf3
 * Typeface is Poppins, self-hosted here: the documentation must not fetch any
 * external host, and the marketing site loads it from Google Fonts.
 */

/* Poppins — SIL Open Font License 1.1, subset to latin and latin-ext. */
@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 400;
  font-display: optional;
  src: url("../fonts/poppins-400-latin.woff2") format("woff2");
  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+2074, U+20AC,
    U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 400;
  font-display: optional;
  src: url("../fonts/poppins-400-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F,
    U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 600;
  font-display: optional;
  src: url("../fonts/poppins-600-latin.woff2") format("woff2");
  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+2074, U+20AC,
    U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 600;
  font-display: optional;
  src: url("../fonts/poppins-600-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F,
    U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 700;
  font-display: optional;
  src: url("../fonts/poppins-700-latin.woff2") format("woff2");
  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+2074, U+20AC,
    U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 700;
  font-display: optional;
  src: url("../fonts/poppins-700-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F,
    U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}

:root {
  --pysae-primary: #00b871;
  --pysae-secondary: #0a4b4d;
  --pysae-soft-bg: #e6faf3;
  /* Darkened primary: #00b871 only reaches 2.6:1 on white, below the 4.5:1
     WCAG AA floor for body text, so links use this 5:1 variant instead. */
  --pysae-primary-readable: #00814f;

  /* The theme builds --md-text-font-family from --md-text-font, so setting the
     composed property directly would be overwritten. */
  --md-text-font: "Poppins";
}

[data-md-color-scheme="default"] {
  --md-primary-fg-color: var(--pysae-secondary);
  --md-primary-fg-color--light: #14676a;
  --md-primary-fg-color--dark: #063234;
  --md-primary-bg-color: #fff;
  --md-primary-bg-color--light: #ffffffb3;

  --md-accent-fg-color: var(--pysae-primary);
  --md-accent-fg-color--transparent: #00b8711a;
  --md-accent-bg-color: #fff;

  --md-typeset-a-color: var(--pysae-primary-readable);
}

/* On the dark scheme the brand green sits on a dark surface, where it clears
   AA on its own — no darkened variant needed. */
[data-md-color-scheme="slate"] {
  --md-primary-fg-color: #063234;
  --md-primary-fg-color--light: var(--pysae-secondary);
  --md-primary-fg-color--dark: #041f20;

  --md-accent-fg-color: var(--pysae-primary);
  --md-accent-fg-color--transparent: #00b8711a;

  --md-typeset-a-color: var(--pysae-primary);
}

/* Headings mirror the marketing site: heavy Poppins in the deep teal, with the
   tight tracking the brand uses on display text. */
.md-typeset h1,
.md-typeset h2,
.md-typeset h3 {
  font-weight: 600;
  letter-spacing: -0.01em;
}

[data-md-color-scheme="default"] .md-typeset h1,
[data-md-color-scheme="default"] .md-typeset h2 {
  color: var(--pysae-secondary);
}

.md-typeset h1 {
  font-weight: 700;
}

/* Brand-tinted admonitions and quotes, echoing the site's soft green blocks. */
[data-md-color-scheme="default"] .md-typeset .admonition.info,
[data-md-color-scheme="default"] .md-typeset details.info {
  background-color: var(--pysae-soft-bg);
}

.md-typeset .md-button--primary {
  background-color: var(--pysae-primary);
  border-color: var(--pysae-primary);
  color: #fff;
}

/* Header selectors, one per variant axis. The theme's .8rem reads oversized for
   dropdown entries, all the more with several selectors side by side. */
.md-select__list {
  font-size: 0.65rem;
}

/* The theme closes a menu on a delay (opacity .25s late, max-height half a
   second late). With adjacent selectors, moving from one to the next
   leaves the previous menu on screen under the one opening. Closing without a
   delay keeps a single menu visible at a time. */
.md-select__inner {
  transition: transform 0.2s, opacity 0.2s, max-height 0ms;
}

/* The language selector shows a flag rather than an icon, so it carries a text
   glyph where its neighbours carry an SVG. Sized to the box the theme gives
   those, so the buttons keep the same footprint in the header. */
.pysae-header-flag > span {
  align-items: center;
  display: flex;
  font-size: 0.85rem;
  height: 1rem;
  justify-content: center;
  line-height: 1;
  width: 1rem;
}

/* One menu at a time: as soon as a selector is hovered or focused, every other
   menu closes instantly instead of lingering under the one opening. Both
   combinators are needed — `~` reaches the siblings that follow the active
   selector, `:has()` those that precede it. */
.md-header__option:hover ~ .md-header__option .md-select__inner,
.md-header__option:focus-within ~ .md-header__option .md-select__inner,
.md-header__option:has(~ .md-header__option:hover) .md-select__inner,
.md-header__option:has(~ .md-header__option:focus-within) .md-select__inner {
  max-height: 0;
  opacity: 0;
  transition: none;
}

/* The theme moves every table into a scrolling wrapper once its script runs,
   and that wrapper is taller than the bare table it replaces: the inline-block
   sits on a text baseline, which reserves descender space under it, and it
   carries a bottom margin the bare table does not have. Everything below the
   table therefore dropped by ~10px on each load, once the wrapper appeared.

   The gap is the descender space of the line box the inline-block sits in, so
   it goes away with that line's strut. Zeroing the font size on the wrapper
   collapses the strut and nothing else: the table declares its own size. */
.md-typeset__scrollwrap {
  font-size: 0;
}

.md-typeset__table {
  margin-bottom: 0;
}

/* The theme tightens the margin of whatever follows a table
   (`table:not([class])+*`), but that selector only holds while the table is
   still a sibling — its own script then wraps the table and the rule silently
   stops applying, so the rest of the page moved up. Carrying it over to the
   wrapper keeps the spacing the theme asked for, in both states.

   Scoped to the article so it outweighs the theme's own `.md-typeset h2`,
   which is what the original rule has to beat too. */
.md-typeset .md-typeset__scrollwrap + * {
  margin-top: 1.5em;
}

/* Until it is wrapped the table is inline-level, so the line it sits on
   reserves descender space underneath it — space the wrapped table does not
   take. Making it a block up front removes that difference too. */
.md-typeset > table:not([class]) {
  display: table;
}

/* Chips naming the variant being read, detached above the table of contents and
   indented to line up with its entries, so they read as a marker on the page
   rather than as a heading of the contents below them. */
.pysae-variant-chips {
  align-items: center;
  /* Lifted above the contents that follow it in the document: without this the
     table of contents paints over the open menu. */
  position: relative;
  z-index: 2;
  /* The theme includes this partial twice — once for the sidebar, once folded
     under the active item of the mobile drawer. The chips belong to the first. */
  display: none;
  /* One line: stacked, the chips read as a list to work through rather than as
     one marker. They are flush with the column instead of indented onto the
     contents entries — three labels and their gaps need every pixel of a
     12.1rem column. */
  align-items: center;
  flex-wrap: nowrap;
  gap: 0.2rem;
  margin-bottom: 0.9rem;
  /* Matches where the contents entries start: their own padding, plus the
     offset of the navigation box that holds them. */
  padding-left: 0.8rem;
}

.md-sidebar--secondary .pysae-variant-chips {
  display: flex;
}

/* The flag rides in a chip like its neighbours, glyph alone — three labels side
   by side do not clear a 12.1rem column. The name it stands for is on the
   button as its accessible name, since a flag sequence is read poorly aloud and
   falls back to two letters where it is not drawn at all. */
.pysae-variant-chip--flag {
  padding-left: 0.3rem;
  padding-right: 0.3rem;
}

/* Each chip carries the icon its axis has in the header, so the two places are
   recognisably the same control. Sized to the label rather than to the theme's
   header button, which is built for a much larger target. */
.pysae-variant-chip__icon svg {
  display: block;
  height: 0.7rem;
  width: 0.7rem;
}

/* Emoji are drawn taller than label text at the same size. Pinning the glyph's
   line box to the label's keeps the pill the height of its neighbours rather
   than stretching it. */
.pysae-variant-chip__flag {
  font-size: 0.6rem;
  /* Set to the line box a label produces at this chip's size, so the flag chip
     is exactly as tall as the ones beside it. */
  line-height: 0.85rem;
}

/* Outlined rather than filled: these name the build, they are not a call to
   action. The brand green is taken in its darkened form, since the raw one
   reaches 2.59:1 on white — the same reason links already use it. */
.pysae-variant-chip {
  align-items: center;
  background-color: transparent;
  border: 0.05rem solid var(--pysae-primary-readable);
  display: inline-flex;
  gap: 0.2rem;
  border-radius: 1rem;
  color: var(--pysae-primary-readable);
  font-size: 0.55rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  /* Kept tight: up to three of these and their gaps have to clear a 12.1rem column,
     and the longest labels leave little room. */
  padding: 0.1rem 0.4rem;
  white-space: nowrap;
}

/* Last resort, and only that: a longer translation, or a font whose metrics run
   wider than the ones measured against, would otherwise push the row past the
   column. Trimming a label beats overflowing. */
.pysae-variant-chips .md-select {
  min-width: 0;
}

.pysae-variant-chip {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}


/* On a dark surface the raw brand green is the legible one. */
[data-md-color-scheme="slate"] .pysae-variant-chip {
  border-color: var(--pysae-primary);
  color: var(--pysae-primary);
}

/* The first chip names the primary axis (on a multi-language API reference, the
   language the examples are written in) and is the only one worth the brand
   colour. The chips after it are metadata beside it, so they stay neutral
   instead of competing with it, and so does a flag wherever it sits. It also
   settles the brand teal, which has no tone that separates from the dark scheme
   without inventing one. */
.pysae-variant-chips > .md-select:not(:first-child) .pysae-variant-chip,
.pysae-variant-chip--flag,
[data-md-color-scheme="slate"] .pysae-variant-chips > .md-select:not(:first-child) .pysae-variant-chip,
[data-md-color-scheme="slate"] .pysae-variant-chip--flag {
  border-color: var(--md-default-fg-color--lighter);
  color: var(--md-default-fg-color--light);
}

/* The entry matching the current variant, so each menu shows what is active. */
.md-select__link[aria-current="true"] {
  color: var(--md-accent-fg-color);
  font-weight: 600;
}

/* Inert selector entries: the dev server holds a single variant, so switching
   is shown as unavailable rather than linking to a path it does not serve. */
.md-select__link--inert {
  cursor: default;
  opacity: 0.5;
}

.md-select__link--inert[aria-current="true"] {
  opacity: 1;
}

/* The search box stopped at the content column while the header ran on to the
   width of the contents beside it, leaving the field narrow for no reason.

   Scoped to the breakpoint where that contents column appears — below it the
   header lays the search out differently. */
@media screen and (min-width: 76.25em) {
  /* The header keeps a column for the repository link. When none is configured
     it renders without content and holds width the search can have. Matched on
     having no child element rather than `:empty`, which the template's own
     whitespace already defeats — and it gives the column back the moment a
     repository is set. */
  .md-header__source:not(:has(*)) {
    display: none;
  }

  .md-search {
    /* Sized and pushed to sit over the contents column rather than stretching
       across the header. The sidebar is 12.1rem, less the .4rem the header
       already insets on that side, so the field starts where that column
       starts. */
    flex: 0 0 auto;
    margin-left: auto;
    width: 11.7rem;
  }

  .md-search__button {
    width: 100%;
  }
}

/* Pages differ in length, so the viewport scrollbar came and went as you moved
   between them and the page jumped by its width each time — 15px here, which is
   what reads as the scrollbar flickering.

   The theme asks for `scrollbar-gutter: stable`, which should reserve the space
   whether or not the page overflows, but the gutter is measurably not reserved.
   Declaring the scrollbar always present holds the width steady regardless. */
html {
  overflow-y: scroll;
}

/* The chips are buttons: they open the same menus the header does, through the
   theme's own `.md-select`, which reveals its list on hover and on focus — so a
   click opens one without a line of script. */
.pysae-variant-chip {
  cursor: pointer;
  font-family: inherit;
}

/* Each menu hangs from whichever edge of its chip leaves it room, since the row
   spans a narrow column. Anchored by position, whatever the axis: the first
   chip (or a lone one) grows rightwards from its left edge, the last leftwards
   from its right edge, and any between them opens centred.

   The horizontal share of the transform is held in a variable because the
   translate also carries the open/close animation, which is the same for every
   chip and stated once below. */
.pysae-variant-chips .md-select__inner {
  --pysae-menu-shift: 0;
  left: 0;
  transform: translate3d(var(--pysae-menu-shift), 0.3rem, 0);
}

.pysae-variant-chips > .md-select:not(:first-child):not(:last-child) .md-select__inner {
  --pysae-menu-shift: -50%;
  left: 50%;
}

.pysae-variant-chips > .md-select:last-child:not(:first-child) .md-select__inner {
  left: auto;
  right: 0;
}

.pysae-variant-chips .md-select:focus-within .md-select__inner,
.pysae-variant-chips .md-select:hover .md-select__inner {
  transform: translate3d(var(--pysae-menu-shift), 0, 0);
}

/* Stacked, a menu opens over the chips beneath it, which follow it in the
   document and would otherwise paint on top. The open one is raised for as
   long as it is open. */
.pysae-variant-chips .md-select:focus-within,
.pysae-variant-chips .md-select:hover {
  z-index: 3;
}

/* One menu at a time here as well, on the same reasoning as the header. */
.pysae-variant-chips .md-select:hover ~ .md-select .md-select__inner,
.pysae-variant-chips .md-select:focus-within ~ .md-select .md-select__inner,
.pysae-variant-chips .md-select:has(~ .md-select:hover) .md-select__inner,
.pysae-variant-chips .md-select:has(~ .md-select:focus-within) .md-select__inner {
  max-height: 0;
  opacity: 0;
  transition: none;
}

/* Diagrams, all framed by a `figure.pysae-diagram`: either an inline SVG written
   in the page and painted by the d-* classes below, or a Mermaid diagram the
   theme renders into the shadow root of a `.mermaid` host, which
   javascripts/diagram-actions.js wraps in such a figure as the canvas. Colours
   come from the theme variables, so a diagram follows the light / dark switch
   like the text. */
/* The theme sizes a figure to its content, which an SVG without intrinsic size
   shrinks to the default 300px. Its selector (`.md-typeset figure`) outweighs a
   lone class, hence the same prefix here. */
.md-typeset .pysae-diagram {
  margin: 1.5em 0;
  text-align: center;
  width: 100%;
}

/* The Mermaid SVG sits in a shadow root and is sized from there by the script —
   a rule here can only reach its host. */
.md-typeset .pysae-diagram__canvas {
  display: block;
  width: 100%;
}

.pysae-diagram svg {
  display: block;
  height: auto;
  width: 100%;
  font-family: inherit;
}

.pysae-diagram figcaption {
  color: var(--md-default-fg-color--light);
  font-size: 0.7rem;
}

.pysae-diagram .d-title,
.pysae-diagram .d-name {
  fill: var(--md-default-fg-color);
  font-weight: 600;
}

.pysae-diagram .d-title {
  font-size: 15px;
}

.pysae-diagram .d-name {
  font-size: 13px;
}

.pysae-diagram .d-line {
  fill: var(--md-default-fg-color--light);
  font-size: 11.5px;
}

.pysae-diagram .d-label {
  fill: var(--md-default-fg-color);
  font-size: 11.5px;
}

.pysae-diagram .d-box {
  fill: var(--md-default-bg-color);
  stroke: var(--md-default-fg-color--lighter);
  stroke-width: 1.25;
}

.pysae-diagram .d-zone {
  fill: var(--md-code-bg-color);
  stroke: var(--md-default-fg-color--lighter);
  stroke-width: 1;
}

.pysae-diagram .d-accent {
  fill: var(--md-accent-fg-color--transparent);
  stroke: var(--pysae-primary);
  stroke-width: 2;
}

.pysae-diagram .d-edge {
  fill: none;
  stroke: var(--md-default-fg-color--light);
  stroke-width: 1.25;
}

.pysae-diagram .d-life {
  stroke: var(--md-default-fg-color--lighter);
  stroke-dasharray: 4 4;
  stroke-width: 1;
}

.pysae-diagram .d-rule {
  stroke: var(--md-default-fg-color--lightest);
  stroke-width: 1;
}

.pysae-diagram .d-fail {
  fill: none;
  stroke: #e8453c;
  stroke-width: 1.5;
}

.pysae-diagram .d-arrowhead {
  fill: var(--md-default-fg-color--light);
}

/* Diagram action bar (full screen, export), added by
   javascripts/diagram-actions.js. */
.pysae-diagram__actions {
  align-items: center;
  display: flex;
  gap: 0.4rem;
  justify-content: flex-end;
  margin-top: 0.4rem;
}

.pysae-diagram__action {
  align-items: center;
  background: none;
  border: 1px solid var(--md-default-fg-color--lightest);
  border-radius: 0.2rem;
  color: var(--md-default-fg-color--light);
  cursor: pointer;
  display: inline-flex;
  font-size: 0.62rem;
  gap: 0.25rem;
  padding: 0.2rem 0.5rem;
  transition:
    color 125ms,
    background-color 125ms,
    border-color 125ms;
}

/* Same hover as the theme's back-to-top button: the accent colour fills the
   control and the label flips to read on it. */
.pysae-diagram__action:hover,
.pysae-diagram__action:focus-visible {
  background-color: var(--md-accent-fg-color);
  border-color: var(--md-accent-fg-color);
  color: var(--md-accent-bg-color);
}

.pysae-diagram__action svg {
  height: 0.9rem;
  width: 0.9rem;
}

/* The export menu opens upwards, the only direction with room to spare both in
   the page and full screen, where the bar sits at the bottom. */
.pysae-diagram__menu {
  position: relative;
}

.pysae-diagram__menu--open .pysae-diagram__action svg:last-child {
  transform: rotate(180deg);
}

.pysae-diagram__formats {
  background-color: var(--md-default-bg-color);
  border: 1px solid var(--md-default-fg-color--lightest);
  border-radius: 0.2rem;
  bottom: calc(100% + 0.2rem);
  box-shadow: var(--md-shadow-z1);
  display: none;
  flex-direction: column;
  position: absolute;
  right: 0;
  z-index: 2;
}

.pysae-diagram__menu--open .pysae-diagram__formats {
  display: flex;
}

.pysae-diagram__format {
  background: none;
  border: none;
  color: var(--md-default-fg-color--light);
  cursor: pointer;
  font-size: 0.62rem;
  padding: 0.3rem 0.9rem;
  text-align: left;
  white-space: nowrap;
}

.pysae-diagram__format:hover,
.pysae-diagram__format:focus-visible {
  color: var(--md-accent-fg-color);
}

/* Full screen: the browser paints the backdrop black, so the frame brings the
   theme's own background, and the diagram takes the whole screen.

   The bar floats over the diagram rather than sitting under it, where it would
   take height from what the reader went full screen to enlarge. Dimmed until
   pointed at, so it stays out of the way while remaining findable. */
.md-typeset .pysae-diagram:fullscreen {
  background-color: var(--md-default-bg-color);
  margin: 0;
  padding: 1rem;
}

.pysae-diagram:fullscreen svg {
  height: 100%;
}

/* The theme's own margin on a `.mermaid` host would push a full-height canvas
   past the bottom of the screen. */
.md-typeset .pysae-diagram:fullscreen .pysae-diagram__canvas {
  height: 100%;
  margin: 0;
}

.pysae-diagram:fullscreen figcaption {
  display: none;
}

.pysae-diagram:fullscreen .pysae-diagram__actions {
  bottom: 0.6rem;
  margin: 0;
  opacity: 0.4;
  position: absolute;
  right: 0.6rem;
  transition: opacity 125ms;
}

.pysae-diagram:fullscreen .pysae-diagram__actions:hover,
.pysae-diagram:fullscreen .pysae-diagram__actions:focus-within {
  opacity: 1;
}

/* Opaque, so the controls stay readable over whatever the diagram puts there —
   restated on hover, this selector outweighing the one that fills it. */
.pysae-diagram:fullscreen .pysae-diagram__action {
  background-color: var(--md-default-bg-color);
}

.pysae-diagram:fullscreen .pysae-diagram__action:hover,
.pysae-diagram:fullscreen .pysae-diagram__action:focus-visible {
  background-color: var(--md-accent-fg-color);
}

/* Exporting is a thing you do from the page; full screen keeps the one control
   it needs. */
.pysae-diagram:fullscreen .pysae-diagram__menu {
  display: none;
}
