/* 2sl-ui web: the parts of a 2sl web page (2sl.to). Requires tokens.css and text.css; the theme switch is web.js.
   What each part is for and how to use it: WEB.md.
   The plugin look on a page (README "Type", "Shape"): Cozette for the brand only (the wordmark, page titles, section
   names, literals), IBM Plex Sans for everything else, --grid hairlines for structure, --ink for values and the
   current choice, --well under anything shown (screenshots, charts, the hero), --now for hover and focus. */

/* --- page: Plex on the pad; header, main and footer centred at --page-width --- */
.web {
  font: var(--text-size)/var(--text-line) var(--text-font);
  -webkit-font-smoothing: antialiased; font-kerning: normal; font-variant-ligatures: normal;
  cursor: auto; /* tokens.css sets default (dialog behaviour); a page keeps the text cursor */
  min-height: 100vh; display: flex; flex-direction: column;
}
/* the header and footer span the window; what's in them lines up with main */
.web-header > div, .web-main, .web-footer > div {
  width: 100%; max-width: calc(var(--page-width) + var(--gutter) * 2);
  margin: 0 auto; padding-left: var(--gutter); padding-right: var(--gutter);
}
.web-main { display: flex; flex-direction: column; gap: calc(var(--gutter) * 2); padding-top: calc(var(--gutter) * 1.5); }
:where(.web) a { color: inherit; } /* :where keeps it under every part's own colour */
:where(.web) img { display: block; max-width: 100%; height: auto; }
.web img.photo { border: 1px solid var(--ink); }
/* a UI screenshot: scaled smoothly, so shrinking it never drops pixels; crisp (.whole, set by web.js) only while each
   of its pixels is a whole number of screen pixels */
.web img.pixel { border: 1px solid var(--ink); box-sizing: content-box; max-width: calc(100% - 2px); } /* the frame
   outside its pixels, so a 720 px shot shows 720 of them */
.web img.pixel.whole { image-rendering: pixelated; }

/* the brand face, and literals (file names, menu paths) stay pixel too */
.web-brand, .web code {
  font: var(--small)/var(--line) var(--font);
  -webkit-font-smoothing: none; font-kerning: none; font-variant-ligatures: none;
}
.web-brand.large { font-size: var(--large); line-height: calc(var(--line) * 2); }
/* a label: what a value is, a count, a caption on an instrument part */
.web-label { font-size: var(--ui-value); line-height: var(--ui-line); color: var(--label); font-variant-numeric: tabular-nums; }
.web .unit { color: var(--label); font-size: var(--ui-value); margin-left: 0.25em; }

/* a link in running text: a hairline underline that turns --now */
.web-prose a:not([class]), .web-readout a:not([class]), .web-link {
  text-decoration-line: underline; text-decoration-thickness: 1px; text-underline-offset: 3px;
  text-decoration-color: var(--label);
}
.web-prose a:not([class]):hover, .web-readout a:not([class]):hover, .web-link:hover { color: var(--now); text-decoration-color: var(--now); }

/* --- header: the site's own, as it was before web.css: wordmark left, nav right in Cozette, the current page
   inverted, an outline on hover. The theme switch sits at the end of the nav and looks like it --- */
.web-header { position: sticky; top: 0; z-index: 2; background: var(--pad); }
.web-header > div {
  display: flex; align-items: center; flex-wrap: wrap; row-gap: calc(var(--unit) * 2); /* the nav's margin spaces it, so a hidden header action leaves no gap */
  padding-top: var(--gutter); padding-bottom: var(--gutter);
}
.web-wordmark {
  font: var(--large)/calc(var(--line) * 2) var(--font); -webkit-font-smoothing: none; font-kerning: none;
  background: var(--ink); color: var(--inverse); padding: 0 calc(var(--unit) * 3); text-decoration: none;
}
.web-nav { display: flex; gap: calc(var(--unit) * 2); flex-wrap: wrap; align-items: center; margin-left: auto; padding-left: var(--gutter); }
.web-nav a, .web-nav button {
  font: var(--small)/var(--line) var(--font); -webkit-font-smoothing: none; font-kerning: none;
  padding: 0 calc(var(--unit) * 2); border: 1px solid transparent; background: transparent; color: var(--ink);
  text-decoration: none; cursor: pointer;
}
.web-nav a:hover, .web-nav button:hover { border-color: var(--ink); }
.web-nav a[aria-current="page"], .web-nav button[aria-checked="true"] { background: var(--ink); color: var(--inverse); }
.web-nav a:has(svg) { display: flex; align-items: center; }
.web-nav svg { display: block; width: var(--small); height: var(--small); }
.web-theme { display: flex; }

