/* ============================================
   Josh Comeau CSS Reset
   ============================================ */

/* 1. Use a more-intuitive box-sizing model */
*, *::before, *::after {
  box-sizing: border-box;
}

/* 2. Remove default margin */
* {
  margin: 0;
}

/* 3. Enable keyword animations */
@media (prefers-reduced-motion: no-preference) {
  html {
    interpolate-size: allow-keywords;
  }
}

body {
  /* 4. Add accessible line-height */
  line-height: 1.5;
  text-rendering: optimizeLegibility;
  /* 5. Improve text rendering */
  -webkit-font-smoothing: antialiased;
}

/* 6. Improve media defaults */
img, picture, video, canvas, svg {
  display: block;
  max-width: 100%;
}

/* 7. Inherit fonts for form controls */
input, button, textarea, select {
  font: inherit;
}

/* 8. Avoid text overflows */
p, h1, h2, h3, h4, h5, h6 {
  overflow-wrap: break-word;
}

/* 9. Improve line wrapping */
p {
  text-wrap: pretty;
}
h1, h2, h3, h4, h5, h6 {
  text-wrap: balance;
}

/* ============================================
   Design tokens

   Every colour, type size and radius the site uses should come from here.

   Each colour is a light-dark() pair, so the site follows the reader's
   light or dark preference with no per-element overrides. The dark half is
   the astrophotography palette: astrophotography posts force
   color-scheme: dark (astrophotography.css), and starfield.css paints the
   night sky behind any page that is dark.
   ============================================ */

:root {
  color-scheme: light dark;

  /* Colour */
  --color-background: light-dark(oklch(0.995 0.01 106), oklch(0.057 0.025 268));
  --color-surface: light-dark(oklch(0.98 0.008 106), oklch(0.1 0.035 268));       /* footer, quiet panels */
  --color-field: light-dark(oklch(1 0 0), oklch(0.1 0.035 268));                  /* form fields */
  --color-headline: light-dark(oklch(0.36 0.09 246), oklch(0.94 0.02 255));       /* titles, article text */
  --color-paragraph: light-dark(oklch(0.5 0.03 252), oklch(0.8 0.03 255));        /* secondary copy */
  --color-muted: light-dark(oklch(0.53 0.02 252), oklch(0.68 0.04 255));          /* dates, meta; 4.5:1 */
  --color-link: light-dark(oklch(0.46 0.16 246), oklch(0.76 0.13 213));
  --color-nav: light-dark(oklch(0.28 0.07 255), oklch(0.86 0.03 255));            /* navbar and footer links */
  --color-on-strong: light-dark(oklch(1 0 0), oklch(0.12 0.03 268));              /* text on headline-filled buttons */
  --color-hover-wash: light-dark(oklch(0.95 0.02 255 / 0.5), oklch(1 0 0 / 0.07));
  --color-rule: light-dark(oklch(0.9 0.01 106), oklch(1 0 0 / 0.12));
  --color-rule-soft: light-dark(oklch(0.93 0.008 106), oklch(1 0 0 / 0.08));
  --color-field-border: light-dark(oklch(0.88 0.01 106), oklch(1 0 0 / 0.18));
  --color-field-border-hover: light-dark(oklch(0.8 0.02 250), oklch(1 0 0 / 0.32));
  --color-focus-wash: light-dark(oklch(0.46 0.16 246 / 0.25), oklch(0.76 0.13 213 / 0.35));
  --color-inline-code: light-dark(oklch(0.95 0.01 106), oklch(1 0 0 / 0.08));
  --color-error: light-dark(oklch(0.52 0.15 22), oklch(0.74 0.14 22));
  --color-error-wash: light-dark(oklch(0.98 0.02 20 / 0.96), oklch(0.22 0.06 20 / 0.96));
  --color-warm: light-dark(oklch(0.46 0.13 44), oklch(0.78 0.12 60));             /* recipes */
  --color-warm-wash: light-dark(oklch(0.95 0.035 62), oklch(0.3 0.06 50));
  --color-error-rule: light-dark(oklch(0.6 0.2 20 / 0.2), oklch(0.74 0.14 22 / 0.3));
  --color-tip: light-dark(oklch(0.6 0.12 160), oklch(0.72 0.12 160));   /* "tip" callouts */

  /* Code blocks are dark in both schemes, a shade deeper at night. */
  --color-code-bg: light-dark(oklch(0.2 0.02 250), oklch(0.09 0.035 268));
  --color-code-header: light-dark(oklch(0.24 0.025 250), oklch(0.12 0.04 270));
  --color-code-rule: light-dark(oklch(0.46 0.04 250 / 0.35), oklch(1 0 0 / 0.12));
  --color-code-text: oklch(0.95 0.01 106);
  --color-code-muted: oklch(0.79 0.07 245);
  --color-code-hover: oklch(1 0 0 / 0.08);
  --color-code-scrollbar: oklch(0.52 0.05 246 / 0.55);

  /* Shadows */
  --shadow-soft: 0 10px 22px oklch(0 0 0 / 0.05);
  --shadow-raised: 0 10px 30px oklch(0 0 0 / 0.16);
  --shadow-float: 0 20px 45px oklch(0.19 0.02 245 / 0.14);
  --shadow-sheet: 0 24px 64px oklch(0 0 0 / 0.08), 0 3px 12px oklch(0 0 0 / 0.04);

  /* Type */
  --font-sans: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --font-mono: ui-monospace, 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
  --font-serif: 'Georgia', 'Times New Roman', serif;
  --font-display-editorial: 'Cormorant Garamond', 'Georgia', serif;

  --text-2xs: 0.75rem;    /* code block labels, tooltips */
  --text-xs: 0.8125rem;   /* meta, captions, hints */
  --text-code: 0.875rem;  /* code blocks */
  --text-sm: 0.9375rem;   /* UI: links, buttons, labels */
  --text-base: 1rem;      /* form fields */
  --text-md: 1.0625rem;   /* body copy */
  --text-lg: clamp(1.05rem, 1.4vw, 1.2rem);           /* intros */
  --text-section: clamp(1.75rem, 2.6vw, 2.25rem);     /* section headings */
  --text-title: clamp(2.75rem, 6vw, 4.5rem);          /* page titles */

  /* Space: an 8px-based scale */
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2rem;
  --space-2xl: 3rem;
  --space-3xl: 4rem;

  /* Breakpoints. Media queries can't read custom properties, so these are
     written out where used; keep to these three:
       40rem  phone        (max-width: 40rem)
       52rem  tablet       (max-width: 52rem) / (min-width: 52rem)
       80rem  wide screen  (min-width: 80rem), room in the margins */

  /* Layout */
  --page-width: 64rem;
  --page-gutter: max(1.25rem, 5vw);
  --measure: 42rem;   /* comfortable line length for article text */

  /* Shape */
  --radius-sm: 0.375rem;
  --radius-md: 0.5rem;
  --radius-lg: 0.75rem;
  --radius-pill: 999px;

  /* Motion */
  --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
  --transition-base: 200ms cubic-bezier(0.4, 0, 0.2, 1);

  /* Keyboard focus ring */
  --focus-ring-width: 2px;
  --focus-ring-offset: 3px;

  /* Navbar */
  --navbar-height: 4rem;
  --navbar-blur: 16px;
}

/* The theme toggle (theme.js) records an explicit choice as data-theme on
   <html>; without one, the reader's system preference decides. */
:root[data-theme="light"] {
  color-scheme: light;
}

:root[data-theme="dark"] {
  color-scheme: dark;
}

/* Paper is light: printing a dark page wastes ink and loses the stars. */
@media print {
  :root,
  :root[data-theme] {
    color-scheme: light;
  }
}

