/* travellight, wearing the same clothes as the fund site.
 *
 * Same vocabulary deliberately: odd/even/high/fore/back, rgb triplets, and the
 * hard offset shadow. Anything learned about one stylesheet applies to the
 * other, and a colour can be carried across by name.
 *
 * No Tailwind here. The look is a dozen colours and a shadow, and a build step
 * between having a thought and publishing it is a tax on the writing.
 *
 * ---------------------------------------------------------------------------
 * THEMES
 *
 * Every colour lives in this block and nowhere else. Add a theme by copying one
 * and changing the numbers; select it with THEME=<name> in the environment.
 *
 * Triplets rather than hex so a colour can be used at partial opacity --
 * rgb(var(--color-high) / 40%) -- which hex cannot do without being restated.
 *
 * Space separated, not comma separated. Both work for rgb(var(--x)), and only
 * this one works for rgb(var(--x) / 40%): the comma form expands to
 * rgb(1, 2, 3 / 40%), which mixes two syntaxes, so the browser drops the whole
 * declaration. It does that silently, and the rule simply does not apply --
 * which is how a gradient came to be written, served, and never drawn.
 * --------------------------------------------------------------------------- */

/* ---------------------------------------------------------------------------
 * TYPE
 *
 * Newsreader, by Production Type, under the SIL Open Font License -- the licence
 * is beside the files in static/fonts. Served from here rather than from Google,
 * for the reason the letter does not fetch anything either: a reader should not
 * have to ask somebody else's server for this page to finish.
 *
 * Two files. The roman carries a weight axis, so one of it covers everything
 * from 200 to 800 -- which is why asking Google for 400 and for 600 hands back
 * the same bytes. The italic does not, so it is the one cut at 400 and a bold
 * italic is left to the browser to slant, which in prose is a handful of words a
 * year.
 *
 * font-display: swap, so the words are readable in the fallback while 81
 * kilobytes arrive rather than invisible until they do.
 * --------------------------------------------------------------------------- */

@font-face {
    font-family: 'Newsreader';
    font-style: normal;
    font-weight: 200 800;
    font-display: swap;
    src: url('fonts/newsreader.woff2') format('woff2');
}

@font-face {
    font-family: 'Newsreader';
    font-style: italic;
    font-weight: 400;
    font-display: swap;
    src: url('fonts/newsreader-italic.woff2') format('woff2');
}

/* Every font this site uses, in one place, the way every colour is. The
   fallbacks are what somebody reads for the moment before the file lands, so
   they are the nearest thing already on the machine rather than a generic.

   One face for the whole page now -- reading and chrome both name it. A plain
   printed page does not set its running head in a different type from its
   body copy, and neither does this. Kept as two tokens rather than one: a
   rule still says which role it is drawing in, and the day these are meant to
   diverge again is a one-line change instead of a search for where --font
   got written in by hand. */
:root {
    --font-reading: 'Newsreader', Georgia, 'Times New Roman', serif;
    --font-ui: 'Newsreader', Georgia, 'Times New Roman', serif;
    --font-code: ui-monospace, 'Courier New', monospace;
}

/* Readability is checked rather than eyeballed. Every ink here is tested
   against the surfaces it is actually drawn on -- see web.TestEveryInkIsReadable
   OnItsSurface, which exists because a theme is a list of triplets and which of
   them ever meet is a question about the stylesheet, not about the list. */
/* A page, not a card on a slate. Fore and back are the same white -- .panel
   wraps every page on the site, not only the admin's, so a tint there was not
   a subtle frame around the writing, it was the background of the whole
   site. A post is white on white now, held apart from the page around it by
   a hairline or by nothing at all, the way a paragraph on paper is. */
html[data-theme='hot'] {
    --color-back: 255 255 255;
    --color-fore: 255 255 255;
    /* A code block has to read as a block. The shadow that used to do that is
       off, so it is a tint of the frame's own colour instead -- far enough
       from the page behind it to be a shape, light enough to keep the syntax
       colours above 4.5. */
    --color-contrast: 240 240 238;
    --color-odd: 255 255 255;
    --color-even: 250 250 248;
    --color-odd-text: 25 25 25;
    --color-even-text: 25 25 25;
    --color-fore-text: 30 30 28;
    --color-fore-link: 30 65 115;
    --color-fore-muted: 110 108 100;
    --color-odd-hover: 235 235 232;
    --color-even-hover: 230 230 227;
    /* White, the same as the page -- a box drawn with this takes a border for
       its shape now rather than a tint, so it reads as outlined rather than
       as another grey surface on the page. */
    --color-high: 255 255 255;
    --color-peak: 222 213 190;
    --color-accent: 222 213 190;
    /* The link colour again rather than the ink -- the one mark of colour on
       an otherwise black-on-white page, so the wordmark reads as a name
       rather than as a heading. */
    --color-title: 40 78 130;
    --color-text: 25 25 25;
    --color-links: 40 78 130;
    --color-muted: 110 110 108;
    --color-disabled: 180 180 178;
    --color-button: 220 220 215;
    --color-button-hover: 200 200 194;
    --color-strong: 165 60 40;
    --color-shadow: 180 180 178;
    --color-rule: 220 220 216;
}

html[data-theme='cold'] {
    --color-back: 51 51 51;
    --color-fore: 195 218 228;
    --color-contrast: 246 253 255;
    --color-odd: 172 204 219;
    --color-even: 195 218 228;
    /* Both rows are light blues here, so both take the theme's own ink. */
    --color-odd-text: 34 34 34;
    --color-even-text: 34 34 34;
    /* The frame here is still a pale blue, so these are --color-text and
       --color-links said again. */
    --color-fore-text: 34 34 34;
    --color-fore-link: 22 70 99;
    /* Darker than --color-muted, which is 3.92:1 on this theme's panel. The
       quiet things on the frame are held to the same 4.5 as the loud ones. */
    --color-fore-muted: 80 94 101;
    --color-odd-hover: 219 187 172;
    --color-even-hover: 228 195 195;
    --color-high: 201 228 195;
    --color-peak: 255 239 197;
    --color-accent: 255 212 163;
    --color-title: 255 255 255;
    --color-text: 34 34 34;
    --color-links: 22 70 99;
    --color-muted: 90 105 112;
    --color-disabled: 140 150 155;
    --color-button: 255 239 197;
    --color-button-hover: 255 212 163;
    --color-strong: 199 91 57;
    --color-shadow: 120 143 153;
    --color-rule: 150 180 195;
}

/* For writing at night, which is when most of it happens. */
html[data-theme='dark'] {
    --color-back: 22 22 20;
    --color-fore: 30 31 29;
    --color-contrast: 38 39 36;
    --color-odd: 30 31 29;
    --color-even: 36 37 34;
    --color-odd-text: 226 224 218;
    --color-even-text: 226 224 218;
    --color-fore-text: 226 224 218;
    --color-fore-link: 138 180 208;
    --color-fore-muted: 150 147 140;
    --color-odd-hover: 46 47 43;
    --color-even-hover: 46 47 43;
    --color-high: 52 54 48;
    --color-peak: 52 54 48;
    --color-accent: 52 54 48;
    --color-title: 232 230 225;
    --color-text: 226 224 218;
    --color-links: 138 180 208;
    --color-muted: 150 147 140;
    --color-disabled: 110 108 103;
    --color-button: 60 62 56;
    --color-button-hover: 74 76 68;
    --color-strong: 217 150 100;
    --color-shadow: 70 72 66;
    --color-rule: 58 60 54;
}

/* --------------------------------------------------------------------------- */

/* The shadows, in one place.
   
   An experiment: they are off. Every offset shadow on the site came from the
   same four shapes, written out thirty times, so trying the page without them
   meant editing thirty rules and trying it with them again meant editing them
   back. Named here instead, so the question is four lines rather than a diff.
   
   To put them back, restore the values in the comment beside each one. The
   shapes are deliberately not per-theme -- only --color-shadow is, and it still
   resolves at the element, so a theme that changes the shadow's colour keeps
   working the moment these are switched on again. */
:root {
    --shadow-large: none;      /* 5px 5px 0 0 rgb(var(--color-shadow)) */
    --shadow: none;            /* 3px 3px 0 0 rgb(var(--color-shadow)) */
    --shadow-small: none;      /* 2px 2px 0 0 rgb(var(--color-shadow)) */
    --shadow-hairline: none;   /* 1px 1px 0 0 rgb(var(--color-shadow)) */
    --shadow-text: none;       /* 1px 1px 0 rgb(var(--color-shadow)) */

    /* Not a box-shadow: a filter tracing the silhouette of a whole column --
       see .front-older. */
    --shadow-silhouette: none; /* drop-shadow(3px 3px rgb(var(--color-shadow))) */
}

/* --------------------------------------------------------------------------- */

* { box-sizing: border-box; }

/* Square, like fund. The shadow does the work a rounded corner would, and the
   two together read as indecision. */
input, textarea, button, table, .panel { border-radius: 0; }

body {
    /* Which ink applies here: the words, a link, and the quiet things beside
       them. The theme's own by default, and the frame's inside the panel -- see
       .panel.
       
       Named separately from the --color-* triplets so that a rule can say which
       role it is drawing without knowing which surface it ended up on, and so a
       card can take the theme's back without knowing what the frame changed
       them to. Three roles rather than one because they are three different
       decisions: a link on the panel is not its text at 60%, it is its own
       colour, chosen for what it is rather than derived from something else. */
    margin: 0;
    background: rgb(var(--color-back));
    color: rgb(var(--ink));
    /* The chrome's size, not the writing's -- see .prose below, where the
       reading is a size up. Reading and chrome share a face -- see the TYPE
       block at the top of this file -- so this is the one place left where
       the two still diverge. */
    font: 15px/1.6 var(--font-ui);
}

