@charset "UTF-8";

:root {
  color-scheme: light;
  --canvas: #f3f5ef;
  --canvas-deep: #e8ece3;
  --surface: #ffffff;
  --surface-soft: #f8faf6;
  --surface-tint: #eef5ec;
  --ink: #13251c;
  --ink-soft: #31483d;
  --muted: #6c7d74;
  --muted-light: #96a49c;
  --line: #dfe5dc;
  --line-strong: #cbd5ca;
  --brand: #b9ed62;
  --brand-bright: #d4ff7f;
  --brand-deep: #82bd32;
  --green: #178653;
  --green-soft: #e0f5e9;
  --blue: #3978e8;
  --blue-soft: #e7efff;
  --amber: #d98518;
  --amber-soft: #fff2d9;
  --red: #c8473f;
  --red-soft: #fde9e7;
  --dark: #10251c;
  --dark-soft: #19362a;
  --shadow-sm: 0 1px 2px rgba(16, 37, 28, 0.04), 0 8px 24px rgba(16, 37, 28, 0.05);
  --shadow-md: 0 18px 50px rgba(16, 37, 28, 0.1);
  --shadow-lg: 0 30px 80px rgba(9, 28, 20, 0.17);
  --radius-sm: 12px;
  --radius-md: 18px;
  --radius-lg: 26px;
  --radius-xl: 34px;
  --content-width: 1180px;
  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  font-family: "Avenir Next", Avenir, "Segoe UI", Helvetica, Arial, sans-serif;
  font-synthesis: none;
  text-rendering: optimizeLegibility;
}

* {
  box-sizing: border-box;
}

html {
  min-width: 320px;
  scroll-behavior: smooth;
  background: var(--canvas);
}

body {
  min-height: 100vh;
  margin: 0;
  color: var(--ink);
  background:
    radial-gradient(circle at 8% 8%, rgba(185, 237, 98, 0.12), transparent 27rem),
    linear-gradient(180deg, #f8faf5 0, var(--canvas) 28rem, var(--canvas) 100%);
  -webkit-font-smoothing: antialiased;
}

body::before {
  position: fixed;
  inset: 0;
  z-index: -3;
  pointer-events: none;
  content: "";
  opacity: 0.16;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 160 160' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.16'/%3E%3C/svg%3E");
}

button,
input {
  font: inherit;
}

button,
a {
  -webkit-tap-highlight-color: transparent;
}

button {
  color: inherit;
}

a {
  color: inherit;
  text-decoration: none;
}

svg {
  display: block;
}

::selection {
  color: var(--dark);
  background: var(--brand);
}

.skip-link {
  position: fixed;
  top: 10px;
  left: 10px;
  z-index: 1000;
  padding: 10px 14px;
  border-radius: 10px;
  color: white;
  background: var(--dark);
  transform: translateY(-150%);
  transition: transform 160ms ease;
}

.skip-link:focus {
  transform: translateY(0);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.ambient {
  position: fixed;
  z-index: -2;
  width: 28rem;
  height: 28rem;
  border-radius: 50%;
  pointer-events: none;
  filter: blur(2px);
  opacity: 0.55;
}

.ambient-one {
  top: 8rem;
  right: -15rem;
  background: radial-gradient(circle, rgba(185, 237, 98, 0.3), transparent 68%);
}

.ambient-two {
  top: 48rem;
  left: -18rem;
  background: radial-gradient(circle, rgba(57, 120, 232, 0.1), transparent 70%);
}

/* Pinned. The blurred backdrop is what makes a translucent bar stay readable
   over content passing underneath it. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  border-bottom: 1px solid rgba(203, 213, 202, 0.72);
  background: rgba(248, 250, 245, 0.82);
  backdrop-filter: blur(20px) saturate(150%);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: min(calc(100% - 40px), var(--content-width));
  min-height: 76px;
  margin: 0 auto;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-width: max-content;
}

.brand-mark {
  display: grid;
  width: 38px;
  height: 38px;
  place-items: center;
  border-radius: 12px;
  color: var(--brand-bright);
  background: var(--dark);
  box-shadow: 0 8px 18px rgba(16, 37, 28, 0.16);
}

.brand-mark svg {
  width: 21px;
  height: 21px;
  fill: currentColor;
}

.brand-name {
  color: var(--ink);
  font-size: 1.17rem;
  font-weight: 750;
  letter-spacing: -0.045em;
}

/* The accented half of the wordmark, named rather than matched as "any nested
   span". The mark is built from several elements now -- ink head, accent tail,
   and a plain surface label such as "Admin" -- and a blanket selector would
   paint that label green too. */
.brand-name-accent {
  color: var(--green);
}

.desktop-nav {
  display: flex;
  align-items: center;
  gap: 34px;
  margin-left: auto;
  margin-right: 40px;
}

.desktop-nav a {
  position: relative;
  color: var(--ink-soft);
  font-size: 0.9rem;
  font-weight: 650;
}

.desktop-nav a::after {
  position: absolute;
  right: 0;
  bottom: -9px;
  left: 0;
  height: 2px;
  border-radius: 999px;
  content: "";
  background: var(--green);
  transform: scaleX(0);
  transition: transform 180ms var(--ease);
}

.desktop-nav a:hover::after,
.desktop-nav a[aria-current="page"]::after {
  transform: scaleX(1);
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Language switch. Compact and low-contrast so it reads as chrome rather than
   competing with the connection pill next to it. */
.lang-switch {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.78rem;
  font-weight: 600;
}

.lang-switch a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 8px;
  border-radius: 6px;
  color: var(--muted);
  text-decoration: none;
}

/* Decorative: the endonym beside it carries the meaning, and the SVG is
   aria-hidden, so this must not add its own spacing or line box. */
.lang-flag {
  display: inline-flex;
  border-radius: 2px;
  overflow: hidden;
  /* A hairline, so a flag whose own colours are close to the background still
     reads as a bounded chip rather than a smudge. */
  box-shadow: 0 0 0 1px rgba(16, 37, 28, 0.12);
}

.lang-flag svg {
  display: block;
  width: 18px;
  height: 12px;
}

.lang-name {
  line-height: 1;
}

.lang-switch a:hover {
  color: var(--ink);
  background: rgba(16, 37, 28, 0.06);
}

.connection-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 36px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--muted);
  background: rgba(255, 255, 255, 0.74);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.01em;
}

.connection-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--muted-light);
}

.connection-pill[data-state="live"] {
  color: var(--green);
  border-color: rgba(23, 134, 83, 0.18);
  background: rgba(224, 245, 233, 0.72);
}

.connection-pill[data-state="live"] .connection-dot {
  background: var(--green);
  box-shadow: 0 0 0 0 rgba(23, 134, 83, 0.35);
  animation: connection-pulse 2.2s infinite;
}

.connection-pill[data-state="connecting"] .connection-dot {
  background: var(--amber);
  animation: soft-blink 1.1s infinite;
}

/* A finished session.

   Deliberately static. The "live" pill pulses, and a pulse is a promise that
   something is on its way; on a charge that has already ended it made the page look
   like it was still waiting for a number. Green because it worked, with no dot
   animation to suggest otherwise. */
.connection-pill[data-state="settled"] {
  color: var(--green);
  border-color: rgba(23, 134, 83, 0.18);
  background: rgba(224, 245, 233, 0.72);
}

.connection-pill[data-state="settled"] .connection-dot {
  background: var(--green);
}

.connection-pill[data-state="offline"] {
  color: var(--red);
  border-color: rgba(200, 71, 63, 0.2);
  background: var(--red-soft);
}

.connection-pill[data-state="offline"] .connection-dot {
  background: var(--red);
}

/* The account entry point in the site header.

   Sized and coloured to sit beside .icon-button (40px square) rather than like a
   call-to-action button: the header is chrome, and a 48px .button among 40px
   controls would read as a mis-sized control rather than as a link. Below 620px
   the word is dropped and the person icon remains, matching how the connection
   pill and language switch collapse in the same breakpoint. */
.account-entry {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 40px;
  padding: 0 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  color: var(--ink-soft);
  background: rgba(255, 255, 255, 0.8);
  font-size: 0.79rem;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
  transition: border-color 160ms ease, color 160ms ease, background 160ms ease, transform 160ms ease;
}

.account-entry:hover {
  border-color: var(--line-strong);
  color: var(--ink);
  background: #fff;
  transform: translateY(-1px);
}

/* The signed-in variant. Same shape as the sign-in entry -- the header must not
   change size when somebody signs in, or the whole bar shifts under them -- but
   in the brand green, because this is the state you are in rather than something
   you need to do. Green here means "already handled", not "go", so it is a tint
   rather than the solid fill a primary button uses. */
.account-entry-signed-in {
  color: var(--green);
  border-color: rgba(52, 168, 83, 0.4);
  background: rgba(52, 168, 83, 0.08);
}

.account-entry-signed-in:hover {
  color: var(--green);
  border-color: var(--green);
  background: rgba(52, 168, 83, 0.14);
}

.account-entry svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2;
}

.icon-button {
  display: grid;
  width: 40px;
  height: 40px;
  padding: 0;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 12px;
  color: var(--ink-soft);
  background: rgba(255, 255, 255, 0.8);
  cursor: pointer;
  transition: border-color 160ms ease, color 160ms ease, background 160ms ease, transform 160ms ease;
}

.icon-button:hover {
  color: var(--ink);
  border-color: var(--line-strong);
  background: white;
  transform: translateY(-1px);
}

.icon-button svg,
.modal-icon svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.8;
}

.icon-button.has-token {
  color: var(--green);
  border-color: rgba(23, 134, 83, 0.25);
  background: var(--green-soft);
}

.connection-banner {
  position: relative;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 44px;
  padding: 8px 20px;
  color: #7c2e28;
  border-bottom: 1px solid #f4c7c3;
  background: #fff0ee;
  font-size: 0.86rem;
  font-weight: 600;
}

.connection-banner[hidden] {
  display: none;
}

.connection-banner-icon {
  display: grid;
  width: 19px;
  height: 19px;
  place-items: center;
  border-radius: 50%;
  color: white;
  background: var(--red);
  font-size: 0.72rem;
  font-weight: 800;
}

.connection-banner button {
  padding: 3px 7px;
  border: 0;
  border-bottom: 1px solid currentColor;
  color: inherit;
  background: transparent;
  cursor: pointer;
  font-size: inherit;
  font-weight: 750;
}

.app-shell {
  width: min(calc(100% - 40px), var(--content-width));
  min-height: 68vh;
  margin: 0 auto;
  padding: 34px 0 90px;
  outline: none;
}

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  min-height: 48px;
  padding: 0 20px;
  border: 1px solid transparent;
  border-radius: 14px;
  cursor: pointer;
  font-size: 0.91rem;
  font-weight: 750;
  letter-spacing: -0.01em;
  /* Stated here, not left to inherit. Every variant below sets its own colour,
     but a `.button` with no variant is a button nobody has decided the colour
     of, and an inherited or user-agent colour is what made the admin labels
     vanish white-on-white in a dark scheme (#25). Safe default: the ink on the
     surface it is on. */
  color: var(--ink);
  background: var(--surface);
  transition: transform 160ms var(--ease), box-shadow 160ms var(--ease), background 160ms ease, border-color 160ms ease, opacity 160ms ease;
}

.button:hover:not(:disabled) {
  transform: translateY(-2px);
}

.button:active:not(:disabled) {
  transform: translateY(0);
}

.button:focus-visible,
.icon-button:focus-visible,
.filter-button:focus-visible,
.station-card:focus-visible,
.back-link:focus-visible,
.password-field button:focus-visible,
.auth-clear:focus-visible {
  outline: 3px solid rgba(57, 120, 232, 0.28);
  outline-offset: 3px;
}

.button:disabled {
  cursor: not-allowed;
  opacity: 0.52;
}

.button svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2;
}

.button-primary {
  color: var(--dark);
  background: var(--brand);
  box-shadow: 0 10px 25px rgba(130, 189, 50, 0.19);
}

.button-primary:hover:not(:disabled) {
  background: var(--brand-bright);
  box-shadow: 0 14px 30px rgba(130, 189, 50, 0.25);
}

