/* droidspaces.org. Every value here is in DESIGN.md or tokens.css; the only hex literals live in tokens.css. */
@import url("tokens.css?v=d788300275");

@font-face {
  font-family: "IBM Plex Sans";
  src: url("../fonts/IBMPlexSans.woff2?v=ad7968b62a") format("woff2");
  font-weight: 400 700;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("../fonts/JetBrainsMono.woff2?v=ad5c3871b2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Material Symbols Rounded";
  src: url("../fonts/MaterialSymbolsRounded.woff2?v=b66606f76c") format("woff2");
  font-weight: 400;
  font-display: block; /* an icon glyph shown as its ligature text is worse than a blank */
}

/* base */
*, *::before, *::after { box-sizing: border-box; }
/* no blue tap box on touch screens: every control has its own press state (the button morph, state layers) */
html { -webkit-tap-highlight-color: transparent; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--md-sys-color-surface);
  color: var(--md-sys-color-on-surface);
  font: 400 16px/24px "IBM Plex Sans", sans-serif;
  letter-spacing: 0.5px;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--md-sys-color-primary); }
main p a, main li a { color: inherit; text-decoration: underline; } /* a link in prose is not the section's one accent */
:focus-visible { outline: 2px solid var(--md-sys-color-primary); outline-offset: 2px; }
p { max-width: 68ch; }

/* scrollbars, every one on the site: 8px, no arrow buttons, a rounded outline-variant thumb on a
   transparent track, so a scroller reads as part of the page and not as browser chrome.
   Chromium and Safari get the ::-webkit-scrollbar parts, which draw no arrows. Only Firefox gets
   scrollbar-width and scrollbar-color: Chromium honours those too, but then ignores the parts
   below and draws its arrow buttons */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { background: transparent; }
::-webkit-scrollbar-thumb { border: 2px solid transparent; border-radius: var(--md-sys-shape-corner-full); background: var(--md-sys-color-outline-variant) padding-box; }
::-webkit-scrollbar-thumb:hover { background-color: var(--md-sys-color-outline); }
::-webkit-scrollbar-button { display: none; }
@supports (-moz-appearance: none) {
  * { scrollbar-width: thin; scrollbar-color: var(--md-sys-color-outline-variant) transparent; }
}

/* type scale: M3 sizes; emphasis is one weight step up, body stays at 400 (DESIGN.md Typography) */
h1, h2, h3, h4 { margin: 0; letter-spacing: 0; }
h1, .display-lg { font-size: 57px; line-height: 64px; font-weight: 700; }
h2, .headline-lg { font-size: 32px; line-height: 40px; font-weight: 700; }
h3, .title-md { font-size: 16px; line-height: 24px; font-weight: 600; letter-spacing: 0.15px; }
.title-lg { font-size: 22px; line-height: 28px; font-weight: 400; color: var(--md-sys-color-on-surface-variant); }
.body-md, small { font-size: 14px; line-height: 20px; letter-spacing: 0.25px; color: var(--md-sys-color-on-surface-variant); }
.label-lg { font-size: 14px; line-height: 20px; font-weight: 600; letter-spacing: 0.1px; }
.label-md { font-size: 12px; line-height: 16px; font-weight: 500; letter-spacing: 0.5px; }
code, pre, kbd, .code { font: 400 14px/22px "JetBrains Mono", monospace; letter-spacing: 0; }
/* no one-word last line under a headline, no orphan at the end of a paragraph */
h1, h2, h3 { text-wrap: balance; }
p { text-wrap: pretty; }
/* display-md under 600, and from 840 to 1199 where the hero's 5fr column is too narrow for 57px */
@media (max-width: 599px), (min-width: 840px) and (max-width: 1199px) { h1, .display-lg { font-size: 45px; line-height: 52px; } }

/* icon: Material Symbols Rounded, FILL 0 at rest (DESIGN.md Buttons) */
.icon {
  font-family: "Material Symbols Rounded";
  font-weight: 400;
  font-size: 24px;
  line-height: 1;
  letter-spacing: 0; /* the glyphs are ligatures, and any letter-spacing turns ligatures off */
  display: inline-block;
  font-variation-settings: "FILL" 0, "opsz" 24;
  user-select: none;
}

