/* The header every tool on this site wears.
 *
 * ONE FILE, ONE BAR, EVERY PAGE. The markup that goes with it is in
 * app/lib/chrome.php; a page gets the whole header by linking this and calling
 * chrome_bar() with its own menu. Nothing in here knows what a tool does.
 *
 * EVERY CLASS IS PREFIXED chrome-, and that is not decoration. The tools on
 * this site already own .top, .who, .home, .nav and .foot in their own
 * stylesheets, with different values; a shared file claiming those names would
 * restyle pages it was never added to. The prefix is what makes this safe to
 * drop into a page that already has a design.
 *
 * THE TOKENS ARE DECLARED ON .chrome RATHER THAN :root, which is the opposite
 * of what the tools do. A tool's stylesheet is the only one on its page, so
 * its :root is the design; this one lands on pages that already have a :root
 * of their own, and writing into it would mean a shared header quietly
 * recolouring the page below it. Scoped here, the bar carries its palette with
 * it and changes nothing else. Values are written out for the same reason the
 * rest of the site writes them out: a var() fallback is a second, different
 * design that only shows up once something is already wrong.
 */

.chrome {
  --chrome-h:      3.5rem;           /* the bar's height, and what sits under it */
  --chrome-bg:     rgba(5, 12, 19, .86);
  --chrome-solid:  #050c13;          /* the same colour for browsers without blur */
  --chrome-line:   rgba(255, 255, 255, .09);
  --chrome-edge:   rgba(255, 255, 255, .16);
  --chrome-ink:    #cfe6f2;
  --chrome-bright: #eaf9ff;
  --chrome-dim:    #7f9aab;
  --chrome-faint:  #54707f;
  --chrome-go:     #62ffb0;
  --chrome-go-ink: #a9f8cd;
  --chrome-go-soft: rgba(98, 255, 176, .1);
  --chrome-go-line: rgba(98, 255, 176, .34);
  --chrome-hover:  rgba(255, 255, 255, .05);
  --chrome-r:      .55rem;

  /* ALWAYS ON TOP. Sticky rather than fixed: fixed takes the bar out of the
     flow and every page using it then needs a matching top padding of its own,
     which is a second thing to keep in step with this file's height. Sticky
     occupies its space and the page below simply follows it. */
  position: sticky;
  top: 0;
  z-index: 60;
  /* NOTHING IN THE BAR CAN WIDEN THE PAGE. clip rather than hidden: hidden on
     one axis forces the other to scroll, which would turn this strip into a
     scroll container and stop the sticky from sticking; clip just clips and
     creates no scrollport at all. It is a guard rather than a fix — the
     animation below travels only vertically — but a header is the one element
     on a page that spans the full width, so anything inside it that drifts
     right by a pixel puts a scrollbar under the whole document. */
  overflow-x: clip;
  /* A flex column body — which both tools here use — would otherwise let this
     stretch or shrink with the content beside it. */
  flex: none;
  background: var(--chrome-bg);
  border-bottom: 1px solid var(--chrome-line);
  backdrop-filter: blur(12px) saturate(1.25);
  -webkit-backdrop-filter: blur(12px) saturate(1.25);
  font: 500 15px/1.5 ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  color: var(--chrome-ink);
}

/* Without blur the translucency is just a page showing through text. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .chrome { background: var(--chrome-solid); }
}

/* AN ANCHOR MUST NOT LAND UNDER THE BAR. Settings links to #cookies from the
   header itself, and a sticky header covers the top of whatever a fragment
   scrolls to unless the browser is told to stop short of it. */
/* Written out rather than var(--chrome-h): the tokens are scoped to the bar on
   purpose, and html is not inside it. The two have to be changed together. */
html { scroll-padding-top: calc(3.5rem + 1rem); }

/* THREE COLUMNS, THE MIDDLE ONE AUTO AND THE SIDES EQUAL. That is what keeps
   the tool's menu on the centre line of the page rather than the centre of
   whatever is left over: flexbox would push it off-centre the moment one side
   got longer than the other, and the two sides here are a fixed label and a
   username that can be any length. */
.chrome-in {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 1rem;
  /* FULL WIDTH, NOT THE PAGE'S COLUMN. The corners are where these belong;
     centring the bar's contents in a 62rem column leaves them floating in the
     middle of a wide screen instead. */
  width: 100%;
  min-height: var(--chrome-h);
  padding: .45rem .9rem;
}

