/* ============================================================
   OPS Connections and Prices Database - stylesheet
   ------------------------------------------------------------
   The Decarbonizer design language, measured from the sandbox
   rather than approximated: a white header closed by a hairline
   rule inset to the gutter, a band of eight summary blocks closed
   by a seam, then de-boxed panels with the row rules carrying the
   structure.

   This tool does NOT take the Decarbonizer's accordions. Those
   exist there because that page has fourteen subsections to fold
   through; this one has two views and a filter column, so the view
   select stays the navigation and the panels stay open. What is
   taken is everything else: the header rule, the seam, the block
   band, the de-boxing, the table treatment, the row grid, the
   square corners and the italic justified copy.

   Three things this file does not do, deliberately, because the
   reference does not: draw a frame around a panel, draw a rule
   between two rows of the same list, or draw a vertical rule in a
   table. Spacing separates; the row rules carry the structure.
   ============================================================ */

/* ===== tokens ===== */
:root {
  --navy: #0A1543;
  --green: #2f9e6f;
  --grey: #aab2c2;
  --line: #e6e9f0;
  --soft: #f6f7fa;
  --sel-row: #f0f1f3;
  --bg: #fff;
  --paper-warm: #f4f5f8;

  --ink: #15151a;
  --ink-soft: #3d3d47;
  --ink-muted: #6b6b75;

  /* Three weights of line, and they are not interchangeable.
     --rule2 draws table headers and block borders; --rule2-soft
     draws the rows underneath them and should be almost invisible;
     --rule-band draws the two structural seams, which have to be
     seen. One value each to dial. */
  --rule2: rgba(10, 21, 67, .12);
  --rule2-soft: rgba(10, 21, 67, .06);
  --rule-band: rgba(10, 21, 67, .38);

  /* The content column. The header and the wrapper both read this,
     so the header rule cannot drift away from the blocks under it.
     1300px rather than the Decarbonizer's 1180: the site table has
     nine columns, three of them text. */
  --col: 1300px;
  --gutter: 26px;

  /* The single gap governing the summary band: above and below the
     seam, so the band reads with one rhythm. */
  --band-gap: 31px;

  /* Height of the heading strip above each panel. The controls
     column has no heading of its own, so it is offset by exactly
     this much to bring its first row level with the panel's.
     Both sides read off one number so they cannot drift.
     6px + 12px padding + a 1.05rem/1.55 heading line = 42.8px. */
  --head-h: 43px
}

* {
  box-sizing: border-box
}

body {
  font-family: 'Poppins', sans-serif;
  color: var(--ink);
  background: var(--bg);
  margin: 0;
  font-size: 14px;
  line-height: 1.55
}

h3 {
  font-weight: 600;
  margin: 0
}

.wrap {
  max-width: var(--col);
  margin: 0 auto;
  padding: 0 var(--gutter)
}

a {
  color: var(--green);
  text-decoration: none
}

a:hover {
  text-decoration: underline
}

.hidden {
  display: none !important
}

/* ===== shapes ===== */
/* Square corners are the house rule. Stated once as a blanket, with
   the exceptions listed immediately after, so there is one place to
   look rather than a border-radius:0 on every rule in the file. */
#siteHeader *,
#tool * {
  border-radius: 0
}

/* Exception 1: the methodology marker. Squared off, it reads as a
   tiny button rather than as the "there is more here" dot it is.
   Scoped with the id: the blanket above is "#siteHeader *", which
   is (1,0,0), and a bare ".dz-info" at (0,1,0) loses to it. */
#siteHeader .dz-info {
  border-radius: 50%
}

/* Exception 2: slider thumbs, with their own rules further down. */

/* ===== header ===== */
/* Centred in the same column as the content, with the same gutter.
   No border-bottom: a border spans the whole 1300px border box, so
   it would sit 26px proud of the content column at each end. The
   rule is drawn by ::after instead, inset to the gutter, so it
   starts at the left edge of the first block and ends at the right
   edge of Download database at every width. */