/* button: 56px pill, morphs to 12px on press over Google's expressive curve */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 56px;
  padding: 0 24px;
  border: 0;
  /* a pill at 56px. Not corner-full: 9999px morphing to 12 on an overshooting curve passes below
     zero and clamps, so the corners go square and jagged for a frame */
  border-radius: var(--md-sys-shape-corner-xl);
  font: inherit;
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  letter-spacing: 0.1px;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: border-radius var(--motion-press), background-color var(--motion-effects-fast);
}
.button .icon { font-size: 20px; font-variation-settings: "FILL" 0, "opsz" 20; }
.button:active { border-radius: var(--md-sys-shape-corner-md); }
.button-filled { background: var(--md-sys-color-primary); color: var(--md-sys-color-on-primary); }
.button-filled:hover { background: color-mix(in srgb, var(--md-sys-color-primary), var(--md-sys-color-on-primary) 8%); }
.button-tonal { background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); }
.button-tonal:hover { background: color-mix(in srgb, var(--md-sys-color-secondary-container), var(--md-sys-color-on-secondary-container) 8%); }
.button-outlined { background: transparent; color: var(--md-sys-color-on-surface-variant); border: 1px solid var(--md-sys-color-outline-variant); }
.button-outlined:hover { background: color-mix(in srgb, transparent, var(--md-sys-color-on-surface) 8%); }
.button:disabled, .button[aria-disabled="true"] {
  background: color-mix(in srgb, transparent, var(--md-sys-color-on-surface) 12%);
  color: color-mix(in srgb, transparent, var(--md-sys-color-on-surface) 38%);
  pointer-events: none;
}
/* back to top: the one icon-only button, tonal so the section's filled button stays the one primary */
.to-top { position: fixed; right: 16px; bottom: 16px; z-index: 9; width: 56px; padding: 0; }
.to-top .icon { font-size: 24px; font-variation-settings: "FILL" 0, "opsz" 24; }
@supports (animation-timeline: scroll()) {
  /* hidden until a viewport of content has scrolled past, without JavaScript. Without scroll
     timelines the button is simply always there, which is still a working page */
  @keyframes to-top-show { from { opacity: 0; visibility: hidden; } }
  .to-top { animation: to-top-show linear both; animation-timeline: scroll(root); animation-range: 100vh calc(100vh + 64px); }
}

/* icon button: theme toggle, copy, drawer. 48px is the tap minimum design does not override */
.icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border: 0;
  border-radius: var(--md-sys-shape-corner-full);
  background: transparent;
  color: var(--md-sys-color-on-surface-variant);
  cursor: pointer;
  transition: background-color var(--motion-effects-fast);
}
.icon-button:hover { background: color-mix(in srgb, transparent, var(--md-sys-color-on-surface) 8%); color: var(--md-sys-color-on-surface); }

/* chip: facts in a row, wraps, never scrolls */
.chip-set { display: flex; flex-wrap: wrap; gap: 8px; padding: 0; margin: 0; list-style: none; }
.chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 40px;
  padding: 0 20px;
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-full);
  background: var(--md-sys-color-surface-container-high);
  color: var(--md-sys-color-on-surface);
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  letter-spacing: 0.1px;
  white-space: nowrap;
  transition: background-color var(--motion-effects-default), color var(--motion-effects-default), border-color var(--motion-effects-default);
}
.chip:hover { background: var(--md-sys-color-secondary-container); border-color: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); }
.chip[aria-pressed="true"], .chip-selected { background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); }

/* boot chain: the one sequence on the home page besides install steps. Ring markers on a rule, no
   numerals and no primary, so it reads as a chain of events rather than a numbered feature list */
.boot-chain { display: grid; gap: 24px; margin: 0 0 32px; padding: 0; list-style: none; }
.boot-chain li { position: relative; padding-left: 32px; }
.boot-chain li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 6px; /* centres the 12px ring on the h3's 24px line */
  width: 12px;
  height: 12px;
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-full);
  background: var(--md-sys-color-surface);
}
.boot-chain li:not(:last-child)::after {
  /* the rule runs from under this ring to the top of the next one, across the 24px gap */
  content: "";
  position: absolute;
  left: 5.5px;
  top: 18px;
  bottom: -30px;
  width: 1px;
  background: var(--md-sys-color-outline-variant);
}
.boot-chain p { margin: 4px 0 0; }
/* the chain fills as it moves up the screen: each ring turns primary in order and the rule between
   them fills. Scroll-linked, so it pauses when the reader stops and runs back if they scroll back.
   The section's one primary; where view timelines are missing it stays drawn and unfilled */
