/* ==========================================================================
   krx theme - reconstruction of the last published krx.one appearance.

   Measurements taken from the original Kraft theme:
     container .wrap   max-width 1170px (970 / 750 at smaller breakpoints)
     header            logo left, menu right, uppercase, sticky
     project page      media 66.66% left, details 33.33% right, 50px gutter
     project title     38px / 44px, subtitle 20px
     body text         left aligned, full container width
   All design tokens live here; content files never carry styling.
   ========================================================================== */

:root {
  --ink: #000;
  --paper: #fff;
  --muted: #707070;
  --line: #e4e4e4;
  --accent: #dd3333;

  --font: 'Barlow', 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, sans-serif;
  --w-body: 400;
  --w-med: 500;
  --w-bold: 600;

  --wrap: 1170px;            /* the original container width */
  --gap: 30px;               /* the original grid gap        */
  --gutter: 50px;            /* media / details gutter       */
  --pad: 15px;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-weight: var(--w-body);
  font-size: 17px;
  line-height: 1.65;
  text-align: left;
  font-synthesis-weight: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }
a { color: inherit; }

.wrap {
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--pad);
  width: 100%;
}

.skip { position: absolute; left: -9999px; top: 0; background: var(--ink);
        color: var(--paper); padding: .6rem 1rem; z-index: 100; }
.skip:focus { left: 0; }

/* ---------------------------------------------------------------- header */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: var(--paper);
  border-bottom: 1px solid var(--line);
}
.site-header .wrap {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--gutter);
}

/* The logo file itself carries a little built-in whitespace to its left
   (common in exported logo art), which was throwing it out of alignment
   with the page text below it. Nudging the whole link left compensates. */
.brand { display: block; line-height: 0; padding: 25px 0 21px; flex: 0 0 auto;
         margin-left: -5px; }
.brand .logo { width: 320px; height: auto; max-width: 100%; }

.site-nav { display: flex; flex-wrap: wrap; gap: 1.9rem; justify-content: flex-end; }
.site-nav a {
  text-decoration: none;
  text-transform: uppercase;
  font-weight: var(--w-med);
  font-size: .82rem;
  letter-spacing: 1px;
  color: var(--muted);
  padding: .35rem 0;
  transition: color .15s, font-weight .15s;
  white-space: nowrap;
}
.site-nav a:hover, .site-nav a:focus-visible { color: var(--ink); }
.site-nav a.is-active { color: var(--ink); font-weight: var(--w-bold); }

/* Reserve the bold width up front so switching weight never reflows the bar. */
.site-nav a::after {
  content: attr(data-label);
  display: block; height: 0; overflow: hidden; visibility: hidden;
  font-weight: var(--w-bold); pointer-events: none;
}

.nav-checkbox { position: absolute; opacity: 0; width: 0; height: 0;
                pointer-events: none; }
.nav-toggle {
  display: none;
  align-items: center; gap: .55rem;
  font-size: .8rem; font-weight: var(--w-med);
  text-transform: uppercase; letter-spacing: 1px;
  border: 1px solid var(--line); border-radius: 2px;
  padding: .4rem .7rem; cursor: pointer; color: var(--muted);
  user-select: none;
}
.nav-checkbox:focus-visible ~ .nav-toggle { outline: 2px solid var(--accent);
                                            outline-offset: 2px; }

/* three bars -> an X while open, pure CSS (the middle bar is the span
   itself, top/bottom are its pseudo-elements) - no icon font, no image. */
.burger, .burger::before, .burger::after {
  display: block; width: 16px; height: 2px;
  background: currentColor; border-radius: 1px;
  transition: transform .2s ease, opacity .2s ease, background .2s ease;
}
.burger { position: relative; flex: 0 0 auto; }
.burger::before, .burger::after { content: ""; position: absolute; left: 0; }
.burger::before { top: -5px; }
.burger::after { top: 5px; }
.nav-checkbox:checked ~ .nav-toggle .burger { background: transparent; }
.nav-checkbox:checked ~ .nav-toggle .burger::before { top: 0; transform: rotate(45deg); }
.nav-checkbox:checked ~ .nav-toggle .burger::after { top: 0; transform: rotate(-45deg); }

/* ---------------------------------------------------------------- layout */
main { padding-block: 45px 70px; }