/* Base Styles */
html {
  scroll-behavior: smooth;
}

body {
  font-family: var(--font-sans);
  font-optical-sizing: auto;
  font-weight: 300;
  background-color: var(--color-background);
  color: var(--color-paragraph);
}

/* Self-hosted English font subsets to avoid the Google Fonts stylesheet hop. */
@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("/assets/fonts/ibm-plex-sans-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: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("/assets/fonts/ibm-plex-sans-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+20AC,
    U+2122,
    U+2191,
    U+2193,
    U+2212,
    U+2215,
    U+FEFF,
    U+FFFD;
}

@font-face {
  font-family: 'Cormorant Garamond';
  font-style: italic;
  font-weight: 300;
  font-display: swap;
  src: url("/assets/fonts/cormorant-garamond-italic-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: 'Cormorant Garamond';
  font-style: italic;
  font-weight: 300;
  font-display: swap;
  src: url("/assets/fonts/cormorant-garamond-italic-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+20AC,
    U+2122,
    U+2191,
    U+2193,
    U+2212,
    U+2215,
    U+FEFF,
    U+FFFD;
}

@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/cormorant-garamond-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;
}

@font-face {
  font-family: 'Cormorant Garamond';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/fonts/cormorant-garamond-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+20AC,
    U+2122,
    U+2191,
    U+2193,
    U+2212,
    U+2215,
    U+FEFF,
    U+FFFD;
}

/* Focus Visible Styles */
:focus-visible {
  outline: 2px solid var(--color-link);
  outline-offset: 2px;
}

/* Skip link: the first focusable thing on every page, visually hidden until
   it takes focus. Without it a keyboard user tabs the brand, the menu toggle
   and every nav link before reaching content, on every page. It is clipped
   rather than display:none / visibility:hidden, both of which would take it
   out of the focus order and defeat the point. */
.skip-link {
  position: absolute;
  inset-inline-start: var(--space-md);
  inset-block-start: var(--space-md);
  z-index: 1000;
  padding: 0.75rem 1.15rem;
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  color: var(--color-link);
  font-weight: 600;
  text-decoration: none;
  transform: translateY(calc(-100% - var(--space-md) - 1rem));
  transition: transform var(--transition-fast);
}

/* :focus, not :focus-visible. A skip link is only ever reached by keyboard,
   so there is no mouse-focus ring to suppress, and :focus-visible's heuristic
   does not match programmatic focus - which would leave the link hidden for
   anything that focuses it directly. */
.skip-link:focus {
  transform: translateY(0);
  /* Only while shown: parked above the viewport, the shadow's blur still
     reached down into the top of the page as a grey smudge by the brand. */
  box-shadow: var(--shadow-raised);
}

/* The skip target itself should never draw a focus ring: focus lands there
   programmatically via the link, not by tabbing. */
#main-content:focus {
  outline: none;
}

/* Hidden on screen but still read by screen readers. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ============================================
   Shared type and link styles

   The site's recurring looks, used across pages. A component's own class
   keeps only what is particular to it (its margins, width, any size
   variation); the look itself comes from these.
   ============================================ */

/* The big italic title at the top of a page */
.page-title {
  color: var(--color-headline);
  font-family: var(--font-display-editorial);
  font-size: var(--text-title);
  font-style: italic;
  font-weight: 700;
  line-height: 1.04;
  letter-spacing: -0.025em;
  text-wrap: balance;
}

/* A section heading within a page */
.section-title {
  color: var(--color-headline);
  font-family: var(--font-display-editorial);
  font-size: var(--text-section);
  font-style: italic;
  line-height: 1.1;
}

/* The serif paragraph introducing a page */
.page-intro {
  max-width: 52ch;
  color: var(--color-paragraph);
  font-family: var(--font-serif);
  font-size: var(--text-lg);
  line-height: 1.7;
}

/* Small grey details: dates, reading time, tags */
.meta {
  color: var(--color-muted);
  font-size: var(--text-xs);
}

/* A quiet standalone link ("All posts ->", "RSS feed"): link colour,
   underlined on hover */
.text-link {
  color: var(--color-link);
  font-size: var(--text-sm);
  font-weight: 500;
  text-decoration: none;
}

/* A link within running text: always underlined */
.inline-link {
  color: var(--color-link);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

.text-link:hover,
.text-link:focus-visible {
  color: var(--color-headline);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.inline-link:hover,
.inline-link:focus-visible {
  color: var(--color-headline);
}

/* Selection Styles */
::selection {
  background-color: color-mix(in oklch, var(--color-link) 22%, transparent);
  color: var(--color-headline);
}

/* Shared shape for the pill-style CTA links/buttons defined independently
   in home.css and recipe-print.css; each keeps its own
   padding, gap, colors, transitions, and hover treatment. */
.home-hero__action,
.recipe-print-toolbar__back,
.recipe-print-toolbar__button {
  display: inline-flex;
  align-items: center;
  border-radius: var(--radius-pill);
  font-weight: 600;
  text-decoration: none;
}

/* When they prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ============================================
   Navbar
   ============================================ */

.site-header {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 100;
  block-size: var(--navbar-height);
}

.navbar {
  position: relative;
  block-size: 100%;
}

/* Frosted strip behind the bar: the page blurred beneath it, fading out
   downwards. The layer is twice the bar's height so the blur has content
   to sample below the bar's edge; the mask cuts it back to the bar. */
.navbar__glass {
  position: absolute;
  inset: 0;
  z-index: 0;
  block-size: 200%;
  background: linear-gradient(to bottom, var(--color-background), transparent 50%);
  backdrop-filter: blur(var(--navbar-blur));
  mask-image: linear-gradient(to bottom, black 0% 50%, transparent 50% 100%);
  pointer-events: none;
}

.navbar__content {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  block-size: var(--navbar-height);
  padding-inline: max(1rem, 3vw);
}

.navbar__brand {
  color: var(--color-headline);
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-decoration: none;
  transition: color var(--transition-base);
}

.navbar__brand:hover,
.navbar__brand:focus-visible {
  color: var(--color-link);
}

.navbar__links {
  display: flex;
  gap: var(--space-xs);
  margin-inline-start: auto;
  padding: 0;
  list-style: none;
}

.navbar__item {
  display: contents;
}

.navbar__link {
  display: block;
  padding: 0.625rem 1rem;
  border-radius: var(--radius-md);
  color: var(--color-nav);
  font-size: var(--text-sm);
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.01em;
  text-decoration: none;
  transition: background-color var(--transition-base), color var(--transition-base);
}

.navbar__link:hover,
.navbar__link:focus-visible {
  background-color: var(--color-hover-wash);
  color: var(--color-headline);
}

/* Icon buttons: the menu toggle (phones) and the theme toggle */
.navbar__toggle,
.theme-toggle {
  display: inline-grid;
  place-items: center;
  inline-size: 2.5rem;
  block-size: 2.5rem;
  padding: 0;
  border: 0;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--color-nav);
  cursor: pointer;
  transition: background-color var(--transition-base), color var(--transition-base);
}

.navbar__toggle:hover,
.navbar__toggle:focus-visible,
.theme-toggle:hover,
.theme-toggle:focus-visible {
  background-color: var(--color-hover-wash);
  color: var(--color-headline);
}

.navbar__toggle {
  display: none;
  color: var(--color-headline);
}

.navbar__toggle svg {
  display: block;
  fill: currentColor;
}

.theme-toggle {
  margin-inline-start: var(--space-xs);
}

.theme-toggle[hidden] {
  display: none;
}

/* The moon shows in the light theme (click for dark), the sun in the dark
   one. Dark is the system preference unless light was picked, or an
   explicit dark pick (theme.js). */
.theme-toggle__sun {
  display: none;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle__sun {
    display: block;
  }

  :root:not([data-theme="light"]) .theme-toggle__moon {
    display: none;
  }
}

:root[data-theme="dark"] .theme-toggle__sun {
  display: block;
}

:root[data-theme="dark"] .theme-toggle__moon {
  display: none;
}

/* Astrophotography posts are always dark, so the toggle would do nothing. */
body:has(.post-page--astrophotography) .theme-toggle {
  display: none;
}

/* Tablet and down: the links move into a menu behind the toggle. */
@media (max-width: 52rem) {
  :root {
    --navbar-height: 4.5rem;
  }

  .navbar__brand {
    font-size: 1.125rem;
    letter-spacing: 0.03em;
  }

  .navbar__links {
    display: none;
  }

  /* brand ... theme toggle, menu button */
  .theme-toggle {
    order: 2;
    margin-inline-start: auto;
  }

  .navbar__toggle {
    display: inline-grid;
    order: 3;
  }

  .navbar__links--open {
    position: absolute;
    inset-inline: 1rem;
    inset-block-start: calc(100% + 0.5rem);
    z-index: 1000;
    display: grid;
    gap: var(--space-xs);
    padding: var(--space-sm);
    border: 1px solid var(--color-rule);
    border-radius: var(--radius-lg);
    background: color-mix(in oklch, var(--color-background) 85%, transparent);
    backdrop-filter: blur(var(--navbar-blur));
    animation: slideDown 200ms cubic-bezier(0.16, 1, 0.3, 1);
  }

  .navbar__links--open .navbar__item {
    display: block;
  }

  .navbar__links--open .navbar__link {
    inline-size: 100%;
    padding: 0.875rem 1rem;
    letter-spacing: 0.005em;
    color: var(--color-headline);
  }
}

@media (max-width: 40rem) {
  .navbar__brand {
    font-size: 1rem;
    letter-spacing: 0.02em;
  }

  .navbar__links--open {
    inset-inline: 0.75rem;
  }
}

@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-0.5rem) scale(0.98);
  }

  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@media (prefers-reduced-transparency: reduce) {
  .navbar__glass,
  .navbar__links--open {
    backdrop-filter: none;
    background: var(--color-background);
  }
}

