/* ==========================================================================
   femboyproxies.com  ·  v4
   A plain text document. Monospace, hairline boxes, no radius, no gradients.
   Dark only.
   ========================================================================== */

:root {
  --bg:     #111111;
  --panel:  #0a0a0a;
  --line:   #2f2f2f;
  --line-2: #1f1f1f;

  --text:  #e1e1e1;
  --muted: #9a9a9a;
  --dim:   #6a6a6a;

  --pink:  #ff3d8b;
  --pink-2:#ff8ec0;
  --link:  #79b8ff;
  --ok:    #4ade80;
  --warn:  #fbbf24;

  /* The interface face.
     
     Claude's own is Styrene, licensed from Klim and not ours to serve, so it
     is named first for anyone who already has it installed and Figtree stands
     in for everybody else. Swap the free names out if the real licence is ever
     bought; nothing else has to change. */
  --ff-ui: "Styrene B", "Styrene A", Figtree, Inter, system-ui,
           -apple-system, "Segoe UI", Roboto, sans-serif;

  /* Monospace, still. Credentials, endpoints, code and figures line up in
     columns, and a proportional face would take that away for no gain. */
  --ff: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
  --ff-word: "Bodoni Moda", Georgia, serif;

  --col: 760px;
}

*, *::before, *::after { box-sizing: border-box; }

html { background: var(--bg); scroll-behavior: smooth; scroll-padding-top: 2rem; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--ff-ui);
  font-size: 16px;
  line-height: 24px;
  -webkit-font-smoothing: antialiased;
}

/* ------------------------------------------------------------------ column */

.doc {
  max-width: var(--col);
  margin-inline: auto;
  border-inline: 1px solid var(--line);
  padding: 0 28px;
}

/* Index runs wide (80rem) as a two column document: a sticky index rail on the left and the
   reading column on the right. The rail is what earns the extra width, otherwise a wide page
   is just a narrow column with dead space beside it. */
.doc-wide { max-width: 80rem; padding: 0 40px; }

.layout { display: grid; grid-template-columns: 216px minmax(0, 1fr); gap: 0 48px; align-items: start; }

