/* Where it runs — the section, and the three devices drawn inside it.
 *
 * ------------------------------------------------------------------------
 * Two coordinate systems live in this file, and keeping them apart is the
 * whole reason it is readable.
 *
 * **The page** is the design system: every colour, size and space is a token
 * from `tokens.css`, exactly as everywhere else on the site.
 *
 * **The insides of a drawn device** are not the page. They are a picture of a
 * different interface, and that interface has its own unit — one point of the
 * thing being drawn. That unit is `--u`, and inside a device every number is a
 * multiple of it: `calc(28 * var(--u))` is the pill's depth, and it is 28
 * because `PillGeometry.depth` is 28. Spending page tokens there would have
 * meant redrawing the pill in the site's spacing ladder, which is how an
 * illustration stops being of the product. Where a number below has a name in
 * the app, the name is in the comment beside it.
 *
 * `--u` is set from `100cqw` so a device scales with the column it is in rather
 * than with the viewport — the same drawing is used in a 7-column slot and in a
 * full-width stack. It is declared on `.mac` / `.phone` / `.win` and never on
 * `.device`, because an element cannot query itself as a container.
 * ------------------------------------------------------------------------ */

/* The pill is dark glass in *both* themes — that is what it is in the app, and
 * a pill that inverts with the page would be a picture of a different product.
 * No token pair says "dark in light mode and dark in dark mode", so the mapping
 * is made here out of tokens rather than out of new hexes: light mode borrows
 * the ink ladder, dark mode borrows the raised-paper ladder, and both land on a
 * dark shell carrying light ink.
 *
 * Same three-block shape as `tokens.css`: bare `:root`, then the system
 * preference guarded against an explicit light choice, then the explicit dark
 * choice. */
:root {
  --pill-shell: var(--ink);
  --pill-ink: var(--paper-raised);
  --pill-edge: var(--ink);
}

:root[data-theme="dark"] {
  /* Sunk, not raised. The devices' own faces are `--paper-raised`, so a
   * raised pill is a pill you cannot see sitting on the window it is docked
   * to — measured, on the drawn Mac window, before this line existed. */
  --pill-shell: var(--paper-sunk);
  --pill-ink: var(--ink);
  /* Light mode needs no outline — a black pill on warm paper is its own
   * edge. Dark mode does: shell against device face is 1.13:1, so the object
   * is held together entirely by this ring, and --paper-edge is not enough
   * of one at that size. */
  --pill-edge: var(--ink-faint);
}

:root[data-theme="dark"] {
  --pill-shell: var(--paper-raised);
  --pill-ink: var(--ink);
  --pill-edge: var(--paper-edge);
}

/* ================================================================== *
 * 1. The section
 * ================================================================== */

.platforms__head {
  max-width: var(--page-narrow);
}

.platforms__head .eyebrow {
  margin-bottom: var(--s-s);
}

.platforms__head .lede {
  margin-top: var(--s-l);
  max-width: 54ch;
}

/* The largest gap in the section. Three platforms is three ideas, and the space
 * between them has to beat the space inside one of them or the page reads as a
 * list of specs. */
.platforms__list {
  display: grid;
  gap: clamp(var(--s-4xl), 11vw, var(--s-5xl));
  margin-top: clamp(var(--s-3xl), 8vw, var(--s-4xl));
}

/* ================================================================== *
 * 2. One platform
 *
 * Twelve columns, and the three entries deliberately do not share an edge: the
 * Mac drawing runs to the right margin, the phone sits inboard on the left, the
 * Windows drawing stops short of the right margin. A grid where every row
 * starts and ends in the same place is a table, and this is an argument.
 * ================================================================== */

.platform {
  display: grid;
  gap: var(--s-2xl);
}

.platform__say > * + * {
  margin-top: var(--s-m);
}

.platform__say .h3 {
  margin-top: var(--s-xs);
  max-width: 18ch;
}

.platform__note {
  max-width: 44ch;
  color: var(--ink-body);
}

.platform__act {
  margin-top: var(--s-l);
}