/* ============================================
   Footer
   ============================================ */

/* The footer sits at the bottom of the viewport on short pages and after the
   content on long ones: body is a column, and the page in between grows to
   fill whatever height the navbar padding and footer leave over. That's why
   the page shells (.home-shell, .posts-page, .post-page) no longer set their
   own min-height: 100svh, which pushed the footer a full screen down. */
body {
  display: flex;
  flex-direction: column;
  min-height: 100svh;
}

#main-content {
  display: flex;
  flex: 1 0 auto;
  flex-direction: column;
}

#main-content > * {
  flex: 1 0 auto;
}

.site-footer {
  border-block-start: 1px solid var(--color-rule);
  background: var(--color-surface);
  color: var(--color-paragraph);
  font-size: var(--text-sm);
}

.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-lg) var(--space-2xl);
  /* Same column as the home and listing pages above it. */
  max-width: var(--page-width);
  margin-inline: auto;
  padding: var(--space-2xl) var(--page-gutter);
}

.site-footer__about {
  display: grid;
  gap: 0.35rem;
  max-width: 34rem;
}

.site-footer__brand {
  justify-self: start;
  color: var(--color-headline);
  font-family: var(--font-display-editorial);
  font-size: 1.35rem;
  font-style: italic;
  font-weight: 700;
  text-decoration: none;
}

.site-footer__tagline {
  line-height: 1.6;
}

.site-footer__profiles {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-md);
  margin-block-start: var(--space-xs);
  padding: 0;
  list-style: none;
}

.site-footer__profile {
  font-weight: 500;
}

.site-footer__nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin-inline-start: -0.75rem;
}

.site-footer__link {
  padding: 0.5rem 0.75rem;
  border-radius: var(--radius-md);
  color: var(--color-nav);
  font-weight: 500;
  text-decoration: none;
  transition: background-color var(--transition-base), color var(--transition-base);
}

.site-footer__brand:hover,
.site-footer__brand:focus-visible,
.site-footer__link:hover,
.site-footer__link:focus-visible {
  color: var(--color-link);
}

.site-footer__link:hover,
.site-footer__link:focus-visible {
  background-color: var(--color-hover-wash);
}

@media (min-width: 52rem) {
  .site-footer__nav {
    margin-inline-start: 0;
    margin-inline-end: -0.75rem;
  }
}

/* ============================================
   Night sky

   Drawn behind every dark page: the whole site under a dark colour-scheme
   preference, and astrophotography posts always (they force dark; see
   astrophotography.css). The markup is the .night-sky block at the top of
   layout.html. Back to front:

     nebula     soft purple and blue glows that slowly breathe
     milky way  the galaxy's band, with its dust lanes and a red nebula
     dust       a haze of faint stars, tiled so it fills any screen
     stars      the brighter field: tinted by temperature (orange, yellow,
                white, icy blue), a few that twinkle, three bright enough
                to show diffraction spikes
     deep sky   the Big Dipper, the Andromeda Galaxy and the Pleiades, at
                their real shapes, in the margins beside the page
     meteors    two rare shooting stars

   Everything is a fixed layer behind the page, so scrolling never repaints
   it. Motion is opacity and transform only, and all of it stops for
   readers who ask for reduced motion. The whole sky is left out for
   readers who ask for more contrast, or use forced colours.

   Star positions were generated once from a fixed seed (81 82, after the
   galaxies in the astrophotography post) and are written out literally
   below; the Big Dipper's are its real ones.
   ============================================ */

.night-sky {
  position: fixed;
  inset: 0;
  z-index: -1;
  display: none;
  overflow: hidden;
  pointer-events: none;
}

/* Shown when the page is dark: the reader's system prefers dark and they
   haven't picked light with the theme toggle (theme.js), or they picked
   dark. */
@media screen and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .night-sky {
    display: block;
  }
}

@media screen {
  :root[data-theme="dark"] .night-sky {
    display: block;
  }
}

body:has(.post-page--astrophotography) .night-sky {
  display: block;
}

@media (prefers-contrast: more), (forced-colors: active) {
  .night-sky {
    display: none !important;
  }
}

.night-sky > div {
  position: absolute;
  inset: 0;
}

/* Nebula, and airglow: the faint green light the upper atmosphere gives
   off at night, strongest low towards the horizon. */
.night-sky__nebula {
  background-image:
    linear-gradient(to top, oklch(0.3 0.07 160 / 0.14), oklch(0.25 0.05 175 / 0.05) 14%, transparent 26%),
    radial-gradient(ellipse 78% 58% at 18% 26%, oklch(0.22 0.14 285 / 0.34) 0%, transparent 58%),
    radial-gradient(ellipse 52% 62% at 79% 65%, oklch(0.18 0.09 250 / 0.28) 0%, transparent 54%),
    radial-gradient(ellipse 44% 34% at 57% 14%, oklch(0.2 0.08 220 / 0.2) 0%, transparent 52%),
    radial-gradient(ellipse 38% 28% at 42% 58%, oklch(0.12 0.08 285 / 0.18) 0%, transparent 50%);
  animation: nebula-breathe 47s ease-in-out infinite alternate;
}

@keyframes nebula-breathe {
  from { opacity: 0.7; }
  to { opacity: 1; }
}

