/* =====================================================
   FONTS
   ===================================================== */
@font-face {
  font-family: 'BauhausBugler';
  src: url('BauhausBugler-Medium.woff2') format('woff2'),
    url('BauhausBugler-Medium.woff') format('woff');
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}

/* =====================================================
   PALETTE — light theme only
   ===================================================== */
:root {
  --c-bg: #EEF2FF;
  --c-surface: #FFFFFF;
  --c-primary: #1A4BFF;
  --c-text: #05091C;
  --c-muted: #5060A0;
  --c-border: rgba(26, 75, 255, 0.15);
  --c-chrome-bg: #FFFFFF;
  --c-chrome-border: rgba(26, 75, 255, 0.35);
  --c-nav-bg: #FFFFFF;
  /*rgba(238, 242, 255, 0.92);*/
  --c-logo: #05091C;
  --c-accent: #E03A22;

  --font-body: 'Fira Sans', sans-serif;
  --font-mono: 'Fira Code', monospace;
  --font-head: 'BauhausBugler', sans-serif;

  --nav-h: 64px;
  --pad-x: clamp(20px, 5vw, 48px);
  --max-w: 720px;
  /* Outer container width so the inner text column (after --pad-x on each
     side) is --max-w wide, matching the nav/footer gradient separators. */
  --content-w: calc(var(--max-w) + 2 * var(--pad-x));
}

/* =====================================================
   RESET
   ===================================================== */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* =====================================================
   BASE
   ===================================================== */
html {
  scroll-behavior: smooth;
}

body {
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  min-height: 100dvh;
  /* Reserve space at the bottom equal to the fixed footer's height
     so content can scroll above it without being covered. */
  padding-bottom: var(--footer-h, 80px);
}

a {
  color: var(--c-primary);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

svg {
  display: block;
}

/* =====================================================
   NAV
   ===================================================== */
.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: var(--nav-h);
  z-index: 100;
  background: var(--c-nav-bg);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.nav::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: min(var(--max-w), 100% - 2 * var(--pad-x));
  height: 2px;
  background: linear-gradient(90deg, var(--c-primary), var(--c-accent));
}

@media (max-width: 760px) {
  .nav::after {
    width: 100%;
  }
}