/* --- header action: a button that comes into the header, beside the wordmark and as tall as it, once the page's own
   one has scrolled away (web.js, data-show-past). Hidden, it takes no room and can't be tabbed to. At ≤ 560px it
   floats instead, and data-hide-while=".web-footer" keeps it off the footer --- */
.web-button.web-header-action {
  height: calc(var(--line) * 2); padding-top: 0; padding-bottom: 0; /* the wordmark's height */
  white-space: nowrap; overflow: hidden; max-width: 0; padding-left: 0; padding-right: 0; border-width: 0;
  margin-left: 0; opacity: 0; visibility: hidden;
  transition: max-width 200ms ease-out, padding 200ms ease-out, margin 200ms ease-out, opacity 200ms ease-out, visibility 0s 200ms;
}
.web-button.web-header-action.shown {
  max-width: 16em; padding-left: calc(var(--unit) * 3); padding-right: calc(var(--unit) * 3); border-width: 1px;
  margin-left: calc(var(--unit) * 2); opacity: 1; visibility: visible;
  transition: max-width 200ms ease-out, padding 200ms ease-out, margin 200ms ease-out, opacity 200ms ease-out;
}

/* --- float: a button held at the bottom right of the window. With data-hide-while it steps aside while
   the thing it stands in for, or the footer, is on screen (web.js; a selector list: hidden while any is) --- */
.web-button.web-float {
  position: fixed; right: var(--gutter); bottom: var(--gutter); z-index: 3;
  transition: opacity 200ms ease-out, transform 200ms ease-out, visibility 0s;
}
.web-button.web-float.hidden { opacity: 0; transform: translateY(8px); visibility: hidden; transition: opacity 200ms ease-out, transform 200ms ease-out, visibility 0s 200ms; }

@media (prefers-reduced-motion: reduce) {
  .web-button.web-header-action, .web-button.web-header-action.shown, .web-button.web-float, .web-button.web-float.hidden { transition: none; }
}

/* --- chips: a few choices, one current --- */
.web-chips { display: inline-flex; border: 1px solid var(--grid); }
.web-chips button {
  border: 0; background: transparent; color: var(--label); cursor: pointer;
  font: var(--ui-value)/22px var(--text-font); padding: 0 calc(var(--unit) * 2);
}
.web-chips button + button { border-left: 1px solid var(--grid); }
.web-chips button:hover { color: var(--now); }
.web-chips button[aria-checked="true"] { background: var(--ink); color: var(--inverse); }
.web-chips button[aria-checked="true"]:hover { background: var(--now); color: var(--now-text); }

/* --- a page's title block: where you are, the title in Cozette, the lede in Plex --- */
.web-title-block {
  display: flex; flex-direction: column; gap: calc(var(--unit) * 3);
  padding-bottom: var(--gutter); border-bottom: 1px solid var(--grid);
}
.web-crumbs { font-size: var(--ui-value); line-height: var(--ui-line); color: var(--label); }
.web-crumbs a { text-decoration: none; }
.web-crumbs a:hover { color: var(--now); }
.web-title { font: var(--large)/calc(var(--line) * 2) var(--font); -webkit-font-smoothing: none; font-kerning: none; }
.web-lede { font-size: var(--text-lede); line-height: var(--text-lede-line); max-width: 36em; }
.web-tags { display: flex; gap: calc(var(--unit) * 4); flex-wrap: wrap; align-items: center; }