.button-dark {
  color: white;
  background: var(--dark);
  box-shadow: 0 12px 28px rgba(16, 37, 28, 0.2);
}

.button-dark:hover:not(:disabled) {
  background: var(--dark-soft);
  box-shadow: 0 16px 34px rgba(16, 37, 28, 0.24);
}

.button-light {
  color: var(--dark);
  background: white;
  box-shadow: 0 10px 28px rgba(5, 20, 13, 0.14);
}

.button-ghost-light {
  color: white;
  border-color: rgba(255, 255, 255, 0.22);
  background: rgba(255, 255, 255, 0.07);
}

.button-ghost-light:hover:not(:disabled) {
  border-color: rgba(255, 255, 255, 0.42);
  background: rgba(255, 255, 255, 0.12);
}

.button-ghost {
  color: var(--ink-soft);
  border-color: var(--line);
  background: white;
}

.button-ghost:hover:not(:disabled) {
  border-color: var(--line-strong);
  background: var(--surface-soft);
}

.button-danger {
  color: white;
  background: var(--red);
  box-shadow: 0 10px 25px rgba(200, 71, 63, 0.18);
}

.button-danger-soft {
  color: var(--red);
  border-color: #f0c5c1;
  background: var(--red-soft);
}

.button-full {
  width: 100%;
}

.button-loader {
  width: 17px;
  height: 17px;
  border: 2px solid rgba(16, 37, 28, 0.22);
  border-top-color: var(--dark);
  border-radius: 50%;
  animation: spin 700ms linear infinite;
}

.eyebrow {
  margin: 0 0 12px;
  color: var(--green);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  line-height: 1.2;
  text-transform: uppercase;
}

.eyebrow-light {
  color: var(--brand);
}

.hero-dashboard {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.22fr) minmax(340px, 0.78fr);
  min-height: 470px;
  padding: clamp(38px, 6vw, 74px);
  overflow: hidden;
  border-radius: var(--radius-xl);
  color: white;
  background:
    radial-gradient(circle at 82% 22%, rgba(185, 237, 98, 0.2), transparent 22rem),
    linear-gradient(130deg, #10251c 0%, #163529 56%, #10251c 100%);
  box-shadow: var(--shadow-lg);
  isolation: isolate;
}

.hero-dashboard::before {
  position: absolute;
  top: -45%;
  left: 42%;
  z-index: -1;
  width: 1px;
  height: 190%;
  content: "";
  background: linear-gradient(transparent, rgba(255, 255, 255, 0.16), transparent);
  transform: rotate(28deg);
}

.hero-dashboard::after {
  position: absolute;
  right: -7rem;
  bottom: -10rem;
  z-index: -1;
  width: 31rem;
  height: 31rem;
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 50%;
  content: "";
  box-shadow:
    0 0 0 4rem rgba(255, 255, 255, 0.025),
    0 0 0 8rem rgba(255, 255, 255, 0.018);
}

.hero-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  max-width: 650px;
  padding-right: 40px;
}

.display-title {
  max-width: 680px;
  margin: 0;
  font-size: clamp(2.8rem, 5.8vw, 5.3rem);
  font-weight: 680;
  letter-spacing: -0.067em;
  line-height: 0.97;
}

.display-title em {
  color: var(--brand);
  font-family: Georgia, "Times New Roman", serif;
  font-weight: 400;
}

.hero-description {
  max-width: 570px;
  margin: 25px 0 0;
  color: rgba(255, 255, 255, 0.68);
  font-size: clamp(1rem, 1.5vw, 1.13rem);
  line-height: 1.72;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 34px;
}

.trust-row {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  margin-top: 34px;
  color: rgba(255, 255, 255, 0.56);
  font-size: 0.78rem;
  font-weight: 650;
}

.trust-item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.trust-item svg {
  width: 15px;
  height: 15px;
  fill: none;
  stroke: var(--brand);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2;
}

.network-visual {
  position: relative;
  display: grid;
  min-height: 320px;
  place-items: center;
}

.network-ring {
  position: relative;
  display: grid;
  width: min(100%, 320px);
  aspect-ratio: 1;
  place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.13);
  border-radius: 50%;
}

.network-ring::before,
.network-ring::after {
  position: absolute;
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 50%;
  content: "";
}

.network-ring::before {
  inset: 14%;
}

.network-ring::after {
  inset: 29%;
  border-color: rgba(185, 237, 98, 0.22);
  background: rgba(185, 237, 98, 0.025);
}

.network-core {
  position: relative;
  z-index: 2;
  display: grid;
  width: 112px;
  height: 112px;
  place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 34px;
  color: var(--brand);
  background: rgba(255, 255, 255, 0.08);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.14), 0 24px 50px rgba(0, 0, 0, 0.19);
  backdrop-filter: blur(10px);
}

.network-core svg {
  width: 46px;
  height: 46px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.45;
}

.network-badge {
  position: absolute;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 150px;
  padding: 11px 13px;
  border: 1px solid rgba(255, 255, 255, 0.13);
  border-radius: 15px;
  color: white;
  background: rgba(20, 47, 36, 0.78);
  box-shadow: 0 16px 34px rgba(3, 15, 9, 0.24);
  backdrop-filter: blur(14px);
}

.network-badge-one {
  top: 7%;
  right: -1%;
}

.network-badge-two {
  bottom: 7%;
  left: -2%;
}

.network-badge-icon {
  display: grid;
  width: 31px;
  height: 31px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 10px;
  color: var(--dark);
  background: var(--brand);
}

.network-badge-icon svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2;
}

.network-badge-copy {
  display: grid;
  gap: 2px;
}

.network-badge-copy strong {
  overflow: hidden;
  font-size: 0.76rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.network-badge-copy span {
  color: rgba(255, 255, 255, 0.52);
  font-size: 0.66rem;
}

.network-badge-dot {
  width: 6px;
  height: 6px;
  margin-left: auto;
  border-radius: 50%;
  background: var(--brand);
  box-shadow: 0 0 0 4px rgba(185, 237, 98, 0.1);
}

.metrics-strip {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  margin: 18px 0 0;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.86);
  box-shadow: var(--shadow-sm);
}

.metric {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  min-height: 92px;
  padding: 20px 25px;
}

.metric:not(:last-child)::after {
  position: absolute;
  top: 22px;
  right: 0;
  bottom: 22px;
  width: 1px;
  content: "";
  background: var(--line);
}

.metric-icon {
  display: grid;
  width: 40px;
  height: 40px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 13px;
  color: var(--green);
  background: var(--green-soft);
}

.metric-icon svg {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.8;
}

.metric-copy {
  display: grid;
  gap: 3px;
}

.metric-value {
  color: var(--ink);
  font-size: 1.45rem;
  font-weight: 760;
  letter-spacing: -0.045em;
  line-height: 1;
}

.metric-label {
  color: var(--muted);
  font-size: 0.74rem;
  font-weight: 650;
}

/* The padding clears the pinned header and the scroll-margin keeps an anchor
   target from landing underneath it. */
.stations-section {
  padding-top: 78px;
  scroll-margin-top: 90px;
}

.section-heading {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 30px;
  margin-bottom: 25px;
}

.section-heading h2 {
  margin: 0;
  font-size: clamp(1.8rem, 3vw, 2.5rem);
  font-weight: 700;
  letter-spacing: -0.052em;
  line-height: 1.08;
}

.section-heading-copy {
  max-width: 410px;
  margin: 9px 0 0;
  color: var(--muted);
  line-height: 1.6;
}

.updated-copy {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-width: max-content;
  color: var(--muted);
  font-size: 0.76rem;
  font-weight: 650;
}

.updated-copy::before {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  content: "";
  background: var(--green);
  box-shadow: 0 0 0 4px rgba(23, 134, 83, 0.1);
}

.station-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  margin-bottom: 25px;
  padding: 10px;
  border: 1px solid var(--line);
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.7);
}

.search-field {
  position: relative;
  width: min(100%, 360px);
}

.search-field svg {
  position: absolute;
  top: 50%;
  left: 15px;
  width: 18px;
  height: 18px;
  fill: none;
  stroke: var(--muted);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.8;
  transform: translateY(-50%);
  pointer-events: none;
}

.search-field input {
  width: 100%;
  height: 44px;
  padding: 0 42px;
  border: 1px solid transparent;
  border-radius: 12px;
  outline: none;
  color: var(--ink);
  background: transparent;
  font-size: 0.88rem;
}

.search-field input::placeholder {
  color: var(--muted-light);
}

.search-field input:focus {
  border-color: var(--line-strong);
  background: white;
  box-shadow: 0 0 0 3px rgba(23, 134, 83, 0.08);
}

.search-clear {
  position: absolute;
  top: 50%;
  right: 9px;
  display: grid;
  width: 28px;
  height: 28px;
  padding: 0;
  place-items: center;
  border: 0;
  border-radius: 8px;
  color: var(--muted);
  background: transparent;
  cursor: pointer;
  transform: translateY(-50%);
}

.search-clear[hidden] {
  display: none;
}

.search-clear svg {
  position: static;
  width: 15px;
  height: 15px;
  transform: none;
}

.filter-group {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px;
  border-radius: 12px;
  background: var(--surface-tint);
}

.filter-button {
  min-height: 36px;
  padding: 0 13px;
  border: 0;
  border-radius: 9px;
  color: var(--muted);
  background: transparent;
  cursor: pointer;
  font-size: 0.76rem;
  font-weight: 700;
  white-space: nowrap;
  transition: color 150ms ease, background 150ms ease, box-shadow 150ms ease;
}

.filter-button:hover {
  color: var(--ink);
}

.filter-button[aria-pressed="true"] {
  color: var(--ink);
  background: white;
  box-shadow: 0 2px 8px rgba(16, 37, 28, 0.08);
}

.station-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 18px;
}

.station-card {
  --status-color: var(--muted);
  --status-soft: var(--surface-tint);
  position: relative;
  display: flex;
  min-width: 0;
  min-height: 270px;
  flex-direction: column;
  padding: 22px;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.9);
  box-shadow: var(--shadow-sm);
  transition: border-color 220ms var(--ease), box-shadow 220ms var(--ease), transform 220ms var(--ease);
}

.station-card::before {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  height: 3px;
  content: "";
  background: var(--status-color);
  opacity: 0;
  transform: scaleX(0.5);
  transition: opacity 180ms ease, transform 220ms var(--ease);
}

.station-card:hover {
  z-index: 1;
  border-color: var(--line-strong);
  box-shadow: var(--shadow-md);
  transform: translateY(-5px);
}

.station-card:hover::before {
  opacity: 1;
  transform: scaleX(1);
}

.station-card[data-category="available"] {
  --status-color: var(--green);
  --status-soft: var(--green-soft);
}

.station-card[data-category="charging"] {
  --status-color: var(--blue);
  --status-soft: var(--blue-soft);
}

.station-card[data-category="busy"] {
  --status-color: var(--amber);
  --status-soft: var(--amber-soft);
}

.station-card[data-category="offline"] {
  --status-color: #819087;
  --status-soft: #edf0ed;
}

.station-card-top,
.station-footer,
.station-state-row,
.station-meta {
  display: flex;
  align-items: center;
}

.station-card-top {
  justify-content: space-between;
}

.station-icon {
  display: grid;
  width: 48px;
  height: 48px;
  place-items: center;
  border-radius: 15px;
  color: var(--status-color);
  background: var(--status-soft);
}

.station-icon svg {
  width: 25px;
  height: 25px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.7;
}

.status-badge,
.session-state-badge {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 27px;
  padding: 0 10px;
  border-radius: 999px;
  color: var(--status-color, var(--green));
  background: var(--status-soft, var(--green-soft));
  font-size: 0.68rem;
  font-weight: 780;
  letter-spacing: 0.015em;
}

.status-badge-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

.status-badge[data-live="true"] .status-badge-dot {
  animation: status-pulse 1.8s infinite;
}