.dz-header {
  max-width: var(--col);
  margin: 0 auto;
  background: #fff;
  color: var(--ink);
  padding: 19px var(--gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;
  border-bottom: 0;
  box-shadow: none
}

.dz-header::after {
  content: '';
  position: absolute;
  left: var(--gutter);
  right: var(--gutter);
  bottom: 0;
  border-bottom: 1px solid var(--rule-band)
}

.dz-left {
  display: flex;
  align-items: baseline;
  gap: 16px;
  flex-wrap: wrap;
  min-width: 0
}

.dz-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap
}

.dz-mark {
  font-weight: 600;
  font-size: 1.25rem;
  letter-spacing: -.01em;
  color: var(--navy)
}

.dz-product {
  font-size: .9rem;
  color: var(--ink-soft)
}

.dz-product strong {
  color: var(--navy);
  font-weight: 600
}

.dz-right {
  display: flex;
  align-items: center;
  gap: 10px
}

/* Methodology is a signpost to a reference panel, not an action on
   the selection, so it reads as a marker beside the product name
   rather than competing with Download database for the same weight
   in the same corner. */
.dz-info {
  width: 18px;
  height: 18px;
  padding: 0;
  border: 1px solid var(--navy);
  background: #fff;
  color: var(--navy);
  font-family: inherit;
  font-size: 11px;
  font-style: italic;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none
}

.dz-info:hover,
.dz-info.active {
  background: var(--navy);
  color: #fff
}

.dz-save {
  background: var(--navy);
  color: #fff;
  border: 0;
  padding: 9px 16px;
  font-family: inherit;
  font-size: .85rem;
  font-weight: 500;
  cursor: pointer
}

.dz-save:hover {
  background: #16224f
}

#tool {
  /* The page used to stop dead at the footer. A tail gives it
     somewhere to end. */
  padding-bottom: 40px
}

/* ===== summary blocks ===== */
/* No rule of its own and no spine down the middle. The seam
   directly below closes the band, so the page carries two
   horizontal lines in total: the header rule and the summary seam.
   The spine read as clutter inside the block area rather than as
   structure; the two group labels say the same thing without
   drawing anything. */
.blk8 {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: 14px;
  row-gap: 14px;
  margin: 14px 0 0;
  padding: 0;
  border-bottom: 0
}

.blk8-lab {
  grid-column: span 2;
  font-size: 10.5px;
  letter-spacing: .06em;
  color: var(--ink-muted);
  font-weight: 500;
  padding-bottom: 2px
}

.blk8-lab.right {
  text-align: right
}

.blk {
  border: 1px solid var(--rule2);
  background: #fff;
  padding: 12px 13px 11px;
  min-height: 118px;
  display: flex;
  flex-direction: column;
  position: relative;
  transition: border-color .14s
}

.blk:hover {
  border-color: var(--navy)
}

.blk-lab {
  font-size: 10.5px;
  color: var(--ink-muted);
  font-weight: 500;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px
}

.blk-lab-t {
  display: block;
  white-space: nowrap
}

.blk-val {
  font-size: 23px;
  font-weight: 600;
  line-height: 1.15;
  margin-top: auto;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
  letter-spacing: -.015em
}

.blk-val.sm {
  font-size: 17px
}

.blk-unit {
  font-size: 12px;
  font-weight: 400;
  color: var(--ink-muted)
}

.blk-sub {
  font-size: 10.5px;
  color: var(--ink-muted);
  margin-top: 3px;
  font-variant-numeric: tabular-nums
}

.blk-sub b {
  font-weight: 500;
  color: var(--ink-soft)
}

/* ===== the summary seam ===== */
/* One hairline in the seam tone, with equal air above and below
   from a single token, so the band reads as one group rather than
   trailing into the panels. This is what the 3px navy border used
   to do, at the weight the reference actually draws it. */