/* The Milky Way: the band of our own galaxy, edge on. It is drawn level
   on a long strip and the strip is rotated to arc across the sky, clear of
   the Big Dipper as in the real sky (the Dipper looks out of the galaxy's
   plane). Back to front: a soft base glow, extra dense stars, brighter
   star clouds, the red North America and Pelican nebulae, and the dark
   dust lanes of the Great Rift down its middle. The mask fades its edges
   and ends so it has no outline. */
.night-sky > .night-sky__milky-way {
  inset: auto;
  top: calc(48% - 22vmax);
  left: calc(38% - 90vmax);
  width: 180vmax;
  height: 44vmax;
  rotate: 34deg;
  mask-image:
    linear-gradient(transparent, black 36%, black 64%, transparent),
    linear-gradient(90deg, transparent, black 22%, black 78%, transparent);
  mask-composite: intersect;
}

/* starfield:begin milky-way (go run ./cmd/starfield) */
.night-sky > .night-sky__milky-way {
  background-image:
    radial-gradient(ellipse 10.86% 3.43% at 27% 49.82%, rgb(3 4 14 / 0.54), transparent 72%),
    radial-gradient(ellipse 6.79% 3.37% at 33.54% 50.55%, rgb(3 4 14 / 0.53), transparent 72%),
    radial-gradient(ellipse 5.78% 3.04% at 38.32% 51.75%, rgb(3 4 14 / 0.48), transparent 72%),
    radial-gradient(ellipse 6.37% 2.78% at 43.13% 51.38%, rgb(3 4 14 / 0.55), transparent 72%),
    radial-gradient(ellipse 10.67% 3.77% at 49.48% 51.23%, rgb(3 4 14 / 0.51), transparent 72%),
    radial-gradient(ellipse 9.56% 3.35% at 54.8% 51.63%, rgb(3 4 14 / 0.4), transparent 72%),
    radial-gradient(ellipse 8.57% 3.8% at 61.58% 51.31%, rgb(3 4 14 / 0.42), transparent 72%),
    radial-gradient(ellipse 8.8% 2.74% at 68.5% 51.76%, rgb(3 4 14 / 0.46), transparent 72%),
    radial-gradient(ellipse 1.8% 4.5% at 17% 47%, rgb(255 96 140 / 0.13), transparent 70%),
    radial-gradient(ellipse 1.5% 3.8% at 19% 50%, rgb(255 96 140 / 0.1), transparent 70%),
    radial-gradient(ellipse 2.2% 5.5% at 24% 54%, rgb(255 96 140 / 0.1), transparent 70%),
    radial-gradient(ellipse 2.6% 7% at 31% 44%, rgb(255 96 140 / 0.2), transparent 70%),
    radial-gradient(ellipse 1.6% 4.5% at 32.9% 46%, rgb(255 96 140 / 0.14), transparent 70%),
    radial-gradient(ellipse 9.84% 16.71% at 16% 53.6%, rgb(226 222 242 / 0.08), transparent 70%),
    radial-gradient(ellipse 9.19% 12.98% at 22.22% 52.07%, rgb(226 222 242 / 0.08), transparent 70%),
    radial-gradient(ellipse 5.65% 17.59% at 32.52% 50.46%, rgb(240 228 230 / 0.07), transparent 70%),
    radial-gradient(ellipse 6.01% 11.42% at 41.85% 53.55%, rgb(240 228 230 / 0.06), transparent 70%),
    radial-gradient(ellipse 5.28% 16.67% at 51.69% 48.41%, rgb(226 222 242 / 0.1), transparent 70%),
    radial-gradient(ellipse 8.34% 16.53% at 60.03% 47.07%, rgb(226 222 242 / 0.07), transparent 70%),
    radial-gradient(ellipse 10.83% 12.6% at 69.99% 50.39%, rgb(226 222 242 / 0.07), transparent 70%),
    radial-gradient(ellipse 8.5% 14.77% at 76.97% 47.44%, rgb(226 222 242 / 0.07), transparent 70%),
    radial-gradient(circle at 92px 95px, rgb(255 241 214 / 0.37) 0 0.57px, transparent 1.12px),
    radial-gradient(circle at 53px 29px, rgb(255 255 255 / 0.26) 0 0.46px, transparent 1.01px),
    radial-gradient(circle at 77px 93px, rgb(255 241 214 / 0.32) 0 0.57px, transparent 1.12px),
    radial-gradient(circle at 54px 67px, rgb(222 230 255 / 0.48) 0 0.58px, transparent 1.13px),
    radial-gradient(circle at 55px 30px, rgb(255 255 255 / 0.46) 0 0.56px, transparent 1.11px),
    radial-gradient(circle at 22px 49px, rgb(255 255 255 / 0.34) 0 0.53px, transparent 1.08px),
    radial-gradient(circle at 29px 8px, rgb(255 255 255 / 0.46) 0 0.56px, transparent 1.11px),
    radial-gradient(circle at 48px 11px, rgb(255 241 214 / 0.43) 0 0.5px, transparent 1.05px),
    radial-gradient(circle at 42px 5px, rgb(222 230 255 / 0.42) 0 0.46px, transparent 1.01px),
    radial-gradient(circle at 5px 48px, rgb(255 255 255 / 0.29) 0 0.56px, transparent 1.11px),
    radial-gradient(circle at 3px 2px, rgb(255 241 214 / 0.35) 0 0.6px, transparent 1.15px),
    radial-gradient(circle at 58px 47px, rgb(222 230 255 / 0.28) 0 0.47px, transparent 1.02px),
    radial-gradient(circle at 43px 22px, rgb(255 255 255 / 0.31) 0 0.53px, transparent 1.08px),
    radial-gradient(circle at 47px 18px, rgb(255 241 214 / 0.33) 0 0.59px, transparent 1.14px),
    radial-gradient(circle at 13px 4px, rgb(255 241 214 / 0.35) 0 0.49px, transparent 1.04px),
    radial-gradient(circle at 31px 23px, rgb(255 241 214 / 0.35) 0 0.58px, transparent 1.13px),
    linear-gradient(transparent 22%, rgb(190 198 235 / 0.06) 40%, rgb(228 216 230 / 0.1) 50%, rgb(190 198 235 / 0.06) 60%, transparent 78%);
  background-size:
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    auto,
    97px 97px,
    97px 97px,
    97px 97px,
    97px 97px,
    97px 97px,
    97px 97px,
    97px 97px,
    97px 97px,
    97px 97px,
    61px 61px,
    61px 61px,
    61px 61px,
    61px 61px,
    61px 61px,
    61px 61px,
    61px 61px,
    auto;
  background-repeat:
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    no-repeat,
    repeat,
    repeat,
    repeat,
    repeat,
    repeat,
    repeat,
    repeat,
    repeat,
    repeat,
    repeat,
    repeat,
    repeat,
    repeat,
    repeat,
    repeat,
    repeat,
    no-repeat;
}
/* starfield:end milky-way */

/* Dust: three tiles of different, unrelated sizes, so their repeats never
   line up into a visible grid. The tiles are drawn on an inner layer; see
   the parallax section for why. */