.station-name {
  margin: 22px 0 4px;
  overflow: hidden;
  font-size: 1.2rem;
  font-weight: 730;
  letter-spacing: -0.035em;
  line-height: 1.2;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.station-code {
  margin: 0;
  overflow: hidden;
  color: var(--muted);
  font-size: 0.73rem;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.station-meta {
  flex-wrap: wrap;
  gap: 8px 15px;
  margin-top: 18px;
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 620;
}

.station-meta span,
.station-state-copy {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.station-meta svg {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.8;
}

.station-state-row {
  gap: 8px;
  margin-top: 15px;
  padding: 11px 12px;
  border-radius: 11px;
  color: var(--ink-soft);
  background: var(--surface-soft);
  font-size: 0.76rem;
  font-weight: 650;
}

.state-dot {
  width: 7px;
  height: 7px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--status-color);
}

.station-card-footer {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  margin-top: auto;
  padding-top: 22px;
}

.station-price {
  display: grid;
  gap: 2px;
}

.station-price strong {
  font-size: 0.92rem;
  letter-spacing: -0.02em;
}

.station-price span {
  color: var(--muted);
  font-size: 0.67rem;
  font-weight: 600;
}

.arrow-circle {
  display: grid;
  width: 37px;
  height: 37px;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 50%;
  color: var(--ink-soft);
  background: white;
  transition: color 180ms ease, border-color 180ms ease, background 180ms ease, transform 180ms var(--ease);
}

.arrow-circle svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2;
}

.station-card:hover .arrow-circle {
  color: var(--dark);
  border-color: var(--brand);
  background: var(--brand);
  transform: rotate(-25deg);
}

.loading-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 18px;
}

.empty-state,
.error-state,
.not-found {
  display: grid;
  min-height: 360px;
  padding: 50px 24px;
  place-items: center;
  text-align: center;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.58);
}

.empty-state-inner,
.error-state-inner,
.not-found-inner {
  max-width: 440px;
}

.empty-illustration,
.error-illustration,
.not-found-illustration {
  display: grid;
  width: 76px;
  height: 76px;
  margin: 0 auto 22px;
  place-items: center;
  border-radius: 24px;
  color: var(--green);
  background: var(--green-soft);
  transform: rotate(-4deg);
}

.error-illustration {
  color: var(--red);
  background: var(--red-soft);
}

.not-found-illustration {
  color: var(--ink-soft);
  background: var(--surface-tint);
}

.empty-illustration svg,
.error-illustration svg,
.not-found-illustration svg {
  width: 34px;
  height: 34px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.6;
}

.empty-state h2,
.error-state h1,
.not-found h1 {
  margin: 0;
  font-size: 1.55rem;
  letter-spacing: -0.04em;
}

.empty-state p,
.error-state p,
.not-found p {
  margin: 12px 0 24px;
  color: var(--muted);
  line-height: 1.65;
}

.how-section {
  display: grid;
  grid-template-columns: 0.72fr 1.28fr;
  gap: 70px;
  margin-top: 90px;
  padding: 58px;
  border-radius: var(--radius-xl);
  color: white;
  background: var(--dark);
  box-shadow: var(--shadow-lg);
  scroll-margin-top: 100px;
}

.how-copy h2 {
  max-width: 360px;
  margin: 0;
  font-size: clamp(2rem, 3.3vw, 3rem);
  font-weight: 670;
  letter-spacing: -0.055em;
  line-height: 1.04;
}

.how-copy p:last-child {
  max-width: 360px;
  margin: 20px 0 0;
  color: rgba(255, 255, 255, 0.6);
  line-height: 1.7;
}

.steps-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 15px;
}

.step-card {
  min-height: 200px;
  padding: 22px;
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.055);
}

.step-number {
  display: grid;
  width: 33px;
  height: 33px;
  place-items: center;
  border-radius: 10px;
  color: var(--dark);
  background: var(--brand);
  font-size: 0.74rem;
  font-weight: 800;
}

.step-card h3 {
  margin: 35px 0 9px;
  font-size: 1rem;
  letter-spacing: -0.025em;
}

.step-card p {
  margin: 0;
  color: rgba(255, 255, 255, 0.53);
  font-size: 0.79rem;
  line-height: 1.6;
}

.back-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 36px;
  margin-bottom: 24px;
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 700;
  transition: color 150ms ease, transform 150ms var(--ease);
}

.back-link:hover {
  color: var(--ink);
  transform: translateX(-3px);
}

.back-link svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2;
}

.station-detail-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(330px, 0.75fr);
  gap: 22px;
  align-items: start;
}

.station-profile,
.booking-card,
.info-card,
.session-content-card,
.receipt-card {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.9);
  box-shadow: var(--shadow-sm);
}

.station-profile {
  --status-color: var(--muted);
  --status-soft: var(--surface-tint);
  min-height: 550px;
  padding: clamp(28px, 5vw, 52px);
  overflow: hidden;
}

.station-profile[data-category="available"] {
  --status-color: var(--green);
  --status-soft: var(--green-soft);
}

.station-profile[data-category="charging"] {
  --status-color: var(--blue);
  --status-soft: var(--blue-soft);
}

.station-profile[data-category="busy"] {
  --status-color: var(--amber);
  --status-soft: var(--amber-soft);
}

.station-profile[data-category="offline"] {
  --status-color: #819087;
  --status-soft: #edf0ed;
}

.station-profile-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 25px;
}

.station-profile-identity {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 20px;
}

.station-emblem {
  position: relative;
  display: grid;
  width: 78px;
  height: 78px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 24px;
  color: var(--status-color);
  background: var(--status-soft);
}

.station-emblem::after {
  position: absolute;
  right: -3px;
  bottom: -3px;
  width: 17px;
  height: 17px;
  border: 4px solid white;
  border-radius: 50%;
  content: "";
  background: var(--status-color);
}

.station-emblem svg {
  width: 38px;
  height: 38px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.55;
}

.station-profile h1 {
  margin: 0 0 6px;
  overflow-wrap: anywhere;
  font-size: clamp(2rem, 4vw, 3.2rem);
  font-weight: 690;
  letter-spacing: -0.06em;
  line-height: 1;
}

.station-profile-code {
  margin: 0;
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 620;
}

.detail-status {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 38px;
  padding: 0 14px;
  border-radius: 999px;
  color: var(--status-color);
  background: var(--status-soft);
  font-size: 0.77rem;
  font-weight: 760;
  white-space: nowrap;
}

.detail-status .state-dot {
  width: 8px;
  height: 8px;
}

.station-hero-divider {
  height: 1px;
  margin: 42px 0 32px;
  background: var(--line);
}

.station-facts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}

.fact {
  min-width: 0;
  padding: 17px;
  border: 1px solid var(--line);
  border-radius: 15px;
  background: var(--surface-soft);
}

.fact-label {
  display: block;
  margin-bottom: 7px;
  color: var(--muted);
  font-size: 0.67rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.fact-value {
  display: block;
  overflow: hidden;
  color: var(--ink);
  font-size: 0.91rem;
  font-weight: 720;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.session-banner {
  display: flex;
  align-items: center;
  gap: 17px;
  margin-top: 28px;
  padding: 18px;
  border: 1px solid rgba(57, 120, 232, 0.16);
  border-radius: 17px;
  background: var(--blue-soft);
}

.session-banner-icon {
  display: grid;
  width: 45px;
  height: 45px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 14px;
  color: var(--blue);
  background: white;
}

.session-banner-icon svg {
  width: 23px;
  height: 23px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.8;
}

.session-banner-copy {
  display: grid;
  min-width: 0;
  gap: 4px;
}

.session-banner-copy strong {
  font-size: 0.88rem;
}

.session-banner-copy span {
  color: #5570a5;
  font-size: 0.74rem;
}

.session-banner .button {
  min-height: 38px;
  margin-left: auto;
  padding: 0 14px;
  white-space: nowrap;
}

/* Sticky within its own column, offset to clear the pinned header. */
.booking-card {
  position: sticky;
  top: 100px;
  padding: 28px;
}

.booking-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
}

.booking-head h2 {
  margin: 0;
  font-size: 1.3rem;
  letter-spacing: -0.04em;
}

.booking-head p {
  margin: 7px 0 0;
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.5;
}

.booking-state-icon {
  display: grid;
  width: 45px;
  height: 45px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 14px;
  color: var(--green);
  background: var(--green-soft);
}

.booking-state-icon svg {
  width: 23px;
  height: 23px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.8;
}

.booking-price {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 15px;
  margin: 28px 0 24px;
  padding-bottom: 24px;
  border-bottom: 1px solid var(--line);
}

.booking-price strong {
  display: block;
  font-size: 2.35rem;
  font-weight: 710;
  letter-spacing: -0.065em;
  line-height: 0.9;
}

.booking-price span {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 650;
}

.booking-price-note {
  color: var(--ink-soft) !important;
  text-align: right;
}

.booking-list {
  display: grid;
  gap: 16px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.booking-item {
  display: flex;
  align-items: center;
  gap: 12px;
}

.booking-item-icon {
  display: grid;
  width: 36px;
  height: 36px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 11px;
  color: var(--ink-soft);
  background: var(--surface-tint);
}

.booking-item-icon svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.8;
}

.booking-item-copy {
  display: grid;
  gap: 2px;
}

.booking-item-copy strong {
  font-size: 0.8rem;
}

.booking-item-copy span {
  color: var(--muted);
  font-size: 0.7rem;
}

.booking-footer {
  margin-top: 28px;
}

.button-hint {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  margin: 13px 0 0;
  color: var(--muted);
  font-size: 0.67rem;
  font-weight: 600;
  text-align: center;
}

.button-hint svg {
  width: 13px;
  height: 13px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2;
}

.booking-unavailable {
  margin-top: 28px;
  padding: 18px;
  border: 1px solid var(--line);
  border-radius: 15px;
  background: var(--surface-soft);
}

.booking-unavailable strong {
  display: block;
  font-size: 0.86rem;
}

.booking-unavailable p {
  margin: 7px 0 0;
  color: var(--muted);
  font-size: 0.75rem;
  line-height: 1.55;
}

.secure-note {
  display: flex;
  gap: 10px;
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.7rem;
  line-height: 1.55;
}

.secure-note svg {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  fill: none;
  stroke: var(--green);
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.9;
}

.detail-bottom-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 22px;
  margin-top: 22px;
}

.info-card {
  padding: 28px;
}

.info-card h2 {
  margin: 0 0 20px;
  font-size: 1.08rem;
  letter-spacing: -0.035em;
}

.info-list {
  display: grid;
  gap: 16px;
  margin: 0;
}

.info-list-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  padding-bottom: 15px;
  border-bottom: 1px solid var(--line);
  font-size: 0.78rem;
}

.info-list-row:last-child {
  padding-bottom: 0;
  border-bottom: 0;
}

.info-list-row dt {
  color: var(--muted);
}

.info-list-row dd {
  margin: 0;
  font-weight: 700;
  text-align: right;
}

.detail-steps {
  display: grid;
  gap: 17px;
}

.detail-step {
  display: flex;
  align-items: flex-start;
  gap: 13px;
}

.detail-step-number {
  display: grid;
  width: 29px;
  height: 29px;
  flex: 0 0 auto;
  place-items: center;
  border-radius: 9px;
  color: var(--green);
  background: var(--green-soft);
  font-size: 0.68rem;
  font-weight: 800;
}

.detail-step strong {
  display: block;
  font-size: 0.79rem;
}

.detail-step span {
  display: block;
  margin-top: 3px;
  color: var(--muted);
  font-size: 0.72rem;
  line-height: 1.5;
}

.session-page {
  max-width: 1040px;
  margin: 0 auto;
}

.session-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 18px;
}

.session-topbar .back-link {
  margin: 0;
}

.session-reference {
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 650;
}

.session-heading {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 25px;
  margin-bottom: 25px;
}

.session-heading h1 {
  margin: 0 0 8px;
  font-size: clamp(2rem, 4vw, 3.2rem);
  font-weight: 690;
  letter-spacing: -0.06em;
  line-height: 1;
}

.session-heading p {
  margin: 0;
  color: var(--muted);
}

.session-state-badge {
  --status-color: var(--blue);
  --status-soft: var(--blue-soft);
  min-height: 34px;
  padding: 0 13px;
  font-size: 0.74rem;
}