.nav__inner {
  max-width: var(--content-w);
  margin: 0 auto;
  padding: 0 var(--pad-x);
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.nav__left {
  display: flex;
  align-items: center;
  gap: 12px;
}

.nav__logo svg {
  height: 28px;
  width: auto;
  color: var(--c-logo);
}

.nav__site-label {
  font-family: var(--font-head);
  font-size: 13px;
  color: var(--c-muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding-left: 12px;
  border-left: 1px solid var(--c-border);
}

.nav__links {
  display: flex;
  align-items: center;
  gap: 24px;
}

.nav__link {
  font-size: 14px;
  color: var(--c-text);
  transition: color 0.15s;
}

.nav__link:hover,
.nav__link[aria-current="page"] {
  color: var(--c-primary);
  text-decoration: none;
}


/* =====================================================
   FOOTER
   ===================================================== */
.footer {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 90;
  /* below the nav (z-index 100) */
  background: var(--c-chrome-bg);
  padding: 20px 0;
}

.footer::before {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: min(var(--max-w), 100% - 2 * var(--pad-x));
  height: 2px;
  background: linear-gradient(90deg, var(--c-primary), var(--c-accent));
}

@media (max-width: 760px) {
  .footer::before {
    width: 100%;
  }
}

.footer__inner {
  max-width: var(--content-w);
  margin: 0 auto;
  padding: 0 var(--pad-x);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

/* The feed link sits beside the credit rather than inside it. Run together as
   one sentence they read as a single line of small print, and the link that
   actually does something disappears into it. The rule and the gap are the same
   pair the nav uses between the wordmark and its label. */
.footer__left {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

.footer__copy {
  font-size: 13px;
  color: var(--c-muted);
}

.footer__feed {
  font-size: 13px;
  color: var(--c-muted);
  padding-left: 14px;
  border-left: 1px solid var(--c-border);
  transition: color 0.15s;
  white-space: nowrap;
}

.footer__feed:hover {
  color: var(--c-primary);
  text-decoration: none;
}

.footer__copy a {
  color: var(--c-muted);
  transition: color 0.15s;
}

.footer__copy a:hover {
  color: var(--c-primary);
  text-decoration: none;
}

.footer__socials {
  display: flex;
  align-items: center;
  gap: 16px;
}

.footer__socials a {
  color: var(--c-muted);
  display: flex;
  align-items: center;
  transition: color 0.15s;
}

.footer__socials a:hover {
  color: var(--c-text);
}

.footer__socials svg {
  width: 17px;
  height: 17px;
}

@media (max-width: 480px) {
  .footer__copy,
  .footer__feed {
    font-size: 11px;
  }

  .footer__left {
    gap: 10px;
  }

  .footer__feed {
    padding-left: 10px;
  }

  .footer__socials {
    gap: 11px;
  }

  .footer__socials svg {
    width: 15px;
    height: 15px;
  }
}

/* =====================================================
   HOME PAGE
   ===================================================== */
.home-content {
  max-width: var(--content-w);
  margin: 0 auto;
  padding: calc(var(--nav-h) + 2rem) var(--pad-x) 2rem;
}

/* Post preview card */
.post-preview {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: 8px;
  padding: 1.5rem 1.75rem;
  margin-bottom: 1.25rem;
  position: relative;
}

.post-preview::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  border-radius: 8px 8px 0 0;
  background: linear-gradient(90deg, var(--c-primary), var(--c-accent));
  opacity: 0;
  transition: opacity 0.15s;
}

.post-preview:hover::before {
  opacity: 1;
}

.post-preview__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 12px;
  margin-bottom: 0.5rem;
}

/* No author or separator rules: news items carry no byline, so the meta line
   holds the date alone. */
.post-preview__date {
  font-size: 12px;
  color: var(--c-muted);
}

.post-preview__title {
  font-family: var(--font-head);
  font-size: 1.3rem;
  font-weight: normal;
  margin-bottom: 0.5rem;
  line-height: 1.3;
}

.post-preview__title a {
  color: var(--c-text);
  transition: color 0.15s;
}

/* Stretch the title's link over the whole card, so a click anywhere on it
   opens the item. The card is the anchor's containing block (it is
   position: relative above), and because the overlay belongs to the anchor,
   hovering any part of the card counts as hovering the link: the title colour,
   the gradient rule, and the read-more affordance all respond together. The
   trade is that card text is no longer selectable — the overlay sits above it —
   which is a fair price on an index whose full text is one click away. */
.post-preview__title a::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 8px;
}

.post-preview__title a:hover {
  color: var(--c-primary);
  text-decoration: none;
}

/* Keyboard users get the outline they would have had on the title alone, drawn
   instead around the card the link now covers. :has(…:focus-visible) rather
   than :focus-within, so a mouse click does not leave a ring behind. */
.post-preview__title a:focus-visible {
  outline: none;
}

.post-preview:has(.post-preview__title a:focus-visible) {
  outline: 2px solid var(--c-primary);
  outline-offset: 2px;
}

.post-preview__desc {
  font-size: 15px;
  color: var(--c-muted);
  margin-bottom: 1rem;
  line-height: 1.6;
}

/* Not a link — the stretched title anchor above already covers the card. It is
   the cue that the card is clickable, so it shows only while the card is
   highlighted, and its space stays reserved so revealing it never reflows the
   list. Devices that cannot hover have nothing to reveal it, so there it
   simply stays visible. */
.post-preview__read-more {
  display: inline-block;
  font-size: 13px;
  color: var(--c-primary);
  font-weight: 500;
}

@media (hover: hover) {
  .post-preview__read-more {
    opacity: 0;
    transition: opacity 0.15s;
  }

  .post-preview:hover .post-preview__read-more,
  .post-preview:has(.post-preview__title a:focus-visible) .post-preview__read-more {
    opacity: 1;
  }
}

/* Pagination */
.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin-top: 2rem;
  padding-bottom: 1rem;
}

.pagination a {
  font-size: 14px;
  color: var(--c-primary);
  padding: 6px 14px;
  border: 1px solid var(--c-border);
  border-radius: 4px;
  transition: background 0.15s, color 0.15s;
}

