/* /fun/design, the list of things Shaun thinks are beautifully designed.
   Tokens, body, h1, .lede, .fun-top and the footer all come from fun.css. Only
   what a wall needs is here.

   WHY A WALL

   The first version of this page was one column of seventeen entries, every
   picture blown up to the full width of the reading column and boxed in a rule,
   at 14,319px of scroll. You never saw the collection, only whichever thing you
   had scrolled to. Shaun called it a big fat list, and picked a horizontal shelf
   from four treatments to replace it.

   The shelf fixed the scroll and kept a new version of the same fault: a rail
   at 1280px showed five objects out of nineteen, so the collection was still
   something you travelled along rather than saw. He was shown four reworks on
   2026-08-30 and picked this one. Every object is on the page at once, in rows
   that share a height and fill the width, and one note opens underneath the row
   you clicked.

   What that cost is the homage. The shelf was the nod to the Stripe Press
   website he calls "an absolute masterclass" further down this same page, for
   showing nothing but books and giving the appearance of a bookshelf. A wall
   still shows nothing but the objects, which is the half of that idea worth
   keeping, but it is not a bookshelf any more and the reference is gone.

   WHY THE ROWS ARE JUSTIFIED

   Each object used to stand at a height of its own, so a ring was small and a
   pizza oven was large, and Shaun asked for one size. One size was then read as
   one HEIGHT on the rail and one SQUARE in the note, and a square is the wrong
   container for a screenshot: the Stripe Press website came out 224x107 against
   the Nest thermostat's 224x224, so the one picture made of fine detail got 48%
   of the area of the one that is a single dial, and he said it looked tiny.

   Equal AREA is the rule now. On the wall a row shares one height and stretches
   to the exact width, which gives every picture close to the same area with
   nothing cropped and nothing stretched; in the note the size is worked out
   from each picture's own proportions in build-design-page.py and written on as
   --w. It is computed there rather than here because sqrt() in CSS would be
   doing arithmetic on a generated page that the generator can simply do once.

   WHY THE NOTE IS A DISCLOSURE

   Clicking an object opens its note under that row, and clicking the same
   object again closes it. Shaun asked for that on 2026-08-30. It also fixes the
   thing the shelf never did: selecting an object used to show you the same
   photograph again 112px lower and 4px narrower, so choosing a thing bought you
   words rather than a better look at it. An open note now shows the picture at
   roughly 500px against the wall's 186px row.

   WHY A PAIR IS LABELLED

   Four entries carry two pictures, and all four second pictures are named in
   Shaun's own writing, so none of them can be dropped without editing what he
   wrote: the Glowstone mug and the NOCO both say "On the right is", the Nest
   says "Compare this to a traditional thermostat", and the Oura says "The
   charger is also beautifully designed". Three of those are a rival he is
   arguing against and the Oura's is a companion he also praises. Drawing all
   four identically at the same size with no caption is what made him ask why
   there were two, so each half now carries a mono caption and a rival is drawn
   smaller than the thing it loses to.

   WHY NOTHING IS IN A BOX

   build-design-page.py gives every press shot a real alpha channel where its
   white studio background was, so fourteen of these entries have no edges of
   their own. A border round one of them is a box drawn around nothing, and a
   tinted card behind one redraws in beige the very rectangle the cut out exists
   to remove. Only the five photographs and screenshots, which do have edges, get
   a hairline, and that difference is load bearing rather than decorative: it
   separates a thing from a picture of a thing.

   Until the cut outs grew that alpha channel this paragraph said the build
   flood filled each background to this page's paper, and counted eleven objects
   against seven pictures. Painting the cream in was only ever correct on a page
   of exactly this cream, which is why it went, and the counts moved with the
   entries. */

/* Wider than the 46rem reading column fun.css sets, because a wall of nineteen
   objects wants the room. The writing stays narrow inside it. */
main { max-width: 62rem; }

.head { max-width: 46rem; }
.head .lede { margin-bottom: 0; }

/* --- the wall ----------------------------------------------------------- */

/* --wall-row is the height a row aims for and --wall-gap the space between
   objects. wall.js reads both off this element rather than carrying its own
   copies, so a narrower window is two numbers here and nothing in the script. */
.the-wall { --wall-row: 186px; --wall-gap: 28px; --cell-h: 150px; margin: 1.2rem 0 0; }