/* --- section: a brand name on a hairline, a label, a link at the end --- */
.web-section { display: flex; flex-direction: column; gap: var(--gutter); }
.web-section-head {
  display: flex; align-items: baseline; gap: calc(var(--unit) * 3);
  padding-bottom: calc(var(--unit) * 2); border-bottom: 1px solid var(--grid);
}
.web-section-head h2 { font: var(--large)/calc(var(--line) * 2) var(--font); -webkit-font-smoothing: none; font-kerning: none; }
.web-section-head .end { margin-left: auto; font-size: var(--text-small); line-height: 20px; }

/* --- prose: running text under --text-measure, subheads as section names --- */
.web-prose { display: flex; flex-direction: column; gap: calc(var(--unit) * 4); max-width: var(--text-measure); }
.web-prose h2 {
  font: var(--small)/var(--line) var(--font); -webkit-font-smoothing: none; font-kerning: none;
  padding-bottom: calc(var(--unit) * 2); border-bottom: 1px solid var(--grid); margin-top: calc(var(--unit) * 6);
}
.web-prose h2:first-child { margin-top: 0; }
.web-prose ol, .web-prose ul { padding-left: 1.5em; display: flex; flex-direction: column; gap: var(--unit); }
.web-prose li::marker { color: var(--label); font-variant-numeric: tabular-nums; }

/* --- readout: label / value rows on hairlines, numbers tabular, units in the label colour --- */
.web-readout { border-collapse: collapse; width: 100%; max-width: var(--text-measure); font-variant-numeric: tabular-nums; }
.web-readout caption { /* a readout's name, like a section's */
  text-align: left; padding-bottom: calc(var(--unit) * 2);
  font: var(--small)/var(--line) var(--font); -webkit-font-smoothing: none; font-kerning: none;
}
.web-readout th, .web-readout td {
  text-align: left; vertical-align: baseline; border-bottom: 1px solid var(--grid);
  padding: calc(var(--unit) * 2) 0;
}
.web-readout tr:first-child > * { border-top: 1px solid var(--grid); }
.web-readout th { font: var(--ui-value)/20px var(--text-font); color: var(--label); width: 10rem; padding-right: calc(var(--unit) * 4); }
.web-readout td { font-size: var(--text-small); line-height: 20px; }

/* --- buttons: ink for the main action, a hairline for the rest; --now on hover --- */
.web-button {
  display: inline-flex; align-items: center; gap: calc(var(--unit) * 2);
  font: var(--text-small)/20px var(--text-font); padding: calc(var(--unit) * 1.5) calc(var(--unit) * 3);
  background: var(--ink); color: var(--inverse); border: 1px solid var(--ink); text-decoration: none; cursor: pointer;
}
.web-button:hover { background: var(--now); border-color: var(--now); color: var(--now-text); }
.web-button.quiet { background: transparent; color: var(--ink); border-color: var(--grid); }
.web-button.quiet:hover { color: var(--now); border-color: var(--now); }
.web-actions { display: flex; gap: calc(var(--unit) * 2); flex-wrap: wrap; align-items: center; }

/* --- display: a well that something is shown on. .grid draws a measured grid behind it --- */
.web-display { position: relative; background: var(--well); border: 1px solid var(--grid); min-width: 0; }
.web-display.grid {
  background-image:
    linear-gradient(to bottom, var(--grid) 0 1px, transparent 1px),
    linear-gradient(to right, var(--grid) 0 1px, transparent 1px);
  background-size: 100% 32px, 32px 100%;
  background-position: 0 -1px, -1px 0;
}
.web-display-head {
  display: flex; align-items: baseline; gap: calc(var(--unit) * 3);
  padding: calc(var(--unit) * 1.5) calc(var(--unit) * 3); border-bottom: 1px solid var(--grid); background: var(--well);
}
.web-display-head .end { margin-left: auto; }
/* one column that can shrink, so a picture's max-width: 100% means the display's width */
.web-display-body { padding: calc(var(--unit) * 6); display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center; }

