/* Kefir III — rows on a 12-column page grid.

   A page is a stack of rows. A row is divided into cells, left to right, and
   the cells' spans add up to 12. An empty cell is a spacer. Content fills its
   cell. Columns stretch with the window; gutters, margins and type do not.
   Below 48rem every row stacks in source order and the spacers disappear. */

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

@property --n { syntax: "*"; inherits: false; }

:root {
  --cols: 12;
  --gap: 1.5rem;
  --row-gap: var(--gap);
  --margin: var(--gap);
  --section-gap: 6rem;

  /* One column plus one gutter, as a length. It is the same everywhere on the
     page, so anything can measure itself in columns. 100vw counts a classic
     (non-overlay) scrollbar; that only moves the point where .snap changes its
     count by a few pixels. */
  --pitch: calc((100vw - 2 * var(--margin) + var(--gap)) / var(--cols));
}

@media (width < 48rem) {
  :root {
    --gap: 1rem;
    --row-gap: 1.5rem;
    --margin: 1.25rem;
    --section-gap: 3.5rem;
  }
}

body {
  margin: 0;
  min-height: 100dvh;
  display: grid;
  grid-template-rows: auto 1fr auto;
}

main {
  display: grid;
  align-content: start;
  row-gap: var(--section-gap);
}


/* --- Rows and cells ------------------------------------------------------- */

.row {
  display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  gap: var(--row-gap) var(--gap);
  padding-inline: var(--margin);
}

/* A cell that is a row again divides its own span, on the page's lines. */
.row > .row {
  grid-template-columns: subgrid;
  padding-inline: 0;
}

/* A cell with no span class takes the whole row. */
.row > * { grid-column: auto / span var(--cols); }

.c1  { grid-column-end: span 1;  --n: 1; }
.c2  { grid-column-end: span 2;  --n: 2; }
.c3  { grid-column-end: span 3;  --n: 3; }
.c4  { grid-column-end: span 4;  --n: 4; }
.c5  { grid-column-end: span 5;  --n: 5; }
.c6  { grid-column-end: span 6;  --n: 6; }
.c7  { grid-column-end: span 7;  --n: 7; }
.c8  { grid-column-end: span 8;  --n: 8; }
.c9  { grid-column-end: span 9;  --n: 9; }
.c10 { grid-column-end: span 10; --n: 10; }
.c11 { grid-column-end: span 11; --n: 11; }
.c12 { grid-column-end: span 12; --n: 12; }

/* .snap — a cell that picks its own span from a preferred width, --w, rounded
   to the nearest whole number of columns (--round: up or down to change that).
   36rem is 6 columns at 1440px and 7 at 1280px. Since the count changes with
   the window, a snapped cell goes alone in its row or last in it.

   .center — puts a cell in the middle of its row. When the columns left over
   are odd, the extra one goes on the right, so both edges stay on grid lines.
   Works with .snap or with a span class.

   Engines that cannot do maths in a span skip both: the cell takes the whole
   row, which is the safe way to be wrong.

   The two lengths are converted to px before the maths sees them. Safari 18
   gets tan(atan2()) wrong when it contains relative units — rem, vw — and the
   result is a number rather than a length: the span came out as 1, and a 36rem
   text column became one column wide. A registered <length> is computed to px
   on the element that declares it, so the division only ever gets px. It is
   declared on the cell, not on :root, because Safari also resolves rem in a
   registered property on :root against 16px instead of the root's own size. */
@property --snap-w     { syntax: "<length>"; inherits: false; initial-value: 0px; }
@property --snap-pitch { syntax: "<length>"; inherits: false; initial-value: 0px; }

@supports (grid-column-end: span round(tan(atan2(1px, 1px)))) {
  .snap {
    --snap-w: calc(var(--w, 100vw) + var(--gap));
    --snap-pitch: var(--pitch);
    --n: clamp(1, round(var(--round, nearest), tan(atan2(var(--snap-w), var(--snap-pitch)))), var(--cols));
    grid-column-end: span var(--n);
  }
  .center { grid-column-start: calc(round(down, (var(--cols) - var(--n, var(--cols))) / 2) + 1); }
}


/* --- Bleeds --------------------------------------------------------------- */

/* Out to the edge of the window. Negative margins, so a bled cell keeps its
   place in the row; it only makes sense on the first or last cell. */
.full        { margin-inline: calc(-1 * var(--margin)); }
.bleed-start { margin-inline-start: calc(-1 * var(--margin)); }
.bleed-end   { margin-inline-end: calc(-1 * var(--margin)); }


/* --- Phones --------------------------------------------------------------- */

/* Every cell takes the whole width, in source order, and spacers go. A cell
   with an m-class keeps a share of the row instead — for the few rows that
   should stay side by side, like a header. Same twelve columns. */
@media (width < 48rem) {
  .row > * { grid-column: 1 / -1; }
  .row > :empty { display: none; }

  .m1  { grid-column: auto / span 1; }
  .m2  { grid-column: auto / span 2; }
  .m3  { grid-column: auto / span 3; }
  .m4  { grid-column: auto / span 4; }
  .m5  { grid-column: auto / span 5; }
  .m6  { grid-column: auto / span 6; }
  .m7  { grid-column: auto / span 7; }
  .m8  { grid-column: auto / span 8; }
  .m9  { grid-column: auto / span 9; }
  .m10 { grid-column: auto / span 10; }
  .m11 { grid-column: auto / span 11; }
  .m12 { grid-column: auto / span 12; }
}


/* --- Media and the ruler -------------------------------------------------- */

img, svg, video { max-width: 100%; height: auto; }

#ruler {
  position: fixed;
  inset: 0;
  z-index: 999;
  pointer-events: none;
  padding-inline: var(--margin);
  background-origin: content-box;
  background-clip: content-box;
  background-image: repeating-linear-gradient(to right,
    color-mix(in srgb, tomato 22%, transparent) 0 calc((100% + var(--gap)) / var(--cols) - var(--gap)),
    transparent 0 calc((100% + var(--gap)) / var(--cols)));
}