.wall-hint {
  margin: 0 0 1.2rem;
  font-family: var(--font-mono);
  font-size: .68rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* Before wall.js runs, and if it never runs, the wall is a wrapping row of
   objects at one height. Ragged down the right rather than justified, which is
   the part that needs to know what ended up in each row, but every object is
   the right shape and the page is the page. */
.wall { display: flex; flex-wrap: wrap; gap: var(--wall-gap); align-items: flex-end; }
.wall .cell { height: var(--cell-h); width: calc(var(--cell-h) * var(--r)); }

/* Once the rows are worked out, the wall stops laying anything out itself and
   the rows do it, each one a flex line with heights already in pixels. */
.wall.is-justified { display: block; }
.wall.is-justified .cell { height: auto; width: auto; }
.wall .row {
  display: flex;
  gap: var(--wall-gap);
  align-items: flex-end;
  margin: 0 0 var(--wall-gap);
}

.cell {
  position: relative;
  flex: none;
  display: block;
  text-decoration: none;
  color: inherit;
  transition: transform .16s;
}
.cell img { display: block; width: 100%; height: 100%; }
.cell.plate img { border: 1px solid var(--rule); background: var(--paper-2); }
.cell:hover { transform: translateY(-4px); }
.cell:focus-visible { outline: 2px solid var(--orange); outline-offset: 5px; }

/* The only thing saying which note is open, so it goes on the object rather
   than under the writing. */
.cell[aria-expanded="true"]::after {
  content: "";
  position: absolute;
  inset: auto 0 -.7rem 0;
  height: 2px;
  background: var(--orange);
}

/* --- the note ----------------------------------------------------------- */

/* Hidden here rather than by wall.js, so nineteen entries do not paint and then
   collapse on every load. Two things put them back for a reader who never gets
   the wall: the <noscript> block in the page head, and .no-wall below. The page
   used to ship its first note open for the second of those, and could not keep
   doing it, because a note open in the markup is on screen before the deferred
   script runs and this page opens with nothing open. */
.entry { display: none; }

/* Scripting on, wall.js absent or bailed. Set on the root by the head script,
   which asks at load whether the wall ever marked itself running, and carries
   the same two rules the <noscript> block does. */
.no-wall .the-wall { display: none; }
.no-wall .entry { display: grid; }

/* Opening and closing move grid-template-rows between 0fr and 1fr, which lets
   the note find its own height rather than having one measured and written in.
   That matters because the height changes with every object and with the width
   of the window, so any number written in would be wrong somewhere.

   The clip is a separate element from the note because a collapsed box still
   paints its own border and .entry carries a rule top and bottom. With the
   clipping one level out, a closed note leaves nothing behind rather than a two
   pixel scar across the wall. */
.opened-wrap {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows .3s ease;
}
.opened-wrap.is-open { grid-template-rows: 1fr; }
.opened-clip { overflow: hidden; min-height: 0; }

.entry.is-open {
  display: grid;
  grid-template-columns: minmax(0, 33rem) 1fr;
  gap: 1.4rem 2.6rem;
  align-items: start;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  margin: 0 0 var(--wall-gap);
  padding: 1.7rem 0;
}

/* The picture column holds the widest thing an open note can put in it, which
   is a pair at 250 plus a gap plus 230, or a single screenshot at 520. The
   figures do not shrink, so a column too narrow spills rather than reflowing,
   and the column is where that is settled. */
.entry .shot { margin: 0; display: flex; gap: 1.1rem; align-items: flex-end; min-width: 0; }
.entry .shot figure { margin: 0; flex: none; width: min(var(--w), 100%); }

/* --w is the width build-design-page.py worked out for this picture from its
   own proportions, so that every one carries the same area. It sits on the
   figure, and the picture simply fills the figure, so the 100% has a definite
   width to resolve against. The height is never pinned beside the width: with
   both set, a clamp on the width holds the height and draws the picture out of
   shape rather than smaller, which is how four of these came to be published
   squashed by a fifth. */
.entry .shot img, .entry .shot svg {
  display: block;
  width: 100%;
  height: auto;
}
.entry .shot.plate img { border: 1px solid var(--rule); background: var(--paper-2); }

/* A caption under half of a pair. Mono and small, so it reads as a label on the
   picture rather than as more of Shaun's writing. */
.cap {
  display: block;
  margin: .55rem 0 0;
  font-family: var(--font-mono);
  font-size: .64rem;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--ink-muted);
  overflow-wrap: anywhere;
}
.cap.is-hero { color: var(--graphite); }
.cap.is-rival::before { content: "\2717\00a0\00a0"; color: var(--orange-deep); }
.cap.is-companion::before { content: "+\00a0\00a0"; color: var(--ink-muted); }

.entry h2 { margin: 0 0 .6rem; font-size: 1.5rem; }
.entry p { margin: 0 0 .8rem; font-size: .98rem; color: var(--ink-2); max-width: 38rem; }
.entry p:last-child { margin-bottom: 0; }

.visit { margin-top: 1.1rem; }
.visit a {
  font-family: var(--font-mono);
  font-size: .8rem;
  letter-spacing: .04em;
  color: var(--orange-deep);
  text-underline-offset: 3px;
}

/* The drawn plate for an entry that arrives with no photograph. Nothing on the
   wall reaches it today, because Cinque Terre has a photograph now, and it is
   kept as the answer to a missing picture rather than a hole in the wall. */
.coast { width: 100%; height: auto; }
.coast .l { fill: none; stroke: var(--graphite); stroke-linecap: round; }
.coast .sea { stroke: var(--rule); }
.coast .v { fill: var(--graphite); }
.coast .roof { fill: var(--orange-deep); }
.cell.drawn .coast { background: var(--paper-2); border: 1px solid var(--rule); }

/* --- narrower windows --------------------------------------------------- */

@media (max-width: 46rem) {
  /* Two numbers, and the rows follow. A shorter target row means more objects
     fit across a narrow page rather than three of them filling it. */
  .the-wall { --wall-row: 132px; --wall-gap: 20px; --cell-h: 108px; }
  .entry.is-open { grid-template-columns: 1fr; gap: 1.1rem; }
}

@media (max-width: 34rem) {
  .entry h2 { font-size: 1.32rem; }
  /* A pair stays side by side however narrow it gets, because Shaun's own
     sentence says "On the right is" and a stacked pair makes it false. Instead
     of a fixed width each figure grows in proportion to the width it would have
     had, so the two halves shrink together and the rival stays the smaller of
     the two. */
  .entry .shot figure { flex: var(--fg, 1) 1 0; width: auto; min-width: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .cell { transition: none; }
  .cell:hover { transform: none; }
  .opened-wrap { transition: none; }
}