.page-head { margin-bottom: 34px; }
.page-head h1, .project-text-head h1 {
  font-size: 38px; line-height: 44px;
  font-weight: var(--w-med);
  margin: 0 0 2px;
}
.subtitle { margin: 0 0 0; font-size: 20px; line-height: 1.4; color: var(--muted); }
.project-text-head .subtitle { margin-bottom: 18px; }

/* ---------------------------------------------------------------- prose */
.prose { text-align: left; }
.prose > :first-child { margin-top: 0; }

.prose h2 {
  font-size: 1.35rem; font-weight: var(--w-bold);
  margin: 2.4rem 0 .7rem; line-height: 1.3;
}

/* h3 and everything deeper render identically on purpose: mis-nested
   headings can never make the page look broken. */
.prose h3, .prose h4, .prose h5, .prose h6 {
  font-size: 1.05rem; font-weight: var(--w-bold);
  margin: 1.9rem 0 .45rem; line-height: 1.35;
}

.prose p { margin: 0 0 1.1rem; }
.prose ul, .prose ol { margin: 0 0 1.1rem; padding-left: 1.25rem; }
.prose li { margin: .22rem 0; }
.prose li > ul, .prose li > ol { margin: .22rem 0; }

.prose a { color: var(--ink); text-decoration: underline;
           text-underline-offset: 2px; text-decoration-thickness: 1px;
           text-decoration-color: var(--muted); }
.prose a:hover { color: var(--accent); text-decoration-color: var(--accent); }

.prose hr { border: 0; border-top: 1px solid var(--line); margin: 2.4rem 0; }
.prose blockquote { margin: 1.4rem 0; padding-left: 1.1rem;
                    border-left: 2px solid var(--line); color: var(--muted); }
.prose code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
              font-size: .88em; background: #f3f3f3; padding: .1em .35em; }