.session-state-badge[data-tone="success"] {
  --status-color: var(--green);
  --status-soft: var(--green-soft);
}

.session-state-badge[data-tone="warning"] {
  --status-color: var(--amber);
  --status-soft: var(--amber-soft);
}

.session-state-badge[data-tone="danger"] {
  --status-color: var(--red);
  --status-soft: var(--red-soft);
}

.session-state-badge[data-tone="neutral"] {
  --status-color: var(--ink-soft);
  --status-soft: var(--surface-tint);
}

.live-energy-card {
  --progress: 0deg;
  position: relative;
  display: grid;
  grid-template-columns: minmax(280px, 0.78fr) minmax(0, 1.22fr);
  min-height: 335px;
  padding: clamp(28px, 5vw, 48px);
  overflow: hidden;
  border-radius: var(--radius-xl);
  color: white;
  background:
    radial-gradient(circle at 18% 46%, rgba(185, 237, 98, 0.14), transparent 16rem),
    var(--dark);
  box-shadow: var(--shadow-lg);
}

.live-energy-card::after {
  position: absolute;
  top: -48%;
  right: -9%;
  width: 28rem;
  height: 28rem;
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 50%;
  content: "";
  box-shadow: 0 0 0 3.5rem rgba(255, 255, 255, 0.016), 0 0 0 7rem rgba(255, 255, 255, 0.012);
}

.energy-orb-wrap {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
}

.energy-orb {
  position: relative;
  display: grid;
  width: min(240px, 70vw);
  aspect-ratio: 1;
  place-items: center;
  border-radius: 50%;
  background: conic-gradient(var(--brand) var(--progress), rgba(255, 255, 255, 0.09) 0);
  box-shadow: 0 0 55px rgba(185, 237, 98, 0.12);
}

.energy-orb::before {
  position: absolute;
  inset: 8px;
  border-radius: 50%;
  content: "";
  background: var(--dark);
  box-shadow: inset 0 0 30px rgba(255, 255, 255, 0.04);
}

.energy-orb-inner {
  position: relative;
  z-index: 1;
  display: grid;
  place-items: center;
  text-align: center;
}

.energy-number {
  font-size: clamp(2.45rem, 5vw, 4.2rem);
  font-weight: 650;
  letter-spacing: -0.07em;
  line-height: 0.9;
}

.energy-number small {
  margin-left: 4px;
  color: rgba(255, 255, 255, 0.54);
  font-size: 0.9rem;
  font-weight: 650;
  letter-spacing: 0;
}

.energy-label {
  margin-top: 10px;
  color: rgba(255, 255, 255, 0.5);
  font-size: 0.67rem;
  font-weight: 750;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.live-summary {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-left: clamp(15px, 4vw, 48px);
}

.live-summary .eyebrow {
  margin-bottom: 13px;
}

.live-summary h2 {
  max-width: 490px;
  margin: 0;
  font-size: clamp(1.8rem, 3.5vw, 3rem);
  font-weight: 650;
  letter-spacing: -0.055em;
  line-height: 1.05;
}

.live-summary > p {
  max-width: 480px;
  margin: 16px 0 0;
  color: rgba(255, 255, 255, 0.58);
  font-size: 0.84rem;
  line-height: 1.65;
}

.session-payment-cta {
  margin-top: 24px;
}

.live-stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-top: 30px;
}

.live-stat {
  padding: 15px;
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.055);
}

.live-stat-label {
  display: block;
  margin-bottom: 6px;
  color: rgba(255, 255, 255, 0.43);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.live-stat-value {
  color: white;
  font-size: 1.02rem;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.session-content-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(300px, 0.75fr);
  gap: 22px;
  margin-top: 22px;
  align-items: start;
}

.session-content-card,
.receipt-card {
  padding: 28px;
}

.session-content-card h2,
.receipt-card h2 {
  margin: 0 0 24px;
  font-size: 1.08rem;
  letter-spacing: -0.035em;
}

.session-timeline {
  display: grid;
}

.timeline-item {
  position: relative;
  display: grid;
  grid-template-columns: 31px minmax(0, 1fr);
  gap: 13px;
  min-height: 78px;
}

.timeline-item:not(:last-child)::before {
  position: absolute;
  top: 28px;
  bottom: -2px;
  left: 14px;
  width: 1px;
  content: "";
  background: var(--line);
}

.timeline-marker {
  position: relative;
  z-index: 1;
  display: grid;
  width: 29px;
  height: 29px;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 50%;
  color: var(--muted-light);
  background: white;
}

.timeline-marker svg {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2.2;
}

.timeline-item[data-status="complete"] .timeline-marker {
  color: white;
  border-color: var(--green);
  background: var(--green);
}

.timeline-item[data-status="current"] .timeline-marker {
  color: var(--green);
  border-color: rgba(23, 134, 83, 0.24);
  background: var(--green-soft);
  box-shadow: 0 0 0 5px rgba(23, 134, 83, 0.07);
}

.timeline-copy {
  padding-top: 3px;
}

.timeline-copy strong {
  display: block;
  font-size: 0.81rem;
}

.timeline-copy span {
  display: block;
  margin-top: 4px;
  color: var(--muted);
  font-size: 0.72rem;
  line-height: 1.5;
}

.timeline-item[data-status="upcoming"] .timeline-copy {
  opacity: 0.52;
}

.receipt-list {
  display: grid;
  gap: 14px;
}

.receipt-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  color: var(--muted);
  font-size: 0.78rem;
}

.receipt-row strong {
  color: var(--ink);
  font-weight: 720;
}

.receipt-total {
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}

.receipt-total .receipt-row {
  color: var(--ink);
  font-size: 0.88rem;
  font-weight: 700;
}

.receipt-total .receipt-row strong {
  color: var(--green);
  font-size: 1.5rem;
  letter-spacing: -0.045em;
}

.payment-state-note {
  display: flex;
  gap: 9px;
  margin-top: 22px;
  padding: 13px;
  border-radius: 12px;
  color: var(--muted);
  background: var(--surface-soft);
  font-size: 0.69rem;
  line-height: 1.5;
}

.payment-state-note svg {
  width: 15px;
  height: 15px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.8;
}

.stop-panel {
  margin-top: 18px;
  padding: 20px;
  border: 1px solid #f1cbc7;
  border-radius: 17px;
  background: #fff9f8;
}

.stop-panel h3 {
  margin: 0;
  font-size: 0.85rem;
}

.stop-panel p {
  margin: 7px 0 15px;
  color: var(--muted);
  font-size: 0.72rem;
  line-height: 1.55;
}

.site-footer {
  border-top: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.45);
}

.footer-inner {
  display: flex;
  /* Wraps so the imprint can take a line of its own below everything else.
     `align-content: center` keeps the whole group vertically centred in the
     footer once it does wrap, which the default `stretch` would not. */
  flex-wrap: wrap;
  align-items: center;
  align-content: center;
  gap: 24px;
  width: min(calc(100% - 40px), var(--content-width));
  min-height: 110px;
  margin: 0 auto;
  color: var(--muted);
  font-size: 0.75rem;
}

.brand-footer {
  transform: scale(0.9);
  transform-origin: left center;
}

.brand-mark-small {
  width: 33px;
  height: 33px;
  border-radius: 10px;
}

.brand-mark-small svg {
  width: 18px;
  height: 18px;
}

.footer-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}

/* The imprint link. Styled as plain muted text at the footer's own size, with no
   underline until it is hovered or focused: it must be findable and legible,
   because the law requires it to be directly reachable and identifiable, but it
   must not read as a feature. A boxed button would do that. */
.footer-legal {
  margin: 0;
  flex-basis: 100%;
  color: var(--muted-light);
}

.footer-legal a {
  color: inherit;
  text-decoration: none;
}

.footer-legal a:hover,
.footer-legal a:focus-visible {
  color: var(--ink-soft);
  text-decoration: underline;
}

.toast-region {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 300;
  display: grid;
  width: min(380px, calc(100vw - 32px));
  gap: 10px;
  pointer-events: none;
}

.toast {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr) auto;
  gap: 11px;
  align-items: center;
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: 15px;
  color: white;
  background: var(--dark);
  box-shadow: var(--shadow-lg);
  animation: toast-in 260ms var(--ease) both;
  pointer-events: auto;
}

.toast[data-tone="error"] {
  background: #572622;
}

.toast-icon {
  display: grid;
  width: 34px;
  height: 34px;
  place-items: center;
  border-radius: 10px;
  color: var(--dark);
  background: var(--brand);
}

.toast[data-tone="error"] .toast-icon {
  color: #6b2924;
  background: #ffd4d0;
}

.toast-icon svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2;
}

.toast-copy {
  font-size: 0.77rem;
  font-weight: 650;
  line-height: 1.45;
}

.toast-close {
  display: grid;
  width: 28px;
  height: 28px;
  padding: 0;
  place-items: center;
  border: 0;
  border-radius: 8px;
  color: rgba(255, 255, 255, 0.62);
  background: transparent;
  cursor: pointer;
}

.toast-close:hover {
  color: white;
  background: rgba(255, 255, 255, 0.08);
}

.toast-close svg {
  width: 14px;
  height: 14px;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-width: 2;
}

.modal {
  width: min(460px, calc(100% - 28px));
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.75);
  border-radius: 24px;
  color: var(--ink);
  background: var(--surface);
  box-shadow: 0 32px 100px rgba(5, 24, 15, 0.3);
}

.modal::backdrop {
  background: rgba(8, 25, 17, 0.62);
  backdrop-filter: blur(5px);
}

.modal[open] {
  animation: modal-in 220ms var(--ease) both;
}

.modal form {
  padding: 30px;
}

.modal-icon {
  display: grid;
  width: 52px;
  height: 52px;
  margin-bottom: 24px;
  place-items: center;
  border-radius: 16px;
  color: var(--green);
  background: var(--green-soft);
}

.modal-icon-warm {
  color: var(--red);
  background: var(--red-soft);
}

.modal-copy h2 {
  margin: 0;
  font-size: 1.55rem;
  letter-spacing: -0.045em;
  line-height: 1.1;
}

.modal-copy > p:last-child {
  margin: 12px 0 24px;
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1.65;
}

.field-label {
  display: block;
  margin: 0 0 8px;
  color: var(--ink-soft);
  font-size: 0.72rem;
  font-weight: 750;
}

.password-field {
  position: relative;
}

.password-field input {
  width: 100%;
  height: 50px;
  padding: 0 74px 0 15px;
  border: 1px solid var(--line-strong);
  border-radius: 13px;
  outline: none;
  color: var(--ink);
  background: var(--surface-soft);
  font-size: 0.86rem;
}

.password-field input:focus {
  border-color: var(--green);
  background: white;
  box-shadow: 0 0 0 3px rgba(23, 134, 83, 0.1);
}

.password-field button {
  position: absolute;
  top: 50%;
  right: 8px;
  padding: 6px 8px;
  border: 0;
  border-radius: 8px;
  color: var(--muted);
  background: transparent;
  cursor: pointer;
  font-size: 0.69rem;
  font-weight: 750;
  transform: translateY(-50%);
}

.auth-clear {
  margin-top: 10px;
  padding: 0;
  border: 0;
  color: var(--muted);
  background: transparent;
  cursor: pointer;
  font-size: 0.68rem;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.form-error {
  min-height: 18px;
  margin: 8px 0 0;
  color: var(--red);
  font-size: 0.7rem;
  font-weight: 650;
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 22px;
}

.page-loading {
  padding-top: 30px;
}

.skeleton {
  position: relative;
  overflow: hidden;
  border-radius: 12px;
  background: #e4e9e1;
}

.skeleton::after {
  position: absolute;
  inset: 0;
  content: "";
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.6), transparent);
  transform: translateX(-100%);
  animation: shimmer 1.5s infinite;
}

.skeleton-title {
  width: min(560px, 78%);
  height: 64px;
  margin: 70px auto 25px;
}

.skeleton-copy {
  width: min(720px, 92%);
  height: 20px;
  margin: 0 auto 65px;
}

.skeleton-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 18px;
}