/* The shell: a panel of --fore on --back, offset shadow, 90% wide. Straight
   from fund's Layout. */
.shell { width: 90%; margin: 0 auto; }

.masthead {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.75rem 0;
}

.wordmark {
    font-size: 1.25rem;
    font-weight: bold;
    color: rgb(var(--color-title) / 90%);
    text-decoration: none;
    text-shadow: var(--shadow-text);
}

/* The ink is set here as well as the background, because the frame is its own
   surface and --color-text was not chosen with it in mind.

   Set on the frame and taken back by everything that paints its own background
   -- the article, a comment, a heading, the pills. That direction rather than
   inking each bare thing: a card that forgets is light on light, which is
   visible the moment anybody looks at the page, where a bare note that forgets
   is dark on dark in a corner of the admin nobody opens for a week. */
.panel {
    --ink: var(--color-fore-text);
    --link: var(--color-fore-link);
    --muted: var(--color-fore-muted);
    color: rgb(var(--ink));
    background: rgb(var(--color-fore));
    box-shadow: var(--shadow-large);
    padding: 1.25rem;
    margin-bottom: 2rem;
}

a { color: rgb(var(--link)); }

.nav { display: flex; gap: 1rem; font-size: 0.95rem; }
/* The admin's nav sits inside the panel -- the reader's is in the masthead,
   which is a sibling of it and still on --color-back. */
.panel > .nav a { color: rgb(var(--color-fore-link)); }

/* A heading that sits in its own block of --high, the way every section title
   in fund does. */
.heading {
    display: inline-block;
    background: rgb(var(--color-high));
    color: rgb(var(--ink));
    box-shadow: var(--shadow);
    border: 1px solid rgb(var(--color-rule));
    padding: 0.4rem 0.6rem;
    font-weight: 600;
    margin-bottom: 0.9rem;
}


/* Everything that paints its own surface over the frame takes the theme's inks
   back -- for itself and for whatever is written inside it.
   
   One list rather than three declarations in each of forty rules, because the
   thing that goes wrong is a surface that was never added to it. That happened:
   the admin table's header and its rows painted themselves and said nothing, so
   the header was cream on cream and every post title was cream on white. Both
   rules set a colour for their own text and neither had any reason to think
   about the links inside them.
   
   Which is why this is about painting rather than about drawing. A rule that
   paints a background is answering for everything on it, not only for the parts
   it happens to mention. */
.post,
.prose pre,
.prose th,
.prose tbody tr,
.post form button,
.post-list,
.post-list > li,
.post-table th,
.post-table tbody tr,
.comment,
.heading,
.subscribe,
.subscribe input,
.subscribe button,
.preview-banner,
.markdown,
.title-input,
.subtitle-input,
.header-caption,
.alt-input,
.publish-at,
.publish button,
.header-image button,
.login input,
.login button,
.send-panel button,
.thread-input,
.thread-sent,
.thread-about select,
.thread-add,
.thread-remove,
.thread-actions button,
.tag-link,
.tag-chip,
.tag-option,
.tag-options,
.tag-remove,
.more,
.fade {
    --ink: var(--color-text);
    --link: var(--color-links);
    --muted: var(--color-muted);
}

/* What the writing looks like, as opposed to the card it sits in.
   
   Its own class because a post is not the only place the writing appears: a
   magazine story is the same words on a page of its own, and every rule written
   as ".post img" was a rule the story did not get -- which is how a photograph
   came to be shown at the size it was uploaded at, several times the width of
   the page it was on.
   
   The card keeps .post. The words take .prose, and anything that shows the
   words takes it too. */

/* Reading -------------------------------------------------------------- */

/* The words, in the serif -- .story-body for the pre-script fallback, where a
   magazine story is a post page stacked in order; .prose for everything else a
   reader reads, including the pages the script makes, which carry this class
   too -- see pages.js. Sized up from the chrome under it, because 15 pixels of
   --font-ui is not 15 pixels of a serif read at the same size. */
.prose, .story-body {
    font: 17px/1.65 var(--font-reading);
    /* Contains a wrapped image -- see .prose img.wrap-left/.wrap-right --
       the same way a float has always needed containing: a card ending
       where the last line of text does, with a floated picture taller than
       that line hanging out past its own border, is the box lying about
       where the writing actually stops. */
    display: flow-root;
}

/* A readable measure for anything standing on its own. The front page and the
   post page both override it: there the width comes from the column. */
.post, .post-list, .empty, .login { max-width: 40rem; }

/* A post is a card wherever it appears. It was one on the front page and bare
   on its own page, so the piece of writing somebody came to read was the one
   thing on the site not sitting on anything.

   With the card's own background the same white as the page, the border is
   what says where it is now -- there is no fill left to do it. */
.post {
    background: rgb(var(--color-odd));
    color: rgb(var(--ink));
    padding: 1rem;
    box-shadow: var(--shadow);
    border: 1px solid rgb(var(--color-rule));
}

/* No top or bottom edge under a hero -- see PostArticle. The sides sit
   flush with the top rather than the writing overrunning it: no rule was
   ever drawn there for the banner above to hand off to, so there is
   nothing for the words to cross, only a card that starts open. */
.post.no-cap-border {
    border-top: 0;
    border-bottom: 0;
    /* Without this the card's own padding still held the writing a rem below
       an edge that is no longer drawn -- open on top, but with the words
       sitting under it anyway rather than starting where it does. The same
       is true at the foot, the same amount: nothing marks that edge either,
       so nothing should hold the last line a rem above it. */
    padding-top: 0;
    padding-bottom: 0;
}

/* Aligns the open top with the visible top of the first letter, and the
   open foot with the visible bottom of the last one, rather than with the
   top and bottom of their line boxes. Line-height puts room around the
   glyphs themselves that padding: 0 does not touch -- the same reason
   .title-input and its siblings needed the opposite correction. Without
   this the first and last lines still sit visibly inside where the
   borders end, just by less than a rem instead of a whole one. */
.post.no-cap-border > :first-child {
    margin-top: -0.4rem;
}

.post.no-cap-border > :last-child {
    margin-bottom: -0.6rem;
}

/* The title and the date are chrome inside a card of reading, the same as
   they are anywhere else on the site -- their own size rather than the one
   .prose set for the paragraphs around them. */
.prose h1 { font-size: 1.5rem; line-height: 1.25; margin: 0 0 0.2rem; }
.prose time { display: block; color: rgb(var(--muted)); font-size: 0.9rem; }

/* Inset, with the card's padding all the way round. Bleeding it to the top and
   left edges left the picture touching two sides and not the others, which
   reads as a mistake rather than as a decision. */
.post .post-header {
    margin: 0 0 1rem;
    box-shadow: none;
}

/* The margin moves to the figure, so the caption sits under the picture rather
   than a rule's width below it. */
.post .post-header-figure {
    margin: 0 0 1rem;
}

.post .post-header-figure .post-header {
    margin: 0;
}

.prose pre {
    overflow-x: auto;
    padding: 0.8rem 0.9rem;
    background: rgb(var(--color-contrast));
    color: rgb(var(--ink));
    box-shadow: var(--shadow);
    font-size: 0.95rem;
}
.prose code { font-family: var(--font-code); }
.prose blockquote {
    margin-left: 0;
    padding: 0.1rem 0 0.1rem 0.9rem;
    border-left: 3px solid rgb(var(--color-strong));
    color: rgb(var(--muted));
}

/* A pull quote -- see render.loneQuote, which is what turns >> in the
   markdown into this rather than an ordinary blockquote. Large, centred
   and its own paragraph's width rather than the column's, so it reads as a
   pause put there on purpose instead of a quotation continuing the text
   around it. The left rule an ordinary blockquote marks itself with is
   gone -- this is not being set apart from the writing, it is a moment of
   the writing itself, said again louder. */
.prose blockquote.pull-quote {
    margin: 2.5rem auto;
    max-width: 32rem;
    padding: 0;
    border-left: 0;
    text-align: center;
    font-size: 1.5rem;
    line-height: 1.4;
    font-style: italic;
    color: rgb(var(--ink));
}

.prose blockquote.pull-quote p { margin: 0; }
.prose table { border-collapse: collapse; width: 100%; }
.prose th { background: rgb(var(--color-high)); color: rgb(var(--ink)); border-bottom: 2px solid rgb(var(--color-rule)); }
.prose th, .prose td { padding: 0.4rem 0.6rem; text-align: left; }
.prose tbody tr:nth-child(odd) { background: rgb(var(--color-odd)); color: rgb(var(--color-odd-text)); }
.prose tbody tr:nth-child(even) { background: rgb(var(--color-even)); color: rgb(var(--color-even-text)); }

/* One block of rows rather than a stack of separate cards. Flush, so the shadow
   belongs to the list and each row does not cast one onto the row beneath it.

   A hairline between rows rather than alternating fills. Striping is what a
   dense run of columns that has to be read across wants -- a table, or the
   admin list where every row carries a date and a state to line up -- and a
   title and a date, one to a row, were never that: the second colour was
   drawing a distinction between rows that nothing else on the page makes. */
.post-list {
    list-style: none;
    padding: 0;
    margin: 0;
    background: rgb(var(--color-odd));
    box-shadow: var(--shadow);
}