.prose pre { background: #f6f6f6; padding: 1rem; overflow-x: auto; }
.prose pre code { background: none; padding: 0; }

.md-figure { margin: 0 0 var(--gap); }
.md-figure img { width: 100%; }
.md-figure .zoom { display: block; cursor: zoom-in; }

/* A YouTube/Vimeo embed sits in the same .md-figure as an image, so it
   automatically gets the exact same width and spacing as the photos next
   to it - nothing embed-specific needs sizing here beyond the player's own
   16:9 shape. */
.md-figure iframe { display: block; width: 100%; aspect-ratio: 16 / 9; border: 0; }

/* Vimeo's own player chrome gives no strong visual cue that this is a
   video before it plays (unlike YouTube's big red play button); this small
   title/badge overlay makes up for that. Purely decorative - pointer-events
   is off so a click always reaches the player underneath, never the label. */
.video-frame { position: relative; display: block; }
.video-overlay {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: .6rem;
  padding: .6rem .7rem;
  background: linear-gradient(to top, rgba(0, 0, 0, .68), rgba(0, 0, 0, 0) 70%);
  pointer-events: none;
}
.video-overlay-title {
  color: #fff;
  font-size: .82rem; font-weight: var(--w-med); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.video-overlay-badge {
  flex: 0 0 auto;
  color: #fff;
  font-size: .66rem; font-weight: var(--w-bold);
  text-transform: uppercase; letter-spacing: .5px;
  background: rgba(255, 255, 255, .22);
  border: 1px solid rgba(255, 255, 255, .5);
  border-radius: 2px;
  padding: .18rem .4rem;
}

/* -------------------------------------------------- click to enlarge
   Pure CSS. The small picture is a link to a hidden overlay; the overlay is
   shown only while it is the :target of the URL. Clicking it sets the hash to
   something that matches nothing, so it closes again. No JavaScript.        */
.lightbox {
  display: none;
  position: fixed; inset: 0; z-index: 200;
  background: rgba(0, 0, 0, .92);
  padding: 2.5vmin;
  align-items: center; justify-content: center;
}
.lightbox:target { display: flex; }
.lightbox > img {
  max-width: 100%; max-height: 95vh;
  width: auto; height: auto;
  object-fit: contain;
  cursor: zoom-out;
  /* CSS stacks ANY positioned element above a plain static one, no matter
     which comes first in the HTML - so without this, .lightbox-close
     (position: absolute, covering the whole overlay) painted on top of
     this image everywhere, not just the backdrop around it, and no click
     (left or right) ever actually reached the image itself. Giving the
     image a position too puts DOM order back in charge - img comes after
     .lightbox-close in the HTML, so it now wins and sits on top within its
     own footprint, leaving .lightbox-close doing its job only where the
     image isn't: the backdrop. */
  position: relative;
}
/* clicking anywhere on the darkened backdrop closes it again - no visible
   close button needed, and right-click > Save Image still works on the img */
.lightbox-close {
  position: absolute; inset: 0; cursor: zoom-out;
}
.md-figure figcaption, .md-figure em { color: var(--muted); font-size: .85rem; }

.md-broken-link { color: var(--muted); border-bottom: 1px dotted var(--muted); }
.md-missing-image {
  display: block; padding: 2rem 1rem; text-align: center; color: var(--muted);
  background: #f6f6f6; border: 1px dashed var(--line); font-size: .85rem;
}

.callout { margin: 1.4rem 0; padding: .9rem 1.1rem;
           border-left: 3px solid var(--muted); background: #f7f7f7; }
.callout-title { font-weight: var(--w-bold); margin-bottom: .3rem; }
.callout-warning, .callout-danger, .callout-caution { border-left-color: var(--accent); }

.tags { margin: 1.8rem 0 0; display: flex; flex-wrap: wrap; gap: .4rem; }
.tag { text-decoration: none; text-transform: uppercase; letter-spacing: 1px;
       font-size: .68rem; font-weight: var(--w-med); color: var(--muted);
       border: 1px solid var(--line); padding: .25rem .55rem; }
.tag:hover { color: var(--ink); border-color: var(--muted); }

.back { margin-top: 2.2rem; font-size: .78rem; text-transform: uppercase;
        letter-spacing: 1px; }
.back a { color: var(--muted); text-decoration: none; }
.back a:hover { color: var(--ink); }

/* ------------------------------------------------- project: media + text */
/* The title/subtitle/opening paragraph and the rest of the text are two
   separate boxes (not one) so narrow screens can slot the media column in
   between them - see the "head" / "media" / "rest" areas below. */
.project.has-media {
  display: grid;
  grid-template-columns: 66.66% 33.34%;
  /* "head" only needs to be as tall as its own content; the "rest" row is
     1fr so the tall media column (which spans both rows) pushes IT to grow
     instead of inflating "head" and leaving a gap under the opening
     paragraph - the classic CSS grid multi-row-span sizing trap. */
  grid-template-rows: auto 1fr;
  grid-template-areas: "media head" "media rest";
  align-items: start;
}
.project-text-head { grid-area: head; }
.project-text-rest { grid-area: rest; }
.project.has-media .project-text-head,
.project.has-media .project-text-rest { padding-left: var(--gutter); }
.project.has-media .project-media { grid-area: media; min-width: 0; }

/* the media column holds only imagery, so drop its section headings */
.project-media h2 { position: absolute; width: 1px; height: 1px;
                    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.project-media .prose, .project-media > * { margin-top: 0; }
.project-media ul { list-style: none; margin: 0 0 var(--gap); padding: 0; }
.project-media li { margin: 0 0 .4rem; }
/* a video embed brings its own .md-figure spacing - the small margin above
   is only for the plain-text video links that a note might still fall back
   to (see the safety note on _lone_embed), so it must not stack with it */
.project-media li:has(.md-figure) { margin: 0; }

/* videos linked in the media column */
.project-media a { color: var(--muted); font-size: .9rem; }

/* ---------------------------------------------------------------- home */
.intro { margin: 0 0 42px; text-align: left; }
.intro p:last-child { margin-bottom: 0; }

/* the radio inputs that drive the CSS-only category filter */
.cat-radio { position: absolute; opacity: 0; width: 0; height: 0;
             pointer-events: none; }

.filters { display: flex; flex-wrap: wrap; gap: 1.4rem; margin: 0 0 32px; }
.filters label {
  position: relative;
  cursor: pointer; user-select: none;
  text-transform: uppercase; letter-spacing: 1px;
  font-size: .8rem; font-weight: var(--w-med); color: var(--muted);
  padding: .3rem 0;
  transition: color .15s;
}
/* same width-reservation trick, so the bar does not jump when bolding */
.filters label::after {
  content: attr(data-label);
  display: block; height: 0; overflow: hidden; visibility: hidden;
  font-weight: var(--w-bold); pointer-events: none;
}
.filters label:hover { color: var(--ink); }
.cat-radio:focus-visible ~ .filters label { outline: 2px solid var(--accent);
                                            outline-offset: 3px; }

/* -------------------------------------------------- bento / masonry grid
   Equal column width, every tile keeps its own height, no cropping.

   This is a CSS grid with a very fine-grained row height (grid-auto-rows)
   and each tile spanning just enough of those rows to match its own real
   height (--rows, computed at build time - see build.py's tile_rows()).
   That is what packs tiles snugly with no JavaScript, same as a plain CSS
   multi-column layout would - but multi-column can only fill top-to-bottom
   PER COLUMN, so a tile's position in Projects/_menu.md would not match
   where it visually lands. Grid fills left-to-right, top-to-bottom instead.

   grid-auto-flow is "dense": plain (sparse) placement never goes back to
   fill a gap once it has moved past it, and since a tile's actual height
   varies, some column always finishes "early" and leaves a gap below it
   that nothing ever fills - normally small, but severe whenever the tile
   count shrinks (like the category filters, which just hide tiles with
   CSS). "dense" fixes that by letting a later, shorter tile drop into an
   earlier gap - Projects/_menu.md order is still what decides the overall
   left-to-right reading order, but a tile can occasionally land one or two
   spots earlier than strict sequence to close a gap left above it. */
.grid {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 2px;
  grid-auto-flow: row dense;
  gap: var(--gap);
}

.tile {
  grid-row-end: span var(--rows, 40);
  min-width: 0;
  display: block;
}
.tile > a { display: block; text-decoration: none; color: inherit; }
/* Both the link and the image default to being natively draggable in every
   browser (you can normally drag a picture out to your desktop). That native
   gesture starts on pointerdown and swallows the event before our own
   pointer-based reorder script below ever sees it - which is what silently
   broke "click and drag anywhere on the tile". draggable="false" is set on
   both elements in the HTML (it does not inherit from a parent), and this
   -webkit- property is a belt-and-suspenders extra for Chrome/Safari. */
.tile > a, .tile img { -webkit-user-drag: none; }

/* the image keeps its natural aspect ratio - nothing is cropped */
.tile img {
  width: 100%; height: auto; display: block;
  background: #f2f2f2;
  transition: opacity .25s;
}
.tile > a:hover img { opacity: .82; }

/* Explicitly transparent, not just left unset, on every element in this
   chain - <li>, its link, the caption block and both lines of text - so
   there is no possible spot for an opaque fill to sneak in from a browser
   default or an inherited value (matters while a tile is being dragged
   over the others: its caption area must never paint over what's beneath). */
.tile, .tile > a, .tile-body, .tile-title, .tile-sub { background: transparent; }
.tile-body { display: block; padding: .7rem 0 0; }
.tile-title { display: block; font-weight: var(--w-med); font-size: .97rem;
              line-height: 1.3; }
.tile-sub { display: block; margin-top: .15rem; color: var(--muted);
            font-size: .82rem; line-height: 1.4; }
.tile > a:hover .tile-title { color: var(--accent); }

/* drag-to-reorder (only if the optional script is loaded).
   iOS-style: the dragged tile lifts, the others glide out of its way. */
.tile { touch-action: manipulation; }
.tile.dragging {
  position: relative;
  z-index: 20;
  cursor: grabbing;
  box-shadow: 0 18px 40px -12px rgba(0, 0, 0, .35);
  will-change: transform;
}
.tile.dragging a { pointer-events: none; }
.tile.dragging img { opacity: 1; }

/* the grid animates its height while filtering */
.grid { will-change: height; }

/* ---------------------------------------------------------------- footer */
.site-footer { border-top: 1px solid var(--line); padding-block: 26px 34px; }
.site-footer .wrap {
  display: flex; flex-wrap: wrap; gap: 1rem 1.6rem;
  align-items: center; justify-content: space-between;
}
.footer-nav { display: flex; flex-wrap: wrap; gap: 1.4rem; }
.footer-nav a, .colophon {
  text-transform: uppercase; letter-spacing: 1px;
  font-size: .72rem; font-weight: var(--w-med); color: var(--muted);
  text-decoration: none;
}
.footer-nav a:hover { color: var(--ink); }
.colophon { margin: 0; }

/* ------------------------------------------------------- breakpoints */
@media (max-width: 1199px) { :root { --wrap: 970px; } }

@media (max-width: 991px) {
  :root { --wrap: 750px; --gutter: 30px; }
  /* --rows was computed for the desktop tile width (370px); at 2 columns a
     tile is close to that (360px), close enough that reusing the same
     --rows here still packs snugly. */
  .grid { grid-template-columns: repeat(2, 1fr); }
  /* single column: title/subtitle/opening paragraph, then the media, then
     the rest of the text - so a phone reader always meets the words first */
  .project.has-media {
    grid-template-columns: 1fr;
    /* three stacked rows now, each just as tall as its own content - the
       desktop "auto 1fr" row sizing (for the two-row media span) does not
       apply here, since media is no longer spanning multiple rows. */
    grid-template-rows: auto auto auto;
    grid-template-areas: "head" "media" "rest";
  }
  .project.has-media .project-text-head,
  .project.has-media .project-text-rest { padding-left: 0; }
  .project.has-media .project-media,
  .project.has-media .project-text-rest { padding-top: 34px; }
  .project-media h2 { position: static; width: auto; height: auto;
                      clip: auto; overflow: visible; }
}

@media (max-width: 767px) {
  body { font-size: 16px; }
  .nav-toggle { display: inline-flex; }
  .site-header .wrap { flex-wrap: wrap; gap: 1rem; }
  .brand { padding: 16px 0 12px; }
  .brand .logo { width: 240px; }
  .site-nav { display: none; width: 100%; flex-direction: column;
              gap: .2rem; justify-content: flex-start; padding-bottom: .8rem; }
  .nav-checkbox:checked ~ .site-nav { display: flex; }
  /* one column: the --rows estimate (sized for a 370px tile) no longer
     applies at this width, so drop it and let each tile size to its own
     content instead - order does not need row-spanning help when there is
     only one column to begin with. grid-auto-rows must ALSO switch back to
     auto here: the desktop value (a fixed, fine-grained 2px, used so a
     tile's --rows span can approximate its real height) would otherwise
     still cap every un-spanned row at 2px tall, and a tile's actual
     content - far taller than that - would overflow straight down into
     the next one instead of the row growing to fit it. */
  .grid { grid-template-columns: 1fr; grid-auto-rows: auto; }
  .tile { grid-row-end: auto; }
  .page-head h1, .project-text-head h1 { font-size: 30px; line-height: 36px; }
  .subtitle { font-size: 18px; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* ---------------------------------------------------------------- print */
@page { size: A4; margin: 18mm 16mm; }

@media print {
  .site-header, .site-footer, .filters, .back, .skip, .nav-toggle { display: none; }
  /* the enlarge overlay must never appear on paper */
  .lightbox, .lightbox:target { display: none !important; }
  body { font-size: 10.5pt; line-height: 1.45; color: #000; background: #fff; }
  main { padding: 0; }
  .wrap { max-width: none; padding: 0; }
  .project.has-media { display: block; }
  .project.has-media .project-text-head,
  .project.has-media .project-text-rest { padding-left: 0; }
  .project-media h2 { position: static; width: auto; height: auto;
                      clip: auto; overflow: visible; }
  .page-head h1, .project-text-head h1 { font-size: 20pt; line-height: 1.2; }
  .subtitle { font-size: 12pt; }
  .prose h2 { font-size: 13pt; }
  .prose h3, .prose h4, .prose h5, .prose h6 { font-size: 11pt; }
  .prose a { text-decoration: none; color: #000; }
  .prose a[href^="http"]::after {
    content: " (" attr(href) ")"; font-size: 8pt; color: #555;
    word-break: break-all;
  }
  .md-figure, .tile { break-inside: avoid; }
  .prose h2, .prose h3 { break-after: avoid; }
  /* one column on paper, same as the narrowest screen breakpoint - no
     reason to fight pagination over a masonry pack here. grid-auto-rows
     must reset to auto too, or the fixed 2px desktop row height caps
     every tile's un-spanned row and its content overflows downward. */
  .grid { grid-template-columns: 1fr; grid-auto-rows: auto; }
  .tile { grid-row-end: auto; }
  img { max-width: 100% !important; }
}

/* reset button for the optional drag-to-reorder script */
.reset-wrap { margin: 1.5rem 0 0; }
.reset-order {
  font: inherit; background: none; border: 0; cursor: pointer;
  text-transform: uppercase; letter-spacing: 1px;
  font-size: .75rem; font-weight: var(--w-med); color: var(--accent);
  padding: .3rem 0;
}
.reset-order[hidden] { display: none; }