/* starfield:begin dust (go run ./cmd/starfield) */
.night-sky__dust > div {
  position: absolute;
  inset: 0;
  background-image:
    radial-gradient(circle at 137px 44px, rgb(255 255 255 / 0.16) 0 0.64px, transparent 1.19px),
    radial-gradient(circle at 51px 54px, rgb(222 230 255 / 0.14) 0 0.61px, transparent 1.16px),
    radial-gradient(circle at 105px 5px, rgb(255 255 255 / 0.27) 0 0.64px, transparent 1.19px),
    radial-gradient(circle at 128px 111px, rgb(222 230 255 / 0.21) 0 0.54px, transparent 1.09px),
    radial-gradient(circle at 121px 104px, rgb(255 255 255 / 0.23) 0 0.56px, transparent 1.11px),
    radial-gradient(circle at 111px 80px, rgb(255 255 255 / 0.22) 0 0.53px, transparent 1.08px),
    radial-gradient(circle at 94px 23px, rgb(255 255 255 / 0.15) 0 0.49px, transparent 1.04px),
    radial-gradient(circle at 115px 31px, rgb(255 255 255 / 0.24) 0 0.48px, transparent 1.03px),
    radial-gradient(circle at 89px 40px, rgb(255 255 255 / 0.3) 0 0.59px, transparent 1.14px),
    radial-gradient(circle at 159px 39px, rgb(255 214 172 / 0.24) 0 0.55px, transparent 1.1px),
    radial-gradient(circle at 145px 70px, rgb(255 255 255 / 0.21) 0 0.64px, transparent 1.19px),
    radial-gradient(circle at 42px 39px, rgb(255 190 152 / 0.22) 0 0.57px, transparent 1.12px),
    radial-gradient(circle at 93px 156px, rgb(255 255 255 / 0.28) 0 0.58px, transparent 1.13px),
    radial-gradient(circle at 130px 118px, rgb(185 205 255 / 0.29) 0 0.6px, transparent 1.15px),
    radial-gradient(circle at 239px 123px, rgb(255 255 255 / 0.19) 0 0.53px, transparent 1.08px),
    radial-gradient(circle at 17px 174px, rgb(255 255 255 / 0.17) 0 0.53px, transparent 1.08px),
    radial-gradient(circle at 20px 220px, rgb(255 255 255 / 0.34) 0 0.56px, transparent 1.11px),
    radial-gradient(circle at 47px 115px, rgb(255 255 255 / 0.36) 0 0.54px, transparent 1.09px),
    radial-gradient(circle at 93px 128px, rgb(255 255 255 / 0.34) 0 0.56px, transparent 1.11px),
    radial-gradient(circle at 52px 59px, rgb(255 255 255 / 0.34) 0 0.63px, transparent 1.18px),
    radial-gradient(circle at 111px 59px, rgb(255 255 255 / 0.22) 0 0.6px, transparent 1.15px),
    radial-gradient(circle at 21px 215px, rgb(255 255 255 / 0.28) 0 0.61px, transparent 1.16px),
    radial-gradient(circle at 19px 65px, rgb(255 255 255 / 0.32) 0 0.46px, transparent 1.01px),
    radial-gradient(circle at 101px 57px, rgb(222 230 255 / 0.18) 0 0.57px, transparent 1.12px),
    radial-gradient(circle at 262px 284px, rgb(255 255 255 / 0.33) 0 0.62px, transparent 1.17px),
    radial-gradient(circle at 73px 121px, rgb(255 255 255 / 0.47) 0 0.58px, transparent 1.13px),
    radial-gradient(circle at 223px 25px, rgb(255 255 255 / 0.4) 0 0.47px, transparent 1.02px),
    radial-gradient(circle at 334px 10px, rgb(255 255 255 / 0.31) 0 0.59px, transparent 1.14px),
    radial-gradient(circle at 54px 242px, rgb(255 255 255 / 0.37) 0 0.52px, transparent 1.07px),
    radial-gradient(circle at 187px 51px, rgb(255 241 214 / 0.4) 0 0.46px, transparent 1.01px),
    radial-gradient(circle at 205px 97px, rgb(255 255 255 / 0.29) 0 0.49px, transparent 1.04px),
    radial-gradient(circle at 338px 116px, rgb(255 255 255 / 0.43) 0 0.54px, transparent 1.09px);
  background-size:
    173px 173px,
    173px 173px,
    173px 173px,
    173px 173px,
    173px 173px,
    173px 173px,
    173px 173px,
    173px 173px,
    173px 173px,
    173px 173px,
    173px 173px,
    173px 173px,
    241px 241px,
    241px 241px,
    241px 241px,
    241px 241px,
    241px 241px,
    241px 241px,
    241px 241px,
    241px 241px,
    241px 241px,
    241px 241px,
    241px 241px,
    241px 241px,
    389px 389px,
    389px 389px,
    389px 389px,
    389px 389px,
    389px 389px,
    389px 389px,
    389px 389px,
    389px 389px;
}
/* starfield:end dust */

/* The brighter field */
/* starfield:begin field (go run ./cmd/starfield) */
.night-sky__stars {
  background-image:
    radial-gradient(circle at 96.68% 5.82%, rgb(255 214 172 / 0.74) 0 1.06px, transparent 1.75px),
    radial-gradient(circle at 57.1% 23%, rgb(185 205 255 / 0.69) 0 1px, transparent 1.65px),
    radial-gradient(circle at 47.54% 29.12%, rgb(222 230 255 / 0.49) 0 0.73px, transparent 1.2px),
    radial-gradient(circle at 45.81% 16.14%, rgb(255 241 214 / 0.49) 0 0.73px, transparent 1.21px),
    radial-gradient(circle at 65.66% 29.52%, rgb(255 255 255 / 0.5) 0 0.74px, transparent 1.22px),
    radial-gradient(circle at 97.23% 62.27%, rgb(255 241 214 / 0.99) 0 1.38px, transparent 2.28px),
    radial-gradient(circle at 86.01% 62.51%, rgb(222 230 255 / 0.56) 0 0.82px, transparent 1.35px),
    radial-gradient(circle at 60.16% 41.51%, rgb(255 241 214 / 0.6) 0 0.87px, transparent 1.44px),
    radial-gradient(circle at 93.6% 18.71%, rgb(255 255 255 / 0.5) 0 0.75px, transparent 1.24px),
    radial-gradient(circle at 2.31% 57.24%, rgb(255 241 214 / 0.9) 0 1.27px, transparent 2.09px),
    radial-gradient(circle at 7.53% 29.01%, rgb(255 255 255 / 0.55) 0 0.81px, transparent 1.34px),
    radial-gradient(circle at 90.74% 92.87%, rgb(255 190 152 / 0.52) 0 0.77px, transparent 1.27px),
    radial-gradient(circle at 32.83% 4.99%, rgb(255 255 255 / 0.73) 0 1.04px, transparent 1.72px),
    radial-gradient(circle at 96.6% 71.1%, rgb(222 230 255 / 0.61) 0 0.9px, transparent 1.48px),
    radial-gradient(circle at 76.47% 54.32%, rgb(222 230 255 / 0.52) 0 0.78px, transparent 1.28px),
    radial-gradient(circle at 82.35% 4.95%, rgb(222 230 255 / 0.49) 0 0.74px, transparent 1.22px),
    radial-gradient(circle at 91.86% 73.2%, rgb(255 214 172 / 0.52) 0 0.78px, transparent 1.28px),
    radial-gradient(circle at 39.3% 82.45%, rgb(255 255 255 / 0.58) 0 0.86px, transparent 1.41px),
    radial-gradient(circle at 7.55% 11.67%, rgb(185 205 255 / 0.53) 0 0.78px, transparent 1.29px),
    radial-gradient(circle at 30.31% 45.7%, rgb(222 230 255 / 0.48) 0 0.72px, transparent 1.19px),
    radial-gradient(circle at 41.57% 95.87%, rgb(255 214 172 / 0.67) 0 0.96px, transparent 1.59px),
    radial-gradient(circle at 8.99% 36.93%, rgb(222 230 255 / 0.5) 0 0.75px, transparent 1.23px),
    radial-gradient(circle at 27.17% 70.7%, rgb(185 205 255 / 0.63) 0 0.92px, transparent 1.51px),
    radial-gradient(circle at 6.66% 19.83%, rgb(255 190 152 / 0.86) 0 1.21px, transparent 2px),
    radial-gradient(circle at 18.93% 12.39%, rgb(185 205 255 / 0.5) 0 0.74px, transparent 1.23px),
    radial-gradient(circle at 71.87% 37.8%, rgb(255 255 255 / 0.51) 0 0.75px, transparent 1.24px),
    radial-gradient(circle at 12.31% 72.1%, rgb(185 205 255 / 0.91) 0 1.28px, transparent 2.12px),
    radial-gradient(circle at 3.68% 86.48%, rgb(222 230 255 / 0.56) 0 0.82px, transparent 1.35px),
    radial-gradient(circle at 6% 90.77%, rgb(255 214 172 / 0.49) 0 0.73px, transparent 1.21px),
    radial-gradient(circle at 55.77% 18.04%, rgb(222 230 255 / 0.54) 0 0.8px, transparent 1.33px),
    radial-gradient(circle at 18.26% 36.05%, rgb(255 241 214 / 0.56) 0 0.83px, transparent 1.37px),
    radial-gradient(circle at 80.54% 15.74%, rgb(222 230 255 / 0.54) 0 0.8px, transparent 1.33px),
    radial-gradient(circle at 45.38% 83.08%, rgb(255 241 214 / 0.59) 0 0.87px, transparent 1.44px),
    radial-gradient(circle at 78.29% 7.18%, rgb(222 230 255 / 0.58) 0 0.85px, transparent 1.41px),
    radial-gradient(circle at 37.65% 57.79%, rgb(255 241 214 / 0.57) 0 0.84px, transparent 1.39px),
    radial-gradient(circle at 94.81% 49.43%, rgb(255 241 214 / 0.65) 0 0.94px, transparent 1.56px),
    radial-gradient(circle at 4.01% 40.38%, rgb(255 255 255 / 0.52) 0 0.78px, transparent 1.28px),
    radial-gradient(circle at 52.29% 78.48%, rgb(255 214 172 / 0.51) 0 0.76px, transparent 1.25px),
    radial-gradient(circle at 10.42% 33.71%, rgb(255 255 255 / 0.67) 0 0.97px, transparent 1.6px),
    radial-gradient(circle at 15.86% 38.17%, rgb(255 190 152 / 0.5) 0 0.75px, transparent 1.24px),
    radial-gradient(circle at 5.49% 55.19%, rgb(185 205 255 / 0.66) 0 0.96px, transparent 1.58px),
    radial-gradient(circle at 47.17% 28.33%, rgb(185 205 255 / 0.74) 0 1.07px, transparent 1.76px),
    radial-gradient(circle at 55.46% 40.81%, rgb(222 230 255 / 0.61) 0 0.89px, transparent 1.47px),
    radial-gradient(circle at 13.67% 36.95%, rgb(255 190 152 / 0.69) 0 1px, transparent 1.64px),
    radial-gradient(circle at 27.98% 77.59%, rgb(255 214 172 / 0.56) 0 0.82px, transparent 1.35px),
    radial-gradient(circle at 81.73% 86.32%, rgb(185 205 255 / 0.56) 0 0.83px, transparent 1.36px);
  background-repeat: no-repeat;
}
/* starfield:end field */