.chain-fill {
  position: absolute;
  left: 5.5px;
  top: 18px;
  bottom: -30px;
  z-index: 1;
  width: 1px;
  background: var(--md-sys-color-primary);
  transform-origin: top;
  scale: 1 0;
}
@supports (animation-timeline: view()) {
  .boot-chain { view-timeline: --chain; }
  @keyframes chain-fill { to { scale: 1 1; } }
  @keyframes chain-ring { to { border-color: var(--md-sys-color-primary); background: var(--md-sys-color-primary); } }
  .boot-chain .chain-fill, .boot-chain li::before { animation: linear both; animation-timeline: --chain; }
  .boot-chain li::before { z-index: 2; animation-name: chain-ring; }
  .boot-chain .chain-fill { animation-name: chain-fill; }
  .boot-chain li:nth-child(1)::before { animation-range: contain 0% contain 5%; }
  .boot-chain li:nth-child(1) .chain-fill { animation-range: contain 5% contain 30%; }
  .boot-chain li:nth-child(2)::before { animation-range: contain 30% contain 35%; }
  .boot-chain li:nth-child(2) .chain-fill { animation-range: contain 35% contain 60%; }
  .boot-chain li:nth-child(3)::before { animation-range: contain 60% contain 65%; }
}

/* card: tonal depth only, one border, no shadow. Hover steps one surface tier */
.card {
  padding: 24px;
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-xl);
  background: var(--md-sys-color-surface-container);
  color: var(--md-sys-color-on-surface);
  transition: background-color var(--motion-effects-default);
}
.card:hover { background: var(--md-sys-color-surface-container-highest); }
.card-nested {
  padding: 16px;
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-lg);
  background: var(--md-sys-color-surface-container-high);
}
.card > :first-child { margin-top: 0; }
.card > :last-child { margin-bottom: 0; }

/* code block: the one place the site goes fully black in dark mode, like the app's terminal */
.code-block { position: relative; margin: 0 0 16px; }
.code-block pre, pre {
  margin: 0;
  padding: 20px;
  border-radius: var(--md-sys-shape-corner-lg);
  background: var(--md-sys-color-surface-container-lowest);
  color: var(--md-sys-color-on-surface);
  overflow-x: auto;
  tab-size: 4;
}
.copy-btn { position: absolute; top: 8px; right: 8px; }
:not(pre) > code {
  padding: 0 4px;
  border-radius: var(--md-sys-shape-corner-xs);
  background: var(--md-sys-color-surface-container-high);
}

/* nav: the M3 top app bar. Opaque, no shade and no rule: a surface of the page that steps one tier
   when content scrolls under it, the way the app's own bar does */
.nav {
  position: sticky;
  top: 0;
  z-index: 10;
  height: 64px;
  background: var(--md-sys-color-surface);
}
/* the scrolled tier is a layer behind the content, faded in by opacity: a colour token inside the
   keyframes does not follow the theme toggle in Chromium, a layer's opacity does */