.platform__art {
  min-width: 0;
}

/* The caption is real text and stays in the accessibility tree while the
 * drawing above it does not. On the two unbuilt platforms it is the sentence
 * that stops a convincing picture from making a promise the product cannot
 * keep, so it is attached to the picture rather than left to the column beside
 * it. */
.platform__cap {
  margin-top: var(--s-m);
  max-width: 40ch;
  font-size: var(--step--1);
  line-height: 1.45;
  color: var(--ink-quiet);
}

@media (min-width: 960px) {
  .platform {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: clamp(var(--s-xl), 4vw, var(--s-3xl));
    align-items: center;
  }

  .platform--mac .platform__say {
    grid-column: 1 / 6;
  }
  .platform--mac .platform__art {
    grid-column: 6 / 13;
  }

  /* Unused while mobile is off the page; the markup lives in
   * `sections/_phone.html`. Kept beside its sibling rules so the two do not
   * drift apart before it comes back.
   *
   * The phone is the narrow one, so it takes the narrow slot and the prose gets
   * the room instead. */
  .platform--phone .platform__art {
    grid-column: 1 / 5;
    grid-row: 1;
  }
  .platform--phone .platform__say {
    grid-column: 6 / 12;
    grid-row: 1;
  }

  .platform--win .platform__say {
    grid-column: 1 / 6;
  }
  .platform--win .platform__art {
    grid-column: 6 / 12;
  }

  /* Mirrored, so the three rows are not three identical rows. The phone entry
   * used to supply this alternation by being the odd shape; with it gone,
   * Linux takes the job deliberately rather than the page losing its rhythm. */
  .platform--linux .platform__art {
    grid-column: 1 / 8;
    grid-row: 1;
  }
  .platform--linux .platform__say {
    grid-column: 8 / 13;
    grid-row: 1;
  }
}

/* ================================================================== *
 * 3. Availability
 *
 * A filled dot for the one you can have, a hollow ring for the three you
 * cannot, and the sentence next to it says which in words. The accent is the
 * system's state colour and "shipping" is a state, so this is the one place in
 * the section it is spent.
 *
 * No badge and no ribbon across a corner: the thing that has to be unmistakable
 * is a fact, and a fact belongs in a sentence someone can read out loud.
 * ================================================================== */

.status {
  display: flex;
  align-items: baseline;
  gap: var(--s-xs);
  font-size: var(--step--1);
  line-height: 1.45;
  max-width: 42ch;
}

.status__mark {
  flex: none;
  /* Sized in em so it tracks the sentence it belongs to rather than needing a
   * second number when the type scale moves. */
  width: 0.58em;
  height: 0.58em;
  border-radius: 50%;
  transform: translateY(0.02em);
}

.status--now {
  color: var(--ink-body);
}

.status--now .status__mark {
  background-color: var(--accent);
}

/* --ink-quiet is 4.9:1 on paper, so the sentence is legal running text.
 *
 * The ring is --ink-quiet too, and that is measured rather than matched:
 * --ink-faint puts it at 2.72:1 on paper, under the 3:1 a graphic needs. It
 * would have been *legal* here, because the sentence beside it carries the
 * whole meaning and the mark is aria-hidden — but a mark nobody can see is not
 * doing the job it was added for. */
.status--soon {
  color: var(--ink-quiet);
}

.status--soon .status__mark {
  box-shadow: inset 0 0 0 1px var(--ink-quiet);
}

/* ================================================================== *
 * 4. The devices — shared
 * ================================================================== */

/* The `max-width` on each `.device--*` below is the one number in this file
 * that is neither a token nor a device point: it is the largest the drawing is
 * allowed to be rendered, which is a property of the picture and not of the
 * page's spacing ladder. Past it the illustration stops being a window and
 * starts being a wall. */
.device {
  container-type: inline-size;
  width: 100%;
  margin-inline: auto;

  /* The surfaces a drawn interface is made of. Named separately from the page's
   * tokens so that "the window's face" and "a card on the page" can diverge
   * without one of them being a magic override at the call site. */
  --dev-face: var(--paper-raised);
  --dev-bar: var(--paper-sunk);
  --dev-edge: var(--paper-edge);
  --dev-ink: var(--ink);
  --dev-quiet: var(--ink-quiet);
  --dev-faint: var(--ink-faint);
}