.chrome a { text-decoration: none; }
.chrome a:hover { text-decoration: none; }
.chrome :focus-visible {
  outline: 2px solid var(--chrome-go-line);
  outline-offset: 2px;
  border-radius: var(--chrome-r);
}

/* ------------------------------------------------------- top left: the site */

/* THE WAY BACK TO THE SITE, IN THE CORNER, ON EVERY TOOL. A tool is reachable
   at its own address, so somebody can be standing inside one having never seen
   the page that lists them. The icon is the site's own favicon, so the thing in
   the corner is the thing in the browser tab — which is the whole of the label
   now that the word beside it has gone. The name lives in the link's title and
   aria-label instead; see chrome_bar(). */
.chrome-home {
  justify-self: start;
  display: inline-flex;
  align-items: center;
  /* EVEN PADDING NOW THE WORD HAS GONE. It used to be lopsided — tight on the
     left for the icon, generous on the right for the label — and keeping that
     with nothing on the right leaves the mark visibly off-centre in its own
     circle. The .42rem matches .chrome-act's icon-only form, so the two corners
     of the bar are the same size as each other. */
  padding: .42rem;
  border: 1px solid var(--chrome-edge);
  border-radius: 999px;
  color: var(--chrome-dim);
  white-space: nowrap;
}
.chrome-home:hover { color: var(--chrome-bright); border-color: var(--chrome-dim); background: var(--chrome-hover); }
.chrome-home img {
  /* Slightly larger than it was beside the word: alone in the corner it is the
     only thing identifying the site, and at 1.3rem it read as an afterthought.
     Keep this in step with the sizes attribute in chrome_bar(). */
  width: 1.45rem; height: 1.45rem; display: block; flex: none;
  /* The artwork is a photographic-looking icon; a touch of contrast stops it
     reading as muddy against the dark bar. */
  filter: saturate(1.05);
}

/* --------------------------------------------------- middle: the tool's menu */

/* THE SWAPPABLE PART. Everything a tool puts in here is its own — the label and
   the links — and the capsule around it is not, which is the point: a new tool
   hands chrome_bar() a different list and gets a header that behaves exactly
   like this one. */
.chrome-menu {
  justify-self: center;
  display: flex;
  align-items: center;
  gap: .2rem;
  max-width: 100%;
  padding: .22rem;
  border: 1px solid var(--chrome-line);
  border-radius: 999px;
  background: rgba(255, 255, 255, .035);
  /* A menu longer than the screen scrolls inside its capsule rather than
     stretching the bar and pushing the account out of the corner. The bar is
     hidden because this is a 2.4rem-tall strip and a scrollbar inside it is
     taller than the thing it is scrolling. */
  overflow-x: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  overscroll-behavior-x: contain;
}
.chrome-menu::-webkit-scrollbar { display: none; }

/* The tool's own name, first inside the capsule, so the centre of the header
   says what you are in as well as where you can go in it. */
.chrome-tool {
  display: inline-flex; align-items: center; gap: .45rem; flex: none;
  padding: .3rem .6rem .3rem .65rem;
  color: var(--chrome-bright);
  font-size: .82rem; font-weight: 650; letter-spacing: -.01em; white-space: nowrap;
}
.chrome-tool .chrome-dot {
  width: .5rem; height: .5rem; border-radius: 50%;
  background: var(--chrome-go); opacity: .9; flex: none;
}
/* Drawn by the markup only where there is something on both sides of it: after
   the tool's name, and before whatever the tool hangs on the end. */
.chrome-menu-sep {
  width: 1px; height: 1.1rem; flex: none;
  background: var(--chrome-line); margin: 0 .25rem;
}

/* THE TOOL'S OWN STATUS, INSIDE THE TOOL'S OWN CAPSULE. The YouTube tool keeps
   a cookie-freshness light here. It is in the middle rather than beside the
   account because it is a fact about the tool and not about the person, and the
   two corners of this bar are the same on every page of every tool — a tool's
   business parked in one of them starts to look like the site's. */
.chrome-menu-extra { display: inline-flex; align-items: center; flex: none; padding-right: .3rem; }