.nav::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--md-sys-color-surface-container);
  opacity: 0;
}
.nav-inner {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 24px;
}
.nav-logo { display: inline-flex; align-items: center; gap: 8px; margin-right: auto; color: var(--md-sys-color-on-surface); text-decoration: none; font-weight: 600; }
.nav-logo svg { width: 24px; height: 24px; color: var(--md-sys-color-primary); }
.nav-links { display: flex; align-items: center; gap: 8px; margin: 0; padding: 0; list-style: none; }
.nav-links a {
  display: inline-flex;
  align-items: center;
  height: 48px;
  padding: 0 16px;
  border-radius: var(--md-sys-shape-corner-full);
  color: var(--md-sys-color-on-surface-variant);
  text-decoration: none;
  font-size: 14px;
  line-height: 20px;
  font-weight: 600;
  letter-spacing: 0.1px;
  transition: background-color var(--motion-effects-fast), color var(--motion-effects-fast);
}
.nav-links a:hover { background: color-mix(in srgb, transparent, var(--md-sys-color-on-surface) 8%); color: var(--md-sys-color-on-surface); }
.nav-links a[aria-current="page"] { color: var(--md-sys-color-primary); }
.nav-menu, .nav-close { display: none; }
/* skip link: the first tab stop, off screen until focused, so a keyboard user can pass the nav */
.skip-link { position: absolute; z-index: 11; top: 4px; left: 16px; translate: 0 -200%; }
.skip-link:focus { translate: 0 0; }
.nav-drawer { display: contents; } /* the drawer is a popover; above 840 its links sit in the bar */
/* the toggle shows the scheme you would switch to; picked in CSS so it is right before any script runs */
.theme-toggle .icon::before { content: "light_mode"; }
@media (prefers-color-scheme: light) { :root:not([data-theme="dark"]) .theme-toggle .icon::before { content: "dark_mode"; } }
:root[data-theme="light"] .theme-toggle .icon::before { content: "dark_mode"; }
@supports (animation-timeline: scroll()) {
  /* the M3 on-scroll container colour, without JavaScript. Without scroll timelines the bar stays at surface */
  @keyframes nav-scrolled { to { opacity: 1; } }
  .nav::before { animation: nav-scrolled linear both; animation-timeline: scroll(root); animation-range: 0 64px; }
}
@media (max-width: 839px) {
  .nav-menu, .nav-close { display: inline-flex; }
  /* the M3 modal navigation drawer: 360 wide, corner-lg on its open edge, no border, the scrim does the separating */
  .nav-drawer {
    display: none;
    position: fixed;
    inset: 0 0 0 auto;
    width: min(360px, 100%);
    height: 100dvh;
    margin: 0;
    padding: 64px 12px 12px;
    border: 0;
    border-radius: var(--md-sys-shape-corner-lg) 0 0 var(--md-sys-shape-corner-lg);
    background: var(--md-sys-color-surface-container-low);
    color: var(--md-sys-color-on-surface);
    translate: 100% 0;
    transition: translate var(--motion-spatial-default), display var(--motion-spatial-default) allow-discrete, overlay var(--motion-spatial-default) allow-discrete;
  }
  .nav-drawer:popover-open { display: block; translate: 0 0; }
  @starting-style { .nav-drawer:popover-open { translate: 100% 0; } }
  .nav-drawer::backdrop { background: color-mix(in srgb, transparent, var(--md-sys-color-surface-container-lowest) 38%); }
  .nav-drawer .nav-links { flex-direction: column; align-items: stretch; }
  .nav-drawer .nav-links a { height: 56px; padding: 0 16px; }
  /* the drawer's active indicator is a fill, as in M3; in the bar the current link is primary text */
  .nav-drawer .nav-links a[aria-current="page"] { background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container); }
  .nav-close { position: absolute; top: 8px; right: 16px; }
}

/* phone mockup: Galaxy S25 Ultra silhouette (DESIGN.md Shapes). The wrapper only sizes it, so every
   length below is a cqw share of the frame width. The frame is one painted shape with the display
   on top of it, never a border, so no page background can show through at the corners */