.bandseam {
  height: 0;
  border-top: 1px solid var(--rule-band);
  margin: var(--band-gap) 0
}

/* ===== panels ===== */
.split {
  display: grid;
  /* minmax(0, 1fr), not 1fr: a bare 1fr is minmax(auto, 1fr) and
     grows to its content's min-content width, which the site table
     and the ranking canvas both exceed. That widens the whole page
     on those views. */
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 26px;
  align-items: start
}

/* De-boxed: a panel is its heading and its content, with no frame
   and no rule under the heading. Spacing does the separating. */
.acc-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 0 12px;
  /* Fixed rather than content-sized, so --head-h stays true and the
     two columns stay level. A heading long enough to wrap at 1300px
     would break the alignment; neither does. */
  height: var(--head-h)
}

.acc-head h3 {
  font-weight: 500;
  font-size: 1.05rem;
  color: var(--navy)
}

.acc-body {
  border: 0;
  padding: 0
}

/* The controls column carries no heading, so it is dropped by the
   height of one to sit level with the panel beside it. */
aside {
  padding-top: var(--head-h)
}

.view {
  display: none
}

.view.active {
  display: block;
  min-width: 0
}

/* A group label above a stack of controls. The reference's own
   treatment for one, taken from the block band's labels. */
.band {
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: .06em;
  text-transform: none;
  color: var(--ink-muted);
  margin: 0 0 10px
}

/* A caption naming the plot or the table under it is smaller than a
   group label and centred over what it names. */
.band.chart-cap {
  font-size: 10px;
  line-height: 1.35;
  letter-spacing: 0;
  text-align: center
}

/* ===== charts ===== */
.chartbox {
  position: relative;
  /* a Chart.js canvas will not shrink below its own width without
     this, which re-breaks the grid track it sits in */
  min-width: 0;
  height: 260px;
  cursor: default;
  contain: layout
}

/* The price ranking carries a rotated site label under every bar
   and needs the extra depth for them. */
.chartbox.xtall {
  height: 470px
}

/* ===== copy ===== */
/* All explanatory copy: italic and justified, so its right edge
   lines up with the tables and blocks above it. */
p,
.note {
  font-style: italic;
  text-align: justify;
  hyphens: auto
}

.note {
  font-size: 12.5px;
  line-height: 1.8;
  color: var(--ink-muted);
  margin-top: 18px;
  border-top: 0;
  padding-top: 0;
  white-space: pre-line
}

/* Two notes in a row are one block of small print, not two
   separated paragraphs. */
.note + .note {
  margin-top: 10px
}

/* ===== controls column ===== */
/* De-boxed, like the panel beside it. The group labels and the row
   rhythm carry the structure. */
.controls {
  border: 0;
  padding: 0
}

/* A parameter row. No rule between rows: the uniform height and the
   shared label column carry the alignment, and lines only add
   noise. */
.field {
  min-height: 34px;
  padding: 6px 0;
  margin-bottom: 0
}

label {
  display: block;
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-soft);
  margin-bottom: 5px
}

/* Marked out from the filters below it with a navy edge, because it
   is navigation rather than a filter. */
.viewfield select {
  border-color: var(--navy);
  font-weight: 500
}

input[type=text],
input[type=number],
input[type=search],
select {
  width: 100%;
  font-family: inherit;
  font-size: 13px;
  padding: 6px 8px;
  height: 30px;
  border: 1px solid var(--rule2);
  background: #fff;
  color: var(--ink)
}

input[type=number] {
  -moz-appearance: textfield;
  appearance: textfield
}

input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0
}

input:focus,
select:focus,
button:focus-visible {
  outline: 1px solid var(--navy);
  border-color: var(--navy)
}

input:disabled {
  background: var(--soft);
  color: var(--grey);
  cursor: not-allowed
}

/* ----- year slider ----- */
/* The published tool drives the operational-year floor with a
   slider, so it stays a slider. The value is echoed in the label,
   because a slider with no readout cannot be set to a year. */
.slab {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px
}