/* --- figure: a display and its caption, numbered --- */
.web-figure { display: flex; flex-direction: column; gap: calc(var(--unit) * 2); min-width: 0; }
.web-figure figcaption {
  font-size: var(--text-small); line-height: var(--text-small-line); color: var(--label); max-width: var(--text-measure);
}
.web-figure figcaption .web-index { color: var(--ink); font-variant-numeric: tabular-nums; margin-right: calc(var(--unit) * 2); }

/* --- chart: an inline SVG drawn with classes, so it follows the theme --- */
.web-chart { display: block; width: 100%; height: auto; }
.web-chart text { font-family: var(--text-font); fill: var(--label); font-variant-numeric: tabular-nums; }
.web-chart .grid { stroke: var(--grid); }
.web-chart .tick { stroke: var(--label); }
.web-chart .trace { stroke: var(--ink); fill: none; }
.web-chart .trace-2 { stroke: var(--trace-2); fill: none; }

/* --- cards: a picture on the well, the title and its kind, one line, a note --- */
.web-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 232px), 1fr)); gap: calc(var(--unit) * 4); }
.web-card { display: flex; flex-direction: column; border: 1px solid var(--grid); text-decoration: none; min-width: 0; }
.web-card-image { aspect-ratio: 16 / 10; width: 100%; object-fit: cover; background: var(--well); border-bottom: 1px solid var(--grid); }
.web-card-image.empty {
  background-image:
    linear-gradient(to bottom, var(--grid) 0 1px, transparent 1px),
    linear-gradient(to right, var(--grid) 0 1px, transparent 1px);
  background-size: 100% 16px, 16px 100%;
}
.web-card-body { display: flex; flex-direction: column; gap: var(--unit); padding: calc(var(--unit) * 3); flex: 1; }
.web-card-head { display: flex; justify-content: space-between; align-items: baseline; gap: calc(var(--unit) * 2); }
.web-card-title { font-size: var(--text-size); line-height: 24px; }
.web-card-summary { font-size: var(--text-small); line-height: var(--text-small-line); color: var(--label); }
.web-card-note { font-size: var(--ui-value); line-height: var(--ui-line); color: var(--label); margin-top: auto; padding-top: calc(var(--unit) * 2); }
.web-card:hover { border-color: var(--now); }
.web-card:hover .web-card-title { color: var(--now); }

/* --- release: each download as a row: file, size, button --- */
.web-release { border: 1px solid var(--grid); }
.web-release-row {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: calc(var(--unit) * 4); align-items: center;
  padding: calc(var(--unit) * 2) calc(var(--unit) * 3);
}
.web-release-row + .web-release-row { border-top: 1px solid var(--grid); }
.web-release-row code { overflow-wrap: anywhere; }

/* --- next: the way on, on a hairline --- */
.web-next {
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--gutter);
  border-top: 1px solid var(--grid); padding-top: calc(var(--unit) * 3); font-size: var(--text-small);
}

/* --- footer: an info line. .web-footer-nav repeats the nav's marks and theme switch, shown only at <= 560px where
   the header has room for just the page links --- */
.web-footer { margin-top: auto; padding-top: calc(var(--gutter) * 3); }
.web-footer > div {
  display: flex; justify-content: space-between; gap: var(--gutter); flex-wrap: wrap;
  border-top: 1px solid var(--grid); padding-top: calc(var(--unit) * 3); padding-bottom: var(--gutter);
  font-size: var(--ui-value); line-height: var(--ui-line); color: var(--label);
}
.web-footer a { color: var(--ink); text-decoration: none; }
.web-footer a:hover { color: var(--now); }
.web-footer-nav { display: none; flex-basis: 100%; gap: var(--unit); align-items: center; margin-left: calc(var(--unit) * -2); }
.web-footer-nav a, .web-footer-nav button {
  font: var(--small)/var(--line) var(--font); -webkit-font-smoothing: none; font-kerning: none;
  padding: 0 calc(var(--unit) * 2); border: 1px solid transparent; background: transparent; color: var(--ink); cursor: pointer;
}
.web-footer-nav a:hover, .web-footer-nav button:hover { border-color: var(--ink); color: var(--ink); }
.web-footer-nav button[aria-checked="true"] { background: var(--ink); color: var(--inverse); }
.web-footer-nav a:has(svg) { display: flex; align-items: center; }
.web-footer-nav svg { display: block; width: var(--small); height: var(--small); }