.rail {
  position: sticky; top: 24px; align-self: start;
  border-right: 1px solid var(--line-2); padding-right: 24px;
}
.rail nav { display: grid; gap: 7px; }
.rail nav a { color: var(--muted); font-size: 15px; }
.rail nav a:hover { color: #fff; text-decoration: none; }
.rail .facts {
  margin-top: 26px; padding-top: 18px; border-top: 1px solid var(--line-2);
  display: grid; gap: 7px; font-size: 14px;
}
.rail .facts div { display: flex; justify-content: space-between; gap: 10px; color: var(--dim); }
.rail .facts b { color: var(--text); font-weight: 400; }

.rail-label { color: var(--dim); font-size: 14px; margin-bottom: 12px; }
.rail nav a.on { color: var(--pink); }

.content { min-width: 0; max-width: 880px; }
.content > * + * { margin-top: 24px; }

/* short pages, forms and the 404, do not need a rail. They keep a narrow measure under the
   full width masthead rather than stretching a login box across 1280px. */
.content-narrow { max-width: 620px; }
/* a page whose only content is one card reads better centred than pinned to the left edge */
.content-center { margin-inline: auto; }
.content > p,
.content section > p,
.content section > ul,
.content section > ol,
.content .notice,
.content blockquote,
.content details.q .a { max-width: 88ch; }

/* boxes that wrap fixed width art should hug it rather than stretch */
.box-fit { width: max-content; max-width: 100%; }

/* ASCII figures sit full width like every other block, with the art centred inside so the
   slack is even on both sides rather than pooling on the right */
.box > pre.ascii { width: max-content; max-width: 100%; margin-inline: auto; }

/* drawn figures scale with the column, but stop shrinking before the labels go unreadable
   and scroll instead, which is how the ASCII behaved */
.figure { overflow-x: auto; }
.figure img { display: block; width: 100%; min-width: 620px; height: auto; }

/* the calculator spans the column, with its readouts laid across the bottom and the button
   sized to its label rather than stretched into a slab */
.calc-foot { display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }
.calc-stats {
  flex: 1 1 380px; margin: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px;
}
.calc-stats dt { color: var(--dim); font-size: 14px; }
/* readouts stay on one line, otherwise the row grows and the box jumps as you drag */
.calc-stats dd { margin: 0; white-space: nowrap; }
.doc > * + * { margin-top: 24px; }

section { scroll-margin-top: 24px; }

/* ------------------------------------------------------------------ type */

h1, h2, h3, h4 {
  margin: 0;
  font-size: 16px;
  line-height: 24px;
  font-weight: 700;
  color: #fff;
}
h1 {
  border-left: 3px solid var(--pink);
  padding-left: 13px;
  margin-left: -16px;
}
h2 { text-decoration: underline; text-underline-offset: 5px; text-decoration-thickness: 1px; }
h3 { color: var(--text); }

p { margin: 0; color: var(--text); }
p + p { margin-top: 24px; }
.muted { color: var(--muted); }
.dim { color: var(--dim); }
.pink { color: var(--pink); }
.ok { color: var(--ok); }

a { color: var(--link); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

b, strong { font-weight: 700; color: #fff; }
hr { border: 0; border-top: 1px solid var(--line-2); margin: 0; }

ul, ol { margin: 0; padding-left: 22px; color: var(--text); }
li { margin: 0; }
li::marker { color: var(--dim); }
ul.tight li + li, ol.tight li + li { margin-top: 0; }

.wordmark { font-family: var(--ff-word); font-weight: 800; letter-spacing: -.01em; }
.wordmark-a { color: var(--pink); }
.wordmark-b { color: #fff; }
.wordmark-dim { color: var(--muted); }
.w-620 { max-width: 620px; }
.kana { font-family: "Noto Sans JP", var(--ff); letter-spacing: .35em; }

/* --------------------------------------------------------------- controls */

.btn {
  display: inline-block;
  padding: 5px 12px;
  font-family: var(--ff-ui);
  font-size: 16px;
  line-height: 24px;
  font-weight: 700;
  border: 1px solid var(--pink);
  background: var(--pink);
  color: #12000a;
  cursor: pointer;
  text-decoration: none;
}
.btn:hover { background: var(--pink-2); border-color: var(--pink-2); color: #12000a; text-decoration: none; }
.btn-ghost { background: transparent; border-color: var(--line); color: var(--text); }
.btn-ghost:hover { background: transparent; border-color: var(--muted); color: #fff; }
.btn-block { display: block; width: 100%; text-align: center; }

/* ------------------------------------------------------------------ boxes */

.box { border: 1px solid var(--line); padding: 20px; }
.box-tight { padding: 12px 16px; }

blockquote {
  margin: 0;
  border-left: 1px solid var(--line);
  padding-left: 16px;
  color: var(--text);
}
blockquote .by { color: var(--muted); }
blockquote .by span { color: var(--link); }

/* grid of bordered cells, like a logo wall */
.cells {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  border: 1px solid var(--line);
  border-bottom: 0;
  border-right: 0;
}
.cells-2 { grid-template-columns: repeat(2, 1fr); }
.cells > div {
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  padding: 18px 16px;
}
.cells > div b { display: block; }
.cells > div span { display: block; color: var(--muted); }

/* ------------------------------------------------------------------ ascii */

pre {
  margin: 0;
  overflow-x: auto;
  font-family: var(--ff);
  font-size: 14px;
  line-height: 20px;
  color: var(--muted);
}
pre.ascii { color: var(--dim); }
pre.ascii b { color: var(--text); font-weight: 400; }
pre.ascii .hot { color: var(--pink); }

pre.code { font-size: 14px; line-height: 22px; color: var(--text); }
pre.code .c { color: var(--dim); }
pre.code .s { color: var(--pink-2); }
pre.code .k { color: var(--link); }
pre.code .n { color: var(--warn); }

code { font-family: var(--ff); color: var(--pink-2); }

/* code tabs */
.tabs { display: flex; align-items: center; gap: 0; border: 1px solid var(--line); border-bottom: 0; }
.tab {
  padding: 4px 12px; font-family: var(--ff); font-size: 14px; line-height: 22px;
  background: transparent; border: 0; border-right: 1px solid var(--line);
  color: var(--dim); cursor: pointer;
}
.tab:hover { color: var(--text); }
.tab.on { color: var(--pink); background: rgba(255, 61, 139, .08); }
.copy {
  margin-left: auto; padding: 4px 12px; background: transparent; border: 0;
  font-family: var(--ff-ui); font-size: 14px; color: var(--dim); cursor: pointer;
}
.copy:hover { color: var(--pink); }
.code-body { border: 1px solid var(--line); padding: 16px; }

/* ------------------------------------------------------------------ table */

table { width: 100%; border-collapse: collapse; font-size: 15px; }

/* A table with more columns than the box is wide scrolls inside its own box
   rather than pushing the page sideways. The admin tables are the wide ones. */
.table-scroll { overflow-x: auto; }
.table-scroll table { min-width: 640px; }

/* The audit detail is json of unknown length, so it is allowed to break. */
/* The audit detail. It used to print the stored object as it came out of the
   database and break it wherever the line ran out, which split words down the
   middle: "reaso n", "grante d". Now it is a list of pairs, broken at spaces,
   and only an unbreakable token like an invoice number is cut. */
td.detail { min-width: 300px; }
.detail-list { display: grid; gap: 3px 0; margin: 0; font-size: 14px; }
.detail-list > div { display: flex; gap: 10px; align-items: baseline; }
.detail-list dt { color: var(--dim); flex: 0 0 auto; }
.detail-list dd { color: var(--muted); margin: 0; min-width: 0; overflow-wrap: break-word; }
th, td { text-align: left; padding: 6px 12px; border: 1px solid var(--line); }
th { color: var(--muted); font-weight: 400; }
td b { color: #fff; }
td.r, th.r { text-align: right; }

/* --------------------------------------------------------------- keyvalue */

.kv { display: grid; grid-template-columns: 200px 1fr; gap: 0 16px; }

/* One pool on one line: name, who supplies it, what it covers. */
.pool-line { margin: 0 0 4px; display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; }
.pool-line b { font-size: 16px; }
.kv dt { color: var(--dim); }
.kv dd { margin: 0; }

/* ----------------------------------------------------------------- header */

.topbar {
  border-bottom: 1px solid var(--line);
  padding: 10px 28px;
  display: flex; align-items: center; gap: 16px; justify-content: center;
  font-size: 15px; color: var(--muted); flex-wrap: wrap;
}
.topbar a.btn { padding: 2px 10px; font-size: 15px; }

.masthead { display: flex; align-items: center; gap: 16px; padding: 22px 0 0; flex-wrap: wrap; }
.masthead .logo { display: flex; align-items: baseline; gap: 6px; font-size: 18px; }
.masthead nav { margin-left: auto; display: flex; align-items: center; gap: 14px; font-size: 15px; }
.masthead nav .sep { color: var(--line); }

details.mainnav { margin-top: 20px; }
details.mainnav > summary {
  list-style: none; cursor: pointer; color: var(--text);
  display: inline-flex; align-items: center; gap: 8px;
}
details.mainnav > summary::-webkit-details-marker { display: none; }
details.mainnav > summary::before { content: "\25B6"; color: var(--pink); font-size: 11px; }
details.mainnav[open] > summary::before { content: "\25BC"; }
details.mainnav .links {
  margin-top: 14px; border: 1px solid var(--line); padding: 14px 16px;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px 20px; font-size: 15px;
}

/* -------------------------------------------------------------------- faq */

details.q { border-top: 1px solid var(--line-2); }
details.q:last-of-type { border-bottom: 1px solid var(--line-2); }
details.q > summary {
  list-style: none; cursor: pointer; padding: 10px 0; display: flex; gap: 12px; color: #fff;
}
details.q > summary::-webkit-details-marker { display: none; }
details.q > summary::before { content: "+"; color: var(--pink); }
details.q[open] > summary::before { content: "-"; }
details.q .a { padding: 0 0 14px 24px; color: var(--muted); }
details.q .a p + p { margin-top: 16px; }

/* ------------------------------------------------------------------ forms */

.field { display: grid; gap: 4px; }
.field label { color: var(--dim); font-size: 15px; }
.field input, .field select, .field textarea {
  width: 100%; padding: 7px 10px;
  background: #000; border: 1px solid var(--line); color: var(--text);
  font-family: var(--ff-ui); font-size: 15px; line-height: 22px; border-radius: 0;
}
.field textarea { min-height: 150px; resize: vertical; }
.field input::placeholder, .field textarea::placeholder { color: #444; }
.field input:focus, .field select:focus, .field textarea:focus { outline: none; border-color: var(--pink); }
.field .hint { color: var(--dim); font-size: 14px; }

.notice { border: 1px solid var(--line); border-left: 3px solid var(--pink); padding: 12px 16px; color: var(--muted); font-size: 15px; }

/* --------------------------------------------------------------- slider */

input[type="range"] {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 24px; background: transparent; cursor: pointer;
}
input[type="range"]::-webkit-slider-runnable-track { height: 1px; background: var(--line); }
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 12px; height: 20px; margin-top: -10px; background: var(--pink); border: 0; border-radius: 0;
}
input[type="range"]::-moz-range-track { height: 1px; background: var(--line); }
input[type="range"]::-moz-range-thumb { width: 12px; height: 20px; background: var(--pink); border: 0; border-radius: 0; }

/* ----------------------------------------------------------------- footer */

.site-footer { border-top: 1px solid var(--line); margin-top: 48px; padding: 28px 0 40px; }
.foot-cols { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; font-size: 15px; }
.foot-cols h4 { color: var(--muted); font-weight: 400; text-decoration: underline; text-underline-offset: 5px; }
.foot-cols ul { list-style: none; padding: 0; margin: 12px 0 0; display: grid; gap: 4px; }
.foot-bottom { margin-top: 28px; display: flex; flex-wrap: wrap; gap: 16px; color: var(--dim); font-size: 14px; }

/* ------------------------------------------------------------------ utils */

.stack-8 > * + * { margin-top: 8px; }
.stack-16 > * + * { margin-top: 16px; }
.stack-24 > * + * { margin-top: 24px; }
.row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.right { margin-left: auto; }
.center { text-align: center; }
.small { font-size: 15px; }
.mt-8 { margin-top: 8px; } .mt-16 { margin-top: 16px; } .mt-24 { margin-top: 24px; } .mt-48 { margin-top: 48px; }

::selection { background: var(--pink); color: #12000a; }
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: #333; border: 3px solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: #444; }

:where(a, button, input, select, textarea, summary):focus-visible { outline: 2px solid var(--pink); outline-offset: 2px; }

/* ------------------------------------------------------------- responsive */

/* the rail replaces the collapsed nav on wide screens, and vice versa */
@media (min-width: 1000px) {
  .doc-wide > details.mainnav { display: none; }
}

@media (max-width: 999px) {
  .layout { grid-template-columns: minmax(0, 1fr); }
  .content { max-width: none; }

  /* There is no room for a column beside the page, and hiding the rail left a
     signed in customer on a phone with no way to reach their proxies, their
     invoices, their settings or the way out.

     It becomes one strip across the top that scrolls sideways, rather than
     wrapping: wrapped, seven links and a button stood 242px tall and pushed
     the page itself past the fold on every visit. */
  .rail {
    /* The wide layout makes this a sticky column with a rule down its right
       side. Across the top, neither applies. */
    position: static;
    border-right: 0;
    padding-right: 0;
    border-bottom: 1px solid var(--line);
    margin-bottom: 20px;
    padding-bottom: 2px;

    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 0 18px;
    overflow-x: auto;
    scrollbar-width: thin;
  }
  .rail-label { display: none; }

  /* The links join the strip directly instead of sitting in a box inside it. */
  .rail nav,
  .rail-admin { display: contents; }

  .rail nav a,
  .rail-admin a {
    align-items: center;
    display: flex;
    min-height: 44px;
    white-space: nowrap;
  }

  /* The overview already shows these as cards, and on a phone they would push
     the page itself below the fold. */
  .rail .facts { display: none; }

  .rail-out { flex: 0 0 auto; margin: 0; }
  .rail-out .btn-block { width: auto; white-space: nowrap; }

  /* The landing page hangs a "Get started" button off the end of its section
     rail. In a column that reads as the next thing to do; in a strip it is a
     block of pink pinned to the right of a row of links, over the top of them
     as they scroll past. The masthead already carries the same button, so on a
     phone this one is only in the way.

     Written as a direct child so it takes the landing page's button and not
     the ones the client area and admin rails keep in a wrapper: Log out has to
     stay reachable. */
  .rail > a.btn { display: none; }
}

@media (max-width: 800px) {
  .doc { border-inline: 0; padding: 0 18px; }
  .cells { grid-template-columns: repeat(2, 1fr); }
  .foot-cols { grid-template-columns: repeat(2, 1fr); }
  details.mainnav .links { grid-template-columns: repeat(2, 1fr); }
  .kv { grid-template-columns: 1fr; gap: 0; }
  .kv dd { margin-bottom: 12px; }
}

/* a table's min-content width does not shrink, so on narrow screens let it scroll inside its
   own box instead of pushing the whole page sideways */
@media (max-width: 620px) {
  table { display: block; overflow-x: auto; white-space: nowrap; }
  .calc-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .calc-foot > .btn { width: 100%; text-align: center; }
}

@media (max-width: 520px) {
  body { font-size: 15px; line-height: 23px; }
  .cells { grid-template-columns: 1fr; }
  .foot-cols { grid-template-columns: 1fr; }
  details.mainnav .links { grid-template-columns: 1fr; }
  /* The nav keeps its place beside the wordmark; it wraps on its own when
     there is genuinely no room, rather than always. */
  .masthead nav { margin-left: auto; }
  pre { font-size: 12px; line-height: 18px; }
}

/* ------------------------------------------------------------ on a phone */

/* Two drawings of the same figures, one shaped for a wide column and one for
   a narrow one. See charts.py: text inside an SVG scales with the SVG, so a
   chart drawn for a desktop column renders its labels at about 5px on a
   phone. Only one is ever displayed. */
.for-narrow { display: none; }

@media (max-width: 620px) {
  .for-wide { display: none; }
  .for-narrow { display: block; }

  /* iOS zooms the whole page in when a field smaller than 16px takes focus,
     and does not zoom back out afterwards. The field is the same size as
     before; only the text in it grows. */
  input, select, textarea,
  .field input, .field select, .field textarea { font-size: 16px; }

  /* A fingertip is about 9mm. Anything meant to be tapped gets the room for
     one, which mostly means the things that were sized for a cursor. */
  .btn,
  button,
  input[type="submit"],
  .rail a,
  .masthead nav a,
  details.mainnav > summary,
  details.mainnav .links a,
  .pager a,
  .pager span {
    min-height: 44px;
  }
  .btn,
  button,
  input[type="submit"] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
  .rail a,
  .masthead nav a,
  details.mainnav .links a {
    display: flex;
    align-items: center;
  }

  /* The header, on a phone.

     It was three things stacked oddly: a wordmark on one line, then a 44px
     row holding a small link, a 23px pipe floating off centre between two
     taller neighbours, and a wide button. Dropping the pipe and taking a
     couple of points off the wordmark fits the whole thing on one line at
     375px, which is the width most phones actually are. */
  .masthead {
    gap: 10px;
    padding-top: 16px;
  }
  .masthead .logo { font-size: 16px; }
  .masthead nav {
    gap: 10px;
    margin-left: auto;
  }
  /* Decoration between two tap targets, and the only thing in the row that did
     not line up with the rest. */
  .masthead nav .sep { display: none; }
  .masthead nav a.btn { padding-left: 14px; padding-right: 14px; }

  /* A scrolling strip that simply stops has a word cut in half at the edge and
     reads as broken. Fading it says there is more to the right. */
  .rail {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 28px), transparent);
  }

  /* A long endpoint or password is the thing people came to copy. It gets the
     full width and wraps rather than making the card scroll. */
  code.copy { display: block; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

/* ---------------------------------------------------- client area ---- */

.rail-label {
  color: var(--dim);
  font-size: 12px;
  letter-spacing: .08em;
  margin-bottom: 12px;
  text-transform: uppercase;
}
.rail nav a.here { color: var(--pink); }
.rail-out { margin-top: 22px; }

/* An email is longer than a 216px rail. Stack the label above the value and
   let the value wrap rather than letting flex squeeze the two into each other.
   Only in the admin rail, where the values are addresses. */
.rail .facts div.stacked { display: block; }
.rail .facts div.stacked b { display: block; margin-top: 2px; overflow-wrap: anywhere; }
.rail-admin { border-top: 1px solid var(--line-2); margin-top: 18px; padding-top: 14px; }
.rail-admin a { color: var(--pink); }

.notice-warn { border-color: var(--pink); }

.pager {
  align-items: center;
  border-top: 1px solid var(--line-2);
  display: flex;
  gap: 20px;
  justify-content: space-between;
  margin: 16px 0 28px;
  padding-top: 12px;
}
.pager-count { color: var(--dim); font-size: 14px; }

/* Four figures across the top, collapsing to two and then one. */
.stats {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin-bottom: 32px;
}
.stat { border: 1px solid var(--line); padding: 14px 16px; }
.stat dt { color: var(--dim); font-size: 13px; margin-bottom: 6px; }
.stat dd { font-size: 24px; font-weight: 700; margin: 0; }
.stat dd span { color: var(--muted); font-size: 14px; font-weight: 400; }

/* Charts are svg the server drew, so they are styled from here like anything
   else rather than carrying their own colours. */
/* The svg carries only a viewBox, so it scales to the box it is in rather
   than being capped by a height attribute. */
.chart { display: block; width: 100%; height: auto; overflow: visible; }
.chart-axis { stroke: var(--line); stroke-width: 1; }
.chart-grid { stroke: var(--line-2); stroke-width: 1; }
.chart-bar { fill: var(--pink); }
.chart-bar-zero { fill: var(--line); }
.chart-tick { fill: var(--dim); font: 12px var(--ff); }
.chart-axis-label { fill: var(--dim); font: 11px var(--ff); }
.chart-empty { fill: var(--dim); font: 13px var(--ff); }
/* The narrow drawing is about half the viewBox of the wide one, so the same
   numbers would render twice the size. These land it back where it started. */
.chart-narrow .chart-tick { font-size: 13px; }
.chart-narrow .chart-axis-label { font-size: 12px; }
.chart-narrow .chart-empty { font-size: 13px; }
.chart-narrow .chart-tip-text { font-size: 12px; }

/* The readout on a column. The browser's own tooltip waits about a second
   before it shows, which makes a chart feel unresponsive; this one is drawn
   into the same SVG and appears the moment the pointer is over the column.
   No transition, deliberately: a fade is a delay. */
.chart-hit { fill: transparent; cursor: pointer; }
.chart-col .chart-tip,
.chart-col .chart-lift { visibility: hidden; pointer-events: none; }
.chart-col:hover .chart-tip,
.chart-col:focus-within .chart-tip,
.chart-col:hover .chart-lift { visibility: visible; }
/* The bar itself is in the layer below, out of reach of this selector, so the
   pointed at one is repainted here instead of recoloured there. */
.chart-lift { fill: var(--pink-2); }
.chart-lift-zero { fill: var(--muted); }
.chart-tip-box { fill: var(--bg); stroke: var(--line); stroke-width: 1; }
.chart-tip-text { fill: var(--text); font: 11px var(--ff); }
/* Sitting inside a full height bar, the box needs to hold its own against it. */
.chart-tip-inside .chart-tip-box { fill: var(--panel); }

.meter-track { fill: var(--line-2); }
.meter-fill { fill: var(--pink); }
.meter-over { fill: #ff5552; }

/* A generated endpoint list is long and must not stretch the page. */
pre.code-body {
  max-height: 340px;
  overflow: auto;
  white-space: pre;
}

/* The proxy password and endpoint lines are meant to be selected and copied. */
code.copy { user-select: all; word-break: break-all; }

@media (max-width: 900px) {
  .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .stats { grid-template-columns: minmax(0, 1fr); }
}

/* ------------------------------------------------- bandwidth slider ---- */

/* A real range input, so it drags from anywhere on the line. Styled rather
   than replaced, because a native range already handles click to jump, drag,
   arrow keys, Home and End, and touch. */
.calc-range {
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  cursor: pointer;
  display: block;
  height: 22px;
  margin: 6px 0 2px;
  width: 100%;
}
.calc-range:focus { outline: none; }
.calc-range:focus-visible { outline: 1px solid var(--pink); outline-offset: 4px; }

/* The rail. Chrome and Firefox need it named separately. */
.calc-range::-webkit-slider-runnable-track {
  background: var(--line);
  border-radius: 0;
  height: 4px;
}
.calc-range::-moz-range-track {
  background: var(--line);
  border-radius: 0;
  height: 4px;
}
/* Only Firefox will paint the filled part for us. */
.calc-range::-moz-range-progress {
  background: var(--pink);
  height: 4px;
}

.calc-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  background: var(--pink);
  border: 0;
  border-radius: 0;
  height: 16px;
  margin-top: -6px;          /* centres a 16px thumb on a 4px rail */
  width: 10px;
}
.calc-range::-moz-range-thumb {
  background: var(--pink);
  border: 0;
  border-radius: 0;
  height: 16px;
  width: 10px;
}
.calc-range:hover::-webkit-slider-thumb { background: var(--pink-2); }
.calc-range:hover::-moz-range-thumb { background: var(--pink-2); }

.calc-scale { display: flex; color: var(--dim); font-size: 13px; }
.calc-scale .right { margin-left: auto; }

/* Every quote is in the page; the slider only changes which is shown. The
   server marks the starting one, so the figures are right before any script
   has run, and right again after a submit if none ever does. */
.calc-slot { display: none; }
.calc-slot.is-on { display: inline; }

/* Screen reader text for the notch labels. */
.voice {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%);
  overflow: hidden;
}

/* -------------------------------------------------- admin: a modal, no js ---

   The overlay is shown while the URL names it, which is what an <a href="#id">
   does. Closing is a link back to the page. Nothing runs, which matters here:
   the policy this site sends allows no inline script, and a dialog element
   cannot be opened without one. */
.modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 20;
  padding: 24px;
  overflow-y: auto;
}
.modal:target { display: flex; align-items: flex-start; justify-content: center; }

/* Covers the page and closes on a click anywhere outside the card. */
.modal-away {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .72);
}
.modal-card {
  position: relative;
  width: 100%;
  max-width: 460px;
  margin: 6vh auto;
  background: var(--panel);
  border: 1px solid var(--line);
  padding: 24px;
}
.modal-card h2 { margin-top: 0; }

.edit-link { font-size: 14px; margin-left: 10px; }

/* A row of actions that stays a row and fits the card it is in.

   The buttons share the width equally and shrink to whatever is left rather
   than running past the edge: four labels at the normal size came to more than
   the card was wide, and the row grew a scrollbar. Text wraps inside a button
   instead of overflowing it, so there is no width at which this scrolls. */
.row-inline {
  display: flex;
  flex-wrap: nowrap;
  gap: 8px;
}
/* Sized to their own labels, not to equal columns: equal columns gave every
   button the same width, so the longest label wrapped while the shortest sat
   in white space. They shrink together only if the row runs out of room. */
.row-inline > form { flex: 0 1 auto; min-width: 0; }
.row-inline .btn {
  width: 100%;
  padding-left: 10px;
  padding-right: 10px;
  font-size: 14px;
  text-align: center;
  white-space: normal;
}

/* Four equal columns on a phone would be about eighty pixels each, which is
   narrower than the words. Below that width they stack instead. */
@media (max-width: 620px) {
  .row-inline { flex-wrap: wrap; }
  .row-inline > form { flex: 1 1 auto; }
  .row-inline .btn { width: auto; }
}

/* An invoice number opens its own overlay; see .modal above. */

/* ---------------------------------------------------------- pool picker ---- */

/* Choosing a pool, with nothing running in the browser.

   The amount on the page is priced on both pools by the server, and both sets
   of figures are rendered. The checked radio decides which set is visible, so
   switching between them costs a click and no round trip.

   The radios sit outside every form and are tied to the repricing one with
   form="repricer", which keeps them ahead of the panels in the document: css
   can only style forwards, so a control has to precede what it reveals. The
   order buttons carry their own pool as a submit value, and only the visible
   one can be pressed, so what is shown and what is bought are the same thing. */

.pool-pick {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  pointer-events: none;
}

.pool-boxes {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  margin-bottom: 14px;
}

.pool-box {
  border: 1px solid var(--line);
  cursor: pointer;
  display: block;
  padding: 14px 16px;
}
.pool-box:hover { border-color: var(--line-2); }
.pool-box b { display: block; font-size: 15px; }
.pool-box span { color: var(--dim); display: block; font-size: 12px; margin-top: 3px; }

/* Hidden radios mean the focus ring has to land on the box standing in for
   them, or the picker cannot be seen while it is tabbed through. */
.pool-pick-0:focus-visible ~ .pool-boxes .pool-box-0,
.pool-pick-1:focus-visible ~ .pool-boxes .pool-box-1,
.pool-pick-2:focus-visible ~ .pool-boxes .pool-box-2,
.pool-pick-3:focus-visible ~ .pool-boxes .pool-box-3 {
  outline: 2px solid var(--pink);
  outline-offset: 2px;
}

/* Everything belonging to an unchecked pool is out of the page entirely, not
   merely faint: a hidden order button cannot be clicked or tabbed onto.

   Both a sibling and a descendant of one. The panel is the form itself, so
   "~ *" alone would never reach it and the page would show a picker with
   nothing underneath. */
.pool-panel { display: none; }
.pool-pick-0:checked ~ .pool-boxes .pool-box-0 {
  border-color: var(--pink);
  box-shadow: inset 0 0 0 1px var(--pink);
}
.pool-pick-0:checked ~ .pool-boxes .pool-box-0 b { color: var(--pink); }
.pool-pick-0:checked ~ .pool-panel-0,
.pool-pick-0:checked ~ * .pool-panel-0 { display: block; }
.pool-pick-1:checked ~ .pool-boxes .pool-box-1 {
  border-color: var(--pink);
  box-shadow: inset 0 0 0 1px var(--pink);
}
.pool-pick-1:checked ~ .pool-boxes .pool-box-1 b { color: var(--pink); }
.pool-pick-1:checked ~ .pool-panel-1,
.pool-pick-1:checked ~ * .pool-panel-1 { display: block; }
.pool-pick-2:checked ~ .pool-boxes .pool-box-2 {
  border-color: var(--pink);
  box-shadow: inset 0 0 0 1px var(--pink);
}
.pool-pick-2:checked ~ .pool-boxes .pool-box-2 b { color: var(--pink); }
.pool-pick-2:checked ~ .pool-panel-2,
.pool-pick-2:checked ~ * .pool-panel-2 { display: block; }
.pool-pick-3:checked ~ .pool-boxes .pool-box-3 {
  border-color: var(--pink);
  box-shadow: inset 0 0 0 1px var(--pink);
}
.pool-pick-3:checked ~ .pool-boxes .pool-box-3 b { color: var(--pink); }
.pool-pick-3:checked ~ .pool-panel-3,
.pool-pick-3:checked ~ * .pool-panel-3 { display: block; }

.label-like { color: var(--dim); display: block; font-size: 13px; }


/* --------------------------------------------------------------- plan cards */

/* One card per plan the customer holds. Square cornered and quiet, the way the
   rest of the client area is: this sits beside .box and .stat, not beside the
   landing page.
   
   Fixed at 350 by 200 so a row of them lines up whatever they contain, with
   max-width so it still fits a phone rather than pushing the page sideways. */
.plan-cards {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(0, 350px));
}

.plan-card {
  display: flex;
  flex-direction: column;
  width: 350px;
  max-width: 100%;
  height: 200px;
  border: 1px solid var(--line);
  padding: 18px 20px;
  /* Nothing here should ever grow the box; if a figure somehow ran long it
     clips rather than dragging the row out of line. */
  overflow: hidden;
}

.plan-card-head { display: flex; align-items: baseline; gap: 10px; }

.plan-card-name {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
}

.plan-card-tag {
  margin-left: auto;
  color: var(--pink);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .06em;
  white-space: nowrap;
}
/* Past four fifths it stops being a statistic and starts being a warning. */
.plan-card-tag.is-warn { color: #e0a800; }

/* The one number worth reading from across the room. */
.plan-card-figure {
  margin: 18px 0 0;
  font-size: 34px;
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1;
}
.plan-card-figure span { color: var(--dim); font-size: 15px; font-weight: 400; }

.plan-card-of {
  margin: 7px 0 0;
  color: var(--dim);
  font-size: 12px;
}

.plan-meter {
  height: 4px;
  margin-top: 14px;
  background: var(--line);
  overflow: hidden;
}
.plan-meter-fill { display: block; height: 100%; background: var(--pink); }

/* Pushed to the bottom edge, so every card's buttons line up however much
   sits above them. */
.plan-card-actions {
  display: flex;
  gap: 8px;
  margin-top: auto;
}
.plan-card-actions .btn {
  flex: 1 1 0;
  text-align: center;
  white-space: nowrap;
  padding: 8px 0;
  font-size: 12px;
}

/* Meter widths in fives. A class rather than a style attribute: style-src is
   'self', so an inline width would be dropped without a word. */
.plan-meter-0 { width: 0%; }
.plan-meter-5 { width: 5%; }
.plan-meter-10 { width: 10%; }
.plan-meter-15 { width: 15%; }
.plan-meter-20 { width: 20%; }
.plan-meter-25 { width: 25%; }
.plan-meter-30 { width: 30%; }
.plan-meter-35 { width: 35%; }
.plan-meter-40 { width: 40%; }
.plan-meter-45 { width: 45%; }
.plan-meter-50 { width: 50%; }
.plan-meter-55 { width: 55%; }
.plan-meter-60 { width: 60%; }
.plan-meter-65 { width: 65%; }
.plan-meter-70 { width: 70%; }
.plan-meter-75 { width: 75%; }
.plan-meter-80 { width: 80%; }
.plan-meter-85 { width: 85%; }
.plan-meter-90 { width: 90%; }
.plan-meter-95 { width: 95%; }
.plan-meter-100 { width: 100%; }