.skeleton-card {
  height: 280px;
  border-radius: var(--radius-md);
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

@keyframes connection-pulse {
  0% { box-shadow: 0 0 0 0 rgba(23, 134, 83, 0.35); }
  70% { box-shadow: 0 0 0 7px rgba(23, 134, 83, 0); }
  100% { box-shadow: 0 0 0 0 rgba(23, 134, 83, 0); }
}

@keyframes status-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.45; transform: scale(0.8); }
}

@keyframes soft-blink {
  50% { opacity: 0.35; }
}

@keyframes shimmer {
  to { transform: translateX(100%); }
}

@keyframes toast-in {
  from { opacity: 0; transform: translateY(12px) scale(0.97); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes modal-in {
  from { opacity: 0; transform: translateY(10px) scale(0.97); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}

@media (max-width: 1020px) {
  .hero-dashboard {
    grid-template-columns: 1fr;
  }

  .hero-copy {
    padding-right: 0;
  }

  .network-visual {
    display: none;
  }

  .station-grid,
  .loading-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .station-detail-grid {
    grid-template-columns: minmax(0, 1fr) 340px;
  }

  .station-facts {
    grid-template-columns: 1fr;
  }

  .how-section {
    grid-template-columns: 1fr;
    gap: 38px;
  }
}

@media (max-width: 820px) {
  .header-inner,
  .app-shell,
  .footer-inner {
    width: min(calc(100% - 28px), var(--content-width));
  }

  .legal-shell {
    width: min(calc(100% - 28px), 760px);
    padding: 34px 0 56px;
  }

  .desktop-nav {
    display: none;
  }

  .app-shell {
    padding-top: 22px;
  }

  .hero-dashboard {
    min-height: auto;
    padding: 44px 34px;
    border-radius: 27px;
  }

  .display-title {
    font-size: clamp(2.7rem, 10vw, 4.3rem);
  }

  .metrics-strip {
    grid-template-columns: repeat(2, 1fr);
  }

  .metric:nth-child(2)::after {
    display: none;
  }

  .metric:nth-child(-n + 2) {
    border-bottom: 1px solid var(--line);
  }

  .station-toolbar {
    align-items: stretch;
    flex-direction: column;
  }

  .search-field {
    width: 100%;
  }

  .filter-group {
    overflow-x: auto;
  }

  .filter-button {
    flex: 1 0 auto;
  }

  .station-detail-grid,
  .detail-bottom-grid,
  .session-content-grid {
    grid-template-columns: 1fr;
  }

  .booking-card {
    position: static;
  }

  .station-profile {
    min-height: auto;
  }

  .station-facts {
    grid-template-columns: repeat(3, 1fr);
  }

  .how-section {
    padding: 42px 34px;
  }

  .live-energy-card {
    grid-template-columns: 1fr;
  }

  .energy-orb-wrap {
    margin-bottom: 34px;
  }

  .live-summary {
    padding-left: 0;
    text-align: center;
  }

  .live-summary .eyebrow,
  .live-summary > p {
    margin-right: auto;
    margin-left: auto;
  }
}

@media (max-width: 620px) {
  .header-inner {
    min-height: 68px;
  }

  .brand-mark {
    width: 35px;
    height: 35px;
    border-radius: 11px;
  }

  .brand-name {
    font-size: 1.05rem;
  }

  .connection-pill {
    width: 36px;
    min-height: 36px;
    padding: 0;
    justify-content: center;
  }

  /* The header has no room for two words on a phone. Keep the switcher but let
     it collapse to a compact control rather than pushing the brand off-screen.
     The endonym is not translated and is short, so it stays legible. */
  .lang-switch a {
    padding: 5px 6px;
    font-size: 0.72rem;
  }

  /* The flag is the recognisable part once space is tight, so it stays at full
     size and the name tightens instead. */
  .lang-flag svg {
    width: 20px;
    height: 13px;
  }

  .connection-pill #connection-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
  }

  .icon-button {
    width: 36px;
    height: 36px;
  }

  /* Collapse to the icon, as the connection pill's label does in this same
     breakpoint. The word stays available to assistive technology, so the control
     keeps an accessible name rather than becoming an unlabelled glyph. */
  .account-entry {
    width: 36px;
    height: 36px;
    padding: 0;
    justify-content: center;
  }

  .account-entry .account-entry-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
  }

  .connection-banner {
    align-items: flex-start;
    flex-wrap: wrap;
    padding: 10px 14px;
    font-size: 0.75rem;
  }

  .app-shell {
    width: min(calc(100% - 20px), var(--content-width));
    padding-bottom: 65px;
  }

  .hero-dashboard {
    padding: 36px 24px;
    border-radius: 24px;
  }

  .display-title {
    font-size: clamp(2.55rem, 14vw, 3.7rem);
  }

  .hero-description {
    font-size: 0.95rem;
    line-height: 1.65;
  }

  .hero-actions {
    width: 100%;
  }

  .hero-actions .button {
    width: 100%;
  }

  .trust-row {
    display: grid;
    gap: 11px;
  }

  .metrics-strip {
    margin-top: 12px;
  }

  .metric {
    min-height: 78px;
    padding: 15px;
  }

  .metric-icon {
    width: 34px;
    height: 34px;
  }

  .metric-value {
    font-size: 1.18rem;
  }

  .metric-label {
    font-size: 0.66rem;
  }

  .stations-section {
    padding-top: 58px;
  }

  .section-heading {
    align-items: flex-start;
    flex-direction: column;
    gap: 15px;
  }

  .station-grid,
  .loading-grid {
    grid-template-columns: 1fr;
  }

  .station-card {
    min-height: 252px;
  }

  .how-section {
    margin-top: 64px;
    padding: 34px 24px;
    border-radius: 25px;
  }

  .steps-grid {
    grid-template-columns: 1fr;
  }

  .step-card {
    min-height: auto;
  }

  .step-card h3 {
    margin-top: 24px;
  }

  .station-profile,
  .booking-card,
  .info-card,
  .session-content-card,
  .receipt-card {
    padding: 22px;
    border-radius: 21px;
  }

  .station-profile-top,
  .session-heading {
    align-items: flex-start;
    flex-direction: column;
  }

  .station-profile-identity {
    align-items: flex-start;
  }

  .station-emblem {
    width: 60px;
    height: 60px;
    border-radius: 19px;
  }

  .station-emblem svg {
    width: 30px;
    height: 30px;
  }

  .station-profile h1 {
    font-size: 2.2rem;
  }

  .station-facts {
    grid-template-columns: 1fr;
  }

  .session-banner {
    align-items: flex-start;
    flex-wrap: wrap;
  }

  .session-banner .button {
    width: 100%;
    margin-left: 0;
  }

  .booking-head {
    gap: 12px;
  }

  .live-energy-card {
    min-height: auto;
    padding: 32px 20px;
    border-radius: 25px;
  }

  .energy-orb {
    width: min(220px, 72vw);
  }

  .live-stats {
    grid-template-columns: 1fr;
  }

  .session-payment-cta {
    width: 100%;
  }

  .live-stat {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .live-stat-label {
    margin: 0;
  }

  .session-reference {
    display: none;
  }

  .footer-inner {
    align-items: flex-start;
    flex-direction: column;
    gap: 10px;
    padding: 26px 0;
  }

  .footer-meta {
    align-items: flex-start;
    flex-direction: column;
    gap: 4px;
    margin: 8px 0 0;
  }

  .footer-meta span[aria-hidden] {
    display: none;
  }

  /* Column layout, so the imprint no longer needs a line of its own -- and must
     not keep the basis that gives it one, which in a column flex container would
     be its height. */
  .footer-legal {
    flex-basis: auto;
    margin-top: 4px;
  }

  .toast-region {
    right: 16px;
    bottom: 16px;
  }

  .modal form {
    padding: 24px;
  }

  .modal-actions {
    flex-direction: column-reverse;
  }

  .modal-actions .button {
    width: 100%;
  }
}

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

  *,
  *::before,
  *::after {
    scroll-behavior: auto !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

@media (forced-colors: active) {
  .station-card,
  .button,
  .icon-button,
  .connection-pill,
  .status-badge {
    border: 1px solid CanvasText;
  }

  .state-dot,
  .connection-dot,
  .status-badge-dot {
    background: CanvasText;
  }
}

/* ---------------------------------------------------------------------------
   End-user account area.

   Appended rather than kept in a second stylesheet, and built from the same
   custom properties as the rest of the file. The account pages previously
   carried an inline <style> block with its own palette, its own font stack and
   its own near-black header, which made the sign-in page look like a different
   product from the landing page. Every value below is a token from :root above
   or a size already used here, so the two surfaces cannot drift apart again.
   --------------------------------------------------------------------------- */

.account-shell {
  width: min(calc(100% - 40px), 760px);
  margin: 0 auto;
  padding: 46px 0 72px;
}

.account-shell:focus {
  outline: none;
}

/* The imprint. A single readable column at the account pages' width rather than
   the landing page's full width: this is reference material somebody has been
   sent to, not a page to be scanned, and the narrow measure is what makes an
   address block legible. */
.legal-shell {
  width: min(calc(100% - 40px), 760px);
  margin: 0 auto;
  padding: 46px 0 72px;
  color: var(--ink-soft);
}

.legal-shell:focus {
  outline: none;
}

/* The operator's own snippet decides its own headings and blocks. These rules
   only set the baseline -- size, rhythm, and the one thing a legal page must
   never have is a heading that competes with the page's own -- and are low
   specificity on purpose, so an operator can override any of them from
   IMPRINT_HTML without having to out-specify the stylesheet. */
.legal-shell > :first-child {
  margin-top: 0;
}

.legal-shell h1 {
  margin: 0 0 6px;
  color: var(--ink);
  font-size: 1.9rem;
  line-height: 1.2;
  letter-spacing: -0.015em;
}

.legal-shell h2,
.legal-shell h3 {
  margin: 30px 0 8px;
  color: var(--ink);
  font-size: 1rem;
  letter-spacing: -0.005em;
}

.legal-shell p,
.legal-shell ul,
.legal-shell ol {
  margin: 0 0 14px;
  line-height: 1.65;
}

.legal-shell ul,
.legal-shell ol {
  padding-left: 1.25rem;
}

.legal-shell a {
  color: var(--green);
  text-decoration: underline;
  overflow-wrap: anywhere;
}

/* A postal address is the one field an imprint cannot do without, and it is the
   one most often written as a single run-on line. Wrapping it here is
   insurance, not styling: if the operator writes <address> with line breaks the
   browser preserves them, and if they do not, this still stops a long street
   name from running off the edge. */
.legal-shell address {
  font-style: normal;
}

/* A two-column grid on wide screens so the primary card and the secondary
   invitation sit side by side, collapsing to one column below 900px where the
   aside would otherwise be squeezed. */
.account-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.62fr);
  gap: 18px;
  align-items: start;
}

.account-card {
  padding: 30px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.92);
  box-shadow: var(--shadow-sm);
}

.account-card-narrow {
  grid-column: 1 / -1;
  max-width: 560px;
}

.account-card-wide {
  grid-column: 1 / -1;
}

.account-card-aside {
  background: var(--surface-tint);
  box-shadow: none;
}

.account-title {
  margin: 4px 0 8px;
  font-size: clamp(1.7rem, 4vw, 2.2rem);
  letter-spacing: -0.045em;
  line-height: 1.1;
}

.account-subtitle {
  margin: 0 0 8px;
  font-size: 1.15rem;
  letter-spacing: -0.03em;
}

.account-lede {
  margin: 0 0 22px;
  color: var(--muted);
  font-size: 0.86rem;
  line-height: 1.65;
}

.account-hint {
  margin: 8px 0 0;
  color: var(--muted);
  font-size: 0.76rem;
  line-height: 1.6;
}

/* The registration form's note about what an account stores in the browser.

   Its own class rather than another .account-hint: that one is 0.76rem, which is
   right for a password-policy aside and too small for the sentence someone is
   meant to read before they agree to something. Neutral rather than amber,
   because it is not a warning -- it is the ordinary cost of an account. */
.account-storage-note {
  margin: 18px 0 0;
  padding: 12px 15px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  color: var(--muted);
  background: var(--surface-soft);
  font-size: 0.8rem;
  line-height: 1.65;
}