.chrome-menu a {
  flex: none;
  padding: .33rem .72rem;
  border-radius: 999px;
  color: var(--chrome-dim);
  font-size: .8rem;
  white-space: nowrap;
}
.chrome-menu a:hover { color: var(--chrome-bright); background: var(--chrome-hover); }
.chrome-menu a.on {
  color: var(--chrome-go-ink);
  background: var(--chrome-go-soft);
  box-shadow: inset 0 0 0 1px var(--chrome-go-line);
}

/* ---------------------------------------------------- top right: the account */

.chrome-who {
  justify-self: end;
  display: flex;
  align-items: center;
  gap: .55rem;
  min-width: 0;
  font-size: .78rem;
}
.chrome-user {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .74rem;
  color: var(--chrome-dim);
  /* A long username shortens rather than shoving the menu off centre. */
  max-width: 11rem;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chrome-act {
  display: inline-flex; align-items: center; gap: .35rem; flex: none;
  padding: .28rem .6rem;
  border: 1px solid var(--chrome-line);
  border-radius: 999px;
  color: var(--chrome-faint);
  font-size: .72rem;
}
.chrome-act:hover { color: var(--chrome-bright); border-color: var(--chrome-edge); background: var(--chrome-hover); }
.chrome-act svg { width: .85rem; height: .85rem; display: block; flex: none; }
/* Lit the same way a menu item is, because a cog pointing at a settings page is
   navigation however little it looks like it. */
.chrome-act.on {
  color: var(--chrome-go-ink);
  border-color: var(--chrome-go-line);
  background: var(--chrome-go-soft);
}
/* THE ICON-ONLY FORM, for a cog or anything else with nothing to read. Bigger
   than the padding of a labelled one, not smaller: a bare icon is the smallest
   thing in the bar and it is also the one most likely to be poked with a thumb,
   so the box around it is sized for a finger rather than trimmed to the glyph.
   This lands at about 34px square. */
.chrome-act.chrome-icon { padding: .42rem; }
.chrome-act.chrome-icon svg { width: .95rem; height: .95rem; }

/* WHATEVER THE TOOL HANGS IN THE CORNER — a cookie-freshness pill here, a build
   number somewhere else. Styled here so the bar looks finished on a page with no
   stylesheet of its own.
   THESE WIN INSIDE THE BAR even over a tool's own .pill loaded afterwards, and
   that is specificity rather than order: .chrome .pill outweighs .pill whatever
   sequence they arrive in. It is the right way round — the bar should look the
   same on every tool — but it does mean a tool wanting a different pill in the
   header has to say .chrome .pill to get it. */
.chrome .pill {
  display: inline-block; flex: none;
  padding: .18rem .55rem;
  border-radius: 999px;
  border: 1px solid var(--chrome-edge);
  background: rgba(255, 255, 255, .04);
  color: var(--chrome-dim);
  font-size: .66rem; letter-spacing: .04em; white-space: nowrap;
}
.chrome .pill:hover { border-color: var(--chrome-dim); }
.chrome .pill.ok   { border-color: var(--chrome-go-line); background: var(--chrome-go-soft); color: var(--chrome-go-ink); }
.chrome .pill.warn { border-color: rgba(255, 209, 102, .3); background: rgba(255, 209, 102, .09); color: #ffd166; }
.chrome .pill.bad  { border-color: rgba(255, 97, 120, .3);  background: rgba(255, 97, 120, .09);  color: #ff6178; }
.chrome .pill.off  { opacity: .75; }

/* ------------------------------------------------------------- narrow screens */

/* THE MENU TAKES A SECOND ROW ONLY WHEN IT GENUINELY WILL NOT FIT ON THE FIRST.
 *
 * IT USED TO BE A WIDTH BREAKPOINT — below 54rem, two rows, always — and that
 * was wrong in both directions because a breakpoint cannot see how many items a
 * tool has. A two-item menu was being pushed onto its own line on a phone with
 * room to spare, which is the most expensive row on the smallest screen; a
 * six-item one would have been squashed on a tablet that a breakpoint said was
 * wide enough.
 *
 * SO THE DECISION IS MEASURED INSTEAD, in assets/chrome.js, which adds
 * .is-stacked when the three clusters' real widths exceed the real space. The
 * rules below are only the two shapes; which one applies is arithmetic done on
 * the actual contents, per tool, per page, and again whenever the window
 * changes. */
.chrome.is-stacked .chrome-in {
  grid-template-columns: 1fr auto;
  row-gap: .5rem;
  padding-top: .5rem;
  padding-bottom: .55rem;
}
.chrome.is-stacked .chrome-home { grid-row: 1; grid-column: 1; }
.chrome.is-stacked .chrome-who  { grid-row: 1; grid-column: 2; }
.chrome.is-stacked .chrome-menu {
  grid-row: 2; grid-column: 1 / -1; justify-self: center;
  /* WRAPS RATHER THAN SCROLLS once it has a row to itself. A capsule that
     scrolls sideways hides its last item behind an edge with nothing to say it
     is there — and the last item is the tool's status light, which is the one
     thing in the bar worth seeing without being looked for. */
  flex-wrap: wrap; justify-content: center; row-gap: .1rem; border-radius: 1.1rem;
}

/* Two rows is a taller bar, and an anchor still has to clear it. :has() asks
   the question the old media query answered by guessing. */
html:has(.chrome.is-stacked) { scroll-padding-top: calc(5.6rem + 1rem); }

@media (max-width: 30rem) {
  .chrome-in { gap: .5rem; padding-left: .6rem; padding-right: .6rem; }
  /* The username shortens on a genuinely small screen whatever else fits: the
     account is still identifiable by the sign-out beside it, and the corner has
     to hold a tappable control more than it has to hold a name. */
  .chrome-user { max-width: 5.5rem; }
  .chrome-menu a { padding: .33rem .6rem; }
}

/* ─────────────────────────────────────────────── .is-tight — the first squeeze
 *
 * WHAT IS GIVEN UP BEFORE A WHOLE ROW IS. chrome.js adds this when the three
 * clusters do not fit on one line, and stacks them only if they still do not
 * after it. Everything in here used to live in the 30rem media query above —
 * which meant a tool with two short links lost its name on a phone it had
 * plenty of room on, and a tool with six kept its name on a tablet it did not.
 *
 * THE TOOL'S NAME GOES AND ITS LINKS STAY, because the name is the least missed
 * thing in the capsule: the page's own heading and the browser tab both still
 * say which tool this is, and neither of them is a way of getting anywhere.
 *
 * A LABEL THAT GOES TAKES ITS BOX WITH IT — hiding the words and keeping the
 * padding of a word-sized control leaves a tall thin sliver, so a collapsed
 * action becomes the same finger-sized square the icon-only form already is. */
.chrome.is-tight .chrome-tool,
.chrome.is-tight .chrome-tool + .chrome-menu-sep { display: none; }
.chrome.is-tight .chrome-act .chrome-act-label { display: none; }
.chrome.is-tight .chrome-act { padding: .42rem; }
.chrome.is-tight .chrome-act svg { width: .95rem; height: .95rem; }
.chrome.is-tight .chrome-menu a { padding: .33rem .62rem; }
.chrome.is-tight .chrome-user { max-width: 6rem; }
}

/* NO height BREAKPOINT THAT UNSTICKS THIS. There was one — a phone in landscape
   is a short viewport and a header pinned across it is a page read through a
   slot — and it was wrong twice over. It fired on any window under 416px tall,
   which is an ordinary small desktop window and not a phone at all; and "always
   on top" is the whole point of this bar, so a rule that quietly suspends it is
   a bar that is missing exactly when somebody goes looking for it. The bar is
   56px; a short viewport can afford it. */

@media (prefers-reduced-motion: no-preference) {
  .chrome-home, .chrome-menu a, .chrome-act { transition: color .12s, background .12s, border-color .12s; }
}

/* ------------------------------------------------------ arriving and leaving */

/* THE BAR COMES IN WHEN THE PAGE DOES AND GOES OUT WHEN YOU LEAVE BY A CORNER.
 *
 * THE HEADER AND NOTHING ELSE MOVES. The page below it is not touched — no body
 * fade, no content slide. A whole-page transition on a tool you use all day
 * becomes a tax on every click; a bar that hands itself over reads as the one
 * thing on the page that knows you are going somewhere.
 *
 * ONLY transform AND opacity ARE ANIMATED, which are the two a browser can do on
 * the compositor without laying the page out again. Animating height or margin
 * here would reflow the document under a sticky element sixty times a second.
 *
 * STRAIGHT DOWN FROM THE TOP, AND NOTHING ELSE. Every moving part travels on one
 * axis: the strip drops in, and the three clusters drop with it, each a fraction
 * behind the last so they land left to right in reading order. No sideways
 * entrance, no scale.
 *
 * THE SIDEWAYS VERSION PUT A SCROLLBAR ON THE PAGE, which is the whole reason
 * this rule is now stated as a rule. The three clusters used to arrive from the
 * direction each of them sits in — and the right-hand one sits against the right
 * edge of the window, so starting it 1.1rem further right meant 1.1rem of the
 * document hanging past the viewport. The browser does exactly what it is told
 * when content overflows to the right: it offers a horizontal scrollbar. It
 * appeared for a third of a second on every page load and then left, which is
 * the most annoying possible duration for a scrollbar, because the page under it
 * jumps twice and there is nothing on screen to blame.
 *
 * THAT IS WHY VERTICAL IS NOT MERELY A PREFERENCE HERE. Overflow above the top
 * of the document cannot be scrolled to and so costs nothing; overflow past the
 * right edge always can be. A horizontal entrance in this bar is a bug waiting
 * to be reintroduced, and .chrome below is clipped on that axis so that the next
 * person to try one gets a clipped animation rather than a jumping page.
 *
 * ALL OF IT IS IN THIS FILE AND assets/chrome.js, with nothing in any tool. A
 * page gets the animation by having the header, and loses it by not.
 */

@keyframes chrome-bar-in  { from { transform: translateY(-100%);  opacity: 0; } }
@keyframes chrome-drop-in { from { transform: translateY(-.75rem); opacity: 0; } }

@keyframes chrome-bar-out  { to { transform: translateY(-100%); opacity: 0; } }
@keyframes chrome-lift-out { to { transform: translateY(-.6rem); opacity: 0; } }

/* The easing is a decelerate curve for the arrival — fast off the mark, settling
   rather than stopping — and a plain accelerate for the exit, which should feel
   like the bar being taken away rather than placed. */
.chrome {
  animation: chrome-bar-in .34s cubic-bezier(.22, 1, .36, 1) both;
}
/* FILL MODE both IS DOING REAL WORK on the three below. Each has a delay, and
   without it they would be drawn in their final position for that fraction of a
   second and then jump back to the start of their own animation.
   THE SAME KEYFRAME THREE TIMES, separated only by when it starts. The stagger
   is the delay and nothing else — which is what keeps this honest: there is no
   per-cluster direction left for somebody to adjust into a scrollbar. */
.chrome-home { animation: chrome-drop-in .40s cubic-bezier(.22, 1, .36, 1) .06s both; }
.chrome-menu { animation: chrome-drop-in .44s cubic-bezier(.22, 1, .36, 1) .10s both; }
.chrome-who  { animation: chrome-drop-in .40s cubic-bezier(.22, 1, .36, 1) .14s both; }

/* LEAVING IS FASTER THAN ARRIVING, and deliberately so: this one is in front of
   a page load, and every millisecond of it is a millisecond somebody is looking
   at a page they have already decided to leave. The clusters go first and the
   strip follows, so the bar empties and then lifts rather than sliding away with
   everything still in it. */
.chrome.is-leaving              { animation: chrome-bar-out  .26s cubic-bezier(.4, 0, 1, 1) .05s both; }
.chrome.is-leaving .chrome-home { animation: chrome-lift-out .18s cubic-bezier(.4, 0, 1, 1) both; }
.chrome.is-leaving .chrome-menu { animation: chrome-lift-out .18s cubic-bezier(.4, 0, 1, 1) both; }
.chrome.is-leaving .chrome-who  { animation: chrome-lift-out .18s cubic-bezier(.4, 0, 1, 1) both; }

/* SOMEBODY WHO HAS ASKED FOR LESS MOTION GETS NONE OF THIS, and gets it as a
   hard off rather than a shortened version. chrome.js reads the same preference
   and stops holding the navigation back, so for that visitor a click on the
   corner is exactly what it was before any of this existed. */
@media (prefers-reduced-motion: reduce) {
  .chrome,
  .chrome-home,
  .chrome-menu,
  .chrome-who,
  .chrome.is-leaving,
  .chrome.is-leaving .chrome-home,
  .chrome.is-leaving .chrome-menu,
  .chrome.is-leaving .chrome-who { animation: none; }
}