/* The two unbuilt platforms lose their shadow. It is the quietest true signal
 * available: the Mac window is sitting on the page and the other two are drawn
 * on it. Everything louder than this is in the text, where it belongs. */
.platform--soon .device {
  --dev-lift: var(--lift-1);
}

/* ---- Text arriving in a field --------------------------------------------
 *
 * Two strings live in the markup: the raw run of words the recogniser hears,
 * and the polished sentence the model returns. Which one is showing is a class
 * on the device, so the **default with no JavaScript is the finished sentence**
 * — a drawing of a text field mid-dictation is a drawing of nothing.
 *
 * The words are pre-split into spans by the markup rather than by script:
 * writing `textContent` once per frame would allocate a string per frame for
 * eleven frames of visible change, and the loop this section runs is allocation
 * free by design. */
.type {
  position: relative;
}

.type__raw {
  display: none;
}

.device.is-raw .type__raw {
  display: inline;
}

.device.is-raw .type__final {
  display: none;
}

/* `display`, not `opacity`. An unrevealed word held at opacity 0 still occupies
 * its box, so the caret ends up parked after the whole sentence instead of
 * after the last word spoken — which is what it looked like, and it read as a
 * bug. The fade is kept as an entrance animation instead of a transition,
 * because there is no `display` to transition from. */
.type__raw > span {
  display: none;
}

.type__raw > span.is-in {
  display: inline;
  animation: word-in var(--t-fast) var(--ease) both;
}

@keyframes word-in {
  from {
    opacity: 0;
  }
}

.type__caret {
  display: inline-block;
  width: calc(1.5 * var(--u));
  height: 1.05em;
  vertical-align: text-bottom;
  background-color: var(--dev-ink);
  animation: caret-blink 1.06s steps(2, jump-none) infinite;
}

@keyframes caret-blink {
  50% {
    opacity: 0;
  }
}

/* The caret is the one thing in the section that animates without the rAF loop.
 * It is paused with the loop rather than left running, because "stops when it
 * is off-screen" has to mean the whole section and not just the canvases. */
.platforms:not([data-running]) .type__caret {
  animation-play-state: paused;
}

/* ---- The pill ------------------------------------------------------------
 *
 * Straight out of `PillGeometry.swift`. depth 28, cornerRadius = depth / 2 so
 * the caps are true semicircles, a 22 key sitting `cornerRadius - keyDiameter /
 * 2` = 3 inboard of the leading arc, and a channel that starts at
 * `keyClearance + keyDiameter + keyChannelGap` = 31 and stops `trailingInset` =
 * 6 short of the trailing cap.
 *
 * **Only the width animates, and it animates at the trailing end.** That is the
 * app's own rule — the leading cap and the key are pinned because a control you
 * are aiming at must not move — and it buys the drawing two things for free:
 * 57 → 157 is the real idle → listening transition, and the ribbon canvas never
 * changes size, so it is measured once and never re-measured mid-animation. */
.pill {
  position: absolute;
  /* Pinned by its *leading* cap at the centre of the live pill, so the idle
   * pill sits half its growth to the left exactly as it does on a real
   * desktop. */
  left: calc(50% - 157 * var(--u) / 2);
  bottom: calc(16 * var(--u));
  width: calc(57 * var(--u)); /* idleLength */
  height: calc(28 * var(--u)); /* depth */
  border-radius: calc(14 * var(--u)); /* depth / 2 */
  background-color: var(--pill-shell);
  /* A ring rather than a border: a border is part of the box and would make the
   * pill 30 deep instead of 28. */
  box-shadow:
    var(--lift-2),
    0 0 0 1px var(--pill-edge);
  overflow: hidden;
  transition: width var(--t-mid) var(--ease-out);
}