.night-sky__twinkles,
.night-sky__bright {
  display: contents;
}

/* Each twinkling star is its own tiny element with its own rhythm, so
   they come and go independently, like real scintillation. */
.night-sky__twinkles span,
.night-sky__bright span,
.night-sky__dipper span,
.night-sky__cassiopeia span,
.night-sky__pleiades span {
  --star: 255 255 255;
  position: absolute;
  translate: -50% -50%;
}

.night-sky__twinkles span {
  width: 9px;
  height: 9px;
  background: radial-gradient(circle, rgb(var(--star)) 0 1.3px, rgb(var(--star) / 0.25) 2.2px, transparent 4.5px);
  opacity: 0.3;
  animation: twinkle 6s ease-in-out infinite;
  will-change: opacity;
}

@keyframes twinkle {
  0%, 100% { opacity: 0.25; }
  40% { opacity: 1; }
  55% { opacity: 0.6; }
  70% { opacity: 0.9; }
}

/* starfield:begin twinkles (go run ./cmd/starfield) */
.night-sky__twinkles span:nth-child(1) { left: 4.77%; top: 11.75%; animation-duration: 7.66s; animation-delay: -0.77s; --star: 255 241 214; }
.night-sky__twinkles span:nth-child(2) { left: 27.16%; top: 23.86%; animation-duration: 7.14s; animation-delay: -7.58s; --star: 255 214 172; }
.night-sky__twinkles span:nth-child(3) { left: 53.42%; top: 9.05%; animation-duration: 8.06s; animation-delay: -1.49s; --star: 185 205 255; }
.night-sky__twinkles span:nth-child(4) { left: 64.97%; top: 24.18%; animation-duration: 4.86s; animation-delay: -8.8s; --star: 255 190 152; }
.night-sky__twinkles span:nth-child(5) { left: 88.27%; top: 7.41%; animation-duration: 4.1s; animation-delay: -3.77s; --star: 185 205 255; }
.night-sky__twinkles span:nth-child(6) { left: 9.32%; top: 57.9%; animation-duration: 4.26s; animation-delay: -1.11s; --star: 255 241 214; }
.night-sky__twinkles span:nth-child(7) { left: 36.92%; top: 41.98%; animation-duration: 6.08s; animation-delay: -3.48s; }
.night-sky__twinkles span:nth-child(8) { left: 49.07%; top: 59.75%; animation-duration: 6.4s; animation-delay: -7.58s; --star: 185 205 255; }
.night-sky__twinkles span:nth-child(9) { left: 71.36%; top: 42.47%; animation-duration: 5.16s; animation-delay: -6.67s; --star: 255 190 152; }
.night-sky__twinkles span:nth-child(10) { left: 92.1%; top: 53.05%; animation-duration: 3.68s; animation-delay: -2.86s; --star: 255 214 172; }
.night-sky__twinkles span:nth-child(11) { left: 15.21%; top: 83.09%; animation-duration: 4.67s; animation-delay: -1.42s; --star: 222 230 255; }
.night-sky__twinkles span:nth-child(12) { left: 24.46%; top: 75.77%; animation-duration: 8.49s; animation-delay: -7.28s; --star: 185 205 255; }
.night-sky__twinkles span:nth-child(13) { left: 51.21%; top: 85.51%; animation-duration: 9.06s; animation-delay: -4.18s; --star: 255 241 214; }
.night-sky__twinkles span:nth-child(14) { left: 63.61%; top: 82.36%; animation-duration: 7.4s; animation-delay: -4.28s; --star: 255 190 152; }
.night-sky__twinkles span:nth-child(15) { left: 89.99%; top: 80.97%; animation-duration: 3.69s; animation-delay: -5.36s; --star: 222 230 255; }
/* starfield:end twinkles */

/* Diffraction spikes: the four-pointed cross a telescope's optics put on a
   bright star, familiar from any astrophotograph. */
.night-sky__bright span {
  width: var(--size);
  height: var(--size);
  background:
    radial-gradient(circle, rgb(var(--star)) 0 1.7px, rgb(var(--star) / 0.5) 2.8px, rgb(var(--star) / 0.1) 7px, transparent 10px),
    linear-gradient(90deg, transparent, rgb(var(--star) / 0.55) 50%, transparent) center / 100% 1px no-repeat,
    linear-gradient(transparent, rgb(var(--star) / 0.55) 50%, transparent) center / 1px 100% no-repeat;
  animation: shimmer 13s ease-in-out infinite;
}

@keyframes shimmer {
  0%, 100% { opacity: 0.85; }
  50% { opacity: 1; }
}