.pagination a:hover {
  background: var(--c-primary);
  color: #fff;
  text-decoration: none;
}

.pagination__info {
  font-size: 13px;
  color: var(--c-muted);
}

/* =====================================================
   ARCHIVE / TOPICS PAGES
   ===================================================== */
.archive-content {
  max-width: var(--content-w);
  margin: 0 auto;
  padding: calc(var(--nav-h) + 1.5rem) var(--pad-x) 4rem;
}

.archive-content__heading {
  font-family: var(--font-head);
  font-size: clamp(1.4rem, 3.5vw, 2rem);
  font-weight: normal;
  margin-bottom: 1.5rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--c-border);
}

.archive-list__year {
  margin-bottom: 2.25rem;
  /* So anchor jumps don't put the heading under the fixed nav. */
  scroll-margin-top: calc(var(--nav-h) + 1rem);
}

.archive-list__year-heading {
  font-family: var(--font-head);
  font-size: 1.4rem;
  font-weight: normal;
  color: var(--c-primary);
  margin-bottom: 0.75rem;
}

.archive-list__month {
  font-family: var(--font-head);
  font-size: 1.05rem;
  font-weight: normal;
  color: var(--c-muted);
  margin: 1.25rem 0 0.5rem;
}

.archive-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.archive-list__item {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: 0.4rem 0;
  border-bottom: 1px dotted var(--c-border);
}

.archive-list__item:last-child {
  border-bottom: none;
}

.archive-list__date {
  flex-shrink: 0;
  font-size: 12px;
  color: var(--c-muted);
  font-variant-numeric: tabular-nums;
  min-width: 8em;
}

.archive-list__title {
  color: var(--c-text);
  text-decoration: none;
  transition: color 0.15s;
}

.archive-list__title:hover {
  color: var(--c-primary);
  text-decoration: none;
}

@media (max-width: 600px) {
  .archive-list__item {
    flex-direction: column;
    gap: 0.1rem;
  }

  .archive-list__date {
    min-width: 0;
  }
}

/* =====================================================
   POST PAGE
   ===================================================== */
.post-content {
  max-width: var(--content-w);
  margin: 0 auto;
  padding: calc(var(--nav-h) + 1.5rem) var(--pad-x) 4rem;
}

/* Post header (injected by news.js) */
.post-header {
  margin-bottom: 2.5rem;
  padding-bottom: 1.5rem;
  border-bottom: 1px solid var(--c-border);
}

.post-header__back {
  display: inline-block;
  font-size: 13px;
  color: var(--c-muted);
  margin-bottom: 1rem;
  transition: color 0.15s;
}

.post-header__back:hover {
  color: var(--c-primary);
  text-decoration: none;
}

.post-header__title {
  font-family: var(--font-head);
  font-size: clamp(1.4rem, 3.5vw, 2rem);
  font-weight: normal;
  line-height: 1.2;
  color: var(--c-text);
  margin-bottom: 0.75rem;
}

.post-header__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  font-size: 13px;
  color: var(--c-muted);
}

/* Typography */
.post-content h1,
.post-content h2,
.post-content h3,
.post-content h4 {
  font-family: var(--font-head);
  font-weight: normal;
  color: var(--c-text);
  line-height: 1.25;
}

.post-content h1 {
  font-size: 1.6rem;
  margin: 2.5rem 0 0.8rem;
}

.post-content h2 {
  font-size: 1.3rem;
  margin: 2rem 0 0.6rem;
}

.post-content h3 {
  font-size: 1.05rem;
  margin: 1.6rem 0 0.5rem;
}

.post-content h4 {
  font-size: 0.95rem;
  margin: 1.4rem 0 0.4rem;
}

.post-content p {
  margin-bottom: 1.1em;
  text-align: justify;
  hyphens: auto;
  text-wrap: pretty;
}

.post-content ul,
.post-content ol {
  padding-left: 1.75em;
  margin: 1em 0;
}

.post-content li {
  margin-bottom: 0.4em;
}