/* The three state classes all sit on `.device` rather than on the part they
 * affect. One element to write per state, and the phone's keyboard strip and
 * the desktop pill's width are then two readings of the same fact instead of
 * two facts that can disagree. */
.device.is-live .pill {
  width: calc(157 * var(--u)); /* liveLength */
}

.pill__key {
  position: absolute;
  left: calc(3 * var(--u)); /* keyClearance */
  top: calc(3 * var(--u));
  width: calc(22 * var(--u)); /* keyDiameter */
  height: calc(22 * var(--u));
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--pill-ink);
  transition:
    background-color var(--t-fast) var(--ease),
    color var(--t-fast) var(--ease);
}

.pill__key svg {
  width: calc(12 * var(--u)); /* glyphBox */
  height: calc(12 * var(--u));
  /* glyphOpticalCenter.y is depth / 2 − 0.25: the mic's cradle puts its visual
   * mass low, so a geometrically centred glyph reads as sitting too low. */
  margin-top: calc(-0.25 * var(--u));
}

.device.is-held .pill__key {
  background-color: var(--accent-quiet);
  color: var(--accent-ink);
}

/* The channel is what clips the ribbon, and it is bounded by the two insets
 * rather than by the shell — which makes the idle channel come out at 57 − 31 −
 * 6 = 20 and the live one at 157 − 31 − 6 = 120, both of them the app's numbers,
 * with no second set of constants to keep in step. */
.pill__channel {
  position: absolute;
  left: calc(31 * var(--u)); /* ribbonCanvasInsetX */
  right: calc(6 * var(--u)); /* trailingInset */
  top: calc(2 * var(--u)); /* (depth − ribbonCanvasHeight) / 2 */
  height: calc(24 * var(--u)); /* ribbonCanvasHeight */
  overflow: hidden;
}

.pill__ribbon {
  width: calc(120 * var(--u)); /* liveChannelWidth */
  height: calc(24 * var(--u));
  /* base.css releases every canvas at `max-width: 100%`, which here would
   * squeeze the ribbon into the 20-wide idle channel instead of letting the
   * channel clip it — and then squeeze it back out on every state change,
   * re-measuring the backing store twice a cycle. The canvas is a fixed 120
   * and the shell decides how much of it you see. */
  max-width: none;
  /* Read once by devices.js and handed to drawRibbon, so the ribbon's colour
   * stays a CSS decision in a file full of them. */
  --ribbon-ink: var(--pill-ink);
}

/* ================================================================== *
 * 5. The Mac window
 * ================================================================== */

.device--mac {
  max-width: 640px;
}

.mac {
  --u: calc(100cqw / 460);
  position: relative;
  border-radius: calc(11 * var(--u));
  border: 1px solid var(--dev-edge);
  background-color: var(--dev-face);
  box-shadow: var(--dev-lift, var(--lift-3));
  overflow: hidden;
}

.mac__bar {
  position: relative;
  display: flex;
  align-items: center;
  height: calc(34 * var(--u));
  padding-inline: calc(14 * var(--u));
  background-color: var(--dev-bar);
  border-bottom: 1px solid var(--dev-edge);
}

/* Hollow rings, not three system hues.
 *
 * The palette has one accent and it means state; importing red, amber and green
 * to decorate a drawing would break that rule for an ornament. macOS draws
 * exactly these rings on a window that is not frontmost — which is what this
 * window is, because the point of the whole section is that the app you were
 * typing in keeps focus. */
.mac__lights {
  display: flex;
  gap: calc(7 * var(--u));
}

.mac__lights i {
  width: calc(11 * var(--u));
  height: calc(11 * var(--u));
  border: 1px solid var(--dev-faint);
  border-radius: 50%;
}

.mac__title {
  position: absolute;
  inset-inline: 0;
  text-align: center;
  font-size: calc(12 * var(--u));
  font-weight: 600;
  color: var(--dev-quiet);
  pointer-events: none;
}

/* Monospace because the file in the titlebar is a `.md` — the notes room writes
 * plain Markdown into a folder you chose, and a proportional face here would be
 * drawing a different app. */