.slab b {
  font-size: 12px;
  font-weight: 600;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
  text-transform: none
}

/* A thin neutral track and a round navy thumb, with no coloured
   fill: the value is printed in the label, so a filled track only
   adds weight. Round is one of the exceptions to the square rule. */
input[type=range] {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  height: 14px;
  margin: 2px 0 0;
  padding: 0;
  background: transparent;
  border: 0;
  cursor: pointer
}

input[type=range]::-webkit-slider-runnable-track {
  height: 3px;
  background: var(--line)
}

input[type=range]::-moz-range-track {
  height: 3px;
  background: var(--line)
}

/* Firefox fills the track left of the thumb by default; match the
   unfilled look of the other browsers. */
input[type=range]::-moz-range-progress {
  height: 3px;
  background: var(--line)
}

input[type=range]::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--navy);
  border: 0;
  margin-top: -5.5px
}

input[type=range]::-moz-range-thumb {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--navy);
  border: 0
}

/* A slider shows its state through the thumb, so a focus box around
   it is noise: dragging one drew a navy rectangle for the whole
   drag. :focus-visible still fires on keyboard, so tabbing to it is
   not left silent. */
input[type=range]:focus {
  outline: none;
  border-color: transparent
}

input[type=range]:focus-visible {
  outline: 1px solid var(--navy)
}

/* ----- checkbox rows ----- */
.check {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  min-height: 34px;
  padding: 6px 0
}

/* Square, no rounding, navy only when checked. The tick is drawn
   from two borders rather than glyphed, so it sits on the pixel
   grid at 14px. */
input[type=checkbox] {
  appearance: none;
  -webkit-appearance: none;
  width: 14px;
  height: 14px;
  flex: 0 0 14px;
  margin: 3px 0 0;
  border: 1px solid var(--grey);
  background: #fff;
  display: inline-grid;
  place-content: center;
  cursor: pointer
}

input[type=checkbox]:checked {
  background: var(--navy);
  border-color: var(--navy)
}

input[type=checkbox]:checked::after {
  content: '';
  width: 3px;
  height: 7px;
  border: solid #fff;
  border-width: 0 1.5px 1.5px 0;
  transform: translateY(-1px) rotate(45deg)
}

.check label {
  margin: 0;
  font-size: 12px;
  color: var(--ink-soft);
  cursor: pointer
}

/* ===== tables ===== */
/* No frame; the row rules carry the structure. No vertical rules,
   ever. A table ends open: the last row draws none. */
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  border: 0
}

th,
td {
  text-align: right;
  padding: 6px 9px
}

th:first-child,
td:first-child {
  text-align: left
}

thead th {
  background: transparent;
  color: var(--ink-muted);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--rule2)
}

td {
  border-bottom: 1px solid var(--rule2-soft)
}

tbody tr:last-child > td {
  border-bottom: 0
}

/* ----- the site table ----- */
/* Country, port, site, category and system type are text; the four
   numeric columns stay right. */
.sitetable td:nth-child(1),
.sitetable th:nth-child(1),
.sitetable td:nth-child(2),
.sitetable th:nth-child(2),
.sitetable td:nth-child(3),
.sitetable th:nth-child(3),
.sitetable td:nth-child(6),
.sitetable th:nth-child(6),
.sitetable td:nth-child(7),
.sitetable th:nth-child(7) {
  text-align: left
}

/* 11.5px and wrapping rather than one nowrap line per cell: the
   longest site name in the data is 58 characters and clipping it to
   an ellipsis loses the part that identifies the berth.
   overflow-wrap rather than word-break, so a word only breaks when
   it cannot fit at all: word-break: break-word split "Unspecified"
   across two lines in the system column. */
.sitetable td,
.sitetable th {
  padding: 7px 9px;
  font-size: 11.5px;
  vertical-align: top;
  white-space: normal;
  word-break: normal;
  overflow-wrap: break-word
}