/* Direct children only. A post's row contains a nested list -- its tags -- and
   without the combinator every tag matched these too: each one took a row's
   padding.

   The same applies to the anchor rule. A tag is a link inside a row, and 600
   is a headline's weight. */
/* --row-ink is set once and read by everything on the row: the title, the
   date, the subtitle. A local property rather than repeating the colour on
   each, so a row cannot end up half in one ink -- which is what happens the
   moment somebody adds a fourth thing to a row.

   The title takes it too. On the frame a link-blue title is under contrast,
   and the row is already a link from edge to edge, so the colour was carrying
   nothing the shape does not. */
.post-list > li {
    padding: 0.7rem 0.8rem;
    border-bottom: 1px solid rgb(var(--color-rule));
    --row-ink: var(--color-odd-text);
    color: rgb(var(--row-ink));
}
.post-list > li:last-child { border-bottom: none; }
/* The hovers are pale in every theme, so this one goes back to the theme's own
   text rather than the row's. */
.post-list > li:hover {
    background: rgb(var(--color-odd-hover));
    --row-ink: var(--color-text);
}
.post-list > li > a { font-weight: 600; text-decoration: none; color: inherit; }
.post-list > li time { display: block; font-size: 0.9rem; color: rgb(var(--row-ink) / 85%); }

/* Writing -------------------------------------------------------------- */

/* Two rows, not one: the title across the top and the controls beneath it.
   
   They shared a row until now, centred against each other, and the controls
   were a column with the timestamp under them -- so centring the group put the
   buttons above the title's centre line. The timestamp was pushing them up.
   
   Stacked, nothing competes for width and nothing is aligned against anything
   of a different height. The title stopped growing into whatever the controls
   left over, which is what moved them across the page every time their own
   width changed. */
.editor-bar { display: flex; flex-direction: column; gap: 0.6rem; margin-bottom: 0.8rem; }

/* A line-height for reading, in a box that holds one line.
   
   Every control here says font: inherit, which is right about the family and
   the size and wrong about the leading: it brings 1.65 with it, and a 1.65 line
   box inside a single-line input is the text floating in the middle of a tall
   box. Nobody notices it as leading -- it reads as the padding being wrong. */
input,
select,
button { line-height: 1.25; }

.title-input {
    /* The whole row. Nothing sits beside it, so there is nothing to negotiate
       with and no width for it to take from anything. */
    width: 100%;
    font: inherit;
    font-size: 1.25rem;
    /* Equal padding is not equal space around the letters. Newsreader's own
       ascent is 0.735em and its descent 0.265em -- measured from the font
       file, not eyeballed -- so a line of it sits low in an ordinary box:
       roughly 0.2em more empty room under a line with no descender than
       above it, at any padding and any line-height, because the skew is in
       the glyphs' own metrics rather than in anything this stylesheet chose.
       0.1em moved from the bottom to the top is the fix, on every field that
       reads in this face -- not on .markdown, which reads in the code face
       and does not have this problem. */
    padding: calc(0.5rem + 0.1em) 0.6rem calc(0.5rem - 0.1em);
    background: rgb(var(--color-odd));
    color: rgb(var(--ink));
    border: 1px solid rgb(var(--color-rule));
}

/* A column, so the timestamp sits under the controls rather than beside them,
   and flex-end so its right edge is the right edge of the row above it.
   
   It was a full-width child of a wrapping row before, which put it on its own
   line and aligned it to whatever width that row had resolved to -- which is
   not the width of the buttons, so it ended some way past them.
   
   align-self at the end because this is now a row of its own, so the group sits
   against the right edge of the editor rather than wherever a title happened to
   leave it. */
.publish {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    align-self: flex-end;
    gap: 0.35rem;
    max-width: 100%;
    font-size: 0.9rem;
}

/* Right-aligned as well as wrapping, so a button pushed onto a second line
   stays under the ones it belongs with rather than starting a new left edge. */
.publish-row {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.5rem;
    align-items: center;
}

.saved-at {
    margin: 0;
    font-size: 0.85rem;
    color: rgb(var(--muted));
}

.saved-at .unsaved { font-style: italic; }

/* Above the saved-at line and reading the same, because they are the same kind
   of remark: two quiet facts about the post rather than two controls.
   
   A minimum height, so the controls do not shift under the pointer when the
   first word is typed and the line stops being empty. */
.reading {
    min-height: 1.1rem;
    margin: 0 0 0.1rem;
    font-size: 0.85rem;
    color: rgb(var(--muted));
}
.publish-at, .publish button {
    font: inherit;
    font-size: 0.9rem;
    padding: 0.4rem 0.6rem;
    background: rgb(var(--color-odd));
    color: rgb(var(--ink));
    border: 1px solid rgb(var(--color-rule));
}
/* Text, not a label -- see .title-input for why it moves. A button's own
   label is centred by the browser regardless of the font's skew, which is
   why this is .publish-at alone and not the button beside it. */
.publish-at {
    padding: calc(0.4rem + 0.1em) 0.6rem calc(0.4rem - 0.1em);
}
.publish button {
    cursor: pointer;
    font-weight: 600;
    background: rgb(var(--color-button));
    box-shadow: var(--shadow);
}
/* The one that does not change what readers see is quieter, so the eye lands on
   publish. */
.publish button.secondary {
    font-weight: 400;
    background: rgb(var(--color-odd));
    color: rgb(var(--muted));
}


/* Full width now that nothing sits beside it. Taller too: the pane it shared
   the row with was setting the height of both, and half a screen of markdown
   was never the constraint. */
.markdown {
    width: 100%;
    min-height: 34rem;
    padding: 0.8rem 0.9rem;
    font: 13px/1.65 var(--font-code);
    background: rgb(var(--color-odd));
    color: rgb(var(--ink));
    border: 1px solid rgb(var(--color-rule));
    box-shadow: var(--shadow);
    resize: vertical;
}

.post-table { border-collapse: collapse; width: 100%; }
.post-table th { background: rgb(var(--color-high)); color: rgb(var(--ink)); border-bottom: 2px solid rgb(var(--color-rule)); }
.post-table th, .post-table td { text-align: left; padding: 0.5rem 0.6rem; font-size: 0.95rem; }
.post-table tbody tr:nth-child(odd) { background: rgb(var(--color-odd)); color: rgb(var(--color-odd-text)); }
.post-table tbody tr:nth-child(even) { background: rgb(var(--color-even)); color: rgb(var(--color-even-text)); }
.post-table tbody tr:nth-child(odd):hover { background: rgb(var(--color-odd-hover)); color: rgb(var(--ink)); }
.post-table tbody tr:nth-child(even):hover { background: rgb(var(--color-even-hover)); color: rgb(var(--ink)); }

/* Plain case rather than a tracked-out kicker -- the word is already draft,
   scheduled or published, and setting it in caps to say so again was an
   editorial affectation the admin table did not need. */
.state { font-size: 0.8rem; }
.state-draft { color: rgb(var(--muted)); }
.state-scheduled { color: rgb(var(--color-strong)); font-weight: 600; }
.state-published { color: rgb(var(--ink)); }

.login { display: flex; flex-direction: column; gap: 0.6rem; max-width: 18rem; }
.login input, .login button {
    font: inherit;
    padding: 0.45rem 0.6rem;
    background: rgb(var(--color-odd));
    color: rgb(var(--ink));
    border: 1px solid rgb(var(--color-rule));
}
/* See .title-input -- a button's own label does not need this. */
.login input {
    padding: calc(0.45rem + 0.1em) 0.6rem calc(0.45rem - 0.1em);
}
.login button {
    cursor: pointer;
    font-weight: 600;
    background: rgb(var(--color-button));
    box-shadow: var(--shadow);
}

.failure { color: rgb(var(--color-strong)); font-size: 0.95rem; font-weight: 600; }
.empty { color: rgb(var(--muted)); }

/* Code highlighting. Chroma emits classes under this prefix -- see
   render/chroma.go -- so colours live here and a post is never re-rendered to
   change them. */