/* --- home hero: what we do, as rows, beside the spinning shapes --- */
.web-hero { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: calc(var(--gutter) * 2); align-items: center; }
.web-hero-text { display: flex; flex-direction: column; gap: var(--gutter); justify-content: center; }
.web-rows { list-style: none; }
.web-rows li {
  display: flex; align-items: baseline; gap: calc(var(--unit) * 3);
  font-size: var(--text-lede); line-height: 40px; border-bottom: 1px solid var(--grid);
}
.web-rows li:first-child { border-top: 1px solid var(--grid); }
.web-rows .glyph { font: var(--large)/40px var(--font); -webkit-font-smoothing: none; color: var(--label); width: 1.5ch; flex: none; }

.web-photos { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: calc(var(--unit) * 2); }
.web-photos img { aspect-ratio: 4 / 3; width: 100%; object-fit: cover; }

/* --- a project page: the story on the left, the readout beside it --- */
.web-project { display: grid; grid-template-columns: minmax(0, 1fr) minmax(280px, 360px); gap: calc(var(--gutter) * 2); align-items: start; }
.web-project > aside { display: flex; flex-direction: column; gap: var(--gutter); position: sticky; top: 96px; }
.web-project > div { display: flex; flex-direction: column; gap: calc(var(--gutter) * 1.5); min-width: 0; }

@media (max-width: 860px) {
  .web-hero, .web-project { grid-template-columns: minmax(0, 1fr); }
  .web-hero > :not(.web-hero-text) { display: none; } /* one column: the work comes first */
  .web-project > aside { position: static; order: -1; } /* one column: the download and facts before the story */
  .web-photos { grid-template-columns: minmax(0, 1fr); }
  .web-figure:has(.web-chart) .web-display-body { overflow-x: auto; justify-items: start; }
  .web-figure .web-chart { min-width: 720px; }
}
@media (max-width: 560px) {
  /* one row: the wordmark and the page links; the marks and the theme switch move to the footer (.web-footer-nav) */
  .web-header > div { flex-wrap: nowrap; }
  .web-nav { gap: var(--unit); flex-wrap: nowrap; }
  .web-nav .mark, .web-nav .web-theme { display: none; }
  .web-footer-nav { display: flex; }
  .web-nav a, .web-nav button, .web-footer-nav a, .web-footer-nav button { padding: calc(var(--unit) * 2); } /* a bigger tap target */
  /* no room beside the nav: the header action floats at the bottom right instead, like .web-float */
  .web-button.web-header-action.shown { position: fixed; right: var(--gutter); bottom: var(--gutter); z-index: 3; margin-left: 0; }
  .web-button.web-header-action.shown.hidden { opacity: 0; transform: translateY(8px); visibility: hidden; transition: opacity 200ms ease-out, transform 200ms ease-out, visibility 0s 200ms; }
  .web-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: calc(var(--unit) * 2); }
  .web-card-body { padding: calc(var(--unit) * 2); }
  .web-card-head { flex-direction: column; gap: var(--unit); }
  .web-display-body { padding: calc(var(--unit) * 3); }
  .web-readout th { width: 7.5rem; }
  .web-release-row { grid-template-columns: minmax(0, 1fr) auto; }
  .web-release-row .web-button { grid-column: 1 / -1; justify-self: start; }
}