.night-sky__bright span:nth-child(1) { left: 9%; top: 86%; --size: 52px; --star: 196 216 255; }
.night-sky__bright span:nth-child(2) { left: 61%; top: 8%; --size: 40px; animation-delay: -5s; }
.night-sky__bright span:nth-child(3) { left: 84%; top: 76%; --size: 46px; --star: 255 240 210; animation-delay: -9s; }

/* The Big Dipper, in Ursa Major: the constellation M81 and M82 sit in.
   Its seven stars (and Alcor) at their true relative positions, as seen
   facing north, with each star's size following its brightness. No
   lines: it's there for whoever recognises it. */
/* It lives in the right-hand margin, sized to fit beside the page column,
   and only where that margin is wide enough to hold it without sitting
   behind text. Its box, and so the quiet patch below, is placed by these. */
.night-sky {
  --dipper-width: min(13rem, (100vw - var(--page-width)) / 2 - 5vw);
  --dipper-x: calc(100vw - 2.5vw - var(--dipper-width) / 2);
  --dipper-y: calc(16vh + var(--dipper-width) / 3.774);
  --pleiades-width: min(6.5rem, (100vw - var(--page-width)) / 2 - 8vw);
  --pleiades-x: calc(100vw - 6vw - var(--pleiades-width) / 2);
  --pleiades-y: calc(56vh + var(--pleiades-width) / 3.856);
  --cassiopeia-width: min(8rem, (100vw - var(--page-width)) / 2 - 7vw);
  --cassiopeia-x: calc(3.5vw + var(--cassiopeia-width) / 2);
  --cassiopeia-y: calc(24vh + var(--cassiopeia-width) / 3.4);
}

.night-sky__dipper {
  position: absolute;
  inset: 16vh 2.5vw auto auto;
  display: none;
  width: var(--dipper-width);
}

/* Quiet patches of sky. The real sky around the Dipper (and the Pleiades)
   has few bright stars, which is what lets the shape stand out; here the
   general field would drown it. So where they show, the rest of the field
   fades down around each (to 12%, not to nothing, so faint dust remains and the
   patch doesn't look cut out). The Dipper is on its own layer, outside
   the mask. */
@media (min-width: 80rem) {
  .night-sky__dipper {
    display: block;
  }

  .night-sky__stars,
  .night-sky__dust {
    mask-image:
      radial-gradient(
        ellipse calc(var(--dipper-width) * 0.95) calc(var(--dipper-width) * 0.62)
          at var(--dipper-x) var(--dipper-y),
        rgb(0 0 0 / 0.12) 55%,
        black 100%
      ),
      radial-gradient(
        ellipse calc(var(--pleiades-width) * 1.3) calc(var(--pleiades-width) * 0.95)
          at var(--pleiades-x) var(--pleiades-y),
        rgb(0 0 0 / 0.12) 50%,
        black 100%
      ),
      radial-gradient(
        ellipse calc(var(--cassiopeia-width) * 0.85) calc(var(--cassiopeia-width) * 0.55)
          at var(--cassiopeia-x) var(--cassiopeia-y),
        rgb(0 0 0 / 0.2) 50%,
        black 100%
      );
    mask-composite: intersect;
  }
}

.night-sky__dipper span,
.night-sky__cassiopeia span,
.night-sky__pleiades span {
  --r: 1.2px;
  width: calc(var(--r) * 7);
  height: calc(var(--r) * 7);
  background: radial-gradient(circle, rgb(var(--star)) 0 var(--r), rgb(var(--star) / 0.3) calc(var(--r) * 1.8), transparent calc(var(--r) * 3.4));
}



/* Andromeda (M31): the nearest big galaxy, a tilted oval glow with a
   bright core, with its two small companions: M32 just below the core and
   M110 further out on the other side. It sits in the left margin, where
   (like the Dipper) there is room for it beside the page. */
.night-sky__andromeda {
  position: absolute;
  top: 58vh;
  left: 4vw;
  display: none;
  width: min(9rem, (100vw - var(--page-width)) / 2 - 6vw);
  aspect-ratio: 4;
  border-radius: 50%;
  rotate: -38deg;
  background: radial-gradient(
    closest-side,
    rgb(255 244 228 / 0.95) 0 2%,
    rgb(255 236 214 / 0.4) 9%,
    rgb(226 220 245 / 0.14) 38%,
    rgb(200 205 240 / 0.05) 70%,
    transparent
  );
}

.night-sky__andromeda span,
.night-sky__andromeda::after {
  content: '';
  position: absolute;
  border-radius: 50%;
}

/* M32 */
.night-sky__andromeda span {
  top: 66%;
  left: 54%;
  width: 6px;
  height: 6px;
  background: radial-gradient(closest-side, rgb(255 240 222 / 0.75), transparent);
}

/* M110 */
.night-sky__andromeda::after {
  top: -2%;
  left: 30%;
  width: 14px;
  height: 8px;
  background: radial-gradient(closest-side, rgb(235 228 240 / 0.35), transparent);
}

/* The Pleiades (M45): the Seven Sisters, a tight cluster of young blue-white
   stars at their true relative positions. Brightness is what makes the
   little-dipper shape read, so each star's size and opacity follow its
   magnitude: Alcyone dominates, Atlas, Electra and Maia come next, and
   Pleione and Celaeno, barely naked-eye stars, are faint. The blue
   reflection nebula is strongest around Merope, as in photographs. In the
   right margin, below the Dipper, with its own quiet patch of sky. */
.night-sky__pleiades {
  position: absolute;
  top: 56vh;
  right: 6vw;
  display: none;
  width: var(--pleiades-width);
}

.night-sky__pleiades::before {
  content: '';
  position: absolute;
  inset: -45% -30%;
  background:
    radial-gradient(ellipse 18% 26% at 60% 80%, rgb(150 185 255 / 0.16), transparent 70%),
    radial-gradient(ellipse 22% 20% at 66% 38%, rgb(140 175 255 / 0.09), transparent 70%),
    radial-gradient(ellipse 48% 52% at 52% 56%, rgb(130 165 255 / 0.07), transparent 72%);
}



/* Cassiopeia: the W-shaped constellation on the Milky Way, just above
   Andromeda, as in the real sky (M31 is found by following the W). In the
   left margin, above the galaxy. */
.night-sky__cassiopeia {
  position: absolute;
  top: 24vh;
  left: 3.5vw;
  display: none;
  width: var(--cassiopeia-width);
}

/* Like the Dipper, only where the margins have room for them. */
@media (min-width: 80rem) {
  .night-sky__andromeda,
  .night-sky__cassiopeia,
  .night-sky__pleiades {
    display: block;
  }
}

/* starfield:begin constellations (go run ./cmd/starfield) */
/* The Big Dipper (Ursa Major), at true relative positions. Each star is sized and
   faded by its magnitude and coloured by its spectral class. */
.night-sky__dipper {
  aspect-ratio: 1.89;
}