/* The account forms' inputs, sized to match .password-field so a password and a
   plain text field in the same form are the same size.

   Scoped to .account-card, and deliberately not merged into the bare
   `.password-field input` rule above. That selector is shared with the landing
   page's own access-token dialogs, where the 74px of right padding reserves room
   for the Show button; overriding it here removed that space and put the button
   on top of the typed token. Same specificity, later in the file, so it won
   silently and no test noticed. */
.account-card .account-input,
.account-card .password-field input {
  width: 100%;
  height: 50px;
  padding: 0 15px;
  border: 1px solid var(--line-strong);
  border-radius: 13px;
  outline: none;
  color: var(--ink);
  background: var(--surface-soft);
  font: inherit;
  font-size: 0.86rem;
}

.account-card .account-input:focus,
.account-card .password-field input:focus {
  border-color: var(--green);
  background: white;
  box-shadow: 0 0 0 3px rgba(23, 134, 83, 0.1);
}

.account-input + .field-label,
/* Stacking space between account form fields. Scoped to .account-card on
   purpose: .field-label is also used by the landing page's own sign-in and
   session-access dialogs, and a bare `.field-label` rule here shifted those
   dialogs' labels down by 18px. `.field-label` alone is also unnecessary here --
   the label sits directly above its input, so there is nothing to push it away
   from; only the gap after a field needs opening up. */
.account-card .field-label + .account-input,
.account-card .field-label + .password-field,
.account-card .password-field + .field-label,
.account-card .account-input + .field-label {
  margin-top: 18px;
}

.account-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 24px;
}

.account-actions .button {
  min-width: 150px;
}

/* The one place a submit button is left-aligned rather than right-aligned: a
   single primary action reads better hugging its label. */
.account-card form > .account-actions:last-child {
  justify-content: flex-start;
}

.account-alert {
  margin: 0 0 20px;
  padding: 11px 14px;
  border: 1px solid;
  border-radius: 12px;
  font-size: 0.78rem;
  font-weight: 600;
  line-height: 1.55;
}

.account-alert-error {
  border-color: rgba(200, 71, 63, 0.35);
  color: var(--red);
  background: var(--red-soft);
}

.account-alert-warn {
  border-color: rgba(217, 133, 24, 0.35);
  color: #8a5a08;
  background: var(--amber-soft);
}

.account-notice {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-bottom: 20px;
  padding: 13px 16px;
  border: 1px solid rgba(217, 133, 24, 0.35);
  border-radius: var(--radius-sm);
  color: #8a5a08;
  background: var(--amber-soft);
  font-size: 0.8rem;
  font-weight: 600;
}

.account-notice form {
  margin-left: auto;
}

.account-chip {
  display: inline-flex;
  align-items: center;
  padding: 5px 12px;
  border-radius: 999px;
  color: var(--green);
  background: var(--green-soft);
  font-size: 0.74rem;
  font-weight: 750;
}

.account-details {
  margin: 0;
}

.account-row {
  display: flex;
  gap: 14px;
  align-items: baseline;
  justify-content: space-between;
  padding: 11px 0;
  border-bottom: 1px solid var(--line);
}

.account-row:last-child {
  border-bottom: 0;
}

.account-row dt {
  color: var(--muted);
  font-size: 0.79rem;
}

.account-row dd {
  margin: 0;
  font-size: 0.86rem;
  font-weight: 700;
  text-align: right;
  word-break: break-all;
}

/* The badge leads the value, so it must not be squeezed by a long address and
   must keep its own space from it. `word-break` above is deliberately not
   repeated here: the address wraps, the badge does not. */
.account-row-email {
  display: flex;
  flex-wrap: wrap-reverse;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

.account-row-email .account-pill {
  flex: 0 0 auto;
}

.account-row-value {
  word-break: break-all;
}

.account-pill {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 750;
}

.account-pill-good {
  color: var(--green);
  background: var(--green-soft);
}

.account-pill-bad {
  color: var(--red);
  background: var(--red-soft);
}

/* The TOTP secret has to be read character by character and typed into another
   app, so it is selectable and breaks anywhere rather than overflowing. */
/* A block box, and stated as one: padding, a dashed border and a background all
   assume a block. Applied to an inline element the padding does not grow the
   line box, so the border and tint are painted over the neighbouring lines and
   the text overlaps. `display: block` makes the class behave the same wherever
   it is used, so a future inline usage looks like the rest of the page instead
   of breaking. */
.account-secret {
  display: block;
  margin: 10px 0;
  padding: 13px 15px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--surface-soft);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.86rem;
  user-select: all;
  word-break: break-all;
}

.account-reveal {
  position: absolute;
  top: 50%;
  right: 8px;
  padding: 6px 8px;
  border: 0;
  border-radius: 8px;
  color: var(--muted);
  background: transparent;
  cursor: pointer;
  font: inherit;
  font-size: 0.69rem;
  font-weight: 750;
  transform: translateY(-50%);
}

.account-reveal:hover {
  color: var(--ink-soft);
}

/* A form inside a flex row must not stretch; the notice relies on this. */
.account-notice form {
  display: inline;
}

.inline {
  display: inline;
}

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

  .account-card {
    padding: 24px;
  }
}

@media (max-width: 820px) {
  .account-shell {
    width: min(calc(100% - 28px), 760px);
    padding-top: 26px;
  }
}

/* Four controls in the header actions -- language switch, account entry,
   connection pill, access settings -- do not fit the 320px minimum width the
   page supports. The brand alone takes roughly 130px of it, so at this width the
   switch drops its endonyms and keeps only the flags, and the gaps tighten.

   The flags carry the recognition and the title attribute carries the name, so
   nothing is lost to a screen reader or a long-press; this is the same trade the
   620px breakpoint already makes for the connection pill's label. Below roughly
   430px there is no honest way to keep four controls and a wordmark side by
   side, and dropping the endonym is the least of it. */
@media (max-width: 430px) {
  .header-actions {
    gap: 6px;
  }

  .lang-name {
    display: none;
  }

  .lang-switch a {
    padding: 5px;
  }
}

@media (max-width: 620px) {
  .account-actions {
    flex-direction: column;
  }

  .account-actions .button {
    width: 100%;
  }

  .account-row {
    flex-direction: column;
    gap: 2px;
  }

  .account-row dd {
    text-align: left;
  }

  /* Stacked, so the badge follows the address down instead of sitting to its
     left, and both align with the label. */
  .account-row-email {
    justify-content: flex-start;
  }
}

.account-input-invalid {
  border-color: var(--red);
  background: var(--red-soft);
}

.account-input-invalid:focus {
  border-color: var(--red);
  box-shadow: 0 0 0 3px rgba(200, 71, 63, 0.12);
}

.account-field-error {
  margin: 7px 0 0;
  color: var(--red);
  font-size: 0.74rem;
  font-weight: 650;
  line-height: 1.5;
}

/* 2FA enrolment: the code and the instructions side by side, collapsing to one
   column on narrow screens so the code stays large enough to scan. */
.totp-enrol {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 24px;
  align-items: start;
  margin: 0 0 26px;
  padding: 20px;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface-soft);
}

/* White padding around the code is part of scanning, not decoration: the quiet
   zone stops a camera from locking onto the surrounding text. */
.totp-qr-frame {
  padding: 10px;
  border-radius: var(--radius-sm);
  background: #fff;
  box-shadow: var(--shadow-sm);
  line-height: 0;
}

.totp-qr-frame svg {
  display: block;
  /* Sized here rather than left at the SVG's intrinsic size. The drawing carries
     a viewBox, so this scales the whole symbol down instead of clipping it, and
     the module count differs per account (a long account name needs a larger QR
     version), so no intrinsic size would fit them all. */
  width: 172px;
  height: 172px;
}

.totp-steps {
  margin: 0 0 4px;
  padding-left: 20px;
  color: var(--ink-soft);
  font-size: 0.82rem;
  line-height: 1.75;
}

.totp-manual {
  margin-top: 14px;
  font-size: 0.8rem;
}

.totp-manual summary {
  color: var(--green);
  cursor: pointer;
  font-weight: 700;
}

.totp-manual summary:hover {
  color: var(--brand-deep);
}

.totp-manual[open] summary {
  margin-bottom: 8px;
}

@media (max-width: 620px) {
  .totp-enrol {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    text-align: center;
  }

  .totp-steps {
    padding-left: 18px;
    text-align: left;
  }
}

/* The "start over" link under the code screen: quiet by design, because it is
   an escape hatch rather than the primary action. */
.account-actions-quiet {
  justify-content: flex-start;
  margin-top: 12px;
}

/* The credential links on the account page: a column of equal buttons rather
   than a row, because they are a list of things to go and do, not a set of
   choices between them. */
.account-actions-stacked {
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
}

.account-actions-stacked .button {
  width: 100%;
}

/* A warning-toned pill, for an address requested but not yet confirmed. Same
   shape as the good and bad variants so the row does not reflow per state. */
.account-pill-warn {
  color: #8a5a08;
  background: var(--amber-soft);
}

/* The balance, set larger than anything else on the account pages: it is the
   one number the page exists to communicate. */
.account-balance {
  margin: 2px 0 6px;
  font-size: clamp(2.4rem, 7vw, 3.4rem);
  font-weight: 800;
  letter-spacing: -0.05em;
  line-height: 1;
}

.account-alert-success {
  border-color: rgba(23, 134, 83, 0.32);
  color: var(--green);
  background: var(--green-soft);
}

/* Radio choices, stacked with the label beside the control rather than above
   it, so a two-option decision reads as one row. */
.account-choice {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.account-radio {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 11px 14px;
  border: 1px solid var(--line-strong);
  border-radius: 13px;
  background: var(--surface-soft);
  cursor: pointer;
  font-size: 0.82rem;
  font-weight: 700;
}

.account-radio:focus-within {
  border-color: var(--green);
  box-shadow: 0 0 0 3px rgba(23, 134, 83, 0.1);
}

.account-amounts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
  margin-top: 18px;
}

@media (max-width: 620px) {
  .account-amounts {
    grid-template-columns: minmax(0, 1fr);
  }

  /* The three amounts stay in one row on a phone as well. They are the point of
     the section, and stacking them would bury the recommended one again. Only
     the gap and the button padding shrink. */
  .account-amount-choices {
    gap: 6px;
  }

  .account-amount-choice .button {
    padding: 0 4px;
    font-size: 0.82rem;
  }

  .account-amount-tag {
    font-size: 0.62rem;
  }
}

/* Amount choices for a manual top-up. The recommended one is larger and filled,
   so the common case reads as the obvious default without the alternatives
   having to be hidden. */
/* Three equal columns rather than a wrapping flex row: a flex row broke onto
   separate lines as soon as the card got narrow, which left the recommended
   amount alone on a row instead of between the two others. A grid cannot wrap by
   accident, so the recommended amount is always the middle of three. */
.account-amount-choices {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  margin-top: 18px;
}

.account-amount-choice {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  margin: 0;
}

/* Full width of the cell, so all three read as one row of equals. */
.account-amount-choice .button {
  width: 100%;
  padding: 0 10px;
}

/* Sits under its own button and is centred against it. The form is a column
   flex with `align-items: stretch`, so the tag filled the cell and its text
   aligned to the left edge -- which put "Recommended" out under the gutter
   rather than under the button it describes. */
.account-amount-tag {
  color: var(--green);
  font-size: 0.68rem;
  font-weight: 750;
  text-align: center;
}

/* The nudge sits on its own so it reads as an invitation rather than part of
   the settings form below it. */
.account-nudge {
  border-color: rgba(23, 134, 83, 0.28);
  background: var(--green-soft);
}

/* Signed-in browsers on the account page.

   A row has to be scannable at a glance, because the decision it supports is
   "is that mine?". So the device name is the largest thing on the row, the
   identifier and timestamp are small and grey beneath it, and the button is at a
   fixed width on the right so the rows line up and can be compared vertically
   rather than read one at a time. */
.session-list {
  list-style: none;
  margin: 16px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.session-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.04);
}

.session-main {
  min-width: 0;
}