/* Opaque, not transparent: the header is frozen and the rows scroll
   under it. Transparent would let them show through and the text
   would collide. */
.sitetable thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: #fff;
  font-size: 10.5px;
  line-height: 1.3;
  vertical-align: bottom
}

.sitetable td:nth-child(4),
.sitetable td:nth-child(5),
.sitetable td:nth-child(8),
.sitetable td:nth-child(9) {
  white-space: nowrap
}

.sitetable tbody tr:hover {
  background: var(--paper-warm)
}

.sitetable {
  table-layout: fixed;
  min-width: 760px
}

.sitetable col.c-country { width: 10% }
.sitetable col.c-port { width: 12% }
.sitetable col.c-site { width: 20% }
.sitetable col.c-op { width: 8% }
.sitetable col.c-pts { width: 7% }
.sitetable col.c-cat { width: 15% }
.sitetable col.c-sys { width: 10% }
.sitetable col.c-pow { width: 8% }
.sitetable col.c-price { width: 10% }

/* The rows scroll inside the box so the page does not grow with the
   selection. The header stays put through it. */
.tablescroll {
  position: relative;
  max-height: 520px;
  overflow: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--rule2) transparent
}

.tablescroll::-webkit-scrollbar {
  width: 8px;
  height: 8px
}

.tablescroll::-webkit-scrollbar-track {
  background: transparent
}

/* Quieter by colour, not by shape. */
.tablescroll::-webkit-scrollbar-thumb {
  background: var(--rule2)
}

/* A dash where a figure is not recorded, so an absent tariff never
   renders as a price of zero. Set by the renderer, not the CSS;
   this only styles it. */
.na {
  color: var(--grey)
}

/* A tariff carrying fixed fees on top of the kWh rate is flagged in
   the table, because the number in the cell is not the whole cost
   of a call. */
.flag {
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--ink-muted);
  border: 1px solid var(--rule2);
  padding: 0 3px;
  margin-left: 4px;
  white-space: nowrap
}

/* ===== methodology panel ===== */
/* No frame: the headings are the structure. The page spans the same
   column as the rest of the tool, so its right edge lines up with
   everything else. */
.mp {
  border: 0;
  padding: 0;
  max-width: none
}

.mp h2 {
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--navy);
  margin: 26px 0 6px
}

.mp h2:first-of-type {
  margin-top: 0
}

.mp h3 {
  font-size: 13px;
  font-weight: 500;
  color: var(--navy);
  margin: 18px 0 4px;
  text-transform: none;
  letter-spacing: 0
}

.mp p {
  margin: 0 0 8px;
  color: var(--ink-soft);
  font-size: 13px;
  line-height: 1.75;
  max-width: none
}

.foot {
  color: var(--ink-muted);
  font-size: 11.5px;
  text-align: center;
  padding: 26px 0 0
}

/* ===== responsive ===== */
@media (max-width: 1100px) {
  .blk8 {
    grid-template-columns: repeat(2, minmax(0, 1fr))
  }

  .blk8-lab.right {
    text-align: left
  }
}

@media (max-width: 860px) {
  .split {
    grid-template-columns: minmax(0, 1fr)
  }

  /* Stacked, there is no panel beside the controls to line up with,
     so the offset becomes a stray gap. The grid gap already
     separates them. */
  aside {
    padding-top: 0
  }

  .dz-header {
    flex-wrap: wrap;
    gap: 12px
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important
  }
}

/* ============================================================
   Freemium layer
   ------------------------------------------------------------
   Inert on the premium deployment: nothing carries these classes
   unless /init reports the free tier, so both Workers serve the
   same stylesheet and the only difference between them is the
   TIER variable in wrangler.toml.

   The vocabulary is the Decarbonizer Freemium's, so a gate reads
   as the same software across the range rather than as a bolt-on
   per tool: .fm-lock, .fm-cell, .fm-col, .fm-snapshot and
   .fm-upgrade. Gated elements stay in the layout, marked, never
   removed.
   ============================================================ */