.post-content a {
  color: var(--c-primary);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* Experiment: render bold in the brand display font (like the wordmark). */
.post-content strong {
  font-family: var(--font-head);
  font-weight: normal;
  font-style: normal;
  /* Same x-height nudge as .wordmark, since it's the same font swap. */
  font-size: 1.185em;
}

.post-content em {
  font-style: italic;
}

/* Spooqy brand wordmark — display font, never bold or italic */
.post-content .wordmark {
  font-family: var(--font-head);
  font-weight: normal;
  font-style: normal;
  /* BauhausBugler has a smaller x-height than the body font; nudge up to match. */
  font-size: 1.185em;
  color: var(--c-primary);
}

/* Inline code */
.post-content code {
  font-family: var(--font-mono);
  font-size: 0.88em;
  background: var(--c-border);
  border-radius: 3px;
  padding: 0.1em 0.35em;
}

/* Code blocks */
.post-content pre {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: 6px;
  padding: 1rem 1.25rem;
  overflow-x: auto;
  margin: 1.5em 0;
}

.post-content pre code {
  background: none;
  padding: 0;
  font-size: 0.85em;
  line-height: 1.6;
}

/* Images */
.post-content img {
  max-width: 100%;
  height: auto;
  display: block;
  margin: 1.5em auto;
  border-radius: 4px;
}

/* Figures */
.post-content figure {
  margin: 1.5em 0;
  text-align: center;
}

/* Multi-image grid inside a single <figure>. Used as a workaround for
   Typst's HTML export silently dropping native grid() contents. */
.post-content .image-grid-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin: 0 auto;
}

.post-content .image-grid-2 img {
  width: 100%;
  height: auto;
  display: block;
}

/* On narrow phones, stack the grid so each image stays legible
   rather than shrinking to a quarter of the screen width. */
@media (max-width: 480px) {
  .post-content .image-grid-2 {
    grid-template-columns: 1fr;
  }
}

.post-content figure figcaption {
  font-size: 15px;
  color: var(--c-muted);
  margin-top: 0.75em;
  line-height: 1.5;
}

/* Tables */
.post-content table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--font-mono);
  font-size: 14px;
  margin: 1.5em 0;
}

.post-content th,
.post-content td {
  border: 1px solid var(--c-border);
  padding: 0.5em 0.75em;
  text-align: left;
}

.post-content th {
  background: var(--c-surface);
  font-weight: 600;
  color: var(--c-text);
}

.post-content tr:nth-child(even) td {
  background: color-mix(in srgb, var(--c-bg) 60%, transparent);
}

/* Blockquotes */
.post-content blockquote {
  border-left: 3px solid var(--c-primary);
  padding: 0.5em 0 0.5em 1.25em;
  margin: 1.5em 0;
  color: var(--c-muted);
  font-style: italic;
}

/* =====================================================
   MATH
   ===================================================== */
.math-inline {
  display: inline;
}

/* Typst's html.frame draws the inline math via a flipped transform whose
   translate component puts the math baseline at the SVG's bottom edge
   (descenders escape the viewBox via overflow: visible). Aligning the
   SVG's bottom to the text baseline therefore aligns the math baseline
   with the surrounding text baseline. */
.math-inline svg {
  /* Typst 0.15+ emits inline `style="display: block"` on the SVG; override
     it with !important so inline math actually sits inline with the text. */
  display: inline !important;
  /* A hair above baseline — the math font's optical baseline sits a touch
     lower than the body font's at the same nominal baseline. */
  vertical-align: 0.02em;
}

.math-block {
  display: block;
  text-align: center;
  margin: 1.75em 0;
  overflow-x: auto;
}

.math-block svg {
  max-width: 100%;
  height: auto;
  display: block;
  margin: 0 auto;
}

/* =====================================================
   RESPONSIVE
   ===================================================== */
@media (max-width: 600px) {
  body {
    font-size: 15px;
    line-height: 1.6;
  }

  .post-content {
    padding: calc(var(--nav-h) + 1rem) var(--pad-x) 3rem;
    font-size: 15px;
  }

  .home-content {
    padding: calc(var(--nav-h) + 1.5rem) var(--pad-x) 1.5rem;
  }

  .post-preview {
    padding: 1.25rem;
  }

  .post-content h1 {
    font-size: 1.35rem;
  }

  .post-content h2 {
    font-size: 1.15rem;
  }

  .post-content h3 {
    font-size: 1rem;
  }

  .post-header__title {
    font-size: 1.2rem;
  }
}