.session-title {
  margin: 0;
  font-weight: 650;
  font-size: 0.92rem;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* The row for the browser reading this page. Marked rather than moved, so it is
   still in date order like the others -- being first would be a lie about when
   it was started. */
.session-row-current {
  border-color: var(--green);
  background: rgba(52, 168, 83, 0.08);
}

.session-row form {
  margin: 0;
  flex: none;
}

/* Same width as every other row's button, so the column of them reads as one
   column. */
.session-row .button {
  min-width: 120px;
  padding: 0 16px;
}

/* On a phone the row stacks: the device name needs the full width, and a button
   squeezed beside it would truncate the name it belongs to. */
@media (max-width: 620px) {
  .session-row {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
  }

  .session-row .button {
    width: 100%;
  }
}

/* The start button and, for an account with a balance, the line beneath it.

   The button carries the whole cost obligation in its own label -- required by
   SS 312j(3) BGB, and nothing else may share the button -- so the supporting
   detail goes below it rather than into it. */
.booking-action-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.account-balance-note {
  margin: 0;
  text-align: center;
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.5;
}

/* The optional name for a session started from the app.

   Above the start button, so it is read as part of setting the charge up rather
   than as something to remember afterwards. It is small and quiet on purpose: the
   feature is decoration on a charge that works without it, and anything heavier
   would be the one part of starting a charge that has to be dealt with. The hint
   is the same size as the label rather than smaller, because a field nobody
   understands is worse than no field. */
.booking-label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 4px;
}

.booking-label-text {
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.5;
}

.booking-label-input {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  outline: none;
  color: var(--ink);
  background: var(--surface-soft);
  font: inherit;
  font-size: 0.86rem;
}

/* The same ring the search field and the account inputs use, so the focus state
   is the one the rest of the site already teaches. */
.booking-label-input:focus {
  border-color: var(--green);
  background: white;
  box-shadow: 0 0 0 3px rgba(23, 134, 83, 0.08);
}

.booking-label-hint {
  margin: 0;
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.5;
}

/* Charging history, on the account page and on its own page.

   A row answers three questions in one glance: when, where and how much. The
   date is its own column so the rows line up and can be scanned down the left
   edge, the description takes the middle, and the amount is right-aligned so the
   figures can be compared without reading them. */
.history-list {
  list-style: none;
  margin: 16px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.history-row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.04);
}

.history-when {
  flex: none;
  width: 92px;
}

.history-date {
  margin: 0;
  font-weight: 650;
  font-size: 0.92rem;
  /* Tabular figures so a column of dates does not shimmer as the minutes tick. */
  font-variant-numeric: tabular-nums;
}

.history-what {
  flex: 1 1 auto;
  min-width: 0;
}

.history-title {
  margin: 0;
  font-weight: 600;
  font-size: 0.92rem;
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* The reference is what an operator asks for, so it is visible rather than
   somewhere to be found. */
.history-reference {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.74rem;
  font-weight: 500;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 1px 6px;
}

.history-amount {
  flex: none;
  font-weight: 650;
  font-size: 0.95rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Narrow: the amount moves under the description rather than being squeezed
   into a column too small to read. */
@media (max-width: 620px) {
  .history-row {
    flex-wrap: wrap;
    gap: 10px;
  }

  .history-when {
    width: auto;
  }

  .history-what {
    flex-basis: 100%;
    order: 3;
  }

  .history-amount {
    margin-left: auto;
  }
}

/* A history row that links to the session's status page.

   An anchor styled as a title rather than a link with a title inside it: a row is
   clickable as a whole thing on a phone, and an underlined blue word in the middle
   of a table is the wrong shape for that. The underline comes back on hover so the
   affordance is still visible on a pointer device. */
.history-link {
  color: inherit;
  text-decoration: none;
}

.history-link:hover {
  text-decoration: underline;
}

/* A charge that is running now, as opposed to one that happened. Marked rather
   than separated: the same person reads both lists on the same screen, and a
   running charge should be the thing the eye lands on first. */
.history-row-active {
  border-color: var(--green);
  background: rgba(52, 168, 83, 0.08);
}

.history-state {
  white-space: nowrap;
}

/* NFC tags on the account page.

   An offer -- a tag seen on one of this account's charges -- is visually a call to
   action, so it gets the green edge and a form in the row. A tag already added is
   a list entry with a rename field and a remove button. */
.tag-list {
  list-style: none;
  margin: 16px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.tag-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.04);
}

.tag-row-offer {
  border-color: var(--green);
  background: rgba(52, 168, 83, 0.08);
  align-items: center;
}

.tag-main {
  min-width: 0;
  flex: 1 1 auto;
}

.tag-name {
  margin: 0;
  font-weight: 650;
  font-size: 0.95rem;
}

.tag-form,
.tag-rename {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 8px 0 0;
  flex-wrap: wrap;
}

.tag-form {
  margin: 0;
  flex: none;
  max-width: 380px;
}

.tag-form .field-label,
.tag-rename .field-label {
  flex: none;
  margin: 0;
  width: auto;
}

.tag-form .account-input,
.tag-rename .account-input {
  flex: 1 1 160px;
  min-width: 0;
  margin: 0;
}

@media (max-width: 620px) {
  .tag-row {
    flex-direction: column;
    align-items: stretch;
  }

  .tag-form {
    max-width: none;
  }
}

/* The add-by-id form on the tags page. Fields stacked with their labels above,
   the same shape as every other account form. */
.account-fields {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-width: 420px;
}

.account-fields .account-input {
  margin: 0 0 10px;
}

/* Which car a charge was started with. Carries the tag's name rather than the
   word "tag", because the name is what connects the row to a car in the list on
   the tags page. Same neutral pill as the other metadata, so it reads as a label
   and not as a state. */
.account-pill-tag {
  color: var(--ink-dim);
  background: rgba(255, 255, 255, 0.09);
  border: 1px solid var(--line);
}

/* Account sub-navigation.

   A row of tabs under the site header, above the content, present on every
   account page. Understated on purpose: it is the answer to "where do I go", so it
   has to be findable without competing with what the page is actually saying.

   Full width rather than hugging its content, because a tab row that stops
   halfway across the panel reads as a set of links rather than as navigation. The
   active tab is marked with a rule underneath as well as by colour, so the
   current page is still identifiable in monochrome. */
.account-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 0 0 22px;
  padding: 0 0 10px;
  border-bottom: 1px solid var(--line);
}

.account-nav-link {
  padding: 9px 14px;
  border-radius: 10px;
  color: var(--ink-dim);
  font-size: 0.88rem;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color 0.15s ease, color 0.15s ease;
}

.account-nav-link:hover {
  background: rgba(255, 255, 255, 0.6);
  color: var(--ink);
}

.account-nav-link[aria-current="page"] {
  color: var(--green);
  background: rgba(52, 168, 83, 0.1);
}

/* A two-column list of label/value facts: top-up mode and card on file.

   Used instead of the account details table for things that are states rather than
   profile fields. The label is small and quiet, the value carries the pill, because
   the value is what the reader is checking for. */
.account-facts {
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.account-fact {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: 11px;
  background: rgba(255, 255, 255, 0.05);
}

.account-fact-label {
  color: var(--ink-dim);
  font-size: 0.82rem;
}

.account-fact-value {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.86rem;
  text-align: right;
}

/* The tags list uses the same rows without the label column, so a tag name and its
   state read as one item rather than as a table row with an empty left-hand side.
   A class rather than `:has()`: the layout should not depend on selector support
   to line up. */
.account-facts-tags .account-fact {
  justify-content: flex-start;
  text-align: left;
}

.account-facts-tags .account-fact-value {
  gap: 10px;
  flex-wrap: wrap;
}

.account-status {
  margin: 12px 0 16px;
}

/* The running-charges list on the dashboard. Same rows as the history list, so a
   charge reads the same whether it is on screen now or already finished. */
.history-list-active {
  margin-top: 12px;
}

/* The dashboard's one-line security summary.

   Not a collapsed panel: the two facts are visible without a click, and only the
   management is one link away. Collapsing it would hide the thing the reader came
   to check. */
.account-card-summary {
  padding: 20px 30px;
}

.account-summary-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 18px;
}

.account-summary-fact {
  display: flex;
  align-items: center;
  gap: 9px;
}

.account-summary-value {
  display: flex;
  align-items: center;
  gap: 8px;
}

.account-summary-label {
  color: var(--ink-dim);
  font-size: 0.85rem;
}

.account-summary-row .account-actions {
  margin: 0 0 0 auto;
}

@media (max-width: 620px) {
  .account-nav {
    gap: 2px;
  }

  .account-nav-link {
    padding: 8px 10px;
    font-size: 0.84rem;
  }

  .account-summary-row .account-actions {
    margin: 4px 0 0;
    width: 100%;
  }
}

/* The row that captures a tag id the account service never saw.

   Beside the rename form rather than in place of it: the tag is bound and working
   either way, so this is a completion, not a repair, and it should not look like
   something went wrong. */
.tag-remember {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 8px 0 0;
  flex-wrap: wrap;
}

.tag-remember .field-label {
  flex: none;
  margin: 0;
  width: auto;
}

.tag-remember .account-input {
  flex: 1 1 180px;
  min-width: 0;
  margin: 0;
}

/* A tag identifier, set in a monospaced face.

   The one number on this page that is read character by character against a
   sticker -- `04:A1:B2:C3`, with the colons -- and a proportional face makes
   `0` and `O` and `1` and `l` too easy to confuse when comparing two of them. */
.tag-hex {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.84rem;
  letter-spacing: 0.02em;
}

/* A settings row: what the setting is, beside what to do about it.

   The security page is three small unrelated settings, so it deliberately does not
   use `.account-layout`. That grid's second column is 0.62fr of a 760px shell --
   about 223px of content width -- and `.account-actions .button` sets a 150px
   minimum, so a row of two buttons needs 310px and overflows it. It also put the
   two-factor card in the *wide* column, where a heading, a line of prose and one
   password field spent 440px on nothing.

   So the page is one column, and each setting is a row sized by its own content.
   The right column takes only what it needs: `auto` rather than a share, so a
   one-button setting does not reserve room for two. */