.night-sky__dipper span:nth-child(1) { left: 98.86%; top: 0%; --r: 1.6px; --star: 255 214 172; opacity: 1; } /* Dubhe, an orange giant, magnitude 1.8 */
.night-sky__dipper span:nth-child(2) { left: 100%; top: 43.16%; --r: 1.42px; --star: 222 230 255; opacity: 0.84; } /* Merak, magnitude 2.4 */
.night-sky__dipper span:nth-child(3) { left: 68.62%; top: 64.77%; --r: 1.42px; --star: 222 230 255; opacity: 0.84; } /* Phecda, magnitude 2.4 */
.night-sky__dipper span:nth-child(4) { left: 55.59%; top: 37.94%; --r: 1.15px; --star: 222 230 255; opacity: 0.61; } /* Megrez, the faintest of the seven, magnitude 3.3 */
.night-sky__dipper span:nth-child(5) { left: 32.29%; top: 46.56%; --r: 1.6px; --star: 222 230 255; opacity: 1; } /* Alioth, magnitude 1.8 */
.night-sky__dipper span:nth-child(6) { left: 14.25%; top: 54.88%; --r: 1.48px; --star: 222 230 255; opacity: 0.9; } /* Mizar, magnitude 2.2 */
.night-sky__dipper span:nth-child(7) { left: 11.67%; top: 50.37%; --r: 0.94px; --star: 222 230 255; opacity: 0.43; } /* Alcor, Mizar's companion, the old eyesight test, magnitude 4 */
.night-sky__dipper span:nth-child(8) { left: 0%; top: 100%; --r: 1.57px; --star: 185 205 255; opacity: 0.97; } /* Alkaid, a hot blue-white star, magnitude 1.9 */

/* Cassiopeia's W, at true relative positions. Each star is sized and
   faded by its magnitude and coloured by its spectral class. */
.night-sky__cassiopeia {
  aspect-ratio: 1.84;
}

.night-sky__cassiopeia span:nth-child(1) { left: 100%; top: 63.37%; --r: 1.57px; --star: 255 255 255; opacity: 0.97; } /* Caph, magnitude 2.3 */
.night-sky__cassiopeia span:nth-child(2) { left: 70.22%; top: 100%; --r: 1.6px; --star: 255 214 172; opacity: 1; } /* Schedar, an orange giant, magnitude 2.2 */
.night-sky__cassiopeia span:nth-child(3) { left: 54.83%; top: 41.41%; --r: 1.6px; --star: 185 205 255; opacity: 1; } /* Gamma Cassiopeiae, a hot, fast-spinning blue star, magnitude 2.2 */
.night-sky__cassiopeia span:nth-child(4) { left: 27.16%; top: 48.15%; --r: 1.45px; --star: 222 230 255; opacity: 0.87; } /* Ruchbah, magnitude 2.7 */
.night-sky__cassiopeia span:nth-child(5) { left: 0%; top: 0%; --r: 1.24px; --star: 185 205 255; opacity: 0.69; } /* Segin, magnitude 3.4 */

/* The Pleiades (M45), at true relative positions. Each star is sized and
   faded by its magnitude and coloured by its spectral class. */
.night-sky__pleiades {
  aspect-ratio: 1.93;
}

.night-sky__pleiades span:nth-child(1) { left: 38.82%; top: 69.81%; --r: 1.6px; --star: 185 205 255; opacity: 1; } /* Alcyone, magnitude 2.9 */
.night-sky__pleiades span:nth-child(2) { left: 0.57%; top: 79.76%; --r: 1.39px; --star: 185 205 255; opacity: 0.82; } /* Atlas, magnitude 3.6 */
.night-sky__pleiades span:nth-child(3) { left: 98.37%; top: 68.2%; --r: 1.36px; --star: 185 205 255; opacity: 0.79; } /* Electra, magnitude 3.7 */
.night-sky__pleiades span:nth-child(4) { left: 76.65%; top: 19.16%; --r: 1.3px; --star: 185 205 255; opacity: 0.74; } /* Maia, magnitude 3.9 */
.night-sky__pleiades span:nth-child(5) { left: 65.25%; top: 100%; --r: 1.21px; --star: 185 205 255; opacity: 0.66; } /* Merope, magnitude 4.2 */
.night-sky__pleiades span:nth-child(6) { left: 90.76%; top: 0%; --r: 1.18px; --star: 185 205 255; opacity: 0.64; } /* Taygeta, magnitude 4.3 */
.night-sky__pleiades span:nth-child(7) { left: 0%; top: 63.7%; --r: 0.94px; --star: 185 205 255; opacity: 0.43; } /* Pleione, magnitude 5.1 */
.night-sky__pleiades span:nth-child(8) { left: 100%; top: 34.26%; --r: 0.85px; --star: 185 205 255; opacity: 0.35; } /* Celaeno, magnitude 5.4 */
/* starfield:end constellations */

/* Shooting stars. Each is visible for about a second of a long cycle, so
   one is a lucky catch, not a screensaver. A meteor's head is the bright
   end of its gradient; the separate translate moves it along the angle it
   is rotated to. */
.night-sky__meteors {
  display: block;
}

.night-sky__meteors span {
  position: absolute;
  width: 10rem;
  height: 1.5px;
  border-radius: 1px;
  background: linear-gradient(90deg, transparent, rgb(220 232 255 / 0.5) 70%, rgb(255 255 255));
  filter: drop-shadow(0 0 2px rgb(200 220 255 / 0.8));
  opacity: 0;
}

.night-sky__meteors span:nth-child(1) {
  top: 14%;
  left: 8%;
  rotate: 22deg;
  animation: meteor-a 37s linear infinite;
  animation-delay: -8s;
}

.night-sky__meteors span:nth-child(2) {
  top: 9%;
  left: 70%;
  rotate: 158deg;
  animation: meteor-b 53s linear infinite;
  animation-delay: -31s;
}

@keyframes meteor-a {
  0% { translate: 0 0; opacity: 0; }
  0.4% { opacity: 1; }
  2.6% { translate: 32vw 12.9vw; opacity: 0; }
  100% { translate: 32vw 12.9vw; opacity: 0; }
}

@keyframes meteor-b {
  0% { translate: 0 0; opacity: 0; }
  0.3% { opacity: 1; }
  1.9% { translate: -26vw 10.5vw; opacity: 0; }
  100% { translate: -26vw 10.5vw; opacity: 0; }
}

/* Parallax: as the page scrolls, the dust drifts up a little and the
   brighter stars a little more, so the sky has depth. Scroll-driven
   animation is pure CSS; browsers without it keep a still sky. The layers
   run past the bottom of the screen so the drift never shows an edge.

   The dust's quiet-patch mask has to stay on the Big Dipper, which moves
   with the stars. So the outer dust layer, which carries the mask, moves
   with the stars too, and the tiles inside it slide back down by the
   difference: the dust lags behind as before, but the patch doesn't. */
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .night-sky__milky-way,
    .night-sky__dust,
    .night-sky__dust > div,
    .night-sky__stars,
    .night-sky__constellation {
      animation: drift linear both;
      animation-timeline: scroll(root);
    }

    .night-sky__dust,
    .night-sky__stars,
    .night-sky__constellation {
      --drift: -12vh;
      inset-block-end: -14vh;
    }

    .night-sky > .night-sky__milky-way {
      --drift: -5vh;
    }

    /* Net drift: -12vh + 7vh = -5vh. */
    .night-sky__dust > div {
      --drift: 7vh;
      inset-block-start: -7vh;
    }
  }
}

@keyframes drift {
  to { translate: 0 var(--drift); }
}

@media (prefers-reduced-motion: reduce) {
  .night-sky__nebula,
  .night-sky__twinkles span,
  .night-sky__bright span {
    animation: none;
  }

  .night-sky__twinkles span {
    opacity: 0.6;
  }

  .night-sky__meteors {
    display: none;
  }
}

/* Let the stars show through the footer, wherever they show. */
@media screen and (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .site-footer {
    background: color-mix(in oklch, var(--color-background) 85%, transparent);
  }
}

@media screen {
  :root[data-theme="dark"] .site-footer {
    background: color-mix(in oklch, var(--color-background) 85%, transparent);
  }
}

body:has(.post-page--astrophotography) .site-footer {
  background: color-mix(in oklch, var(--color-background) 85%, transparent);
}

