/* The reflections index — the list, its rows, its hover, and the mark on the
   one that is current.

   Ported from the lab prototype (lab/list.css) where every decision in here was
   argued out against the alternatives; the comments are that record and are
   worth keeping. The lab's own review strip is not here — it was chrome for
   comparing variants, never part of the design.

   Loaded only by /reflections. */

/* ============================================================
   The reflections index
   ============================================================ */

  /* ==========================================================
     Archive lab — the list

     A row is a link. It goes to /reflection/<slug> for everybody, and the
     reading page is the only place a subscriber and a visitor see different
     things. The peek panel that used to sit between the two is gone: it made
     the subscriber pay two clicks and a context switch for every reading, and
     it rendered the wall a second time, in a narrower column, one screen early.

     Everything here is scoped to this page; the palette, the type scale and
     the fonts come from the real site stylesheets above.
     ========================================================== */

  body { background: var(--bg); }

  /* veil.js is deliberately NOT loaded here: it writes --scrim to fade the nav
     out as a reading gets going, and this is the one page you scroll to find
     something rather than to read, so the nav stays put. */

  /* ---------- the container ----------
     880 against the nav's 960 (nav.css:8), same 3rem inset — so the list sits
     40px inside the nav rule on both sides rather than flush with it. Held to
     one number so the whole page still has a single left edge; --measure is the
     *reading* width and belongs to a reading. */
  .idx-col {
    max-width: 880px; margin: 0 auto; padding-left: 3rem; padding-right: 3rem;
  }

  /* ---------- masthead ---------- */
  /* .hb-about-name (about/index.html:92) and .legal h1 (legal.css:25) are the
     same declaration, so it is the site's page heading rather than one page's
     idea of one. Archive had been running 1.75rem at 400 — smaller and lighter
     than every other title on the site, and barely above the wordmark. */
  /* ---------- the heading is the search control ----------
     One centred row: the word, the glass, and a field that has no width until
     it is asked for. Because the row is centred, growing the field to the
     right of the glass is what slides the word left — the movement is the
     row re-centring, not an animation of the title, so it cannot get out of
     step with the field it is making room for.

     It is the wall's control, at page-title scale: collapsed it is one word,
     the press opens the field, and the glass stops meaning "search this" and
     starts meaning "close this". `?head=inline` keeps the word as Archive
     throughout, for the version without the swap. */
  .idx-head {
    padding-top: var(--space-4);
    display: flex; align-items: center; justify-content: center; gap: 12px;
  }

  /* Both words occupy one cell, so the title is as wide as the wider of them —
     `Reflections` — and neither the crossfade nor the slide has to account for
     the other. Right-aligned inside that cell: the shorter word then ends
     exactly where the longer one ended, so the gap to the glass never changes
     and the difference in length shows up on the left, as the word appearing
     to shorten from that side while the field opens on the other. Left-aligned
     it read as dead space between Search and a glass that had not moved. */
  .head-title {
    display: grid; justify-items: end; margin: 0;
    font-family: 'Source Serif 4', serif;
    font-weight: 500; font-size: clamp(1.5rem, 3.5vw, 2rem);
    letter-spacing: -0.015em; line-height: 1.15;
    color: var(--heading);
  }
  .head-title .w {
    grid-area: 1 / 1;
    transition: opacity 0.26s ease;
  }
  .head-title .w-search { opacity: 0; }
  .idx-head.is-open .head-title .w-title { opacity: 0; }
  .idx-head.is-open .head-title .w-search { opacity: 1; }

  .search-open {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    width: 30px; height: 30px; padding: 0;
    background: none; border: 0; cursor: pointer;
    color: var(--muted-strong);
    transition: color 0.15s;
  }
  .search-open:hover { color: var(--accent); }
  .idx-head.is-open .search-open { color: var(--accent); }
  .search-open:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 50%; }

  /* Present, occupying nothing. Width animates so the row re-centres smoothly
     rather than jumping to its new middle. */
  .search-field {
    display: flex; align-items: center; gap: 6px;
    flex: 0 0 0; width: 0; min-width: 0; overflow: hidden;
    border-bottom: 1px solid transparent;
    opacity: 0;
    transition: flex-basis 0.34s cubic-bezier(0.22, 0.61, 0.36, 1),
                width 0.34s cubic-bezier(0.22, 0.61, 0.36, 1),
                opacity 0.2s ease 0.06s,
                border-color 0.2s ease;
  }
  .idx-head.is-open .search-field {
    flex: 0 1 20rem; width: 20rem;
    opacity: 1;
    border-bottom-color: var(--hairline);
  }
  .idx-head.is-open .search-field:focus-within { border-bottom-color: var(--muted-strong); }

  .search-field input {
    flex: 1 1 auto; min-width: 0;
    font-family: 'Inter', sans-serif; font-size: 1rem;
    color: var(--heading); background: none; border: 0; outline: none;
    padding: 4px 0 5px;
  }
  .search-field input::placeholder { color: var(--muted); }
  .search-field input::-webkit-search-cancel-button { display: none; }
  .search-clear {
    flex: 0 0 auto; background: none; border: 0; padding: 0 2px; cursor: pointer;
    color: var(--muted); line-height: 1;
    display: flex; align-items: center;
  }
  .search-clear:hover { color: var(--heading); }
  .search-clear[hidden] { display: none; }

  .idx-count { margin-top: 8px; text-align: center; }

  @media (prefers-reduced-motion: reduce) {
    .head-title .w, .search-field { transition: none; }
  }

  @media (max-width: 640px) {
    .idx-head.is-open .search-field { flex-basis: 11rem; width: 11rem; }
  }

  /* The count speaks only while a search is narrowing something, which is the
     only moment the number is news. The line stays reserved while empty so the
     list doesn't jump down the moment you type. */
  .count {
    display: block; min-height: 1em; text-align: center;
    font-family: 'Inter', sans-serif; font-size: 0.72rem;
    text-transform: uppercase; letter-spacing: 0.14em; color: var(--muted-strong);
  }

  /* ---------- the column ---------- */
  /* padding-top is explicit because typography.css gives <main> 64px of it,
     which put a hundred pixels of nothing between the search field and the
     first month. */
  .idx-list { margin-top: var(--space-2); padding-top: var(--space-2); padding-bottom: var(--space-5); }
  .rows { list-style: none; margin: 0; padding: 0; }

    /* ---------- the row ----------
     One left edge. The attribution used to sit in a right-aligned column at the
     far side of the container, which is a data-table convention — trailing
     columns exist so values can be compared down them, and nobody compares
     author names. On a short title it stranded the name 700px from the thing it
     belonged to and asked a hairline to bridge the gap.

     An <a>, not a <button>. That is most of what "professional" turns out to
     mean in a list: the URL shows in the status bar on hover, cmd-click and
     middle-click open a reading in a new tab, right-click copies, and the
     browser remembers where you have been.

     The box bleeds 16px past the text column on both sides so the hover state
     covers the whole hit target. */
  .row {
    display: block;
    width: calc(100% + 32px); margin: 0 -16px;
    padding: 18px 16px; min-height: 44px;
    border-bottom: 1px solid var(--hairline);
    text-decoration: none; color: var(--heading);
  }
  .rows li:last-child .row { border-bottom: 0; }

  /* No filled band, and no mark in the margin either — both were the row being
     given a new piece of furniture to say something a link already knows how to
     say. The lead line goes accent and takes an underline, which is what a text
     link does everywhere, and the row is a text link. Keyboard focus keeps a
     real outline, because a ring is the only thing that shows where focus is on
     a row this tall. */
  .row:focus-visible {
    outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 3px;
  }

  /* colour: inherit on the lead line, so it takes it from the <a>. That is not
     a style preference — a browser applies :visited to the link element and to
     nothing inside it, so a rule written as .row:visited .t is silently
     ignored. */
  .row .t {
    display: block;
    font-family: 'Source Serif 4', serif; font-weight: 500;
    /* .interpretation h2 (typography.css:136) — the site's own heading size
       inside prose, rather than a number picked to look about right. */
    font-size: 1.4rem; line-height: 1.3; letter-spacing: -0.01em;
    color: inherit;
    transition: color 0.2s cubic-bezier(0.22, 0.61, 0.36, 1);
  }

  /* The quote as the lead line. Italic and serif because that is what a quote
     is everywhere else on this site — it is the reading's own h1 — held to two
     lines so a long one cannot own the scroll. */
    /* ---------- hover ----------
     The lead line goes accent and to 700. Chosen out of eleven, which are in
     `git show 526db95` if the question reopens: an underline, colour alone, the
     date answering at the far edge, the row's own hairline taking the accent,
     the whole row firming by weight or by stroke, a staggered version of that,
     an 8px step toward the reader — and, before all of them, three fills.

     Nothing fills the row. A `--surface2` band was tried and cut, then a 3%
     accent tint and an inset panel were tried and cut too; the answer to "which
     fill" turned out to be none of them.

     Source Serif 4 is variable (fonts.css:44, 200-900), so 700 is a real weight
     rather than a synthesised one. Only the lead line takes it: it is one line
     on every row and has nowhere to show the width change, where the quote and
     the snippet are clamped and would re-wrap under the pointer. Measured
     across all of them, no row changes height. */
  .row:hover .t, .row:focus-visible .t {
    color: var(--accent);
    font-weight: 700;
  }
  .row .t {
    transition: color 0.2s cubic-bezier(0.22, 0.61, 0.36, 1),
                font-weight 0.2s cubic-bezier(0.22, 0.61, 0.36, 1);
  }
  @media (prefers-reduced-motion: reduce) {
    .row .t, .row .q { transition: none; }
  }

  /* The attribution, under the line it belongs to. */
      /* ---------- what is open right now ----------
     The archive is public, and a reflection is readable for two days from its
     own date — so at any moment one or two rows out of the whole list can
     actually be opened, and the rest are a wall. Marking them is the
     difference between a list of doors and a list of locked doors.

     This is the deleted /reflections index's `.ix-item.is-open`, which had the
     same problem and solved it the same way. It is a label rather than a
     colour so it survives being scanned rather than looked at, and it sits in
     the gutter with the date because that is where the row already says when.

     Off with `?open=off` — worth seeing both, since it is the one thing on the
     row that is about access rather than about the writing. */
  /* In the log row this sits under the date in the gutter, on its own line. In
     the other three there is no gutter, so it rides at the head of the line the
     row already uses for metadata — inline, with the attribution after it. */
  .row .open {
    display: block; margin-top: 6px;
    font-family: 'Inter', sans-serif; font-size: 0.7rem; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.1em;
    color: var(--accent);
  }
  .row .open::before {
    content: ''; display: inline-block; vertical-align: 0.1em;
    width: 5px; height: 5px; margin-right: 6px; border-radius: 50%;
    background: var(--accent);
  }
  /* In the log row it sits beside the title, which is where the reader decides
     whether this is the one — not in the date rail, where it was the widest and
     the only coloured thing in a column of six-character dates, pulling the eye
     to the far left of a row whose subject is on the right. An annotation on the
     title rather than on the date. */
  .row .t .open {
    display: inline; margin: 0 0 0 0.85em;
    /* `vertical-align: middle` centres an inline box on the parent's x-height
       midline — which is the optical centre of the line it sits in, and is
       exactly the alignment this needs. It only works once the badge's box is
       the letters and not the letters plus empty space, which is what the
       text-box trim below achieves. Without the trim, middle centres a box
       that is mostly invisible descender and the badge lands low. */
    vertical-align: middle;
    font-family: 'Inter', sans-serif; font-size: 0.7rem; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.1em;
    white-space: nowrap;
  }
  .row .t .open::before { content: none; }

  /* ---------- the badge ----------
     Coloured caps read as subtle because a coloured word is ambiguous — it can
     be a link, a name, or emphasis, and this page uses colour for all three. A
     container is what makes it a label: the shape says "metadata about this
     item", which no amount of extra colour on bare text can say.

     The precedent is GitHub's `Latest` on a releases list — the same problem,
     a dated list newest-first with one marked — and npm's `latest`, and
     Polaris's Badge, which documents the rule as one per item, used sparingly.

     This is not the filled surface the wall's card and the hover band were.
     Those were surfaces: a 900px band, a box around a paragraph. Sixty pixels
     of container is a mark.

     Solid, chosen out of five — bare caps, a hairline capsule, a 10% tint, this,
     and bracketed to match the row's own tags. The others are in
     `git show 526db95`. */

  .row .t .open {
    /* Not symmetrical, for two reasons that both come from setting caps in a
       box.

       Right is 0.4em against left's 0.5 — the difference is the 0.1em of
       letter-spacing, which is applied after the last letter as well as
       between them, so it lands inside the padding as a phantom column. At
       0.5/0.5 the word sits a pixel left of centre: measured at 4x, 24px of
       gap on the left against 28 on the right. At 0.4 it is 24 and 24.

       And the vertical split is 0.30 above against 0.08 below — nearly four to
       one — because uppercase has no descenders. The glyphs occupy the top of
       their line box and the empty descender space sits below them, so even
       padding leaves a visibly bigger gap under the word than over it. The sum
       is unchanged, so the pill is the same size; only the text moves inside
       it. Arrived at by rendering five splits at 8x and looking. */
    /* Trim the line box to the cap height and the baseline, so the box IS the
       letters. Uppercase normally sits in the top of its line box with the
       empty descender space underneath, which is why every padding value I
       tried needed a different fudge above and below to look even — I was
       padding around a box that did not match the ink.
       
       With the trim, the vertical padding can be symmetrical and simply be
       true. The horizontal still is not: letter-spacing is applied after the
       last letter as well as between them, so 0.1em of it sits inside the
       right padding as a phantom column, and taking it back off gives 0.4
       against 0.5. */
    text-box: trim-both cap alphabetic;
    padding: 0.3em 0.4em 0.3em 0.5em;
    border-radius: 3px;
    line-height: 1;
  }
  .row .t .open {
    background: var(--accent);
    color: var(--bg);
  }

  @media (max-width: 640px) {
    .row .open { margin-top: 2px; margin-left: 0; }
  }

  /* ---------- the log ----------
     A hanging-indent record: when in a fixed left column, what in a second.
     Two rigid left edges rather than one, which is the changelog / ship's-log
     / CV layout — and it reads as a record kept rather than a feed served,
     which is the right register for an archive.

     The gutter only holds together if the figures are tabular; proportional
     numerals put "Aug 12" and "May 04" at different widths and the second
     column stops being a column. */
  .row.is-log {
    display: grid; grid-template-columns: 4.75rem 1fr; column-gap: 1.5rem;
    align-items: baseline;
  }

  /* ---------- a list with no dates in it ----------
     The gutter goes when the dates go. Kept, it was 100px of air on every row,
     and it pushed the titles to x=458 while the wordmark and the footer sat at
     318 — the list read as floating right of its own page, which is what an
     empty column does that a full one does not. Measured against three left
     edges on the page: 318 for the frame, 358 for the list's rules, 458 for the
     text. Now the text sits on the rule at 358, one step in from the frame.

     .what still has to be named. Grid auto-placement is what put it in the
     gutter in the first place, and naming column 1 is what keeps it out.

     The max-width is the measure the gutter used to enforce, and it sits on the
     row rather than on the text inside it, so the rules end where the text ends.
     Capping only the text left four right edges on the page — text at 1042, the
     list's rule at 1142, the row separators at 1158, the footer at 1182 — and
     over a hundred pixels of naked rule past the last word. Now the list is one
     column: 76 characters a line, which is inside the comfortable range, where
     letting it run to the full width put it at 87.

     And the rule on top: the year heading gave the list its top edge as well as
     its grouping, so without the headings the first row began in mid-air under
     the page title. */
  .is-undated .row.is-log { grid-template-columns: 1fr; max-width: 44.75rem; }
  .is-undated .row.is-log > .what { grid-column: 1; }

  /* The top rule goes on the first row, not on the list. On the list it drew at
     the ul's edges while every separator below it drew at the row's, which are
     16px wider on each side for the hover bleed — two rules on one page at two
     lengths, the lower one poking out past the upper. Same element now, so they
     cannot disagree. */
  .is-undated .rows > li:first-child .row { border-top: 1px solid var(--hairline); }
  .row .what { min-width: 0; }

  /* The quote under the title, smaller and at body colour — subordinate to the
     title above it rather than competing with it. */
  .row .ql {
    display: block; margin-top: 8px;
    font-family: 'Source Serif 4', serif; font-style: italic; font-weight: 500;
    font-size: 1.15rem; line-height: 1.6; color: var(--heading);
  }

  /* The snippet is the only line here in the author's own voice, so it is set
     in the reading's face rather than the interface's. Two lines. */
  /* .interpretation, exactly: 1.15rem, line-height 1.75, --body. This is the
     reading's own copy, and it is the only line in the author's voice. */
  .row .snip {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
    margin-top: 10px;
    font-family: 'Source Serif 4', serif; font-size: 1.15rem; line-height: 1.75;
    color: var(--body);
  }

  .row .meta {
    display: block; margin-top: 12px;
    font-family: 'Inter', sans-serif; font-size: 0.85rem; letter-spacing: 0.02em;
    color: var(--muted);
  }
  @media (max-width: 640px) {
    .row.is-log { display: block; }
  }

  /* Why this row is in the results, when the word is in neither line the row
     already shows. Without it a search for a word that lives in the body
     returns rows with nothing marked, which reads as the search having made a
     mistake. */
  .row .ctx {
    display: block; margin-top: 8px;
    font-family: 'Inter', sans-serif; font-size: 0.9rem; line-height: 1.5;
    color: var(--muted-strong);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }

  /* Where you have been. An archive is the one surface where this is real
     information rather than a browser default nobody asked for — 105 titles
     and no way to tell the read ones from the unread is the complaint every
     back catalogue gets. Scoped to a subscriber: a visitor's history is a
     record of walls they bounced off, which is not worth marking.

     Literal, not var(--muted-strong): custom properties do not resolve inside a
     :visited rule — the browser evaluates it in a restricted context that has
     no access to them, and the declaration is dropped. #5F6A6E is the token. */
  /* Applies to everyone. The comment above described scoping this to
     subscribers via .is-visitor — a class nothing on this page ever sets, so
     the exclusion was always true and the scoping was decorative. It is also no
     longer the right idea: with the archive open, a visited row is a reflection
     the reader actually read, not a wall they bounced off. */
  .row:visited { color: #5F6A6E; }

  .row mark { background: color-mix(in srgb, var(--accent) 16%, transparent); color: inherit; }
  .row .ctx mark { background: color-mix(in srgb, var(--accent) 14%, transparent); }

  .empty {
    padding: var(--space-4) 0;
    font-family: 'Source Serif 4', serif; font-style: italic;
    color: var(--muted-strong); font-size: 1.05rem; text-align: center;
  }


  /* No hairline over the footer, and its clearance goes with it. That pair is
     footer.css:33 — the site already drops the rule and its 2.5rem padding-top
     together for pages that end in a band, on the grounds that the padding was
     only ever clearance for the rule and without it the footer floats in dead
     space. The footer's own 4rem inset is the spacing. */
  footer .footer-row { border-top: 0; padding-top: 0; }

  @media (max-width: 640px) {
    .idx-col { padding-left: 1.5rem; padding-right: 1.5rem; }
    .row { padding-top: 12px; padding-bottom: 12px; }
    .row .q { font-size: 1.05rem; }
  }