.mac__body {
  font-family: var(--mono);
  font-size: calc(13 * var(--u));
  line-height: 1.6;
  color: var(--dev-ink);
  padding: calc(22 * var(--u)) calc(26 * var(--u)) calc(72 * var(--u));
}

.mac__h {
  color: var(--dev-quiet);
}

.mac__line + .mac__line {
  margin-top: calc(10 * var(--u));
}

/* Room for the longer of the two strings. Without it the window changes height
 * at the moment the polished sentence replaces the raw one, and the pill under
 * it jumps. */
.mac__line--live {
  min-height: calc(63 * var(--u));
}

.mac__line--live .dash {
  color: var(--dev-quiet);
}

/* ================================================================== *
 * 6. The phone
 *
 * Drawn in real iPhone points: 390 across, which is why the keyboard rows below
 * are 42 tall with a 6 gutter — those are the numbers the keys actually have.
 * The scale is honest, so the keyboard is legible at exactly the sizes a phone
 * keyboard is legible at.
 * ================================================================== */

.device--phone {
  max-width: 340px;
}

.phone {
  --u: calc(100cqw / 390);
  padding: calc(12 * var(--u));
  border-radius: calc(58 * var(--u));
  background-color: var(--pill-shell);
  box-shadow:
    var(--dev-lift, var(--lift-3)),
    0 0 0 1px var(--pill-edge);
}

.phone__screen {
  display: flex;
  flex-direction: column;
  /* Declared rather than accumulated from the parts, so the thread absorbs the
   * remainder and the phone is the same shape at every width. Shorter than a
   * real handset: at a true 390 x 844 the fixed furniture only accounts for
   * about half of it and the thread opens a hole above the bubbles. */
  aspect-ratio: 390 / 700;
  border-radius: calc(46 * var(--u));
  background-color: var(--dev-face);
  overflow: hidden;
}

.phone__status {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: calc(46 * var(--u));
  padding: 0 calc(20 * var(--u)) 0 calc(26 * var(--u));
  font-size: calc(13 * var(--u));
  font-weight: 600;
  color: var(--dev-ink);
}

.phone__island {
  width: calc(96 * var(--u));
  height: calc(28 * var(--u));
  border-radius: calc(14 * var(--u));
  background-color: var(--pill-shell);
}

.phone__meters svg {
  width: calc(44 * var(--u));
  color: var(--dev-ink);
}

.phone__nav {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: calc(8 * var(--u));
  height: calc(42 * var(--u));
  border-bottom: 1px solid var(--dev-edge);
  font-size: calc(13 * var(--u));
  font-weight: 600;
  color: var(--dev-ink);
}

.phone__avatar {
  width: calc(20 * var(--u));
  height: calc(20 * var(--u));
  border-radius: 50%;
  background-color: var(--dev-edge);
}

/* Pinned to the bottom, the way a thread you are in the middle of actually
 * sits. */
.phone__thread {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: calc(8 * var(--u));
  padding: calc(12 * var(--u)) calc(14 * var(--u));
}

.bubble {
  max-width: 76%;
  padding: calc(9 * var(--u)) calc(13 * var(--u));
  border-radius: calc(18 * var(--u));
  font-size: calc(13.5 * var(--u));
  line-height: 1.35;
}

.bubble--in {
  align-self: flex-start;
  background-color: var(--paper-sunk);
  color: var(--dev-ink);
}

/* Ink and paper, not a messaging app's blue. Borrowing another product's one
 * recognisable colour would make this a drawing of that product.
 *
 * `--ink` on `--paper` rather than the pill's pair: the bubble's job is to be
 * the *opposite* of the incoming one at a glance, and the ink ladder inverts
 * with the theme where the pill's deliberately does not. */
.bubble--out {
  align-self: flex-end;
  background-color: var(--ink);
  color: var(--paper);
}

.phone__composer {
  flex: none;
  display: flex;
  align-items: flex-end;
  gap: calc(8 * var(--u));
  padding: calc(8 * var(--u)) calc(12 * var(--u)) calc(10 * var(--u));
  border-top: 1px solid var(--dev-edge);
}