.account-settings {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.account-setting {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 18px 28px;
  align-items: start;
}

.account-setting-what {
  min-width: 0;
}

.account-setting-do {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
  /* Enough for the widest thing in it -- the password field -- without reserving
     that width for the settings that only hold a button. */
  max-width: 320px;
}

/* A button here is sized by its label, not by the 150px floor the page-level action
   rows use. That minimum is right for a lone primary action and wrong for a pair of
   links that only need to fit. */
.account-setting-do .button {
  min-width: 0;
}

.account-setting-do .account-actions {
  margin: 0;
  width: 100%;
}

.account-setting-do .account-actions {
  flex-wrap: wrap;
}

/* Turning the second factor off: password above, button below, both right-aligned
   against the status pill. A row would need 150px plus a 220px field, which is the
   width this column does not have. */
.account-setting-form {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  width: 100%;
}

.account-setting-form .field-label {
  margin: 0;
}

.account-setting-form .button {
  margin-top: 4px;
}

.account-setting .account-details {
  margin-top: 12px;
  max-width: 460px;
}

@media (max-width: 760px) {
  .account-setting {
    grid-template-columns: minmax(0, 1fr);
  }

  .account-setting-do {
    align-items: stretch;
    max-width: none;
  }
}

/* Money authorised but not yet here.

   Its own list above the amount buttons rather than a row in the ledger: the
   question a holder asks after pressing a top-up button is "where has my money
   gone", and it is asked immediately. Making them scroll to a table to find out
   that nothing is wrong is the failure this placement avoids.

   Amber rather than green on purpose. It is not an error and not a success --
   it is money in transit, and a green tick would claim it has arrived. */
.account-pending {
  list-style: none;
  margin: 16px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.account-pending-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--amber, #c98a12);
  border-radius: 11px;
  background: rgba(201, 138, 18, 0.07);
}

.account-pending-amount {
  margin: 0;
  font-weight: 700;
  font-size: 0.95rem;
}

/* The amount buttons now carry which method pays and whether it is slow, so each
   button is a wider block than a bare figure. Two columns at a comfortable width,
   one on a phone. */
.account-amount-choice {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
}

.account-amount-tag {
  color: var(--ink-dim);
  font-size: 0.74rem;
  line-height: 1.25;
  text-align: center;
}

.account-amount-tag-slow {
  color: #8a5a08;
  font-weight: 600;
}

.account-amount-tag-recommended {
  color: var(--green);
  font-weight: 600;
}

/* One amount grid per payment method, with the method named above it.

   The grid itself is three equal columns because the amounts are a set of
   alternatives; the group heading is what stops a slow method's row reading as
   though it were a set of slow methods, and what tells a holder which row they are
   about to pay with before they press anything.

   The vertical rule between groups does the work of a heading repeat: with two
   methods the second one is below the fold of a phone, and a list of grids with no
   separator reads as one long grid with four rows of odd sizes. */
.account-amount-group {
  margin-top: 18px;
}

.account-amount-group + .account-amount-group {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

.account-amount-group-label {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
  color: var(--ink-dim);
  font-size: 0.84rem;
  font-weight: 600;
}

.account-amount-group .account-amount-choices {
  margin-top: 0;
}

/* The payment-method list.

   Each method is a row with its state, its name, and what can be done to it. The
   actions sit on their own line under the name on a narrow screen, because a
   three-across row of "Connected | Card ending 4242 | Remove | Use by default"
   wraps into something unreadable at the width a phone actually has.

   Not colour alone anywhere here: the default marker and the slow-method marker
   both carry words, so the row is fully described without relying on either. */
/* A little more room between methods than between the rows of a details list, so
   two cards in a row read as two separate instruments rather than as one entry
   repeated. */
.account-methods .account-row + .account-row {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

.account-method {
  flex-wrap: wrap;
}

.account-method-actions {
  display: flex;
  gap: 8px;
  margin: 0;
  flex-basis: 100%;
  padding-top: 6px;
}

.account-method-actions .button {
  padding: 0 12px;
  font-size: 0.8rem;
}

/* Quiet, because these are decisions rather than the page's main act. Removing a
   method is still a real button -- 44px of it, above the 9px minimum -- so it is
   not made to look unavailable. */
.button-quiet {
  color: var(--ink-dim);
  background: transparent;
  border: 1px solid var(--line);
}

.button-quiet:hover,
.button-quiet:focus-visible {
  color: var(--ink);
  border-color: var(--ink-dim);
  background: rgba(0, 0, 0, 0.02);
}

/* One button per instrument that can be added.

   Stacked rather than side by side: the label lengths differ a lot between them --
   "Add a PayPal account" is much longer than "Add a card" -- and a row of
   differently-sized buttons reads as though one of them mattered more. */
.account-add-methods {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  margin-top: 14px;
}

.account-add-method {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* ---------------------------------------------------------------------------
   Admin area.

   Appended rather than kept in a second stylesheet, and built from the same
   custom properties as the rest of the file -- the same decision, and for the
   same reason, as the account area above.

   The admin pages previously carried their own inline <style> block: a separate
   palette, a separate font stack, a near-black header of their own, and their own
   idea of what a card looked like. The result was an admin area that looked like
   a different product from the landing page and the account pages, which is
   what issue #26 is about. Every value below is a token from :root above.

   The two surfaces still differ where the work differs: the admin area is denser
   and its tables are the point, so `--content-width` is wider and the type is a
   step smaller. Same design, a denser job. #}

.admin-body {
  background: var(--canvas);
  color: var(--ink);
}

/* Wider than the account pages and the landing page: an operator is reading
   tables of chargers, sessions and users across, and the narrow measure those
   pages use would put a column of six fields into a scroll. */
.admin-shell {
  width: min(calc(100% - 40px), var(--content-width));
  margin: 0 auto;
  padding: 34px 0 72px;
}

.admin-shell:focus {
  outline: none;
}

/* The signed-in administrator and the sign-out button, in the header's action
   area. Not a link, because there is nothing here to link to.

   Ink on the light header. This was first written as white with a
   light-on-dark ghost button, carried over from the admin area's own near-black
   header; against the shared light header that renders as white on cream and is
   invisible. A screenshot is the only thing that catches that, and it caught
   this, which is why `tests/test_admin_design.py` now asserts the colour is not
   the header's light-on-dark one rather than trusting a screenshot to be looked
   at. */
.admin-who {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin-left: 8px;
  font-size: 0.84rem;
  color: var(--ink-soft);
}

.admin-title {
  font-size: 1.6rem;
  line-height: 1.2;
  letter-spacing: -0.02em;
  margin: 0 0 4px;
}

.admin-lede {
  color: var(--muted);
  margin: 0 0 22px;
}

/* A panel is a form or a block of content on a surface. The account pages call
   this an account-card; same thing, same radius and shadow, so the two read as
   one product. */
.admin-panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: 22px;
  margin-bottom: 20px;
}

.admin-panel-wide {
  max-width: 720px;
}

/* The four counts on the dashboard. */
.admin-cards {
  display: flex;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 26px;
}

.admin-card {
  flex: 1 1 170px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: 16px 18px;
}

.admin-card-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}

.admin-card-value {
  font-size: 1.5rem;
  font-weight: 760;
  letter-spacing: -0.02em;
  margin-top: 2px;
}

/* A table is the admin area's main instrument, so it gets a surface of its own
   rather than sitting straight on the page background. */
.admin-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  font-size: 0.89rem;
}

.admin-table th {
  background: var(--surface-soft);
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  text-align: left;
  padding: 11px 14px;
  border-bottom: 1px solid var(--line);
}

.admin-table td {
  padding: 11px 14px;
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}

.admin-table tr:last-child td {
  border-bottom: 0;
}

/* A cell holding an id, a timestamp or anything else that is reference rather
   than prose: monospaced so a column of them can be scanned vertically. */
.admin-table td.muted {
  color: var(--muted);
  font-size: 0.84rem;
}

/* The status pill. Same shape as the account pages' pill, and the same rule
   about its colour being declared here rather than left to the user agent. */
.admin-pill {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 0.74rem;
  font-weight: 650;
  color: var(--ink-soft);
  background: var(--surface-tint);
  border: 1px solid var(--line);
}

.admin-pill-good {
  color: var(--green);
  background: var(--green-soft);
  border-color: transparent;
}

.admin-pill-bad {
  color: var(--red);
  background: var(--red-soft);
  border-color: transparent;
}

/* An error and its counterpart, the same shape so a reader scanning the page
   finds both in the same place. */
.admin-alert {
  padding: 11px 14px;
  border-radius: 12px;
  margin-bottom: 16px;
  font-size: 0.9rem;
}

.admin-alert-error {
  color: var(--red);
  background: var(--red-soft);
  border: 1px solid rgba(200, 71, 63, 0.24);
}

.admin-alert-notice {
  color: var(--green);
  background: var(--green-soft);
  border: 1px solid rgba(23, 134, 83, 0.24);
}

/* The quiet text under a field: what it means, what is allowed, what happens. */
.admin-hint {
  display: block;
  margin: 5px 0 0;
  font-size: 0.8rem;
  color: var(--muted);
}

/* Forms. The label sits above its field, at the weight the rest of the product
   uses for one. */
.admin-field {
  display: block;
  margin: 16px 0 5px;
  font-size: 0.82rem;
  font-weight: 650;
  color: var(--ink-soft);
}

/* A checkbox and its label on one line -- the block rule above is for a label
   sitting above its field, which is what a stacked form needs and what a
   checkbox cannot use. */
.admin-field-inline {
  display: flex;
  align-items: center;
  gap: 9px;
  margin-top: 16px;
  font-size: 0.87rem;
}

.admin-field-inline input {
  width: auto;
}

.admin-input,
.admin-panel input[type="text"],
.admin-panel input[type="email"],
.admin-panel input[type="password"],
.admin-panel input[type="tel"],
.admin-panel input[type="number"],
.admin-panel select,
.admin-panel textarea {
  font: inherit;
  width: 100%;
  padding: 10px 12px;
  color: var(--ink);
  background: var(--surface-soft);
  border: 1px solid var(--line-strong);
  border-radius: 10px;
}

.admin-panel textarea {
  resize: vertical;
  min-height: 86px;
}

.admin-input:focus-visible,
.admin-panel input:focus-visible,
.admin-panel select:focus-visible,
.admin-panel textarea:focus-visible {
  outline: 3px solid rgba(57, 120, 232, 0.28);
  outline-offset: 1px;
  border-color: var(--blue);
}

/* Two fields side by side. Not `display: flex` with `flex: 1` children, which
   was the old rule and which cannot make two columns of *different* field
   widths; a grid can, and the charger properties page needs that. */
.admin-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 0 14px;
}

/* A short code, a token, anything that has to be read character by character.
   Stated as one block box: padding, a dashed border and a background all say
   "this is a value, not prose" before a word is read. */
.admin-token {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.86rem;
  background: var(--surface-tint);
  border: 1px dashed var(--line-strong);
  border-radius: 10px;
  padding: 11px 13px;
  word-break: break-all;
  margin: 8px 0 0;
}

/* A form beside a control rather than under it. Without this a form is a block
   and pushes the control it belongs to onto the next line. */
form.admin-inline {
  display: inline-block;
  margin: 0;
}

.admin-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: center;
  margin-top: 20px;
}

/* The refuse-a-thing control, and its link form. The colour is declared rather
   than inherited: left unset, the user agent supplies it and the label vanishes
   on a dark scheme -- which is what #25 was. */
.admin-danger-link {
  color: var(--red);
  font-weight: 650;
}

.admin-danger-link:hover {
  color: #a52626;
}

/* A definition list used for label/value pairs inside a panel. */
.admin-defs {
  margin: 12px 0 0;
}

.admin-defs > div {
  display: flex;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--line);
}

.admin-defs > div:last-child {
  border-bottom: 0;
}

.admin-defs dt {
  color: var(--muted);
  font-size: 0.84rem;
  min-width: 120px;
}

.admin-defs dd {
  margin: 0;
  font-size: 0.89rem;
}

@media (max-width: 720px) {
  .admin-shell {
    width: calc(100% - 28px);
    padding-top: 24px;
  }

  /* A table with six columns cannot be read on a phone at this size. Allowed
     to scroll sideways inside its own surface rather than reflowing, because
     reflowing a table means either dropping a column or inventing a layout for
     every one of them. */
  .admin-table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }

  .admin-who {
    font-size: 0.78rem;
  }
}

/* Reference text inside a table or a row: an id, a timestamp, a count. */
.admin-muted {
  color: var(--muted);
}

/* A row in a list of records: the thing on the left, its metadata on the right.
   Wraps rather than compressing, so a long name on a narrow screen moves its
   date onto the next line instead of squeezing it to nothing. */
.admin-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

/* The name side of a row, with the badges that qualify it. */
.admin-row-title {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  font-weight: 700;
}

/* Charger pictures on the properties page (issue #49, restyled by #26).

   These were inline `style` attributes with the old admin palette spelled out in
   the markup: #c9ced8 for the border, #f6f7f9 behind the image, a hard-coded
   near-black behind the delete label. A colour in a template cannot follow a
   change to the design system and there is no way to test for one, which is
   the whole reason the admin area moved onto this file. */
.admin-picture-grid {
  margin-top: 18px;
}

.admin-picture-grid-label {
  font-size: 0.75rem;
  color: var(--muted);
  margin-bottom: 8px;
}

.admin-pictures {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}

.admin-picture {
  position: relative;
}

.admin-picture img {
  display: block;
  width: 120px;
  height: 90px;
  object-fit: cover;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface-soft);
}

.admin-picture-delete {
  position: absolute;
  top: 4px;
  right: 4px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  /* Over a photograph rather than on the page, so it carries its own
     background and states its own colour rather than inheriting one. */
  background: rgba(16, 37, 28, 0.74);
  color: white;
  padding: 3px 7px;
  border-radius: 6px;
  font-size: 0.7rem;
  cursor: pointer;
  line-height: 1;
}

.admin-picture-delete input {
  width: auto;
  margin: 0;
  accent-color: var(--brand);
}