.chroma-k, .chroma-kd, .chroma-kn, .chroma-kp, .chroma-kr, .chroma-kt { color: #8b3fa8; }
.chroma-s, .chroma-s1, .chroma-s2, .chroma-sb, .chroma-sc { color: #356c35; }
.chroma-c, .chroma-c1, .chroma-cm, .chroma-cs { color: rgb(var(--muted)); font-style: italic; }
.chroma-nf, .chroma-nx { color: #2f5fb0; }
.chroma-m, .chroma-mi, .chroma-mf { color: #7a4f16; }

html[data-theme='dark'] .chroma-k, html[data-theme='dark'] .chroma-kd,
html[data-theme='dark'] .chroma-kn, html[data-theme='dark'] .chroma-kp,
html[data-theme='dark'] .chroma-kr, html[data-theme='dark'] .chroma-kt { color: #c678dd; }
html[data-theme='dark'] .chroma-s, html[data-theme='dark'] .chroma-s1,
html[data-theme='dark'] .chroma-s2, html[data-theme='dark'] .chroma-sb,
html[data-theme='dark'] .chroma-sc { color: #98c379; }
html[data-theme='dark'] .chroma-nf, html[data-theme='dark'] .chroma-nx { color: #61afef; }
html[data-theme='dark'] .chroma-m, html[data-theme='dark'] .chroma-mi,
html[data-theme='dark'] .chroma-mf { color: #d19a66; }

/* Pictures ------------------------------------------------------------- */

/* Bounded in both directions.
   
   width: 100% forced every picture to the column's width, and a picture forced
   to a width is as tall as that width times whatever shape it is. A book cover
   or a phone photograph -- taller than it is wide -- became a column-width
   picture two or three screens deep, with the post it belongs to somewhere
   below the fold.
   
   max-width instead, so a tall picture is free to be narrower than the column,
   and a height it may not exceed. The browser satisfies both while keeping the
   proportions, so nothing is cropped or squashed: a wide picture is limited by
   the column as before, and a tall one by the height.
   
   Centred, because a portrait narrower than the column against the left edge
   reads as a picture that failed to load properly. */
.post-header {
    display: block;
    max-width: 100%;
    max-height: 32rem;
    width: auto;
    height: auto;
    margin: 0 0 1rem;
    box-shadow: var(--shadow-large);
}

/* Clear of the editor above it. Flush against the textarea the picture reads as
   part of what is being written rather than as a control of its own. */
.header-image { margin-top: 2rem; }

/* Under the picture it captions, and full width of the panel: it is a line of
   prose about the thing above it, not a label beside it. */
.header-caption {
    display: block;
    width: 100%;
    margin-top: 0.5rem;
    font: inherit;
    font-size: 0.95rem;
    /* See .title-input. */
    padding: calc(0.4rem + 0.1em) 0.6rem calc(0.4rem - 0.1em);
    background: rgb(var(--color-odd));
    color: rgb(var(--ink));
    border: 1px solid rgb(var(--color-rule));
}

.header-thumb {
    display: block;
    /* A thumbnail. The picture is shown at its real size on the post itself,
       and at that size here it pushes the controls off the screen. */
    max-width: 16rem;
    width: 100%;
    height: auto;
    margin-bottom: 0.75rem;
    box-shadow: var(--shadow);
}

.image-form { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }

.image-form input[type="file"] { font: inherit; font-size: 0.9rem; }

.alt-input {
    flex: 1 1 14rem;
    font: inherit;
    font-size: 0.9rem;
    /* See .title-input. */
    padding: calc(0.4rem + 0.1em) 0.6rem calc(0.4rem - 0.1em);
    background: rgb(var(--color-odd));
    color: rgb(var(--ink));
    border: 1px solid rgb(var(--color-rule));
}

.header-image button {
    font: inherit;
    font-size: 0.9rem;
    padding: 0.4rem 0.6rem;
    cursor: pointer;
    font-weight: 600;
    background: rgb(var(--color-button));
    color: rgb(var(--ink));
    border: 1px solid rgb(var(--color-rule));
    box-shadow: var(--shadow);
}

.header-image button.secondary {
    font-weight: 400;
    background: rgb(var(--color-odd));
    color: rgb(var(--muted));
}

.hint { color: rgb(var(--muted)); font-size: 0.85rem; margin: 0.4rem 0 0; }

/* The newest post, opened up ------------------------------------------- */

/* The featured post is the same card, deeper. The deeper of fund's two shadows,
   so it reads as the one thing on the page rather than as the first of many
   equal ones. */
.featured {
    margin-bottom: 2rem;
    box-shadow: var(--shadow-large);
}

/* Link coloured, like every other post title on the page. */
.featured h1 a { text-decoration: none; color: rgb(var(--link)); }

/* The last of the visible text fades into the card, so there is a reason to
   look for the link rather than only a link to find.
   
   Positioned above its own wrapper rather than over the body of the post. The
   body is a run of sibling blocks, so a gradient attached to the last one would
   sit wherever that block happened to end; anchored to the control it stays
   against the boundary between what is shown and what is not.
   
   Hidden from screen readers -- it is a gradient standing in for "there is more
   below", and the link beside it already says that in words. */
.more-wrap { position: relative; }

.fade {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 100%;
    height: 4.5rem;
    pointer-events: none;
    background: linear-gradient(to bottom,
        rgb(var(--color-odd) / 0%),
        rgb(var(--color-odd) / 85%) 70%,
        rgb(var(--color-odd)));
}

/* Subtle, because it continues the paragraph above it rather than being a thing
   to do to the page -- at full width with a shadow it competed with the post it
   was introducing.
   
   An <a> in the markup as well as in appearance now that it goes to the post.
   It was a <button> while it acted on the page, which was right then and would
   be wrong here: a link that is not an anchor cannot be opened in a new tab,
   does not show where it goes, and is announced as the wrong thing entirely. */
.more {
    display: inline-block;
    margin-top: 0.5rem;
    padding: 0;
    font: inherit;
    font-size: 1rem;
    cursor: pointer;
    color: rgb(var(--link));
    background: none;
    border: 0;
    box-shadow: none;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.more:hover { color: rgb(var(--color-strong)); }

/* What people said ----------------------------------------------------- */

.comments { max-width: 40rem; margin-top: 2.5rem; }

.comment {
    padding: 0.7rem 0.8rem;
    margin-bottom: 0.75rem;
    background: rgb(var(--color-odd));
    color: rgb(var(--ink));
    box-shadow: var(--shadow);
}

/* Nesting, capped at three. Past that it says nothing a reader is tracking and
   walks off the right-hand side of the page. */
.comment.depth-2 { margin-left: 1.5rem; }
.comment.depth-3 { margin-left: 3rem; }

.comment-who {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    align-items: center;
    font-size: 0.9rem;
    margin-bottom: 0.35rem;
}

.avatar { width: 1.4rem; height: 1.4rem; object-fit: cover; }
.comment-name { font-weight: 600; }
.comment-handle, .comment-who time { color: rgb(var(--muted)); }
.comment-body { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
/* On the frame rather than in a card, so its link takes the frame's ink --
   the ordinary --color-links was chosen against the page, not the frame, and
   is not held to the same ratio there in every theme. */
.comment-join { font-size: 0.95rem; margin-top: 1rem; }
.comment-join a { color: rgb(var(--color-fore-link)); }

/* Subtitles ------------------------------------------------------------ */

.subtitle {
    margin: 0 0 1rem;
    font-size: 1.05rem;
    color: rgb(var(--muted));
}

.post-list .list-subtitle {
    margin: 0.15rem 0;
    font-size: 0.95rem;
    color: rgb(var(--row-ink) / 85%);
}

.subtitle-input {
    width: 100%;
    margin-bottom: 0.8rem;
    font: inherit;
    font-size: 1rem;
    /* See .title-input. */
    padding: calc(0.4rem + 0.1em) 0.6rem calc(0.4rem - 0.1em);
    background: rgb(var(--color-odd));
    color: rgb(var(--ink));
    border: 1px solid rgb(var(--color-rule));
}

/* The front page ------------------------------------------------------- */

/* The gap is shared with the post page, which derives its width from this grid
   -- see .post-page. Written once so the two cannot drift apart by a change
   made to only one of them. */
:root { --front-gap: 2rem; }

/* Both grids, declared together and before the media query below overrides
   them. Same specificity, so a base rule written after that block would win at
   every width and neither page would ever have two columns. */
.front, .post-page { display: grid; grid-template-columns: 1fr; gap: var(--front-gap); }

/* minmax(0, …) rather than plain fractions: a grid column's default minimum is
   its content, so one long word in a title would push the column wider than its
   share and the layout would stop adding up. */
@media (min-width: 62rem) {
    .front { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); align-items: start; }

    /* The post page is the same two columns, so the reading column is the same
       column: a post is the same width whether it is being previewed on the
       front page or read on its own, because it is now the same grid rather
       than an arithmetic copy of one.

       Below this width both are a single column. */
    .post-page { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); align-items: start; }

    /* Capped at the window's own height and scrolling on its own past that,
       rather than pushing the page taller than the featured post beside it
       -- enough posts accumulate here that the list outgrows a screen
       before the front page does. Only at this width: below it the two
       columns are one, the list is already the page's own scroll, and a
       second one nested inside would just be confusing.

       Sticky rather than merely capped, so the list a reader was partway
       down does not jump back to its own top -- and out of view -- every
       time the page underneath it scrolls.

       The frame carries the stickiness; see .front-older for why the cap
       and the scrolling itself are not also done here. position: sticky
       still establishes a containing block, the same as position: relative
       would, which is what lets .front-older-frame::after below anchor to
       this box's own edge rather than the page's. */
    .front-older-frame {
        position: sticky;
        top: var(--front-gap);
    }

    /* The same cap named again here, in max-height rather than height,
       because the two say different things when the list is shorter than
       the window: height: 100% of a frame whose own height only exists to
       be capped is asking the browser to solve a box's height from a box's
       height, which resolves to nothing constraining anything -- the list
       stopped clipping at all, and both the scroll and the fade below it
       stopped happening with it. max-height leaves a short list exactly as
       tall as its own posts and only steps in once there is more of them
       than the window can hold, which is what was meant either way.

       The scrolling lives here rather than on the frame itself so the fade
       below can sit outside it: painted on an element that scrolls, a fade
       anchored to that element's own bottom edge scrolls the same distance
       the content does and is never where the visible edge actually is
       except by coincidence -- which is what a first attempt at this, fading
       .front-older itself with position: sticky on the fade, actually did:
       right at rest, wrong the moment anything scrolled. */
    .front-older {
        max-height: calc(100vh - 2 * var(--front-gap));
        overflow-y: auto;
        /* Counted inside the scrolling box rather than outside it, so
           scrolling to the last post still leaves room under it instead of
           stopping flush with the frame's own edge. */
        padding-bottom: var(--front-gap);
    }

    /* The padding above only shows once the list is scrolled all the way to
       its own end -- everywhere short of that, a row sits exactly as tall
       as the frame and the last one visible is cut off flush against the
       edge doing the clipping, mid-word as often as not. The same problem
       .fade already solves for a post cut short above its read-more link --
       see .more-wrap -- answered the same way here: painted over the edge
       rather than left to the clip alone, so it reads as this list fading
       out rather than stopping short.

       On the frame, absolutely positioned, rather than on .front-older,
       which is the thing actually scrolling -- see the note there. Anchored
       to the frame's own bottom instead, this stays exactly where the
       visible edge is regardless of how far the list inside has scrolled. */
    .front-older-frame::after {
        content: "";
        position: absolute;
        bottom: 0;
        left: 0;
        right: 0;
        height: 3rem;
        pointer-events: none;
        /* Transparent for most of its own height rather than a plain
           top-to-bottom fade, the same curve .fade already uses -- see
           .more-wrap. A straight linear fade is already half-opaque by its
           own midpoint, which over 3rem is enough to grey out a whole
           short entry's title, date and tags together rather than
           softening the one line actually touching the edge. */
        background: linear-gradient(to bottom,
            rgb(var(--color-fore) / 0%),
            rgb(var(--color-fore) / 85%) 70%,
            rgb(var(--color-fore)));
        /* Answering for its own ink like every other painted surface has to
           -- see TestEveryPaintedSurfaceTakesTheThemeInksBack -- even though
           it carries no text of its own: the fade is painted in .panel's
           own background colour, not a photograph's unknown one, so
           .panel's own inks are the right answer here too, not an arbitrary
           one picked to satisfy the rule. */
        --ink: var(--color-fore-text);
        --link: var(--color-fore-link);
        --muted: var(--color-fore-muted);
    }
}

/* One column rather than two, under a hero -- see the note beside Post's own
   post-page for why. A single class beats the media query above on
   specificity alone, so this holds at every width without repeating itself
   inside it. */
.post-page.centred { grid-template-columns: 1fr; }

/* The same reading measure everywhere a post's own words are the thing being
   read, feature or not -- see .post-page .post below, which is what actually
   answers "how wide is the article" in every case: this bounds the column
   they read that width from, rather than the article's own width directly.
   An ordinary post used to fill however wide the 2fr grid column happened to
   be, wider than a feature's fixed measure for no reason a reader could see
   -- the same words, meaning the same thing, read across a different amount
   of the eye's own travel per line depending on which kind of post they
   were on. */
.post-main { max-width: 40rem; }

/* The reading measure, centred, now that there is no narrower grid column
   doing that job for it. max-width: none further in -- see the rule below
   about .post-page .post et al -- so the card fills whatever room this
   gives it rather than fighting it for a second opinion on the width.

   width: 100% first, the same fix .hero-banner needed for the same reason
   -- see its own note in the media query above. A grid item with an auto
   margin on an axis is not stretched on that axis the way a plain block
   child would be; it is sized to its own content instead, auto margins or
   not, so a feature's body shrank to fit a paragraph's own words rather
   than filling the column and only then being capped and centred within
   it. width: 100% gives it a tentative size to cap and centre in the first
   place. */
.post-page.centred .post-main,
.post-page.centred .related {
    width: 100%;
    max-width: 40rem;
    margin: 0 auto;
}

/* The card's own max-width is what keeps a post readable on its own page --
   the same 40rem .post-main bounds it to everywhere else a post's words are
   read, so the front page shows a post exactly as wide as its own page
   does, not the width of a wider grid column deciding on its behalf.
   .side-list .post-list is a different thing this rule used to share a
   selector with: the past and related lists, which do want to fill their
   own (much narrower) column, not read at the article's own measure. */
.front-latest .post { max-width: 40rem; }
.side-list .post-list { max-width: none; }

/* A pinned post's cover, standing in for the featured card -- see
   web.PinnedFeatured. Wearing .story-cover for the look and .pinned-cover for
   what is different about wearing it here: no page behind it to be as tall as,
   so a shape of its own rather than --page-room, and a border, because
   nothing in this column paints its own background and a white cover on a
   white page would otherwise have no edge.

   A ratio rather than a min-height. A magazine cover is not a strip that
   happens to be as tall as the room left over -- it is a fixed shape the
   photograph is cropped to, the same shape on a narrow phone as on a wide
   desktop column. 3:4 is an ordinary magazine trim; object-fit: cover on
   .story-cover-picture is what does the cropping, filling the shape however
   the photograph's own proportions differ from it. */
.pinned-cover-link {
    display: block;
    text-decoration: none;
    color: inherit;
}

.story-cover.pinned-cover {
    aspect-ratio: 3 / 4;
    /* A ceiling as well as a ratio -- 4:3 of a wide column is taller than
       most windows, and a cover a reader cannot see the foot of before
       scrolling is a cover that has stopped reading as a cover. Whichever
       of the two the browser hits first decides the shape; the photograph
       still fills whatever it lands on, cropped by object-fit: cover on
       .story-cover-picture same as always. */
    max-height: 80vh;
    /* Cancels --page-room -- see .story-page, .story-cover further down --
       which would otherwise put a floor of 60vh under the ratio and win
       almost everywhere, since 60% of most screens is taller than 4:3 of
       this column is wide. */
    min-height: auto;
    border: 1px solid rgb(var(--color-rule));
    /* Was on .pinned-cover-link, back when that anchor wrapped the whole
       card and so was the box the front page actually flowed around. It
       wraps only the picture and title now -- see PinnedFeatured -- so the
       space after the card belongs on the card itself. */
    margin-bottom: 2rem;
}

/* The title at the top rather than the foot -- see .story-cover-plate.over-
   picture further down for the ordinary magazine story's cover, which this
   deliberately does not match: a cover this tall asks to be scrolled
   before the words show at all, and a title that is the first thing under
   the masthead is the more familiar shape of a magazine's own front page
   besides. The scrim is turned to match -- dark at the top, fading down
   the photograph rather than up it -- and the room the words wanted below
   them (over-picture's own padding-top) moves to below them instead.

   Absolutely positioned to the top rather than aligned there by the
   cover's own flex -- see PinnedFeatured, which now gives the cover two
   plates, one for the title and one for the tags. flex was never going to
   place two things at two fixed edges of the same box; align-items puts
   every flex child at the same edge, and the sole item left in flow after
   the tags' own plate went absolute (see .story-cover-tags) was measuring
   itself against its own content rather than the width flex-basis: auto
   was meant to hand it -- a flex sizing quirk neither plate needs once
   both simply pin themselves to an edge of a box they do not otherwise
   share layout with. */
.pinned-cover .story-cover-plate {
    position: absolute;
    inset: 0 0 auto 0;
    width: auto;
}

.pinned-cover .story-cover-plate.over-picture {
    /* Fully transparent by the foot, the same trade .story-cover-plate.
       over-picture makes for the ordinary hero -- the stops are percentages
       of this box's own height, so a longer title or subtitle wrapping a
       line further stretches the box and the gradient scales with it
       rather than getting cut off early. A floor here instead of that
       fade-out was a hard-edged seam where the box ended and the plain
       photograph began. */
    background: linear-gradient(to bottom,
        rgb(0 0 0 / 82%),
        rgb(0 0 0 / 55%) 55%,
        rgb(0 0 0 / 0%));
    padding-top: 1.5rem;
    padding-bottom: 1.5rem;
}

/* A plate of its own for the tags, at the actual foot of the cover -- see
   PinnedFeatured. The title's own plate is anchored to the top now -- see
   .story-cover.pinned-cover.has-picture -- so tags appended to it would sit
   just under the date instead of near the bottom of the photograph, which
   was the whole point of asking for them here rather than under the card
   the rest of the front page uses. Absolutely positioned rather than a
   second flex child of .story-cover: that flex only ever expected the one
   plate, and a second one would compete with it for the row rather than
   sitting at a fixed edge regardless of what the first one is doing. */
.story-cover-tags {
    position: absolute;
    inset: auto 0 0 0;
    padding: 1.5rem;
}

/* No scrim here, on purpose -- unlike the title's plate, which darkens the
   whole width of the cover behind it, a row of a few tags does not want a
   bar spanning the photograph just for their sake. Each tag carries its
   own legibility instead -- see .story-cover-tags .tag-link -- the same
   way the title's own words rely on the plate behind all of them together,
   because there is no bar here for them to rely on. */
.story-cover-tags.over-picture {
    --ink: 255 255 255;
    --link: 255 255 255;
    --muted: 224 224 224;
    color: rgb(var(--ink));
}

/* A dark chip rather than a bordered one, so a tag reads against whatever
   part of the photograph happens to be behind it -- a light sky is exactly
   where an outline-only tag, which is what .tag-link is everywhere else on
   the site, would disappear.

   --ink named again here rather than left to inherit from .story-cover-
   tags.over-picture above: .tag-link sets its own --ink, to the theme's
   ordinary text colour, so that ancestor's white never reached it -- the
   text was rendering dark-on-dark. */
.story-cover-tags .tag-link {
    --ink: 255 255 255;
    /* A bit roomier than an ordinary tag -- floating on a photograph rather
       than sitting on the page's own even rhythm of things, it reads better
       with some air around the word. Keeps the same 0.1em shift from bottom
       to top the base rule makes for Newsreader's own metrics. */
    padding: calc(0.25rem + 0.1em) 0.7rem calc(0.25rem - 0.1em);
    background: rgb(0 0 0 / 55%);
    border-color: rgb(255 255 255 / 35%);
}

/* .tag-link:hover reaches for --color-button, a theme colour meant for an
   ordinary page background -- near-white in the light theme, which put
   white text on a near-white chip to begin with. Inverted instead of
   darkened: a solid white chip with black text is a press anybody can see
   against any photograph, light or dark, rather than one more shade of an
   already-dark background. */
.story-cover-tags .tag-link:hover {
    background: rgb(255 255 255);
    color: rgb(0 0 0);
}

/* A magazine story takes the whole measure. There is no column beside it: what
   to read next is at the end of the story, where a story's apparatus goes. */
/* The measure a page is worked out against, and what a story is as wide as
   until the script widens it for a spread.
   
   The preview is deliberately not held to it. A spread inside a wrapper this
   wide is a spread of two narrow pages, which is a preview of a page nobody
   will be shown -- and fidelity is the whole of what this page is for. */
.story { max-width: 46rem; margin: 0 auto; }

/* A page is made by the script -- see pages.js -- so this is what one wears
   once it exists. The cover is one from the start, which is why it takes the
   same rules. */
.story-page, .story-cover {
    background: rgb(var(--color-odd));
    --ink: var(--color-text);
    --link: var(--color-links);
    --muted: var(--color-muted);
    color: rgb(var(--ink));
    padding: 1.5rem;
    box-shadow: var(--shadow);
}

/* The cover. Room above and below the title, because the whole point of it is
   that there is nothing else on the page. */
/* The cover. The words sit at the foot of the photograph when there is one and
   in the middle of the page when there is not -- which is the difference
   between a cover and a page with a title at the top of it. */
.story-cover {
    position: relative;
    display: flex;
    align-items: center;
    padding: 0;
    overflow: hidden;
}

.story-cover.has-picture { align-items: flex-end; }

/* Cropped to the page rather than fitted inside it. max-height is lifted
   because the site's is what stops a picture in the body taking over a post,
   and on a cover taking over is the job. */
.story-cover-picture {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    max-height: none;
    object-fit: cover;
}

.story-cover-plate {
    position: relative;
    width: 100%;
    padding: 1.5rem;
}

/* Dark enough at the foot to read white against, and gone by the middle of the
   page so the photograph is not dimmed by a scrim it does not need.
   
   The inks are named here rather than taken from the theme: what is under them
   is a photograph, and no theme knows what colour that is. */
.story-cover-plate.over-picture {
    background: linear-gradient(to top,
        rgb(0 0 0 / 82%),
        rgb(0 0 0 / 55%) 45%,
        rgb(0 0 0 / 0%));
    --ink: 255 255 255;
    --link: 255 255 255;
    --muted: 224 224 224;
    color: rgb(var(--ink));
    padding-top: 4rem;
}

/* The shape of a magazine page, as large as the window allows -- see pages.js,
   which sets both from the window it is actually in. Usually narrower than the
   column, because a page that tall at the column's full width does not fit on a
   laptop screen.
   
   The body wears the same width and padding as a page while the writing is
   still loose in it, or the blocks are measured across a wider measure than
   they will be read at and every page comes out short. */
.story-page, .story-cover {
    width: var(--page-width, 100%);
    flex: 0 0 auto;
}

/* Two facing each other, the way a magazine is open in front of somebody --
   which is also what fills the room a page of this shape leaves either side of
   it in a window. One when there is not room for two: see pages.js, which
   decides and says so on the story. */
.story-spread {
    display: flex;
    justify-content: center;
    gap: 2rem;
}

/* The body wears the page's measure only while the writing is still loose in
   it. Blocks measured across a wider column than they will be read at make
   every page come out short, and the padding is part of that measure.
   
   Taken off again once the pages exist, because they carry their own -- and a
   page inside a padded body is a page pushed sideways out of it, which is what
   made the cover and the second page sit in different places. */
.story-body.story-measuring {
    width: var(--page-width, 100%);
    /* The page's padding, all four sides. The sides are what the writing wraps
       to; the top and bottom are room the writing does not get, and measuring
       without them made every page overflow by exactly them. */
    padding: 1.5rem;
    margin-left: auto;
    margin-right: auto;
}

/* A floor rather than a height, and the same for every page: it was a floor of
   60vh against a fill of 82, which is why a page could be a third of a screen
   shorter than the one before it. A photograph taller than its page still has
   to go somewhere, and a page grown to hold one is better than a page showing
   half of it. */
.story-page, .story-cover { min-height: var(--page-room, 60vh); }

/* A page the script has turned away from.
   
   The hidden attribute is display:none in the browser's own stylesheet, which
   is the weakest rule there is -- any display of ours outranks it. The cover
   sets display:flex to put its words at the foot of the photograph, so it
   quietly stopped being hideable and stayed on the screen through every turn
   while the pages behind it changed.
   
   Said here rather than by not setting display on a page: the pages need
   layout, and a rule that works only while nobody gives them any is a rule
   waiting to break the next time somebody does. */
.story-page[hidden], .story-cover[hidden] { display: none; }

/* The title of the piece at the top of every page of it, the way a magazine
   runs a head. Quiet, because it is there to be glanced at rather than read
   again. */
.story-head {
    margin: 0 0 1.2rem;
    padding-bottom: 0.5rem;
    border-bottom: 1px solid rgb(var(--color-rule));
    font-size: 0.85rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: rgb(var(--muted));
}
.story-cover h1 { font-size: 2rem; line-height: 1.15; margin: 0; }

.story-cover-line {
    margin: 0.4rem 0 0;
    font-size: 1.05rem;
    color: rgb(var(--muted));
}

.story-cover-by {
    margin: 0.9rem 0 0;
    font-size: 1rem;
    font-weight: 600;
}

.story-cover time {
    display: block;
    margin-top: 0.15rem;
    font-size: 0.9rem;
    color: rgb(var(--muted));
}

/* The pager is the one part of this the script draws, so it is empty until it
   does. Reserved rather than appearing, for the same reason the count in the
   editor is. */
.story-pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    min-height: 2.2rem;
    margin-top: 1rem;
}

.story-turn {
    font: inherit;
    font-size: 0.95rem;
    padding: 0.4rem 0.7rem;
    cursor: pointer;
    background: rgb(var(--color-button));
    --ink: var(--color-text);
    --link: var(--color-links);
    --muted: var(--color-muted);
    color: rgb(var(--ink));
    border: 0;
    box-shadow: var(--shadow-hairline);
}

.story-turn:hover:not(:disabled) { background: rgb(var(--color-button-hover)); }

/* Still there, so the row does not resize at the ends of the story. */
.story-turn:disabled { visibility: hidden; }

.story-where { font-size: 0.85rem; color: rgb(var(--muted)); }

/* Inside the reading column, the column is the width. Left at 40rem these would
   sit narrower than the article above them, which reads as the page having two
   minds about how wide it is. */
.post-page .post, .post-page .comments, .post-page .subscribe { max-width: none; }

/* Across both columns rather than into one of them. It is a notice about the
   page, not something to read beside the post -- and as an ordinary child of
   the grid it took the reading column and pushed the article into the narrow
   one meant for what to read next. */
.post-page .preview-banner { grid-column: 1 / -1; }

/* A post's own cover, when it is featured -- see web.heroBanner. The same
   idea as a magazine story's own cover and the front page's pinned one: a
   photograph with the title on it rather than beside it. A different shape
   from both, though: this spans the whole page rather than filling a
   single column, so it reads as a banner, wide and comparatively short,
   rather than as a page of its own. */
.hero-banner {
    grid-column: 1 / -1;
    position: relative;
    overflow: hidden;
    /* Explicit rather than left to stretch, which is what a grid item's
       width is normally sized by. With both aspect-ratio and min-height
       set and no explicit width, the browser resolved width from the
       min-height through the ratio instead of the other way around -- a
       banner tall enough for min-height's floor at whatever width that
       took, 704px on a phone where the column is 410 -- and the whole page
       scrolled sideways to fit it. Naming the width outright leaves nothing
       for min-height to resolve backwards into. */
    width: 100%;
    aspect-ratio: 2 / 1;
    /* A floor under the ratio, not a replacement for it -- see width above.
       At a phone's width the ratio alone gives this well under 200px,
       nowhere near enough room for a wrapped title, its subtitle, the
       byline and the date, and .hero-banner-plate is absolutely positioned
       inside this box rather than in flow, so it cannot grow the box to
       fit itself the way ordinary content would; the overflow just clipped
       instead. 22rem is comfortable for a three-line title at that width
       with room left for the rest of the plate under it. */
    min-height: 22rem;
    border: 1px solid rgb(var(--color-rule));
}

.hero-banner-picture {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    max-height: none;
    object-fit: cover;
}

/* Dark enough at the foot to read white against, and gone by the middle of
   the banner so the photograph is not dimmed by a scrim it does not need --
   the same trade .story-cover-plate.over-picture makes. */
.hero-banner-plate {
    position: absolute;
    inset: auto 0 0 0;
    padding: 2rem 1.5rem 1.5rem;
    background: linear-gradient(to top,
        rgb(0 0 0 / 82%),
        rgb(0 0 0 / 55%) 45%,
        rgb(0 0 0 / 0%));
    --ink: 255 255 255;
    --link: 255 255 255;
    --muted: 224 224 224;
    color: rgb(var(--ink));
}

.hero-banner-plate h1 { font-size: 2rem; line-height: 1.15; margin: 0; }

.hero-banner-line {
    margin: 0.4rem 0 0;
    font-size: 1.05rem;
    color: rgb(var(--muted));
}

.hero-banner-by {
    margin: 0.9rem 0 0;
    font-size: 1rem;
    font-weight: 600;
}

.hero-banner time {
    display: block;
    margin-top: 0.15rem;
    font-size: 0.9rem;
    color: rgb(var(--muted));
}

/* One shadow around the whole column, the way fund's Section does it: a
   drop-shadow filter on the wrapper rather than a box-shadow on each piece.
   
   The difference is what the shadow traces. box-shadow follows each element's
   own rectangle, so a heading pill above a wider block casts two shadows that
   overlap where they meet. drop-shadow follows the silhouette of everything
   inside, so the narrow pill and the block below come out as one outline -- a
   heading with a shadow down its right edge, continuing into the list.
   
   It works here because both have opaque backgrounds. The filter follows the
   alpha channel, so on a transparent container it would trace the text itself
   and print a grey copy of every word behind it. */
.side-list { filter: var(--shadow-silhouette); }

/* Flush, which is the point of the filter above: a heading pill with a gap
   under it is two things stacked, and a heading sitting on its list is one
   thing with a label. */
.side-list .heading { margin-bottom: 0; box-shadow: none; }
.side-list .post-list { box-shadow: none; }

.side-list .post-list > li { padding: 0.6rem 0.7rem; }
.side-list .post-list > li > a { font-size: 0.95rem; }

/* Hidden for now: the checkbox still submits its value when the form does, so
   a post already read a page at a time stays that way. Only the way to change
   it is gone. */
.format { display: none; }
.format label { display: flex; gap: 0.4rem; align-items: center; }
.format-note { margin: 0.2rem 0 0 1.4rem; color: rgb(var(--muted)); }

.announce { font-size: 0.9rem; margin-bottom: 0.8rem; }
.announce label { display: flex; gap: 0.4rem; align-items: center; }
.announce .announced { color: rgb(var(--muted)); }

/* Where to write a thread about a post, offered beside the announce box. */
.announce .thread-link {
    display: inline-block;
    margin-top: 0.3rem;
    color: rgb(var(--link));
}

/* Threads ---------------------------------------------------------------- */

/* A page of boxes rather than one box with rules in it. The breaks are the
   thing being decided, so they are the thing you can see. */
.thread-form { margin-top: 0.5rem; }

.thread-about { margin-bottom: 0.9rem; font-size: 0.95rem; }
.thread-about label { margin-right: 0.4rem; color: rgb(var(--muted)); }
.thread-about select {
    font: inherit;
    font-size: 0.95rem;
    padding: 0.25rem 0.4rem;
    max-width: 100%;
    background: rgb(var(--color-odd));
    color: rgb(var(--ink));
    border: 1px solid rgb(var(--color-rule));
}

.thread-note {
    margin: 0.4rem 0 0;
    font-size: 0.9rem;
    color: rgb(var(--muted));
}

/* Beside the article rather than beside the boxes. It is a decision about the
   thread, like what the thread is about, and not about any one post. */
.thread-numbering {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 0.5rem;
    font-size: 0.9rem;
    color: rgb(var(--muted));
    cursor: pointer;
}

.thread-boxes { list-style: none; margin: 0; padding: 0; }

.thread-box { margin-bottom: 0.7rem; }

.thread-box-head {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    margin-bottom: 0.2rem;
    font-size: 0.85rem;
    color: rgb(var(--muted));
}

/* The number a post will carry, which is a fact about the thread rather than
   about the box -- so it moves when a box above it does. */
.thread-box-number { font-weight: 600; }

/* Pushed to the end, because it is the thing being watched while typing. */
.thread-box-count { margin-left: auto; font-variant-numeric: tabular-nums; }
.thread-box-count.over { color: rgb(var(--link)); font-weight: 600; }

.thread-box-link { margin-left: auto; color: rgb(var(--link)); }

.thread-remove {
    font: inherit;
    font-size: 0.85rem;
    padding: 0;
    background: none;
    border: none;
    color: rgb(var(--muted));
    cursor: pointer;
    text-decoration: underline;
}
.thread-remove:hover { color: rgb(var(--ink)); }

.thread-input {
    width: 100%;
    min-height: 5.5rem;
    resize: vertical;
    font: inherit;
    font-size: 1rem;
    line-height: 1.5;
    /* See .title-input -- the skew is per line, so a box with room for
       several still wants it on the first and the last. */
    padding: calc(0.4rem + 0.1em) 0.6rem calc(0.4rem - 0.1em);
    background: rgb(var(--color-odd));
    color: rgb(var(--ink));
    border: 1px solid rgb(var(--color-rule));
}

.thread-add {
    font: inherit;
    font-size: 0.9rem;
    padding: 0.3rem 0.7rem;
    background: none;
    color: rgb(var(--ink));
    border: 1px dashed rgb(var(--color-rule));
    cursor: pointer;
}
.thread-add:hover { border-style: solid; }

.thread-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-top: 1rem;
}
.thread-actions button {
    font: inherit;
    font-size: 0.9rem;
    padding: 0.4rem 0.6rem;
    cursor: pointer;
    font-weight: 600;
    color: rgb(var(--ink));
    background: rgb(var(--color-button));
    border: 1px solid rgb(var(--color-rule));
    box-shadow: var(--shadow);
}
/* The ones that do not put anything in somebody's feed are quieter, so the eye
   lands on the button that does. */
.thread-actions button.secondary,
.thread-actions button.danger {
    font-weight: 400;
    background: rgb(var(--color-odd));
    color: rgb(var(--muted));
    box-shadow: none;
}

/* Pushed onto its own line under the buttons, because it is the answer to
   pressing one rather than another thing to press. */
.thread-state {
    flex-basis: 100%;
    margin: 0.2rem 0 0;
    font-size: 0.85rem;
    color: rgb(var(--muted));
}

/* What went out: a record, so it keeps the line breaks it was written with. */
.thread-sent {
    margin: 0;
    padding: 0.4rem 0.6rem;
    white-space: pre-wrap;
    font-size: 1rem;
    line-height: 1.5;
    color: rgb(var(--muted));
    background: rgb(var(--color-odd));
    border: 1px solid rgb(var(--color-rule));
}

.new-thread { font-size: 0.95rem; color: rgb(var(--link)); }

.state-posting { color: rgb(var(--color-strong)); font-weight: 600; }
.state-posted { color: rgb(var(--ink)); }

/* The mailing list control, beside the editor rather than inside its form. */
.send-panel { margin-top: 1.5rem; font-size: 0.9rem; }
.send-panel .send-state { color: rgb(var(--muted)); }
/* The ink and the link, not an accent. Every accent in this palette is a
   background colour, and one of them as text is invisible against whatever it
   sits on. These sit on the frame, so they resolve to the frame's pair. */
.send-panel .send-state.sent { color: rgb(var(--ink)); }
.send-panel .send-state.sending { color: rgb(var(--link)); }
.send-panel button {
    font: inherit;
    font-size: 0.9rem;
    padding: 0.4rem 0.6rem;
    cursor: pointer;
    font-weight: 600;
    background: rgb(var(--color-button));
    color: rgb(var(--ink));
    border: 1px solid rgb(var(--color-rule));
    box-shadow: var(--shadow);
}

.send-note { margin: 0.4rem 0 0; color: rgb(var(--muted)); font-size: 0.85rem; }

/* The mailing list ----------------------------------------------------- */

.subscribe {
    max-width: 40rem;
    margin-top: 2rem;
    padding: 1rem;
    background: rgb(var(--color-high));
    color: rgb(var(--ink));
    box-shadow: var(--shadow);
    border: 1px solid rgb(var(--color-rule));
}

.subscribe label { display: block; font-weight: 600; margin-bottom: 0.5rem; }

.subscribe-row { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.subscribe input {
    flex: 1 1 14rem;
    font: inherit;
    padding: 0.45rem 0.6rem;
    background: rgb(var(--color-odd));
    color: rgb(var(--ink));
    border: 1px solid rgb(var(--color-rule));
}

/* Bordered rather than filled -- the weight says this is the button to press,
   without a block of colour saying it again. */
.subscribe button, .post form button {
    font: inherit;
    font-weight: 600;
    padding: 0.45rem 0.9rem;
    cursor: pointer;
    background: none;
    color: rgb(var(--ink));
    border: 1px solid rgb(var(--ink));
}

.subscribe .hint { margin: 0.5rem 0 0; }

/* What the form says back. Always the same sentence, whatever happened -- see
   SubscribeForm. */
.subscribe-said { margin: 0; font-weight: 600; }

.masthead .nav { font-size: 0.95rem; }

/* Pressing things ------------------------------------------------------- */

/* Every control that sits on a shadow behaves the same way, in one place rather
   than in the six rules that each define one of them.
   
   Only .publish button had a hover before this, so most of the editor's buttons
   gave no sign they could be clicked at all -- and nothing anywhere had a
   pressed state.
   
   Down is the shadow shrinking while the control moves into it by the same
   amount. Both halves are needed: moving without shrinking slides the whole
   thing sideways, and shrinking without moving makes it shallower while it sits
   still. Together the top face stays put and the gap beneath it closes, which
   is what a key going down does. */
.publish button,
.login button,
.header-image button,
.send-panel button,
.subscribe button,
.post form button,
.tag-link {
    /* Quick enough to feel like a response to the press rather than an
       animation of it. */
    transition: background 80ms ease, box-shadow 80ms ease, transform 80ms ease;
}

.publish button:hover,
.login button:hover,
.header-image button:hover,
.send-panel button:hover,
.subscribe button:hover,
.post form button:hover {
    background: rgb(var(--color-button-hover));
}

/* The quiet ones keep their place in the hierarchy when hovered: they lighten
   towards the row highlight rather than towards the primary button's colour,
   which would make them look like the thing to press. */
.publish button.secondary:hover,
.header-image button.secondary:hover {
    background: rgb(var(--color-odd-hover));
}

/* A tag lightens towards the interactive colour rather than towards the row
   highlight.
   
   It cannot use the highlight, because hovering a tag hovers the row it is in
   as well -- the tag would turn the colour the row has just turned and vanish
   into it. And it cannot use --color-contrast, which is what this did at first:
   six points of red away from --color-even in one theme and two points in
   another, which is a hover nobody can see. */
.tag-link:hover { background: rgb(var(--color-button)); }

.publish button:active,
.login button:active,
.header-image button:active,
.send-panel button:active,
.subscribe button:active,
.post form button:active {
    transform: translate(2px, 2px);
    box-shadow: var(--shadow-hairline);
}

/* A tag sits on a shallower shadow than a button, so it travels less far --
   the same gesture, to scale. */
.tag-link:active {
    transform: translate(1px, 1px);
    box-shadow: var(--shadow-hairline);
}

/* Keyboard users get the same affordance as the mouse. Without this a button
   reached by Tab looks exactly like one nobody is on. */
.publish button:focus-visible,
.login button:focus-visible,
.header-image button:focus-visible,
.send-panel button:focus-visible,
.subscribe button:focus-visible,
.post form button:focus-visible,
.tag-link:focus-visible {
    outline: 2px solid rgb(var(--color-links));
    outline-offset: 2px;
}

/* Somebody who has asked for less movement gets the colour change and no
   travel. The shadow still shrinks, so the press is still visible. */
@media (prefers-reduced-motion: reduce) {
    .publish button,
    .login button,
    .header-image button,
    .send-panel button,
    .subscribe button,
    .post form button,
    .tag-link {
        transition: background 80ms ease;
    }

    .publish button:active,
    .login button:active,
    .header-image button:active,
    .send-panel button:active,
    .subscribe button:active,
    .post form button:active,
    .tag-link:active {
        transform: none;
    }
}

/* A preview and the published page are the same pixels, which is the point and
   also the hazard: a tab left open is otherwise indistinguishable from the live
   post. This is the one thing on the page that says which it is. */
.preview-banner {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    align-items: baseline;
    margin-bottom: 1.5rem;
    padding: 0.5rem 0.7rem;
    font-size: 0.95rem;
    background: rgb(var(--color-high));
    color: rgb(var(--ink));
    box-shadow: var(--shadow);
    border: 1px solid rgb(var(--color-rule));
}

.preview-banner .preview-note { color: rgb(var(--muted)); font-size: 0.85rem; }

/* The line that ends a post. Quiet, and set away from the last paragraph so it
   reads as signing the writing rather than as another sentence of it. */
.signature {
    margin: 2rem 0 0;
    font-size: 0.95rem;
    color: rgb(var(--muted));
}

/* The old print mark for the end of a piece -- see PostArticle, which wraps
   the writing in .body only for a feature, exactly so this has a last child
   to reach for regardless of whatever tags optionally follow it.

   Generated content on the writing's own last element rather than a
   paragraph of its own: a block after the writing, however little margin it
   carries, is still a new line, and the mark kept reading as a line under
   the writing instead of the writing's own last line ending in it. This is
   the only way to actually put it there -- immediately after the last
   word, wrapping with it if the line was already full. */
.body > :last-child::after {
    content: " \25C6";
    color: rgb(var(--ink));
}

/* Pictures in a post ----------------------------------------------------- */

/* Stored images are up to 1600px on the long edge, which is wider than the
   column they sit in. Never wider than the text, and never distorted by being
   given only one of the two dimensions. */
/* The same bound for a picture inside a post. A photograph dropped into the
   middle of one is as likely to be taller than it is wide as the header is, and
   a reader who has to scroll three screens to get back to the sentence has lost
   the thread of it. */
.prose img {
    display: block;
    max-width: 100%;
    max-height: 32rem;
    width: auto;
    height: auto;
}

/* An image inside a sentence -- see render.figures, which is what decides a
   picture gets one of these classes instead of becoming a figure. Bounded to
   under half the column rather than 100%, because a floated image at the
   full width leaves the paragraph nothing to wrap around -- it would only
   ever run below it, which is what .prose img already does on its own. */
.prose img.wrap-left, .prose img.wrap-right {
    max-width: 45%;
    max-height: 22rem;
    margin-bottom: 1rem;
}

.prose img.wrap-left {
    float: left;
    margin-right: 1.5rem;
}

.prose img.wrap-right {
    float: right;
    margin-left: 1.5rem;
}

/* A picture standing on its own, with room around it and its caption under it
   -- see render.figures. */
.prose figure {
    margin: 1.5rem 0;
}

.prose figcaption {
    margin-top: 0.4rem;
    font-size: 0.9rem;
    color: rgb(var(--muted));
}

/* What an upload is doing, and why it did not. Reserved rather than appearing,
   so the editor does not jump when a message arrives. */
.image-note {
    min-height: 1.2rem;
    margin: 0.4rem 0 0;
    font-size: 0.9rem;
    color: rgb(var(--muted));
}

/* Tags ------------------------------------------------------------------ */

/* Under a post rather than beside its title. A tag is where to go next, which
   is a question worth asking after reading and a distraction before it. */
.tag-list {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin: 1.2rem 0 0;
    padding: 0;
    list-style: none;
}

/* Bordered rather than a filled chip -- see .subscribe button above for the
   same trade. A tag is a label, not a control with a hierarchy to signal, so
   there is no weight here doing the work a fill did on a button. */
.tag-link {
    display: inline-block;
    /* The same skew documented at .title-input, and the same fix: Newsreader
       sits low in an ordinary line box, roughly 0.2em more empty room below
       the text than above it, so 0.1em moves from the bottom padding to the
       top. Also a line-height of its own for the same reason .title-input's
       neighbours get one from the input/select/button rule above -- left to
       the page's own leading, a single-line pill floats the text inside a
       box taller than the word needs, which reads as the same bottom-heavy
       misalignment rather than as leading. */
    padding: calc(0.1rem + 0.1em) 0.5rem calc(0.1rem - 0.1em);
    line-height: 1.25;
    font-size: 0.9rem;
    text-decoration: none;
    color: rgb(var(--ink));
    background: none;
    border: 1px solid rgb(var(--color-rule));
}

/* Smaller in a list, where they sit under a title and a date and should not
   out-weigh either.
   
   Selected through .tag-list rather than as .post-list .tag-link, which loses
   to ".front-older .post-list a" on specificity and left every tag in the
   column rendering at the size and weight of a post title -- three heavy boxes
   under a heading, which is what they looked like.
   
   The weight is set here rather than inherited for the same reason: ".post-list
   a" makes every link in a list bold, and a tag is not a headline. */
/* Tighter than the row above them: they are one thing -- a set of labels for
   this post -- rather than a list of separate controls, and at the row's
   spacing they read as three unrelated boxes. */
.post-list .tag-list { margin-top: 0.4rem; gap: 0.25rem; }

.post-list .tag-list .tag-link {
    font-size: 0.8rem;
    font-weight: 400;
    /* Its own padding, so the same 0.1em shift from bottom to top the base
       .tag-link rule makes has to be repeated here rather than inherited. */
    padding: calc(0.05rem + 0.1em) 0.4rem calc(0.05rem - 0.1em);
    box-shadow: var(--shadow-hairline);
}

/* No shadow of their own inside the column, which is drawn with a drop-shadow
   filter on the wrapper -- the filter traces the silhouette of everything in
   it, so a tag carrying its own box-shadow gets a second one printed behind the
   first. The heading and the list opt out for the same reason. */
.side-list .tag-link { box-shadow: none; }


.tag-page { max-width: 48rem; }

/* The editor's control, a card like the picture panel below it rather than a
   labelled box floating under the editor. */
.tags { margin-top: 2rem; position: relative; }

.tag-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.tag-chips:not(:empty) { margin-bottom: 0.4rem; }

.tag-chip {
    color: rgb(var(--ink));
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.1rem 0.4rem;
    font-size: 0.9rem;
    background: rgb(var(--color-even));
    box-shadow: var(--shadow-small);
}

.tag-remove {
    padding: 0;
    font: inherit;
    line-height: 1;
    cursor: pointer;
    color: rgb(var(--muted));
    background: none;
    border: 0;
    box-shadow: none;
}

.tag-remove:hover { color: rgb(var(--color-strong)); }

.tag-enter { display: none; }

.tag-input { width: 100%; max-width: 20rem; font: inherit; font-size: 1rem; }

/* Over the page rather than in it, so choosing a tag does not push the editor
   around while it is open. */
.tag-suggestions { position: relative; }

/* Focused by the arrow keys as well as hovered, so walking the list looks the
   same as pointing at it. */
.tag-option:focus { background: rgb(var(--color-even-hover)); outline: none; }

.tag-options {
    color: rgb(var(--ink));
    position: absolute;
    z-index: 2;
    margin: 0.2rem 0 0;
    padding: 0;
    list-style: none;
    min-width: 14rem;
    background: rgb(var(--color-odd));
    box-shadow: var(--shadow);
}

.tag-option {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    width: 100%;
    padding: 0.3rem 0.5rem;
    font: inherit;
    font-size: 0.95rem;
    text-align: left;
    cursor: pointer;
    background: none;
    border: 0;
    box-shadow: none;
}

.tag-option:hover { background: rgb(var(--color-even-hover)); }
.tag-count { color: rgb(var(--muted)); font-size: 0.85rem; }
.tag-create { font-style: italic; }