.phone__field {
  flex: 1;
  min-width: 0;
  /* Two lines' worth, reserved. The polished sentence is longer than the raw
   * run of words on this device, and a field that grows on landing would shove
   * the keyboard down at the one moment the reader is looking at it. */
  min-height: calc(52 * var(--u));
  padding: calc(7 * var(--u)) calc(12 * var(--u));
  border: 1px solid var(--dev-edge);
  border-radius: calc(17 * var(--u));
  font-size: calc(13.5 * var(--u));
  line-height: 1.35;
  color: var(--dev-ink);
}

.phone__send {
  flex: none;
  display: grid;
  place-items: center;
  width: calc(30 * var(--u));
  height: calc(30 * var(--u));
  border-radius: 50%;
  background-color: var(--ink);
  color: var(--paper);
}

.phone__send svg {
  width: calc(17 * var(--u));
}

/* ---- The keyboard ---- */

/* The keyboard's ground is the page's own `--paper` so that the three kinds of
 * key can each sit on a different side of it: letters raised above it, command
 * keys cut into it, and the Speechfy key inverted against it. On `--paper-sunk`
 * the mic key and the ground were the same value in dark mode. */
.phone__kb {
  flex: none;
  padding: calc(6 * var(--u)) calc(3 * var(--u)) calc(10 * var(--u));
  background-color: var(--paper);
}

/* The strip along the top edge holds two things in the same box: the
 * suggestions a keyboard shows when it is idle, and the ribbon it shows while
 * the key is held. Stacked in one grid cell so the swap costs no height and the
 * keys below never move. */
.kb__strip {
  display: grid;
  align-items: center;
  height: calc(32 * var(--u));
}

.kb__strip > * {
  grid-area: 1 / 1;
  transition: opacity var(--t-mid) var(--ease);
}

.kb__suggest {
  display: flex;
  justify-content: space-around;
  font-size: calc(13.5 * var(--u));
  font-weight: 400;
  color: var(--dev-quiet);
}

.kb__ribbon {
  width: 100%;
  height: calc(22 * var(--u));
  opacity: 0;
  --ribbon-ink: var(--ink);
}

.device.is-live .kb__suggest {
  opacity: 0;
}

.device.is-live .kb__ribbon {
  opacity: 1;
}

.kb__row {
  display: flex;
  gap: calc(6 * var(--u));
  margin-top: calc(11 * var(--u));
  padding-inline: calc(3 * var(--u));
}

/* The home row is inset by half a key, which is the only reason a QWERTY
 * keyboard reads as one at a glance. */
.kb__row--home {
  padding-inline: calc(22 * var(--u));
}

.key {
  flex: 1 1 0;
  min-width: 0;
  display: grid;
  place-items: center;
  height: calc(42 * var(--u));
  border-radius: calc(5 * var(--u));
  background-color: var(--dev-face);
  box-shadow: 0 1px 0 var(--dev-edge);
  font-size: calc(17 * var(--u));
  color: var(--dev-ink);
}

.key svg {
  width: calc(19 * var(--u));
  color: var(--dev-ink);
}

/* Shift, backspace, 123, globe and return sit on the keyboard's own ground
 * rather than on a keycap — the same distinction a phone keyboard makes between
 * a letter and a command. */
.key--fn,
.key--go {
  background-color: var(--paper-edge);
}

.key--wide {
  flex: 1.4 1 0;
}

.key--num {
  font-size: calc(14 * var(--u));
}

.key--space {
  flex: 4.4 1 0;
  font-size: calc(13 * var(--u));
  color: var(--dev-quiet);
}

.key--go {
  flex: 2.2 1 0;
  font-size: calc(13 * var(--u));
}

/* The Speechfy key: full ink against the board, so it is the one key that is
 * obviously not a letter. Inverted rather than given the pill's fixed dark
 * pair, because a key has to read as *the odd one out among these keys* — and
 * in dark mode the pill's shell and a keycap are two near-blacks. */