.phone { container-type: inline-size; width: 100%; }
.phone-screen {
  position: relative;
  padding: 1.75cqw;
  border-radius: 7.8cqw;
  background: var(--phone-frame);
  box-shadow: var(--shadow-phone); /* the only shadow on the site: a photographed object, not a surface */
}
/* the screenshots stop at the app's title bar, so the status bar and the gesture bar are drawn in the app's own colours */
.phone-display {
  position: relative;
  padding: 7cqw 0 5cqw;
  border-radius: 6.2cqw;
  background: var(--phone-app-surface);
  color: var(--phone-app-on-surface);
  overflow: hidden;
}
.phone-display[data-bar] { padding-bottom: 0; border-bottom: 5cqw solid var(--phone-app-bar); } /* the screenshot ends in the app's bottom bar */
.phone-display img { display: block; width: 100%; height: auto; aspect-ratio: 1080 / 2142; }
.phone-display::before, .phone-display::after { position: absolute; top: 0; height: 7cqw; display: flex; align-items: center; }
.phone-display::before { content: "12:00"; left: 4.5cqw; font-size: 3.4cqw; font-weight: 500; letter-spacing: 0; }
.phone-display::after {
  /* wifi, signal and battery are the AOSP status bar drawables (assets/phone/), masked so the colour is a token */
  content: "";
  right: 3cqw;
  width: 15cqw;
  background: var(--phone-app-on-surface);
  mask: url(/assets/phone/wifi.svg) left center / 4.2cqw no-repeat, url(/assets/phone/signal.svg) center / 4.2cqw no-repeat, url(/assets/phone/battery.svg) right center / 4.2cqw no-repeat;
}
.phone-screen::before {
  /* gesture pill, centred in the bottom bar, above the display */
  content: "";
  position: absolute;
  z-index: 1;
  bottom: 3.75cqw;
  left: 50%;
  width: 28cqw;
  height: 1cqw;
  translate: -50% 0;
  border-radius: var(--md-sys-shape-corner-full);
  background: color-mix(in srgb, transparent, var(--phone-app-on-surface) 38%);
}
.phone-screen::after {
  /* punch-hole camera, 5.2% of the screen width, centred in the status bar, in the bezel colour, nothing drawn inside */
  content: "";
  position: absolute;
  z-index: 1;
  top: 2.74cqw;
  left: 50%;
  width: 5.02cqw;
  aspect-ratio: 1;
  translate: -50% 0;
  border-radius: var(--md-sys-shape-corner-full);
  background: var(--phone-frame);
}
/* phone group: a swipe carousel by default, an overlapping trio you hover through on a wide pointer screen */
.phone-group {
  display: flex;
  gap: 16px;
  width: 100%;
  margin: -96px 0; /* the padding keeps the phone shadow (40px down, 90px blur) from being cut by the scroller */
  padding: 96px 12%; /* the side padding lets the first and last phone snap to the centre, and the neighbours peek into it */
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
}
.phone-group .phone { flex: 0 0 100%; margin: 0; scroll-snap-align: center; } /* 100% of the padded box, so 76% of the scroller */
/* stacked, the scroller runs edge to edge so its sides never cut a shadow into a visible box */
@media (max-width: 839px) { .phone-group { width: 100vw; margin-inline: calc(50% - 50vw); padding-inline: 12vw; } }
.phone-group .phone:focus-visible { outline: none; }
.phone-group .phone:focus-visible .phone-screen { outline: 2px solid var(--md-sys-color-primary); outline-offset: 4px; }
.phone-group figcaption { margin-top: 24px; text-align: center; transition: opacity var(--motion-effects-default); }
@supports (animation-timeline: view()) {
  /* the phone in the middle of the scroller is full size and captioned, its neighbours step back */
  @keyframes centred { 0%, 100% { scale: 0.85; } 40%, 60% { scale: 1; } }
  @keyframes centred-caption { 0%, 30%, 70%, 100% { opacity: 0; } 45%, 55% { opacity: 1; } }
  .phone-group .phone { animation: centred linear both; animation-timeline: view(inline); }
  .phone-group figcaption { animation: centred-caption linear both; animation-timeline: view(inline); animation-range: cover; }
}
@media (hover: hover) and (min-width: 840px) {
  .phone-group { justify-content: center; gap: 0; margin: 0; padding: 0; overflow: visible; }
  .phone-group .phone { flex-basis: 44%; scale: 0.8; transition: scale var(--motion-spatial-default); }
  .phone-group .phone + .phone { margin-left: -14%; }
  .phone-group figcaption { opacity: 0; }
  /* the front phone: the hovered one, else the focused one, else the middle one. Exactly one at a time */
  .phone-group .phone:hover,
  .phone-group:not(:has(.phone:hover)) .phone:focus-visible,
  .phone-group:not(:has(.phone:hover, .phone:focus-visible)) .phone:nth-child(2) { z-index: 1; scale: 1; }
  .phone-group .phone:hover figcaption,
  .phone-group:not(:has(.phone:hover)) .phone:focus-visible figcaption,
  .phone-group:not(:has(.phone:hover, .phone:focus-visible)) .phone:nth-child(2) figcaption { opacity: 1; }
  /* the side phones fan out from behind the front one when site.js marks the group arrived */
  @keyframes fan-left { from { translate: 60% 0; } }
  @keyframes fan-right { from { translate: -60% 0; } }
  .phone-group .phone, .phone-group figcaption { animation: none; }
  .phone-group.arrived .phone:first-child { animation: fan-left var(--motion-spatial-slow) both; }
  .phone-group.arrived .phone:last-child { animation: fan-right var(--motion-spatial-slow) both; }
}