/* The single lock glyph, drawn rather than typed, so it carries no
   font dependency and no emoji. Sized to sit on a line of body
   text and coloured by whatever it sits inside. */
:root {
  --fm-lock-svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 10V7a6 6 0 0 1 12 0v3h1a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h1zm2 0h8V7a4 4 0 0 0-8 0v3z'/%3E%3C/svg%3E")
}

.fm-lock {
  display: inline-block;
  width: 11px;
  height: 11px;
  vertical-align: -1px;
  margin-right: 5px;
  flex: none;
  background: currentColor;
  -webkit-mask: var(--fm-lock-svg) center / contain no-repeat;
  mask: var(--fm-lock-svg) center / contain no-repeat
}

/* A withheld cell. Italic and muted, so a column of them reads as
   one state rather than as data, and never as a dash: a dash means
   the field is not recorded, this means it is not being served.

   One line, always. Two earlier attempts got this wrong. Leaving it
   nowrap let the phrase overflow into the next column, because rated
   power is an 8% column; letting it wrap then broke the word itself
   into "Premiu / m only", because the cell carries
   overflow-wrap: break-word for long site names and 8% is narrower
   than the word. Neither is a wrapping problem. The column is simply
   too narrow for the phrase, so the free tier widens it below and
   the phrase stays on one line here. */
.fm-cell {
  color: var(--ink-muted);
  font-style: italic;
  white-space: nowrap;
  overflow-wrap: normal;
  word-break: keep-all
}

/* Withheld cells read from the left of their column, including in
   the two numeric ones. Right-aligning them the way the figures they
   stand in for are aligned pushed the same two words to a different
   x in every column; on the left they line up with the header above
   them and the column reads as one state. */
.sitetable td.fm-td {
  text-align: left
}

/* The free tier's own column widths. Three columns carry a phrase
   rather than a figure, and the premium widths were set for "0.42"
   and a site name, not for "Premium only". The site column gives up
   what the other two need, since it holds the same phrase and had
   the most to spare. Still totals 100.

   The table's floor goes up with them: at 760px a 12% column is
   91px, which is under the phrase at 11.5px once the cell padding is
   taken off. At 900px it is 108px and the phrase sits with room. The
   table already scrolls inside .tablescroll, so a wider floor costs
   a little more horizontal scroll on a narrow screen and nothing
   else. */
body.is-free .sitetable {
  min-width: 900px
}

body.is-free .sitetable col.c-country { width: 10% }
body.is-free .sitetable col.c-port { width: 13% }
body.is-free .sitetable col.c-site { width: 14% }
body.is-free .sitetable col.c-op { width: 8% }
body.is-free .sitetable col.c-pts { width: 7% }
body.is-free .sitetable col.c-cat { width: 15% }
body.is-free .sitetable col.c-sys { width: 9% }
body.is-free .sitetable col.c-pow { width: 12% }
body.is-free .sitetable col.c-price { width: 12% }

/* The data-currency line, sitting above the summary band because it
   qualifies every figure in it. */
.fm-snapshot {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-style: italic;
  color: var(--ink-muted);
  padding-top: 14px
}

/* The single upgrade route. One consistent link, navy, no shouting,
   and it appears once per gated area rather than on every cell. */
.fm-upgrade {
  color: var(--navy);
  font-weight: 600;
  font-style: normal;
  text-decoration: none;
  border-bottom: 1px solid rgba(10, 21, 67, .35);
  white-space: nowrap
}

.fm-upgrade:hover {
  border-bottom-color: var(--navy);
  text-decoration: none
}

/* A control the free tier fixes. Visible at its set value, greyed
   and inert, so the reader can see the option exists and read what
   it is currently doing. The row carries the state, not the input
   alone, so the label dims with its box. */
.fm-locked {
  opacity: .5;
  cursor: not-allowed
}

.fm-locked input {
  pointer-events: none
}

.fm-locked label {
  cursor: not-allowed
}