.key--mic {
  flex: 1.6 1 0;
  background-color: var(--ink);
  color: var(--paper);
  box-shadow: none;
  transition:
    box-shadow var(--t-fast) var(--ease),
    transform var(--t-fast) var(--ease);
}

.key--mic svg {
  width: calc(21 * var(--u));
  color: var(--paper);
}

/* Held. The ring is the accent, and being held is a state — the same
 * justification the pill's key uses, because it is the same key. */
.device.is-held .key--mic {
  transform: translateY(calc(1 * var(--u)));
  box-shadow: 0 0 0 calc(3 * var(--u)) var(--accent);
}

/* ================================================================== *
 * 7. The Windows window
 *
 * Square, with caption buttons at the trailing end at 46 x 32 — enough for the
 * eye to place it in one glance. Not a caricature: no fake taskbar, no fake
 * wallpaper, and the same pill in the same place, because the pill is the
 * product and the chrome is only where it is standing.
 * ================================================================== */

.device--win,
/* Linux reuses the `.win` frame: the thing being drawn — a held key putting a
 * finished sentence into the window that had focus — is identical, and two
 * near-identical desktop frames differing only in their chrome would be two
 * drawings competing for one glance. */
.device--linux {
  max-width: 580px;
}

.win {
  --u: calc(100cqw / 420);
  position: relative;
  /* The one place on this site a corner is not rounded. It is doing the whole
   * job of saying "different desktop" on its own. */
  border-radius: 0;
  border: 1px solid var(--dev-edge);
  background-color: var(--dev-face);
  box-shadow: var(--dev-lift, var(--lift-2));
  overflow: hidden;
}

.win__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: calc(32 * var(--u));
  padding-left: calc(14 * var(--u));
  background-color: var(--dev-bar);
  border-bottom: 1px solid var(--dev-edge);
}

.win__title {
  font-size: calc(12 * var(--u));
  color: var(--dev-quiet);
}

.win__controls {
  display: flex;
  align-self: stretch;
}

.win__controls i {
  display: grid;
  place-items: center;
  width: calc(46 * var(--u));
  color: var(--dev-quiet);
}

.win__controls svg {
  width: calc(11 * var(--u));
  fill: none;
}

.win__body {
  font-size: calc(13.5 * var(--u));
  line-height: 1.6;
  color: var(--dev-ink);
  padding: calc(22 * var(--u)) calc(24 * var(--u)) calc(72 * var(--u));
}

.win__line + .win__line {
  margin-top: calc(10 * var(--u));
}

.win__line--live {
  min-height: calc(66 * var(--u));
}

/* ================================================================== *
 * 8. Narrowing down
 *
 * A drawn interface has a size below which it stops being a drawing and starts
 * being a texture: the Mac window's note is 13 device points, and under about
 * 0.8 CSS pixels to the point that is grey noise. So the drawings are dropped
 * one at a time rather than all three being shrunk together, worst argument
 * first.
 *
 * The phone survives to 320px and is the last one standing, because it is the
 * one drawn at its own true scale — a 390-point keyboard rendered at 288px is a
 * keyboard, where a 460-point window at 288px is not a window. Every fact in
 * every dropped drawing is in the prose beside it, so nothing is lost but the
 * picture.
 * ================================================================== */

@media (max-width: 640px) {
  .platform--win .platform__art {
    display: none;
  }
}

@media (max-width: 420px) {
  .platform--mac .platform__art {
    display: none;
  }
}

/* ================================================================== *
 * 9. Reduced motion
 *
 * base.css already cuts every transition in the file. What is left is the one
 * thing a global rule cannot do: devices.js draws a single composed frame
 * instead of starting the loop, and the class it leaves behind has to be the
 * settled one. Nothing here needs to hide anything, because the still frame is
 * the *finished* state — the sentence landed, the channel open — rather than an
 * empty pill.
 * ================================================================== */

@media (prefers-reduced-motion: reduce) {
  .type__caret {
    animation: none;
  }

  .device.is-held .key--mic {
    transform: none;
  }
}