/* stage: a phone or phone group with the section's one decorative shape behind it */
.stage {
  position: relative;
  display: flex;
  justify-content: center;
  /* the shape's z-index: -1 stays inside the stage, behind the phone. Isolating the section instead
     would cut the phone shadow off where the next section starts */
  isolation: isolate;
}
.stage > .phone { width: min(100%, 380px); z-index: 1; }
.shape {
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(640px, 100%); /* never wider than its own column, so it cannot reach under the copy */
  aspect-ratio: 1;
  translate: -50% -50%;
  background: var(--md-sys-color-tertiary-container);
  mask: var(--shape) center / contain no-repeat;
  z-index: -1; /* positioned and 640px wide, it would otherwise paint over the copy column beside it */
}
.shape-secondary { background: var(--md-sys-color-secondary-container); }
/* stacked, the shape stays inside the screen (a clipped shape reads as a rendering fault) and the
   phone gives up a quarter of the column, so the shape still frames it on both sides */
@media (max-width: 839px) { .stage > .phone { width: min(72%, 380px); } .shape { width: min(100%, 520px); } }

/* table: the comparison. Header a tier up, rules in outline-variant, the Droidspaces column tinted */
.table-wrap { overflow-x: auto; border: 1px solid var(--md-sys-color-outline-variant); border-radius: var(--md-sys-shape-corner-xl); background: var(--md-sys-color-surface-container); }
table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; font-size: 14px; line-height: 20px; letter-spacing: 0.25px; }
th { padding: 12px 16px; text-align: left; background: var(--md-sys-color-surface-container-high); color: var(--md-sys-color-on-surface-variant); font-size: 12px; line-height: 16px; font-weight: 500; letter-spacing: 0.5px; white-space: nowrap; }
td { padding: 12px 16px; border-top: 1px solid var(--md-sys-color-outline-variant); color: var(--md-sys-color-on-surface-variant); vertical-align: top; }
td:first-child { color: var(--md-sys-color-on-surface); }
.hl { background: color-mix(in srgb, transparent, var(--md-sys-color-primary-container) 20%); color: var(--md-sys-color-on-surface); }

/* footer */
.footer { background: var(--md-sys-color-surface-container-low); border-top: 1px solid var(--md-sys-color-outline-variant); color: var(--md-sys-color-on-surface-variant); font-size: 14px; line-height: 20px; letter-spacing: 0.25px; }
.footer-inner { display: grid; gap: 24px; max-width: 1200px; margin: 0 auto; padding: 48px 24px; }
.footer a { color: inherit; }
.footer ul { margin: 0; padding: 0; list-style: none; }
.footer li { margin: 12px 0; }
.footer h3 { margin: 0 0 12px; color: var(--md-sys-color-on-surface); }
.footer p { margin: 12px 0 0; }
@media (min-width: 840px) { .footer-inner { grid-template-columns: 2fr 1fr 1fr 1fr; } }

/* motion: arrival for phones and shapes, once, when they reach the screen. Shapes then turn. Text never
   animates. Nothing floats: two idle loops beside each other drift out of step and read as a glitch.
   Arrival is timed, not scroll-driven: site.js adds .pending to an object below the screen and swaps it
   for .arrived when it comes into view, so a fast flick and a slow scroll see the same entrance. Without
   JavaScript neither class is ever set, so nothing is hidden */
@keyframes arrive { from { opacity: 0; translate: 0 24px; } }
/* a shape's translate centres it, so it arrives by scale instead */
@keyframes shape-arrive { from { opacity: 0; scale: 0.9; } }
/* shapes turn once every two minutes, like the M3 Expressive loading indicator they come from */
@keyframes spin { to { rotate: 1turn; } }
.arrive.pending { opacity: 0; }
.arrive.arrived { animation: arrive var(--motion-spatial-slow) both; }
.shape.arrive { animation: spin 120s linear infinite; }
.shape.arrive.arrived { animation: shape-arrive var(--motion-spatial-slow) both, spin 120s linear infinite; }
.shape-secondary.arrive { animation-direction: reverse; } /* neighbouring shapes turn opposite ways */
.shape-secondary.arrive.arrived { animation-direction: normal, reverse; }

