/* ==========================================================================
   layout.css — the notebook
   Depends on tokens.css. Section-specific rules live in their own files.
   ========================================================================== */

/* ---- media never sets the page width -------------------------------------
   Several content images carry a fixed width attribute. A percentage max-width
   is ignored while the browser computes min-content, so without an explicit
   floor they widen their grid track and push the page sideways on small screens. */
img, svg, video, canvas, iframe { max-inline-size: 100%; block-size: auto; }
img { min-inline-size: 0; }

/* ---- containers --------------------------------------------------------- */
.shell {
  width: 100%;
  max-inline-size: var(--width-shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.site-main { display: block; }

/* ==========================================================================
   Header
   ========================================================================== */
.site-header { position: sticky; top: 0; z-index: 1030; }

.navbar-main {
  background: color-mix(in srgb, var(--paper) 97%, transparent);
  backdrop-filter: blur(10px) saturate(1.1);
  border-block-end: 1px solid var(--rule);
}
.navbar-main__inner {
  display: flex; align-items: center; gap: .5rem 1rem; flex-wrap: wrap;
  min-block-size: 3.5rem; padding-block: .35rem;
}
/* On a narrow phone the three section links drop to their own row rather than
   pushing the page sideways. */
@media (max-width: 30rem) {
  .navbar-main__inner { gap: .15rem .5rem; }
  .navbar-main__links { order: 3; flex-basis: 100%; margin: 0; justify-content: center; }
  .navbar-main__links a { padding-inline: .5rem; }
  .brand__host { font-size: 1rem; }
}

.brand { display: flex; align-items: center; gap: .45rem; text-decoration: none; color: var(--ink); }
.brand__join {
  inline-size: .95rem; block-size: .5rem; flex: none;
  background-color: var(--brand-join);
  -webkit-mask-image: var(--dash); mask-image: var(--dash);
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
.brand__mark { flex: none; inline-size: 1.9rem; display: block; }
.brand__mark .mascot { inline-size: 100%; }
.brand__host { font-family: var(--font-hand); font-size: 1.1rem; color: var(--ink); letter-spacing: .005em; }
.navbar-main__links { display: flex; flex-wrap: wrap; list-style: none;
                      margin: 0 auto; padding: 0; gap: .1rem .35rem; }
.navbar-main__links a {
  position: relative; display: block; padding: .45rem .7rem .5rem;
  border-radius: var(--radius); text-decoration: none; color: var(--ink);
  font-size: var(--step--1);
}
.navbar-main__links a:hover { background: var(--paper-sunken); }
.navbar-main__links a[aria-current] { font-weight: 700; }
.navbar-main__links a[aria-current]::after { content: ""; }

.btn-icon { color: var(--ink); border: 1px solid transparent; background: none; border-radius: var(--radius); }
.btn-icon:hover { background: var(--paper-sunken); border-color: var(--rule); color: var(--ink); }

/* ==========================================================================
   Notice band
   ========================================================================== */
.notice-band { padding-block-start: 1rem; }
.notice {
  max-inline-size: var(--width-shell); margin-inline: auto;
  margin-block-end: .5rem;
  display: flex; gap: .6rem; align-items: flex-start;
  padding: .7rem 1rem; border-radius: var(--radius);
  font-size: var(--step--1);
  background: var(--surf-amber); border: 1px solid var(--rule);
  border-inline-start: 3px solid var(--orange-gfx);
}
.notice i { color: var(--orange-ink); }
.notice--danger { background: var(--surf-red); border-inline-start-color: var(--red-gfx); }
.notice--danger i { color: var(--red-ink); }

/* ==========================================================================
   Prose — the reading column
   ========================================================================== */
.prose { max-inline-size: var(--width-content); margin-inline: auto; }
.prose > * { max-inline-size: var(--measure); }
.prose > .breakout, .prose > .table-scroll, .prose > figure, .prose > .callout { max-inline-size: none; }

.prose p, .prose li { overflow-wrap: break-word; }
.prose p { margin-block: 0 .95em; text-wrap: pretty; }
.prose h1, .prose h2, .prose h3, .prose h4, .prose h5, .prose h6 {
  font-family: var(--font-hand); line-height: var(--leading-tight); text-wrap: balance;
  margin-block: 2.2em .55em; scroll-margin-block-start: 5rem;
}
.prose h1 { font-size: var(--step-3); margin-block-start: 0; }
.prose h2 { font-size: var(--step-2); }
.prose h3 { font-size: var(--step-1); }
.prose h4, .prose h5, .prose h6 { font-size: var(--step-0); font-weight: 700; }
.prose ul, .prose ol { padding-inline-start: 1.4rem; margin-block-end: 1em; }
.prose li + li { margin-block-start: .3em; }
.prose a { color: var(--blue-ink); text-underline-offset: .18em; text-decoration-thickness: 1px; }
.prose a:hover { text-decoration-thickness: 2px; }
.prose strong { font-weight: 700; }
.prose hr { border: 0; border-block-start: 1px solid var(--rule); margin-block: 2rem; }
.prose blockquote {
  margin: 1.4rem 0; padding: .2rem 0 .2rem 1.1rem;
  border-inline-start: 3px solid var(--rule-strong); color: var(--ink-muted); font-style: italic;
}
.prose blockquote > :first-child { margin-block-start: 0; }
.prose blockquote > :last-child { margin-block-end: 0; }
.prose img { max-inline-size: 100%; height: auto; }
.prose figure { margin: 1.6rem 0; }
.prose figcaption { font-size: var(--step--1); color: var(--ink-muted); margin-block-start: .5rem; }

/* Anchor links on headings — revealed on hover or keyboard focus */
.heading-anchor {
  margin-inline-start: .4em; opacity: 0; text-decoration: none;
  color: var(--ink-muted); font-size: .7em; vertical-align: middle;
}
:is(h1,h2,h3,h4,h5,h6):hover .heading-anchor, .heading-anchor:focus-visible { opacity: 1; }
@media (hover: none) { .heading-anchor { opacity: .5; } }

/* Selection keeps its own ink color, so muted text stays legible when highlighted. */
::selection { background: var(--selection); color: var(--ink); }

/* ---- notation ----------------------------------------------------------- */
.excalifont { font-family: var(--font-hand); overflow-wrap: break-word; }
/* Display formulas scroll inside themselves; inline runs of symbol images wrap. */
div.excalifont, .math-display { overflow-x: auto; overscroll-behavior-x: contain; }
/* inline formal-language notation keeps the hand */
.shanns, .math-inline, .math-content, .Boolean, .Kleene { font-family: var(--font-formal); }
/* multi-line source code is read character by character: a code face, at a
   readable size, upright. Comic Shanns' slant costs comprehension here. */
code, kbd, samp, pre, pre code, .chroma, .highlight { font-family: var(--font-code); font-style: normal; }
/* Ordinary Markdown code keeps its programming face; dollar math uses Shanns. */
code { color: inherit; background: none; border: 0; padding: 0;
       font-size: 1.02em; overflow-wrap: break-word; }
pre { background: var(--paper-sunken); border: 1px solid var(--rule); border-radius: var(--radius);
      padding: .9rem 1rem; overflow-x: auto; max-inline-size: 100%;
      font-size: .92em; line-height: 1.6; tab-size: 2; }
pre code { background: none; border: 0; padding: 0; }

/* Chroma emits line numbers as a table inside div.highlight; the wrapper has to
   be the scroller or long code lines push the whole page sideways. */
/* .85em is the size these blocks have always rendered at: .92em used to be
   applied twice, because transform.Highlight's own .highlight wrapper sat
   inside ours. Now that there is one wrapper, the declared size is the real
   one — and it has to stay at .85 so the longest lines still fit without a
   sideways scroll. */
.highlight { overflow-x: auto; overscroll-behavior-x: contain; max-inline-size: 100%;
             border: 1px solid var(--rule); border-radius: var(--radius); background: var(--paper-sunken);
             font-size: .85em; line-height: 1.6; margin-block: 1.4rem; }
.code-block { margin-block: 1.4rem; }
.highlight pre, .highlight .chroma { overflow: visible; border: 0; margin: 0; background: none; }
.highlight table, .highlight .lntable { inline-size: auto; margin: 0; border: 0; }
.highlight .lntable td { border: 0; padding: 0; text-align: start; }
.highlight .lnt { color: var(--ink-muted); user-select: none; }
.math-display, .set-figure { font-size: 1.05em; font-weight: 400; line-height: 1.35; margin-block: .75em; }


/* Two formal languages must differ by more than hue (WCAG 1.4.1) */
.Boolean { font-family: var(--font-formal); color: var(--blue-ink);
           text-decoration: underline solid 1px; text-underline-offset: .22em; }
.Kleene  { font-family: var(--font-formal); color: var(--red-ink);
           text-decoration: underline dotted 2px; text-underline-offset: .22em; }
.dark-green { color: var(--green-ink); } .dark-blue { color: var(--blue-ink); }
.dark-red { color: var(--red-ink); }     .dark-orange { color: var(--orange-ink); }

/* Remaining raster inline art (dice, portraits, vendor logos) has an opaque
   white ground, so it sits on a small light card rather than being inverted. */
.inline-icon { vertical-align: -.12em; border-radius: 3px; }
:root[data-theme="dark"] .inline-icon,
:root:not([data-theme="light"]) .inline-icon { background: #FBFAF7; }

/* ---- tables -------------------------------------------------------------
   Ruled the way you would rule a table by hand: a stroke between the columns
   and one under the header, and nothing else. The strokes are SVG masks, so a
   single asset takes any colour and stretches to any cell height. Each path
   starts and ends at the same offset, so the segments drawn by adjacent cells
   meet instead of stepping.

   Scoped to .table-scroll: Chroma renders line numbers as a table too, and it
   must not be ruled.
   -------------------------------------------------------------------------- */
:root {
  --rule-hand-v: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 100' preserveAspectRatio='none'%3E%3Cpath d='M5,0 C7.1,17 2.6,31 5,47 C7.4,63 2.7,85 5,100' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round'/%3E%3C/svg%3E");
  --rule-hand-h: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 10' preserveAspectRatio='none'%3E%3Cpath d='M0,5 C17,7.1 31,2.7 47,5 C63,7.3 85,2.6 100,5' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round'/%3E%3C/svg%3E");
}

.table-scroll {
  overflow-x: auto; margin-block: 1.5rem;
  border-radius: var(--radius);
}
.table-scroll:focus-visible { outline: 3px solid var(--focus-inner); outline-offset: 2px; }

.table-scroll table {
  border-collapse: collapse; margin: 0 auto; inline-size: auto; max-inline-size: 100%;
  font-variant-numeric: tabular-nums; font-size: var(--step--1);
}
.table-scroll :is(th, td) {
  position: relative; border: 0;
  padding: .5rem .95rem; text-align: center; vertical-align: top;
}
/* a layout grid wants tighter, quieter spacing than a data table */
.table--headless :is(th, td) { padding-block: .3rem; }
.table-scroll :is(th, td):first-child { text-align: start; }
.table-scroll thead th { font-weight: 700; background: none; padding-block-end: .65rem; }
.table-scroll tbody tr:first-child :is(th, td) { padding-block-start: .65rem; }
.table-scroll caption {
  caption-side: top; padding: .25rem .5rem .6rem; font-size: var(--step--1);
  color: var(--ink-muted); text-align: start;
}

/* Between the columns — but only on real data tables. Every headless table in
   this book is a layout grid aligning a formula with its name, and ruling those
   is noise. A header row is what marks a table as data. */
.table-scroll table:not(.table--headless) :is(th, td):not(:last-child)::after {
  content: ""; position: absolute; inset-block: 0; inset-inline-end: -4px;
  inline-size: 8px; background-color: var(--table-rule);
  -webkit-mask-image: var(--rule-hand-v); mask-image: var(--rule-hand-v);
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  pointer-events: none;
}
/* and one under the header */
.table-scroll thead th::before,
.table-scroll thead td::before {
  content: ""; position: absolute; inset-inline: 0; inset-block-end: -4px;
  block-size: 8px; background-color: var(--table-rule);
  -webkit-mask-image: var(--rule-hand-h); mask-image: var(--rule-hand-h);
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  pointer-events: none;
}
@media (forced-colors: active) {
  .table-scroll table:not(.table--headless) :is(th, td):not(:last-child)::after,
  .table-scroll thead :is(th, td)::before {
    background-color: CanvasText;
    -webkit-mask-image: none; mask-image: none;
  }
}

/* ==========================================================================
   Mascot sizing
   ========================================================================== */
.mascot-sm { inline-size: 3rem; }
.mascot-md { inline-size: 6rem; }
.mascot-lg { inline-size: min(15rem, 40vw); }
.mascot-hero { inline-size: min(18rem, 46vw); }

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn-hand {
  display: inline-flex; align-items: center; gap: .5rem;
  font-family: var(--font-hand); font-size: var(--step-0);
  padding: .55rem 1.15rem; text-decoration: none;
  color: var(--ink); background: var(--paper-raised);
  border: 2px solid var(--ink);
  border-radius: 255px 14px 225px 16px / 16px 225px 16px 255px;   /* sketched box */
  box-shadow: 2px 2px 0 var(--ink);
  transition: transform .12s ease, box-shadow .12s ease;
}
.btn-hand:hover { transform: translate(-1px, -1px); box-shadow: 3px 3px 0 var(--ink); color: var(--ink); }
.btn-hand:active { transform: translate(1px, 1px); box-shadow: 1px 1px 0 var(--ink); }
.btn-hand--primary { background: var(--surf-red); border-color: var(--red-ink); box-shadow: 2px 2px 0 var(--red-ink); }
.btn-hand--primary:hover { box-shadow: 3px 3px 0 var(--red-ink); }

/* ==========================================================================
   Back to top
   ========================================================================== */
.back-to-top {
  position: fixed; inset-block-end: 1rem; inset-inline-end: 1rem; z-index: 1020;
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .5rem .85rem; font-size: var(--step--1);
  background: var(--paper-raised); color: var(--ink);
  border: 1px solid var(--rule-strong); border-radius: 999px;
  box-shadow: var(--shadow-raised);
  opacity: 0; visibility: hidden; transition: opacity .2s ease, visibility .2s ease;
}
.back-to-top.is-visible { opacity: 1; visibility: visible; }
.back-to-top:focus-visible { opacity: 1; visibility: visible; }

/* ==========================================================================
   Footer
   ========================================================================== */
.site-footer { margin-block-start: 4rem; border-block-start: 1px solid var(--rule);
               background: var(--paper-sunken); }
.site-footer__inner {
  display: grid; gap: 1.25rem 2rem; padding-block: 2rem 1.5rem;
  grid-template-columns: 1fr;
  font-size: var(--step--1); color: var(--ink-muted);
}
@media (min-width: 48rem) { .site-footer__inner { grid-template-columns: repeat(3, 1fr); } }
.site-footer__col { margin: 0; }
.site-footer__head { display: block; font-weight: 700; color: var(--ink); margin-block-end: .25rem; }
.site-footer a { color: var(--blue-ink); }
.site-footer__meta { margin: 0; grid-column: 1 / -1; padding-block-start: 1rem;
                     border-block-start: 1px solid var(--rule); font-family: var(--font-code);
                     font-size: var(--step--2); }

/* ==========================================================================
   Print — the notebook page, without the furniture
   ========================================================================== */
@media print {
  body { background: #fff !important; background-image: none !important; color: #000; font-size: 11pt; }
  .site-header, .site-footer, .offcanvas, .back-to-top, .chapter-rail, .on-this-page,
  .page-nav, .theme-toggle, .skip-link, .notice-band { display: none !important; }
  .prose, .prose > * { max-inline-size: none; }
  .prose a::after { content: " (" attr(href) ")"; font-size: .85em; word-break: break-all; }
  .prose a[href^="#"]::after { content: ""; }
  .callout { break-inside: avoid; border: 1px solid #999; }
  .table-scroll { break-inside: avoid; }
  h1, h2, h3 { break-after: avoid; }
}

/* the ∀I wordmark when it appears inline in prose */
.logo-mark { font-family: var(--font-hand); white-space: nowrap; }

/* Floated content images crush the measure on a narrow screen: below the
   tablet breakpoint they become full-width blocks instead. */
@media (max-width: 47.99rem) {
  .prose .float-end, .prose .float-start,
  .prose [class*="float-"] { float: none !important; display: block;
                             margin-inline: auto !important; margin-block: 1rem !important; }
}

/* ==========================================================================
   The hand-drawn underline
   A mask lets one squiggle take any color, in either theme.
   ========================================================================== */
.underline-hand::after,
.navbar-main__links a[aria-current]::after {
  content: "";
  position: absolute; inset-inline: .25rem; inset-block-end: .05rem;
  block-size: .42em;
  background-color: var(--red-gfx);
  -webkit-mask-image: var(--squiggle); mask-image: var(--squiggle);
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  pointer-events: none;
}
:root {
  /* Drawn fast, not carefully: a thick stroke, gentle drift, one slight tilt —
     the way you underline a heading, not the way you rule a line. */
  --squiggle: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 12' preserveAspectRatio='none'%3E%3Cpath d='M2,8.4 C18,6.1 34,5.4 52,4.9 68,4.5 84,4.6 98,3.4' fill='none' stroke='%23000' stroke-width='5.2' stroke-linecap='round'/%3E%3C/svg%3E");
  --dash: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 12' preserveAspectRatio='none'%3E%3Cpath d='M3,7.6 C13,6.2 26,5.6 37,4.6' fill='none' stroke='%23000' stroke-width='5' stroke-linecap='round'/%3E%3C/svg%3E");
}
@media (forced-colors: active) {
  .underline-hand::after, .navbar-main__links a[aria-current]::after {
    background-color: CanvasText;
  }
}

/* the hand-drawn buttons keep their label on one line */
.btn-hand { white-space: nowrap; }
.btn-hand .hand-icon { inline-size: 1.35em; block-size: 1.35em; flex: none; }
.hand-icon { display: inline-block; vertical-align: -.28em; color: inherit; }

/* the active mark, wherever it appears */
.chapter-rail__item.is-current,
.prose .chapter-rail__item.is-current { background: none; border-inline-start-color: transparent; position: relative; }

/* ==========================================================================
   Weight for a single-weight handwriting face
   Excalifont ships one weight. font-weight:700 would trigger the browser's
   synthetic bold, which smears the strokes. A hairline text-stroke thickens
   the same outline evenly instead.
   ========================================================================== */
.prose h1, .prose h2, .prose h3,
.hero__title, .section-head, .section-head-block__title, .chapter__name {
  font-weight: 400;
  -webkit-text-stroke: .016em currentColor;
  paint-order: stroke fill;
}
.prose h4, .prose h5, .prose h6 { -webkit-text-stroke: .012em currentColor; }

/* 15 — sections were separated by more air than they needed */
.prose h2 { margin-block: 1.5em .45em; }
.prose h3 { margin-block: 1.3em .4em; }
.prose h4, .prose h5, .prose h6 { margin-block: 1.15em .35em; }

/* ==========================================================================
   Figures
   The book's diagrams are Excalidraw screenshots on a white ground. On paper,
   multiply lets the white disappear. In dark mode the colors cannot be
   inverted without lying about them, so the figure sits on its own light
   card instead — a printout pasted into the notebook.
   ========================================================================== */
.prose :is(p, div) > img:not(.inline-icon),
.prose > img:not(.inline-icon) { border-radius: 4px; }
:root .prose img:not(.inline-icon):not(.book-figure > img) { mix-blend-mode: multiply; }
:root[data-theme="dark"] .prose img:not(.inline-icon):not(.book-figure > img),
:root[data-theme="dark"] .figure-card { mix-blend-mode: normal; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .prose img:not(.inline-icon):not(.book-figure > img) { mix-blend-mode: normal; }
}
:root[data-theme="dark"] .prose img:not(.inline-icon):not(.book-figure > img) {
  background: #FBFAF7; padding: .5rem; border-radius: 6px;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .prose img:not(.inline-icon):not(.book-figure > img) {
    background: #FBFAF7; padding: .5rem; border-radius: 6px;
  }
}

/* ==========================================================================
   Logic symbols
   Traced from the original drawings. currentColor means one asset serves
   paper, dark mode and forced colors, and it scales with the text.
   ========================================================================== */
.sym { display: inline-block; vertical-align: -.12em; line-height: 1; margin-inline: .06em; }
.sym .sym__svg { display: block; block-size: calc(var(--sym-scale, .75) * 1em);
                 inline-size: auto; fill: currentColor; }


/* A logic symbol rendered as text, in the patched object-language face. */
.notation { font-family: var(--font-formal); font-style: normal; }

/* ==========================================================================
   The mascot in dark mode
   The mark is line art and follows currentColor. The figures carry real color
   (skin, antenna tips, the heart), so they are inverted whole: that keeps every
   internal relationship intact — pupil against eye-white, ink against skin —
   which per-part recoloring does not.
   ========================================================================== */
:root[data-theme="dark"] .mascot--figure { filter: invert(1) hue-rotate(180deg); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .mascot--figure { filter: invert(1) hue-rotate(180deg); }
}
@media (forced-colors: active) { .mascot--figure { filter: none; } }

/* 10 — the outbound marker leads the link, close to it */
.link-external { font-size: .78em; margin-inline-end: .18em; vertical-align: .04em; opacity: .75; }
.link-out:hover .link-external { opacity: 1; }

.math-display { text-align: center; }
.math-inline { font-size: 1.02em; }
.math-display > .math-content { display: inline-block; white-space: pre; tab-size: 24; text-align: start; font-size: 1em; font-weight: 400; vertical-align: middle; }
