/* foodiephiles.com
 *
 * The inked frame is the UI primitive: the masthead is drawn as a panel, and
 * the artwork's gutter-to-panel ratio is the spacing unit. Two exceptions --
 * the strips arrive already framed by the artist, so they wear no CSS border,
 * and the close control is soft and glassy so it floats above the artwork.
 *
 * No webfonts. The wordmark is lettered into the artwork, so the only type set
 * here is chrome: nameplate, strip title, footer.
 */

/* ---------------------------------------------------------------- tokens -- */

:root {
  /* Sampled from the artwork. The reds across all five strips sit at hue 10-18. */
  --paper: #f4f2ed;
  --card: #ffffff;
  --ink: #141414;
  --wine: #c2451e;
  --shade: #5f5b53;

  /* All layout spacing follows an 8px grid (multiples of 0.5rem / 8px). */
  --gutter: 1.5rem;
  --card-gap: 2.5rem;
  --frame: 3px;
  --measure: 70rem;

  color-scheme: light;
}

@media (max-width: 900px) {
  :root {
    --gutter: 1rem;
    --card-gap: 2rem;
  }
}

/* One tint per strip, hand-picked to harmonize with its artwork. The index is
   plain white -- only a comic page, where one strip fills the screen, takes a
   colour. Keep these in sync with each page's theme-color meta tag. */
[data-bg="index"]                { --comic-bg: #ffffff; }
[data-bg="sunday-preservation"]  { --comic-bg: oklch(0.958 0.022  45); }
[data-bg="locavores-fest"]       { --comic-bg: oklch(0.958 0.024  85); }
[data-bg="the-holiday-spirits"]  { --comic-bg: oklch(0.958 0.020 150); }
[data-bg="the-art-of-freshness"] { --comic-bg: oklch(0.958 0.024 128); }
[data-bg="the-offal-date"]       { --comic-bg: oklch(0.958 0.018 205); }

/* ----------------------------------------------------------------- reset -- */

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

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scrollbar-gutter: stable;
}

body {
  min-height: 100svh;
  background: var(--comic-bg, var(--paper));
  color: var(--ink);
  font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  font-size: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
  line-height: 1.5;
}

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

ol,
ul {
  list-style: none;
  padding: 0;
}

a { color: inherit; }

/* The frame width doubles as the focus ring. The white halo keeps it visible
   over inked frames and red artwork, which the wine alone disappears against. */
:focus-visible {
  outline: var(--frame) solid var(--wine);
  outline-offset: 3px;
  box-shadow: 0 0 0 6px rgb(255 255 255 / 0.95);
}

/* ------------------------------------------------------------ primitives -- */

.eyebrow {
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--shade);
}