main { overflow-x: clip; } /* a shape wider than its column must not scroll the page sideways */

/* page layout: 1200px content, 24px gutters (16 under 600), sections 96 tall (64 under 840) */
.section {
  --gutter: 24px;
  /* the 1200px measure lives in the padding */
  padding: 96px max(var(--gutter), calc((100% - 1200px) / 2));
}
.section > h1, .section > h2 { margin-bottom: 16px; }
.section > .title-lg { margin: 0 0 32px; }
.split { display: grid; gap: 48px; align-items: center; }
.split > * { min-width: 0; } /* long unbroken content must not set the column minimum */
.split .stage { order: 1; } /* stacked: copy first, then the phone */
@media (min-width: 840px) {
  .split { grid-template-columns: 5fr 7fr; }
  .split-phone-left { grid-template-columns: 7fr 5fr; }
  .split-phone-left .stage { order: -1; }
}
.columns { display: grid; gap: 24px; }
@media (min-width: 600px) { .columns { grid-template-columns: 1fr 1fr; } }
.actions { display: flex; flex-wrap: wrap; gap: 12px; margin: 32px 0; }
.hero { padding-top: 64px; }
.hero h1 { margin-bottom: 24px; }
@media (max-width: 839px) { .section { padding-top: 64px; padding-bottom: 64px; } }

/* downloads page: the two download cards are the only cards allowed a button (DESIGN.md Cards) */
.download-cards { margin-bottom: 24px; }
.card h3 { display: flex; align-items: center; gap: 8px; }
.card h3 .icon { color: var(--md-sys-color-on-surface-variant); }
.card .button { margin-top: 16px; }
/* downloads: one short page, not a sequence of features, so sections share one 96px gap instead of stacking two */
.compact .section + .section { padding-top: 0; }
.changelog { padding: 0 24px; border: 1px solid var(--md-sys-color-outline-variant); border-radius: var(--md-sys-shape-corner-lg); background: var(--md-sys-color-surface-container); }
.changelog summary { display: flex; align-items: center; min-height: 56px; font-size: 14px; line-height: 20px; font-weight: 600; letter-spacing: 0.1px; cursor: pointer; }
.changelog h2 { font-size: 16px; line-height: 24px; font-weight: 600; letter-spacing: 0.15px; }
.changelog li { font-size: 14px; line-height: 20px; letter-spacing: 0.25px; color: var(--md-sys-color-on-surface-variant); }
.changelog hr { border: 0; border-top: 1px solid var(--md-sys-color-outline-variant); }
.notfound { min-height: calc(100dvh - 64px); }

/* gutters and breakpoints: 16 under 600, 24 above; only 600, 840, 1200, 1600 exist */
@media (max-width: 599px) { .nav-inner, .footer-inner { padding-left: 16px; padding-right: 16px; } .section { --gutter: 16px; } }

/* shape: Material squircle where the browser has it, plain rounding elsewhere. Not on buttons: an
   animating squircle radius renders with rough edges in Chromium */
@supports (corner-shape: squircle) {
  .card, .card-nested, .chip, pre, .table-wrap, .phone-screen, .phone-display { corner-shape: squircle; }
}

/* reduced motion is not optional: shapes stop turning, arrival is an instant fade, the press morph is off,
   the boot chain does not fill */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  @keyframes arrive { from { opacity: 0; } }
  .arrive.arrived, .shape.arrive, .shape.arrive.arrived, .shape-secondary.arrive.arrived { animation: arrive var(--motion-effects-fast) both; }
  .boot-chain .chain-fill, .boot-chain li::before { animation: none; } /* the chain stays drawn, unfilled */
  .button:active { border-radius: var(--md-sys-shape-corner-xl); }
  .nav-drawer, .icon, .button, .card, .chip, .phone-group .phone, .phone-group figcaption { transition: none; }
  .phone-group .phone:nth-child(n) { animation: none; } /* no fan-out, no scroller scale; the swipe itself still works */
  .phone-group figcaption { animation: none; }
}