.page {
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ----------------------------------------------------------------- index -- */

/* Banner and nameplate are one framed object, and the only outlined box on the
   index -- the strips below carry the artist's own frames. */
.masthead {
  border: var(--frame) solid var(--ink);
  background: var(--card);
  margin-block-start: var(--card-gap);
  margin-block-end: var(--card-gap);
}

.masthead img { width: 100%; }

.masthead-caption {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem 1.5rem;
  border-block-start: var(--frame) solid var(--ink);
  padding: 1rem 1.5rem;
}

.masthead-caption h1 {
  font-size: clamp(1.5rem, 0.9rem + 3vw, 2.5rem);
  font-weight: 800;
  letter-spacing: 0.02em;
  line-height: 1;
  text-transform: uppercase;
}

/* One strip per row. */
.archive {
  display: grid;
  gap: var(--card-gap);
  padding-block-end: var(--card-gap);
}

.archive a {
  display: block;
  text-decoration: none;
}

.archive img { width: 100%; }

/* No hover state -- nothing should compete with the artwork. Focus keeps the
   ring, since a shadow alone would not clear WCAG's 3:1 for an indicator, but
   against white it can be a soft lift rather than the halo used over artwork. */
.archive a:focus-visible {
  outline-offset: 6px;
  box-shadow: 0 6px 20px rgb(20 20 20 / 0.18);
}

/* Only the 404 page's link home uses this now. */
.archive-title {
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* ------------------------------------------------------------ comic page -- */

.comic-page {
  --measure: 92rem;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: var(--gutter);
  padding-inline: calc(var(--gutter) * 0.5);
  cursor: pointer;
}

.comic-header {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding-block-end: calc(var(--gutter) * 1.5);
}

.comic-title {
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* One uncut strip, paged by four invisible snap targets laid over it, so the
   morph target stays a single <img>. Quarters are honest snap points: measured
   off the artwork, every drawn panel centre lands within 0.26% of the image
   width of its exact quarter -- about 10px on a 3900px strip. A sixth strip
   drawn to different proportions would need that re-checked. */
.strip-track { position: relative; }
.strip-track img { width: 100%; cursor: auto; }
.strip-snaps { display: none; }

/* The one soft thing on the page: glass, fixed at bottom centre. Centred with
   margin, not a transform, so hover and press can own `transform` outright. */
.close {
  position: fixed;
  z-index: 10;
  bottom: max(calc(env(safe-area-inset-bottom, 0px) + var(--gutter) + 0.25rem), calc(1.75rem + env(safe-area-inset-bottom, 0px)));
  inset-inline: 0;
  margin-inline: auto;
  display: grid;
  place-items: center;
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  background: rgb(255 255 255 / 0.9);
  box-shadow:
    0 2px 10px rgb(20 20 20 / 0.14),
    inset 0 0 0 1px rgb(20 20 20 / 0.45);
  color: var(--ink);
  font-size: 1.15rem;
  line-height: 1;
  text-decoration: none;
  touch-action: manipulation;
  transition: background-color 0.15s, transform 0.15s;
}

.close:hover { background: rgb(255 255 255 / 1); transform: scale(1.06); }
.close:active { transform: scale(0.92); }

/* iOS Safari only bubbles clicks from non-interactive background areas when the
   element carries a pointer cursor, and the click-the-background-to-close
   handler in js/close.js depends on that bubbling. It is presentation, so it
   belongs here rather than being assigned to body.style from the script. */
body:has(.close) { cursor: pointer; }

/* --------------------------------------------------- comic page, narrow -- */

@media (max-width: 900px) {
  .close {
    bottom: max(calc(env(safe-area-inset-bottom, 0px) + 1.5rem), 2rem);
  }

  /* One panel per screen, flick to advance. scroll-snap-stop: always is what
     makes a hard flick settle on the next panel instead of coasting past. */
  .strip {
    height: 100svh;
    overflow-x: auto;
    overscroll-behavior: contain;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    display: grid;
    align-content: safe center;
    cursor: pointer;
  }

  .strip::-webkit-scrollbar { display: none; }

  /* The scroller is full-bleed, so an outset ring would draw off-screen. */
  .strip:focus-visible { outline-offset: -6px; }

  /* Four viewport-widths wide, so one quarter fills the screen. Padding on the
     inline ends simulates the natural gutters that middle panels have. */
  .strip-track {
    width: 400%;
    padding-inline: calc(var(--gutter) * 0.5);
  }

  .strip-snaps {
    position: absolute;
    inset: 0;
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 25%;
  }

  .strip-snaps > li {
    scroll-snap-align: center;
    scroll-snap-stop: always;
  }

  /* The header overlays the artwork so the panel gets the full screen. The fade
     must use the page's own colour -- the per-strip tint, not paper. */
  .comic-header {
    position: fixed;
    z-index: 9;
    inset-block-start: 0;
    inset-inline: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    padding-inline: var(--gutter);
    padding-block: calc(env(safe-area-inset-top, 0px) + 0.6rem) 0.6rem;
    background: linear-gradient(
      var(--comic-bg, var(--paper)) 70%,
      rgb(from var(--comic-bg, var(--paper)) r g b / 0));
    pointer-events: none;
  }

  .comic-title {
    font-size: 0.85rem;
    max-width: 85vw;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* The scroller is already exactly one viewport tall; any padding here is
     added on top of it and makes the page scroll vertically. */
  .comic-page { padding: 0; }
}

/* ------------------------------------------------------- scroll markers -- */

/* Progressive enhancement, and the only thing telling you there are four
   panels. Chromium 135+ only; elsewhere you get plain snapping, which stands on
   its own. Markers are bars rather than dots so they read as panel edges. */
@supports (scroll-marker-group: after) {
  @media (max-width: 900px) {
    .strip { scroll-marker-group: after; }

    .strip::scroll-marker-group {
      position: fixed;
      z-index: 9;
      inset-block-end: max(6.5rem, calc(50svh - 60vw - 2rem));
      inset-inline: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      gap: 0.5rem;
    }

    .strip-snaps > li::scroll-marker {
      content: "";
      width: 1.75rem;
      height: 3px;
      border: none;
      border-radius: 2px;
      background: rgb(20 20 20 / 0.22);
      transition: background-color 0.15s, height 0.15s;
    }

    .strip-snaps > li::scroll-marker:target-current {
      background: var(--ink);
      height: 5px;
      border-radius: 3px;
    }
  }
}

/* ---------------------------------------------------- morph transitions -- */

@view-transition { navigation: auto; }

/* A view-transition-name must be unique per document but match across the two
   documents to morph, so each comic's index thumbnail and its strip share one.
   Spelled out rather than derived with attr(): that is Chromium-only, and
   elsewhere the invalid declaration would drop the morph in Safari too. */
[data-comic="sunday-preservation"]  { view-transition-name: strip-sunday-preservation; }
[data-comic="the-holiday-spirits"]  { view-transition-name: strip-the-holiday-spirits; }
[data-comic="locavores-fest"]       { view-transition-name: strip-locavores-fest; }
[data-comic="the-art-of-freshness"] { view-transition-name: strip-the-art-of-freshness; }
[data-comic="the-offal-date"]       { view-transition-name: strip-the-offal-date; }

/* Narrow needs a different pairing. The strip img is laid out four viewports
   wide, so naming it would morph the thumbnail into something 4.2x the visible
   area. The scroller is what you actually see -- exactly one panel -- so the
   name moves onto it here.

   That pairs a 3.2:1 thumbnail with a roughly 1:2 screen, and the default
   `object-fit: fill` stretches each snapshot to whatever the group passes
   through. `cover` crops instead, anchored left so the strip narrows towards
   panel one, which is the panel the page opens on. */
@media (max-width: 900px) {
  .strip-track img[data-comic] { view-transition-name: none; }

  [data-bg="sunday-preservation"]  .strip { view-transition-name: strip-sunday-preservation; }
  [data-bg="the-holiday-spirits"]  .strip { view-transition-name: strip-the-holiday-spirits; }
  [data-bg="locavores-fest"]       .strip { view-transition-name: strip-locavores-fest; }
  [data-bg="the-art-of-freshness"] .strip { view-transition-name: strip-the-art-of-freshness; }
  [data-bg="the-offal-date"]       .strip { view-transition-name: strip-the-offal-date; }

  /* Spelled out for the same reason the names above are: a `*` name selector
     is in the spec but the whole list would be dropped anywhere it is not. */
  ::view-transition-old(strip-sunday-preservation),
  ::view-transition-new(strip-sunday-preservation),
  ::view-transition-old(strip-the-holiday-spirits),
  ::view-transition-new(strip-the-holiday-spirits),
  ::view-transition-old(strip-locavores-fest),
  ::view-transition-new(strip-locavores-fest),
  ::view-transition-old(strip-the-art-of-freshness),
  ::view-transition-new(strip-the-art-of-freshness),
  ::view-transition-old(strip-the-offal-date),
  ::view-transition-new(strip-the-offal-date) {
    object-fit: cover;
    object-position: left center;
  }
}

/* ---------------------------------------------------------------- footer -- */

.site-footer {
  padding-block: 2rem;
  text-align: center;
  font-size: 0.75rem;
  color: var(--shade);
}

/* -------------------------------------------------------- reduced motion -- */

@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }

  html { scroll-behavior: auto; }

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

  .close:hover,
  .close:active { transform: none; }
}

/* ------------------------------------------------------------ 404 notice -- */

/* Sits where the strips would be. */
.page-notice {
  padding-block: 2rem 4rem;
}

.page-notice a { text-decoration: underline; text-underline-offset: 0.25em; }
