@layer reset, tokens, global, compositions, utilities, blocks, exceptions;

/* ── reset.css ── */
@layer reset {
/* Box sizing rules */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* Prevent font size inflation */
html {
  -moz-text-size-adjust: none;
  -webkit-text-size-adjust: none;
  text-size-adjust: none;
}

/* Typography — pretty wrapping costs layout time, so only on running text */
p,
li,
dd,
figcaption,
blockquote {
  text-wrap: pretty;
}

h1,
h2,
h3,
h4 {
  text-wrap: balance;
}

/* Scroll behavior */
html:focus-within {
  scroll-behavior: smooth;
}

/* Body defaults */
body {
  min-height: 100vh;
  min-height: 100dvh;
  text-rendering: optimizeSpeed;
  line-height: 1.5;
}

/* Margins */
body,
h1,
h2,
h3,
h4,
p,
figure,
blockquote,
dl,
dd {
  margin: 0;
}

/* List styles */
ul[role='list'],
ol[role='list'] {
  list-style: none;
}

/* Padding for lists */
[role='list'] {
  padding: 0;
}

/* Anchor styles */
a:not([class]) {
  text-decoration-skip-ink: auto;
}

/* Image styles */
img,
picture,
svg,
canvas {
  max-inline-size: 100%;
  block-size: auto;
  vertical-align: middle;
  font-style: italic;
  background-repeat: no-repeat;
  background-size: cover;
  shape-margin: 0.75rem;
}

/* Inherit fonts for inputs and buttons */
button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

/* Textarea styles */
textarea {
  resize: vertical;
  resize: block;
}

/* Textarea height */
textarea:not([rows]) {
  min-height: 10em;
}

/* Cursor styles */
button,
label,
select,
summary,
[role='button'],
[role='option'] {
  cursor: pointer;
}

/* Scroll margin above anchor links */
:target {
  scroll-margin-block-start: 2ex;
}

/* Scroll margin below focused elements */
:focus {
  scroll-margin-block-end: 8vh;
}

/* Reduced motion — smooth scrolling off here; each animated block (skeleton.css,
   header.css) carries its own reduced version rather than a global 0.01ms cut,
   which can make motion more jarring, not less */
@media (prefers-reduced-motion: reduce) {
  html:focus-within {
    scroll-behavior: auto;
  }
}
}
/* ── tokens.css ── */
@layer tokens {
:root {
  /* Light unless the dark tokens below take over; the browser themes its own
     scrollbars and form controls to match */
  color-scheme: light;

  /* Type scale — Utopia fluid (320→1240 viewport range) */
  /* D uses four roles: --step--1 small labels, --step-0 body, --step-1 headings/data values, --step-2 hero heading */
  --step--1: clamp(0.83rem, calc(0.82rem + 0.08vw), 0.88rem);
  --step-0: clamp(1rem, calc(0.92rem + 0.39vw), 1.25rem);
  --step-1: clamp(1.2rem, calc(1.02rem + 0.88vw), 1.77rem);
  --step-2: clamp(1.44rem, calc(1.11rem + 1.65vw), 2.5rem);
  --step-3: clamp(1.73rem, calc(1.17rem + 2.8vw), 3.53rem);
  --step-4: clamp(2.07rem, calc(1.17rem + 4.54vw), 5rem);

  /* Type family and tracking */
  --font-body: "Poppins", -apple-system, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --tracking-heading: -0.5px;      /* uppercase headings */
  --tracking-label: 0.1em;         /* uppercase small labels, table headers */
  --tracking-button: 0.06em;       /* uppercase buttons */

  /* Spacing — Utopia tokens (320→1240 viewport range) */
  /* https://utopia.fyi/space/calculator?c=320,18,1.2,1240,20,1.25,5,2,&s=0.75|0.5|0.25,1.5|2|3|4|6,s-l&g=s,l,xl,12 */
  --space-3xs: clamp(0.3125rem, 0.3125rem + 0vi, 0.3125rem);
  --space-2xs: clamp(0.5625rem, 0.5408rem + 0.1087vi, 0.625rem);
  --space-xs: clamp(0.875rem, 0.8533rem + 0.1087vi, 0.9375rem);
  --space-s: clamp(1.125rem, 1.0815rem + 0.2174vi, 1.25rem);
  --space-m: clamp(1.6875rem, 1.6223rem + 0.3261vi, 1.875rem);
  --space-l: clamp(2.25rem, 2.163rem + 0.4348vi, 2.5rem);
  --space-xl: clamp(3.375rem, 3.2446rem + 0.6522vi, 3.75rem);

  /* Fluid pairs */
  --space-3xs-2xs: clamp(0.3125rem, 0.2038rem + 0.5435vi, 0.625rem);
  --space-2xs-xs: clamp(0.5625rem, 0.4321rem + 0.6522vi, 0.9375rem);
  --space-xs-s: clamp(0.875rem, 0.7446rem + 0.6522vi, 1.25rem);
  --space-s-m: clamp(1.125rem, 0.8641rem + 1.3043vi, 1.875rem);
  --space-m-l: clamp(1.6875rem, 1.4049rem + 1.413vi, 2.5rem);
  --space-l-xl: clamp(2.25rem, 1.7283rem + 2.6087vi, 3.75rem);
  --space-s-l: clamp(1.125rem, 0.6467rem + 2.3913vi, 2.5rem);

  /* Semantic spacing */
  --gutter: var(--space-s);
  --band-padding: 24px;            /* block padding of a full-bleed band; the inner .center supplies the gutter */

  /* Measure */
  --measure: 60ch;
  --content-width: 30rem;

  /* Colours — Sea Glass palette, light theme */
  --color-bg: #EEF6F3;                       /* page ground */
  --color-bg-muted: #FFFFFF;                 /* card surface */
  --color-text: #163A3A;                     /* ink: body/headings */
  --color-muted: #3F5F5D;                    /* labels, footnotes */
  --color-border: rgba(22, 58, 58, 0.14);    /* card borders, rules, table lines */

  /* Status — deliberately not brand teal. Go and no-go carry white text; caution
     is a yellow, so anything filled with it takes ink text */
  --color-go: #2C9C6E;                       /* sea-glass green: dots, badge, verdict */
  --color-go-light: #E3F4EC;
  --color-caution: #FFC400;
  --color-caution-light: #FFF6D6;
  --color-nogo: #ca3535;
  --color-nogo-light: #fbe8e8;

  /* Brand */
  --color-brand: #1F6F6B;                    /* teal: eyebrows, links, focus rings */
  --color-brand-bright: #BFEBD9;             /* mint: icons, eyebrows and rules on the ink hero/footer */
  --color-brand-deep: #163A3A;               /* ink: hero + footer background */
  --color-brand-navy: #163A3A;               /* same ink; the navy band tone is no longer used */
  --color-band: #BFEBD9;                     /* the mint tone */
  --color-accent: #1E2A6B;                   /* periwinkle ink — the dip pin, the same as the curve; takes white text */
  --color-on-accent: #FFFFFF;                /* text on --color-accent (dip pin label, dip-day chip) */
  --color-on-status: #FFFFFF;                /* text on go and no-go fills (badges, the FYI button) */
  --color-chip: rgba(255, 255, 255, 0.55);   /* frosted tide day chip on its band */
  --color-panel: rgba(0, 0, 0, 0.28);        /* verdict panel on ink */
  --color-tint-line: rgba(191, 235, 217, 0.22); /* tile borders on ink */
  --color-tint-fill: rgba(255, 255, 255, 0.04); /* tile fill on ink */
  --color-on-dark: #FFFFFF;                  /* text on ink */
  --color-on-dark-body: rgba(255, 255, 255, 0.75); /* body copy on ink */
  --color-on-dark-muted: rgba(255, 255, 255, 0.55); /* labels on ink */
  --color-on-dark-line: rgba(191, 235, 217, 0.22); /* rules inside ink bands */

  /* Pastel band tones (Sea Glass). Each has a fill, a muted text colour and a
     rule colour; text on all of them is --color-text */
  --tone-mint: #BFEBD9;   --tone-mint-muted: #2F5F5B;   --tone-mint-line: rgba(22, 58, 58, 0.16);
  --tone-sand: #F3E6CC;   --tone-sand-muted: #6F5A2A;   --tone-sand-line: rgba(111, 90, 42, 0.20);
  --tone-peri: #C9D2F7;   --tone-peri-muted: #4B5AA6;   --tone-peri-line: rgba(30, 42, 107, 0.18);
  --tone-coral: #F6C2AE;  --tone-coral-muted: #7A3E28;  --tone-coral-line: rgba(122, 62, 40, 0.20);

  /* Shape and border */
  --border-thin: 1px;
  --radius: 4px;                             /* buttons, badges, switcher */
  --radius-pill: 999px;                      /* theme toggle only */
  --rule-brand: 4px;                         /* the hero panel's left rule */
}

/* Dark theme — pastel dark. It applies two ways, so the values appear twice: when
   the system prefers dark and nothing is pinned (no JS needed), and when the toggle
   has pinned dark. Keep the two blocks identical */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    /* Pastel dark (Sea Glass): deep versions of each tone, the pastel carried
       by the muted text; light ink text throughout */
    --color-bg: #0F2322;
    --color-bg-muted: #15302F;
    --color-text: #E8F3EF;
    --color-muted: #9DBDB5;
    --color-border: rgba(191, 235, 217, 0.14);
    --color-brand: #7FD6BE;                  /* mint-teal: links, eyebrows, focus rings */
    --color-band: #17392F;
    --color-brand-deep: #0A1918;
    --color-panel: rgba(0, 0, 0, 0.35);
    --color-accent: #C9D2F7;                 /* the pastel periwinkle: curve and pin */
    --color-on-accent: #1E2A6B;
    --color-on-status: #0A1918;              /* the lifted go and no-go are too light for white */
    --color-chip: rgba(255, 255, 255, 0.08);
    --color-go: #4CC08F;
    --color-go-light: #173A2E;
    --color-caution: #FFC400;
    --color-caution-light: #3A3312;
    --color-nogo: #E86A6A;
    --color-nogo-light: #3a1e1e;
    --tone-mint: #17392F;   --tone-mint-muted: #A9D8C4;   --tone-mint-line: rgba(191, 235, 217, 0.16);
    --tone-sand: #372D1C;   --tone-sand-muted: #E0CB9C;   --tone-sand-line: rgba(224, 203, 156, 0.18);
    --tone-peri: #222A4E;   --tone-peri-muted: #B3BEEF;   --tone-peri-line: rgba(179, 190, 239, 0.20);
    --tone-coral: #3F241B;  --tone-coral-muted: #EFB29B;  --tone-coral-line: rgba(239, 178, 155, 0.20);
  }
}

[data-theme="dark"] {
  color-scheme: dark;
  /* Pastel dark (Sea Glass): deep versions of each tone, the pastel carried
     by the muted text; light ink text throughout */
  --color-bg: #0F2322;
  --color-bg-muted: #15302F;
  --color-text: #E8F3EF;
  --color-muted: #9DBDB5;
  --color-border: rgba(191, 235, 217, 0.14);
  --color-brand: #7FD6BE;                  /* mint-teal: links, eyebrows, focus rings */
  --color-band: #17392F;
  --color-brand-deep: #0A1918;
  --color-panel: rgba(0, 0, 0, 0.35);
  --color-accent: #C9D2F7;                 /* the pastel periwinkle: curve and pin */
  --color-on-accent: #1E2A6B;
  --color-on-status: #0A1918;              /* the lifted go and no-go are too light for white */
  --color-chip: rgba(255, 255, 255, 0.08);
  --color-go: #4CC08F;
  --color-go-light: #173A2E;
  --color-caution: #FFC400;
  --color-caution-light: #3A3312;
  --color-nogo: #E86A6A;
  --color-nogo-light: #3a1e1e;
  --tone-mint: #17392F;   --tone-mint-muted: #A9D8C4;   --tone-mint-line: rgba(191, 235, 217, 0.16);
  --tone-sand: #372D1C;   --tone-sand-muted: #E0CB9C;   --tone-sand-line: rgba(224, 203, 156, 0.18);
  --tone-peri: #222A4E;   --tone-peri-muted: #B3BEEF;   --tone-peri-line: rgba(179, 190, 239, 0.20);
  --tone-coral: #3F241B;  --tone-coral-muted: #EFB29B;  --tone-coral-line: rgba(239, 178, 155, 0.20);
}
}
/* ── global.css ── */
@layer global {
/* Poppins — self-hosted, latin subset */
@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/fonts/poppins-400.woff2") format("woff2");
}

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/fonts/poppins-600.woff2") format("woff2");
}

@font-face {
  font-family: "Poppins";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/fonts/poppins-700.woff2") format("woff2");
}

/* Measure axiom */
* {
  max-inline-size: var(--measure);
}

html,
body,
div,
header,
nav,
main,
footer,
section,
article,
fieldset,
figure,
dl,
output {
  max-inline-size: none;
}

/* Global border reset */
*,
*::before,
*::after {
  border-style: solid;
  border-width: 0;
}

body {
  font-family: var(--font-body);
  font-size: var(--step-0);
  /* Poppins' x-height (0.548em, measured from the woff2). While it loads, or if it
     fails, the fallback is scaled to the same x-height (Helvetica 0.523, Arial 0.519,
     the system font 0.444) so the swap does not shift the layout. A number rather than
     `from-font`, which would read the fallback's own metric during the swap */
  font-size-adjust: 0.548;
  line-height: 1.5;
  color: var(--color-text);
  background-color: var(--color-bg);
}

h1, h2, h3, h4 {
  line-height: 1.1;
}

h1, h2, h3 {
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--tracking-heading);
}

h1 { font-size: var(--step-1); }
h2 { font-size: var(--step-1); }
h3 { font-size: var(--step-0); }

a:not([class]) {
  color: currentColor;
  text-decoration-skip-ink: auto;
}

:focus-visible {
  outline: 3px solid var(--color-brand);
  outline-offset: 0.3ch;
}

::selection {
  background-color: var(--color-text);
  color: var(--color-bg);
}
}

/* ── compositions/flow.css ── */
@layer compositions {
/* https://every-layout.dev/layouts/stack/ */
.flow {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}

.flow > * {
  margin-block: 0;
}

.flow > * + * {
  margin-block-start: var(--flow-space, var(--space-s));
}
}
/* ── compositions/box.css ── */
@layer compositions {
/* https://every-layout.dev/layouts/box/ */
.box {
  padding: var(--box-padding, var(--space-s));
  border: var(--border-thin) solid;
  outline: var(--border-thin) transparent;
  outline-offset: calc(var(--border-thin) * -1);
  color: var(--color-text);
  background-color: var(--color-bg);
}

.box * {
  color: inherit;
}

.box[data-invert] {
  color: var(--color-bg);
  background-color: var(--color-text);
}
}
/* ── compositions/center.css ── */
@layer compositions {
/* https://every-layout.dev/layouts/center/ */
/* Sections below the hero are full-bleed bands, so .center[data-wide] sits
   inside each band (as it does in the header, hero and footer) rather than
   wrapping a run of sections. Never fake full-bleed with negative margins. */
.center {
  inline-size: 100%;
  max-inline-size: var(--center-measure, var(--measure));
  margin-inline: auto;
  padding-inline: var(--center-gutters, var(--gutter));
}

.center[data-wide] {
  --center-measure: var(--content-width);
}

.center[data-intrinsic] {
  display: flex;
  flex-direction: column;
  align-items: center;
}
}
/* ── compositions/cluster.css ── */
@layer compositions {
/* https://every-layout.dev/layouts/cluster/ */
.cluster {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cluster-gap, var(--gutter));
  justify-content: var(--cluster-horizontal-alignment, flex-start);
  align-items: var(--cluster-vertical-alignment, center);
}
}
/* ── compositions/repel.css ── */
@layer compositions {
/* https://every-layout.dev/layouts/repel/ */
.repel {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: var(--repel-vertical-alignment, center);
  gap: var(--repel-gap, var(--gutter));
}
}
/* ── compositions/grid.css ── */
@layer compositions {
/* https://every-layout.dev/layouts/grid/ */
.grid {
  display: grid;
  gap: var(--grid-space, var(--gutter));
  --grid-minimum: 20ch;
}

@supports (width: min(var(--grid-minimum), 100%)) {
  .grid {
    grid-template-columns: repeat(auto-fit, minmax(min(var(--grid-minimum), 100%), 1fr));
  }
}
}
/* ── compositions/switcher.css ── */
@layer compositions {
/* https://every-layout.dev/layouts/switcher/ */
.switcher {
  display: flex;
  flex-wrap: wrap;
  gap: var(--switcher-space, var(--gutter));
  --switcher-threshold: 30rem;
}

.switcher > * {
  flex-grow: 1;
  flex-basis: calc((var(--switcher-threshold) - 100%) * 999);
}

.switcher > :nth-last-child(n+5),
.switcher > :nth-last-child(n+5) ~ * {
  flex-basis: 100%;
}
}
/* ── compositions/sidebar.css ── */
@layer compositions {
/* https://every-layout.dev/layouts/sidebar/ */
.sidebar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sidebar-gap, var(--gutter));
}

.sidebar > :first-child {
  flex-basis: var(--sidebar-target-width, 20rem);
  flex-grow: 1;
}

.sidebar > :last-child {
  flex-basis: 0;
  flex-grow: 999;
  min-inline-size: var(--sidebar-content-min, 50%);
}
}

/* ── utilities/spacing.css ── */
@layer utilities {
.flow-space\:space-3xs { --flow-space: var(--space-3xs) !important; }
.flow-space\:space-2xs { --flow-space: var(--space-2xs) !important; }
.flow-space\:space-xs { --flow-space: var(--space-xs) !important; }
.flow-space\:space-s { --flow-space: var(--space-s) !important; }
.flow-space\:space-m { --flow-space: var(--space-m) !important; }
.flow-space\:space-l { --flow-space: var(--space-l) !important; }
.padding\:space-2xs { padding: var(--space-2xs) !important; }
.padding\:space-xs { padding: var(--space-xs) !important; }
.padding\:space-s { padding: var(--space-s) !important; }
.padding\:space-m { padding: var(--space-m) !important; }
}
/* ── utilities/typography.css ── */
@layer utilities {
.font-size\:step--1 { font-size: var(--step--1) !important; }
.font-size\:step-0 { font-size: var(--step-0) !important; }
.font-size\:step-1 { font-size: var(--step-1) !important; }
.font-size\:step-2 { font-size: var(--step-2) !important; }
.tabular-nums { font-variant-numeric: tabular-nums !important; }
}
/* ── utilities/colors.css ── */
@layer utilities {
.color\:muted { color: var(--color-muted) !important; }
.color\:go { color: var(--color-go) !important; }
.color\:caution { color: var(--color-caution) !important; }
.color\:nogo { color: var(--color-nogo) !important; }
}
/* ── utilities/visibility.css ── */
@layer utilities {
/* Hidden visually, present for assistive tech. Anything focusable inside becomes
   visible while it has focus (skip links) */
.visually-hidden:where(:not(:focus-within, :active)) {
  position: absolute !important;
  clip-path: inset(50%) !important;
  overflow: hidden !important;
  width: 1px !important;
  height: 1px !important;
  margin: -1px !important;
  padding: 0 !important;
  border: 0 !important;
  white-space: nowrap !important;
}
}

/* ── blocks/band.css ── */
@layer blocks {
/* ── Band: a full-bleed colour section below the hero ──
   The band owns the background and block padding; the .center[data-wide]
   inside it supplies the gutter and the centre measure. Tone is set by
   attribute, and everything inside reads --band-text / --band-muted /
   --band-line rather than hard-coding navy or muted, so a section can
   change tone by attribute alone. Bands butt together: no border, radius,
   shadow or gap — the colour change is the separator. */
.band {
  --band-bg: var(--color-bg-muted);
  --band-text: var(--color-text);
  --band-muted: var(--color-muted);
  --band-line: var(--color-border);
  background: var(--band-bg);
  padding-block: var(--band-padding);
  color: var(--band-text);
}

/* Sea Glass pastels; cyan is kept as an alias of mint */
.band[data-tone="mint"],
.band[data-tone="cyan"] { --band-bg: var(--tone-mint); --band-muted: var(--tone-mint-muted); --band-line: var(--tone-mint-line); }
.band[data-tone="sand"]  { --band-bg: var(--tone-sand); --band-muted: var(--tone-sand-muted); --band-line: var(--tone-sand-line); }
.band[data-tone="peri"]  { --band-bg: var(--tone-peri); --band-muted: var(--tone-peri-muted); --band-line: var(--tone-peri-line); }
.band[data-tone="coral"] { --band-bg: var(--tone-coral); --band-muted: var(--tone-coral-muted); --band-line: var(--tone-coral-line); }

/* No longer used on the beach page */
.band[data-tone="navy"] {
  --band-bg: var(--color-brand-navy);
  --band-text: var(--color-on-dark);
  --band-muted: var(--color-on-dark-muted);
  --band-line: var(--color-on-dark-line);
}

/* Deep ink — the footer (the hero has its own block) */
.band[data-tone="deep"] {
  --band-bg: var(--color-brand-deep);
  --band-text: var(--color-on-dark);
  --band-muted: var(--color-on-dark-muted);
  --band-line: var(--color-on-dark-line);
}

/* Flow spacing between a band's contents */
.band__inner {
  --flow-space: var(--space-xs);
}

/* Section heading — style unchanged, colour follows the band tone */
.card-label {
  color: var(--band-text, var(--color-text));
  font-size: var(--step-1);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--tracking-heading);
}

.card-divider {
  padding-block-start: var(--space-xs);
  border-block-start: var(--border-thin) solid var(--band-line, var(--color-border));
}

.card-hr {
  width: 100%;
  border: none;
  border-block-start: var(--border-thin) solid var(--band-line, var(--color-border));
}
}
/* ── blocks/header.css ── */
@layer blocks {
.app-header {
  background: var(--color-bg-muted);
  border-block-end: var(--border-thin) solid var(--color-border);
  padding-block: 18px;
}

.app-header__inner {
  --repel-gap: 12px;
  flex-wrap: nowrap;
}

/* With a search field the row may wrap: brand and toggle first, search beneath (beach-search.css) */
.app-header__inner:has(.app-header__search) {
  flex-wrap: wrap;
}

.app-brand {
  --cluster-gap: 12px;
  flex-wrap: nowrap;
}

.app-title {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.app-header h1 {
  color: var(--color-text);
  font-size: var(--step-1);
  line-height: 1.1;
  margin: 0;
}

/* The title links home without looking like a link */
.app-title__link {
  color: inherit;
  text-decoration: none;
}

.app-title .eyebrow {
  line-height: 1.2;
}

/* Small uppercase teal label above a heading */
.eyebrow {
  font-size: var(--step--1);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--color-brand);
  margin: 0;
}

/* Brand mark — the inline Lucide bot icon in ink */
.app-logo {
  width: 48px;
  height: 48px;
  display: block;
  color: var(--color-text);
}

/* ── Theme toggle switch ── */
.theme-toggle {
  --toggle-width: 44px;
  --toggle-height: 26px;
  --toggle-thumb: 16px;
  --toggle-offset: 3px;

  position: relative;
  display: inline-flex;
  align-items: center;
  align-self: center;
  flex-shrink: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.theme-toggle__input {
  appearance: none;
  -webkit-appearance: none;
  width: var(--toggle-width);
  height: var(--toggle-height);
  margin: 0;
  border: 2px solid var(--color-text);
  border-radius: var(--radius-pill);
  background: var(--color-bg-muted);
  cursor: pointer;
  position: relative;
}

/* The thumb is drawn with a border, not a background, so it survives forced-colours
   mode (backgrounds are stripped there, borders are kept) */
.theme-toggle__input::after {
  content: "";
  position: absolute;
  top: 50%;
  left: var(--toggle-offset);
  width: var(--toggle-thumb);
  height: var(--toggle-thumb);
  border: calc(var(--toggle-thumb) / 2) solid var(--color-text);
  border-radius: 50%;
  transform: translateY(-50%);
  transition: translate 0.15s ease;
}

.theme-toggle__input:checked::after {
  translate: calc(var(--toggle-width) - var(--toggle-thumb) - var(--toggle-offset) * 2 - 4px) 0;
}

@media (prefers-reduced-motion: reduce) {
  .theme-toggle__input::after {
    transition: none;
  }
}

.theme-toggle__input:focus-visible {
  outline: 3px solid var(--color-brand);
  outline-offset: 0.3ch;
}

/* Same as .visually-hidden (utilities/visibility.css) */
.theme-toggle__label {
  position: absolute !important;
  clip-path: inset(50%) !important;
  overflow: hidden !important;
  width: 1px !important;
  height: 1px !important;
  margin: -1px !important;
  padding: 0 !important;
  border: 0 !important;
  white-space: nowrap !important;
}
}
/* ── blocks/beach-title.css ── */
@layer blocks {
/* ── Beach title: the beach name at the top of the hero ── */
.beach-title__name {
  font-size: var(--step-2);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: var(--tracking-heading);
  text-transform: uppercase;
  color: var(--color-on-dark);
  margin: 0;
}

.beach-title__place {
  font-size: var(--step--1);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--color-on-dark-muted);
  margin-block-start: 6px;
}
}
/* ── blocks/beach-list.css ── */
@layer blocks {
/* ── Beach list: the home page's plain index of beaches, in columns ── */
.beach-list {
  columns: 16ch auto;
  column-gap: var(--space-s);
  padding: 0;
  margin: 0;
}

.beach-list li {
  break-inside: avoid;
  padding-block: 6px;
  line-height: 1.3;
}

/* Nation heading: a disclosure button inside the h2, the beach count beside the
   name and a chevron that points down when the list is open */
.beach-list__toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  min-height: 44px;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
}

.beach-list__count {
  color: var(--band-muted, var(--color-muted));
  font-weight: 600;
}

.beach-list__toggle::after {
  content: "";
  inline-size: 0.5em;
  block-size: 0.5em;
  margin-inline-start: 0.2em;
  border-inline-end: 2px solid currentColor;
  border-block-end: 2px solid currentColor;
  transform: translateY(-0.15em) rotate(45deg);
}

.beach-list__toggle[aria-expanded="false"]::after {
  transform: rotate(-45deg);
}
}
/* ── blocks/beach-search.css ── */
@layer blocks {
/* ── Beach search: input plus a panel of results (js/search.js) ──
   Two settings. In the header it is a dropdown over the page, on the page's own
   colours. With [data-inline] (the home page hero) the panel is always open, sits
   in the flow and reads white on the ink hero. */
.beach-search {
  --search-text: var(--color-text);
  --search-muted: var(--color-muted);
  --search-line: var(--color-border);
  --search-hover: var(--color-band);
  position: relative;
  color: var(--search-text);
}

.beach-search[data-inline] {
  --search-text: var(--color-on-dark);
  --search-muted: var(--color-on-dark-muted);
  --search-line: var(--color-tint-line);
  --search-hover: var(--color-tint-fill);
}

.beach-search__input {
  width: 100%;
  min-height: 44px;
  padding: 10px 14px;
  border: var(--border-thin) solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg);
  color: var(--color-text);
  font: inherit;
  font-size: max(16px, var(--step-0)); /* 16px stops iOS zooming the page on focus */
}

.beach-search[data-inline] .beach-search__input {
  min-height: 52px;
  border-color: transparent;
  background: #fff;
  color: var(--color-brand-navy); /* ink in both themes, since the field stays white */
}

.beach-search__input::placeholder {
  color: var(--color-muted);
  opacity: 1;
}

.beach-search[data-inline] .beach-search__input::placeholder {
  color: color-mix(in srgb, var(--color-brand-navy) 75%, transparent); /* the field is white in both themes */
}

/* Dropdown (header) */
.beach-search__panel {
  position: absolute;
  z-index: 10; /* above the hero and its FYI notice */
  inset-inline: 0;
  top: calc(100% + 4px);
  max-height: min(70vh, 480px);
  overflow-y: auto;
  padding: 8px;
  border: var(--border-thin) solid var(--search-line);
  border-radius: var(--radius);
  background: var(--color-bg);
  box-shadow: 0 8px 24px color-mix(in srgb, var(--color-brand-navy) 18%, transparent);
}

[data-theme="dark"] .beach-search__panel {
  background: var(--color-bg-muted); /* one step up from the page, so the dropdown lifts */
}

/* Inline (home page hero) */
.beach-search[data-inline] .beach-search__panel {
  position: static;
  max-height: none;
  overflow: visible;
  margin-block-start: 12px;
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
}

/* A solid mint fill in both settings and both themes, so the text is ink, never white */
.beach-search__near {
  min-height: 44px;
  padding: 10px 14px;
  border: var(--border-thin) solid transparent;
  border-radius: var(--radius);
  background: var(--color-brand-bright); /* the pastel mint, the same on dark */
  color: var(--color-brand-navy);
  font-size: var(--step--1);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tracking-button);
  width: 100%;
  cursor: pointer;
}

.beach-search__near:hover {
  background: color-mix(in srgb, var(--color-brand-bright) 85%, var(--color-brand-navy));
}

.beach-search__status {
  margin: 0;
  padding: 10px 4px 2px;
  font-size: var(--step--1);
  color: var(--search-muted);
}

.beach-search__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.beach-search__heading {
  padding: 14px 4px 6px;
  font-size: var(--step--1);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--search-muted);
}

.beach-search__option {
  border-block-start: var(--border-thin) solid var(--search-line);
}

.beach-search__link {
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: 12px;
  align-items: baseline;
  min-height: 44px;
  padding: 10px 4px;
  color: inherit;
  text-decoration: none;
}

.beach-search__name {
  font-weight: 600;
}

.beach-search__place {
  grid-column: 1;
  font-size: var(--step--1);
  color: var(--search-muted);
}

.beach-search__sauna {
  grid-column: 1;
  font-size: var(--step--1);
  font-weight: 600;
}

.beach-search__km {
  grid-column: 2;
  grid-row: 1 / span 3; /* name, place and the sauna tag when there is one */
  align-self: center;
  font-size: var(--step--1);
  font-weight: 600;
  white-space: nowrap;
}

.beach-search__link:hover,
.beach-search__option[aria-selected="true"] .beach-search__link {
  background: var(--search-hover);
}

/* In the header: a full-width row under the brand and the theme toggle (the page
   column is too narrow to fit all three in one row at any width) */
.app-header__search {
  order: 3;
  flex: 1 0 100%;
}

/* Home page hero */
.search-hero__title {
  font-size: var(--step-2);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: var(--tracking-heading);
  text-transform: uppercase;
  color: var(--color-on-dark);
  margin: 0;
}

.search-hero__intro {
  color: var(--color-on-dark-body);
  line-height: 1.6;
}
}
/* ── blocks/status.css ── */
@layer blocks {
/* ── Hero: dark band holding the beach title, verdict panel and summary tiles ── */
.hero {
  position: relative; /* anchors the FYI notice */
  background: var(--color-brand-deep);
  padding-block: 20px 24px;
  color: var(--color-on-dark);
}

/* Verdict panel */
.verdict-panel {
  background: var(--color-panel);
  border-inline-start: var(--rule-brand) solid var(--color-brand-bright);
  padding: 24px 22px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* The rule takes the status colour, matching the label and dot; mint while loading */
.verdict-panel[data-status="go"]      { border-inline-start-color: var(--color-go); }
.verdict-panel[data-status="caution"] { border-inline-start-color: var(--color-caution); }
.verdict-panel[data-status="nogo"]    { border-inline-start-color: var(--color-nogo); }

.verdict-panel .eyebrow {
  letter-spacing: 0.14em;
  color: var(--color-on-dark-muted);
  display: flex;
  align-items: center;
  gap: 8px;
}

.verdict-heading {
  font-size: var(--step-2);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: var(--tracking-heading);
  text-transform: uppercase;
  color: var(--color-on-dark);
}

.next-dip-date {
  color: var(--color-on-dark);
}

.status-label {
  display: inline;
  color: var(--color-brand-bright);
}

.status-label.go      { color: var(--color-go); }
.status-label.caution { color: var(--color-caution); }
.status-label.nogo    { color: var(--color-nogo); }
.status-label.loading { color: var(--color-on-dark-muted); }

/* Best window band under the hero: "Best window today" / "09:00–10:00" */
.band--window { --band-padding: 18px; }
.band--window .band__inner { --flow-space: 0; display: flex; flex-direction: column; gap: 4px; }
.status-window {
  font-size: var(--step-1);          /* the data-value size */
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--band-text, var(--color-text));
  margin: 0;
}

.status-reason {
  font-size: var(--step-0);
  line-height: 1.6;
  color: var(--color-on-dark-body);
  margin-block-start: 0;
  text-align: start;
  white-space: pre-line;
}

/* Status dot — kept for screen-reader / colour-blind redundancy */
.status-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
  display: inline-block;
}

.status-dot.go      { background: var(--color-go); }
.status-dot.caution { background: var(--color-caution); }
.status-dot.nogo    { background: var(--color-nogo); }
.status-dot.loading { background: var(--color-on-dark-muted); }

[data-status="go"] .status-dot      { background: var(--color-go); }
[data-status="caution"] .status-dot { background: var(--color-caution); }
[data-status="nogo"] .status-dot    { background: var(--color-nogo); }

/* Pollution / sample status indicators inside cards; the tone is a class, as on .status-dot */
.status-indicator {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  display: inline-block;
  flex-shrink: 0;
}

.status-indicator.go      { background: var(--color-go); }
.status-indicator.caution { background: var(--color-caution); }
.status-indicator.nogo    { background: var(--color-nogo); }

.pollution-ok,
.pollution-warn,
.pollution-bad {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 600;
  color: var(--band-text, var(--color-text));
}
}
/* ── blocks/fyi.css ── */
@layer blocks {
/* FYI notice — covers the hero until dismissed */
.fyi {
  position: absolute;
  inset: 0;
  z-index: 1;
  max-inline-size: none; /* global measure axiom caps asides at 60ch */
  background: var(--color-brand-deep);
  color: var(--color-on-dark);
  padding-block: 20px 24px;
  overflow: auto;
  display: flex; /* lets the inner column fill the height */
}

/* display: flex above would otherwise override the hidden attribute */
.fyi[hidden] {
  display: none;
}

.fyi__inner {
  position: relative;
  display: flex;
  flex-direction: column;
}

.fyi__heading {
  padding-inline-end: 44px; /* the only line that shares space with the close icon */
  font-size: var(--step-1);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--tracking-heading);
  color: var(--color-on-dark);
}

.fyi__copy {
  font-size: var(--step-0);
  line-height: 1.6;
  color: var(--color-on-dark-body);
  margin-block-start: 10px;
}

/* The button fills everything below the copy: full width, bot icon over the label.
   Scoped under .fyi so it outranks .btn's align-self in button.css (imported later, same layer). */
.fyi .fyi__cta {
  flex: 1;
  align-self: stretch;
  flex-direction: column;
  justify-content: center;
  gap: 12px;
  margin-block-start: 18px;
  border: 0;
  font-family: inherit;
  font-size: var(--step-0);
}

.fyi .fyi__cta svg {
  width: 64px;
  height: 64px;
  flex-shrink: 0;
}

.fyi__close {
  position: absolute;
  /* centre the 44px button on the heading's line box (line-height 1.1) */
  top: calc(var(--step-1) * 0.55 - 22px);
  /* 44px button, 24px icon with 3px internal whitespace: pull it out by 13px so the strokes sit on the gutter */
  right: calc(var(--gutter) - 13px);
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  background: transparent;
  border: 0;
  padding: 0;
  color: var(--color-on-dark);
  cursor: pointer;
}

.fyi__close svg {
  width: 24px;
  height: 24px;
}

.fyi__close:focus-visible {
  outline: 3px solid var(--color-brand-bright);
  outline-offset: 0.3ch;
}
}
/* ── blocks/summary.css ── */
@layer blocks {
/* Summary tiles — 4-up grid inside the hero */
.summary-icons {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px;
}

.summary-icon-col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  min-width: 0;
  padding: 14px 8px;
  background: var(--color-tint-fill);
  border: 1px solid var(--color-tint-line);
  container-type: inline-size; /* labels scale to the tile below ~390px */
}

.summary-icon-circle {
  display: flex;
  align-items: center;
  color: var(--color-brand-bright);
}

.summary-icon-circle svg {
  width: 32px;
  height: 32px;
}

.summary-icon-label {
  /* "WEATHER" is ~74px at --step--1 but a phone tile is ~60px wide: cap at --step--1, shrink with the tile */
  font-size: clamp(0.625rem, 18cqi, var(--step--1));
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  white-space: nowrap;
  color: var(--color-on-dark-muted);
}

.summary-icon-data {
  font-size: clamp(0.875rem, 25cqi, var(--step-0));
  font-weight: 700;
  line-height: 1.2;
  color: var(--color-on-dark);
}

/* Secondary line (direction, "Not impacted") */
.summary-icon-data + .summary-icon-data {
  font-size: var(--step--1);
  font-weight: 500;
  color: var(--color-on-dark-body);
  margin-block-start: -6px;
}
}
/* ── blocks/data-grid.css ── */
@layer blocks {
.data-grid {
  --grid-space: var(--space-xs);
  /* Always two columns: auto-fit found room for a third at --content-width */
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.data-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.data-item[data-full] { grid-column: 1 / -1; }
/* Support existing .full class too */
.data-item.full { grid-column: 1 / -1; }

.data-key {
  font-size: var(--step-0);
  font-weight: 400;
  color: var(--band-muted, var(--color-muted));
}

.data-key.mt {
  margin-block-start: var(--space-3xs);
}

.data-value {
  font-size: var(--step-1);
  font-weight: 600;
  color: var(--band-text, var(--color-text));
  font-variant-numeric: tabular-nums;
}

.data-value svg {
  width: 1.5em;
  height: 1.5em;
  vertical-align: -0.125em;
}

.data-unit {
  color: var(--band-muted, var(--color-muted));
}

/* Multi-line value (water quality sample): body size, regular weight */
.data-value.small {
  font-size: var(--step-0);
  font-weight: 400;
}
}
/* ── blocks/button.css ── */
@layer blocks {
/* Native buttons never carry a shadow */
button {
  box-shadow: none;
}

/* Solid uppercase button / link */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  align-self: flex-start;
  min-height: 44px;
  border-radius: var(--radius);
  font-size: var(--step--1);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tracking-button);
  line-height: 1.2;
  text-decoration: none;
  color: var(--color-on-dark);
  cursor: pointer;
}

.btn:hover,
.btn:focus-visible {
  color: var(--color-on-dark);
  text-decoration: none;
  filter: brightness(1.1);
}

/* Secondary — navy */
.btn--navy {
  background: var(--color-brand-navy);
  padding: 12px 18px;
}

/* Primary — teal */
.btn--brand {
  background: var(--color-brand);
  padding: 11px 16px;
}

/* Green — the status "go" green with --color-on-status text (white; ink on dark) */
.btn--go {
  background: var(--color-go);
  color: var(--color-on-status);
  padding: 11px 16px;
}

.btn--go:hover,
.btn--go:focus-visible {
  color: var(--color-on-status);
}

/* On the dark theme the navy button would vanish into the band. Pinned dark, and the
   system preference when nothing is pinned (tokens.css) */
[data-theme="dark"] .btn--navy {
  background: var(--color-brand-bright);
  color: var(--color-brand-deep);
}

[data-theme="dark"] .btn--navy:hover,
[data-theme="dark"] .btn--navy:focus-visible {
  color: var(--color-brand-deep);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .btn--navy {
    background: var(--color-brand-bright);
    color: var(--color-brand-deep);
  }

  :root:not([data-theme="light"]) .btn--navy:hover,
  :root:not([data-theme="light"]) .btn--navy:focus-visible {
    color: var(--color-brand-deep);
  }
}
}
/* ── blocks/tide.css ── */
@layer blocks {
/* Tides sit on the periwinkle band (.band[data-tone="peri"]); the curve and pin
   are the periwinkle ink */

.tide-band {
  --tide-ink: var(--color-accent);          /* #1E2A6B; the pastel periwinkle on dark */
}

.tide-content {
  --flow-space: 14px;
}

/* Day chips */
.tide-days {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 4px;
}

.tide-day {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 44px;
  padding: 6px 0;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--color-chip);
  color: var(--band-text, var(--color-text));
  font-size: var(--step--1);
  font-weight: 600;
  line-height: 1.2;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: pointer;
}

.tide-day__num {
  font-weight: 400;
  font-variant-numeric: tabular-nums;
}

.tide-day.active {
  background: var(--color-text);
  border-color: var(--color-text);
  color: var(--color-bg);
}

/* Dip day — the same periwinkle ink as the pin */
.tide-day.dip-day {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-on-accent);
}

/* Curve */
.tide-curve {
  width: 100%;
  height: auto;
  display: block;
  font-family: var(--font-body);
  font-style: normal; /* reset.css italicises svg as an image fallback */
}

.tide-curve__fill {
  fill: color-mix(in srgb, var(--tide-ink) 12%, transparent);
}

.tide-curve__line {
  fill: none;
  stroke: var(--tide-ink);
  stroke-width: 2.5;
  stroke-linecap: round;
}

.tide-axis {
  stroke: var(--band-line, var(--color-border));
  stroke-width: 1;
}

.tide-axis-label {
  fill: var(--band-muted, var(--color-muted));
  font-size: 12px;
  font-weight: 500;
}

/* Dip pin — --color-accent, the same ink as the curve */
.tide-pin-pill,
.tide-pin-tri {
  fill: var(--color-accent);
}

.tide-pin-label {
  fill: var(--color-on-accent);
  font-size: 12px;
  font-weight: 700;
}

.tide-pin-stem {
  stroke: var(--color-accent);
  stroke-width: 2;
}

.tide-pin-dot {
  fill: var(--color-on-dark);
  stroke: var(--color-accent);
  stroke-width: 2.5;
}

/* High / low water rows */
.tide-grid {
  --grid-space: 12px;
}

.tide-time,
.tide-height {
  font-variant-numeric: tabular-nums;
}
}
/* ── blocks/footer.css ── */
@layer blocks {
/* Footer — deep-navy band (.band[data-tone="deep"]) in two columns:
   brand (bot icon, Updated line, strapline) left, data sources right. */
.app-footer {
  --band-padding: 28px 32px;
  container-type: inline-size;
  line-height: 1.6;
}

.app-footer p { margin: 0; }

.footer-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  align-items: start;
}

.footer-brand {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.footer-icon {
  color: var(--color-brand-bright);
}

.footer-strap {
  font-size: var(--step--1); /* the same size as the data list */
  font-weight: 700;
  line-height: 1.3;
  color: var(--color-on-dark);
}

.last-updated {
  font-size: var(--step--1); /* the same size as the data list */
  font-weight: 400;
  color: var(--color-on-dark-muted);
}

.footer-data {
  display: flex;
  flex-direction: column;
  gap: 6px;
  border-inline-start: var(--border-thin) solid var(--band-line);
  padding-inline-start: 16px;
}

.footer-label {
  font-size: var(--step--1);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--color-brand-bright);
}

.footer-sources {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.footer-sources li,
.footer-credit {
  margin: 0;
  font-size: var(--step--1); /* matches .text-muted-xs ("Updates every 15 minutes") */
  color: var(--color-on-dark-muted);
}

/* Links in the lists (OpenStreetMap, the design credit) read as list text and
   underline only on hover and keyboard focus */
.footer-sources a,
.footer-credit a {
  color: inherit;
  text-decoration: none;
}

.footer-sources a:hover,
.footer-sources a:focus-visible,
.footer-credit a:hover,
.footer-credit a:focus-visible {
  text-decoration: underline;
}

/* Design sits under Data with a little more room than the list's own gap */
.footer-sources + .footer-label {
  margin-block-start: var(--space-2xs);
}

/* A narrow phone with large text: one column, no rule */
@container (max-width: 360px) {
  .footer-grid {
    grid-template-columns: 1fr;
  }

  .footer-data {
    border-inline-start: 0;
    padding-inline-start: 0;
  }
}
}
/* ── blocks/skeleton.css ── */
@layer blocks {
.skeleton {
  display: inline-block;
  background: var(--band-line, var(--color-border));
  /* border-radius: 6px; */
  height: 1.3rem;
  width: 60px;
  animation: skeleton-pulse 1.5s ease-in-out infinite;
}

@keyframes skeleton-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

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

.error-msg {
  color: var(--color-nogo);
  opacity: 0.8;
}
}
/* ── blocks/badge.css ── */
@layer blocks {
.badge {
  display: inline-flex;
  align-items: center;
  min-height: 44px; /* same box as .btn (meeting point buttons) */
  padding: 11px 16px;
  border: 0;
  border-radius: var(--radius);
  background: var(--color-brand-bright);
  color: var(--color-text);
  font-size: var(--step--1);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  line-height: 1.2;
}

/* Classifications take the verdict colours: --color-on-status text (white, ink on
   dark), ink on the caution yellow */
.badge.excellent,
.badge.good       { background: var(--color-go); color: var(--color-on-status); }
.badge.sufficient { background: var(--color-caution); color: var(--color-brand-navy); }
.badge.poor       { background: var(--color-nogo); color: var(--color-on-status); }

/* No grade to show: a beach awaiting its first classification (or SEPA's
   "Unclassified") and a closed bathing water take an outline in the band's text
   colour, so they read as a status rather than a verdict */
.badge.pending,
.badge.closed {
  background: transparent;
  border: var(--border-thin) solid currentColor;
  padding: 10px 15px; /* the border keeps the box the same size as the filled badges */
  color: var(--band-text, var(--color-text));
}

/* .badge--large is still emitted by the water quality renderer but no longer
   changes the size: the badge sits beside the section heading at base size. */
}
/* ── blocks/overflow.css ── */
@layer blocks {
.overflow-item {
  padding-block: var(--space-3xs);
  border-block-start: var(--border-thin) solid var(--band-line, var(--color-border));
}

.overflow-name {
  color: var(--band-text, var(--color-text));
  font-weight: 600;
}

/* Status lines at the top of the sewage band: live discharges, then the RSW verdict */
.sewage-status {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
}

.sewage-status + .overflow-item {
  margin-block-start: var(--space-2xs);
}

.rsw-spill-msg {
  color: var(--band-text, var(--color-text));
}

/* No rule above: the gap alone sets the history apart from the status lines */
.releases-section {
  margin-block-start: var(--space-s);
}

.releases-header {
  font-size: var(--step-0);
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  color: var(--band-muted, var(--color-muted));
  margin-block-end: var(--space-3xs);
}

/* Release date groups */
.release-group {
  padding-block: var(--space-3xs);
}

.release-date {
  color: var(--band-text, var(--color-text));
  font-weight: 600;
  display: block;
  margin-block-end: var(--space-3xs);
}

/* Release table */
.release-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}

/* Duration takes what is left: the third (Impacting) column is only there with RSW data */
.release-table col:nth-child(1) { width: 25%; }
.release-table col:nth-child(3) { width: 25%; }

.release-table th {
  padding-block: var(--space-3xs);
  font-size: var(--step--1);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--band-muted, var(--color-muted));
  text-align: start;
  border: none;
  border-block-end: var(--border-thin) solid var(--band-line, var(--color-border));
}

.release-table td {
  padding-block: var(--space-2xs);
  color: var(--band-text, var(--color-text));
  font-variant-numeric: tabular-nums;
  border: none;
}

.release-table tr + tr td {
  border-block-start: var(--border-thin) solid var(--band-line, var(--color-border));
}

.release-table .td-time     { color: var(--band-text, var(--color-text)); font-weight: 600; }
.release-table .td-duration { color: var(--band-muted, var(--color-muted)); }
.release-table th:nth-child(3),
.release-table td:nth-child(3) { text-align: center; font-weight: 600; }

/* Incidents */
.section-header {
  color: var(--band-text, var(--color-text));
  margin-block-end: var(--space-2xs);
}

.incident-row {
  padding-block: var(--space-3xs);
}

.incident-row + .incident-row {
  border-block-start: var(--border-thin) solid var(--band-line, var(--color-border));
}

.incident-date {
  color: var(--band-text, var(--color-text));
}

.incident-reason {
  color: var(--band-muted, var(--color-muted));
  margin-block-start: 2px;
}

/* Water quality band — the classification badge sits under the heading */
.quality-class {
  display: flex;
}

.quality-sample {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-block-start: var(--space-2xs);
}

.quality-source {
  margin-block-start: var(--space-2xs);
}

.quality-sample__label {
  font-size: var(--step-0); /* matches .data-key */
  font-weight: 400;
  color: var(--band-muted, var(--color-muted));
}

.quality-sample__body {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Water quality sample row */
.sample-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.sample-label {
  font-weight: 600;
  color: var(--band-text, var(--color-text));
}
}
/* ── blocks/places.css ── */
@layer blocks {
/* Place bands — the saunas and cafés near the beach from the index (renderSaunas(),
   renderCafes() via placeRow()): up to three rows of name, then a muted detail line,
   with a Website link beside them; or one line inviting a place to be added to
   OpenStreetMap (placeEmpty()). Colours follow the band tone. */
.place-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.place-item {
  display: grid;
  grid-template-columns: 1fr auto;
  column-gap: var(--space-xs);
  align-items: center;
  padding-block: var(--space-2xs);
  border-block-start: var(--border-thin) solid var(--band-line, var(--color-border));
}

.place-item:last-child {
  border-block-end: var(--border-thin) solid var(--band-line, var(--color-border));
}

.place-name {
  grid-column: 1;
  font-size: var(--step-0);
  font-weight: 700;
  color: var(--band-text, var(--color-text));
}

.place-detail {
  grid-column: 1;
  font-size: var(--step--1);
  color: var(--band-muted, var(--color-muted));
}

.place-link {
  grid-column: 2;
  grid-row: 1 / span 2;
  font-weight: 600;
  color: var(--band-text, var(--color-text));
  white-space: nowrap;
}

.place-empty {
  color: var(--band-muted, var(--color-muted));
}

.place-empty a {
  color: var(--band-text, var(--color-text));
}
}
/* ── blocks/helpers.css ── */
@layer blocks {
/* Muted text helpers (used in JS-rendered content) */
.text-muted-sm {
  color: var(--band-muted, var(--color-muted));
}

.text-muted-xs {
  color: var(--band-muted, var(--color-muted));
  font-size: var(--step--1);
}

.text-muted-xs.mt {
  margin-block-start: var(--space-3xs);
}

.mt-md { margin-block-start: var(--space-3xs); }
.mb-sm { margin-block-end: var(--space-2xs); }

/* Wind direction arrow; the renderer passes the bearing as --wind-deg */
.wind-arrow {
  display: inline-block;
  rotate: var(--wind-deg, 0deg);
}
}

/* ── exceptions/tv.css ── */
@layer exceptions {
/* ── TV board ──
   `?tv` sets [data-tv] on <html> (see "TV mode" in index.html). The page becomes
   the Verdict board (TV-BRIEF.md): one unscrolled 1920×1080 screen read from across
   a room. Header, then the verdict beside four driver tiles (Sea, Wind, Weather,
   Sewage), then the tide curve. The beach name is the header's pill.
   Same DOM as the phone: main, the hero and the footer flatten (display: contents)
   so their parts sit on one body grid.

   Every size is the brief's pixel value at 1920×1080, written as calc(N * var(--tv)).
   The root font-size scales the whole board to fit whatever screen it lands on. */

:root[data-tv] {
  /* A 96 × 54rem canvas that always fits the screen: 1rem is 20px at 1920×1080 */
  font-size: min(100vw / 96, 100vh / 54);
  --tv: calc(1rem / 20); /* one design pixel */
  /* The board is the same in both themes: Sea Glass light (tokens.css), pinned
     here so nothing a viewer set can reach it. Bright for a café wall */
  --color-brand-deep: #EEF6F3;               /* the pale mint page ground */
  --color-brand: #1F6F6B;                    /* teal: bot, eyebrow, beach pill */
  --color-on-dark: #FFFFFF;                  /* the tide pin's dot */
  --color-go: #2C9C6E;
  --color-caution: #FFC400;
  --color-nogo: #ca3535;
  --color-accent: #1E2A6B;                   /* periwinkle ink: tide curve and pin */
  --color-on-accent: #FFFFFF;
  --tv-ink: #163A3A;
  --tv-label: #3F5F5D;
  --tv-body: rgba(22, 58, 58, 0.8);
  --tv-muted: #3F5F5D;
  /* One pastel tone per driver tile, as the phone's bands: Sea mint, Wind sand,
     Weather periwinkle, Sewage coral. The pastel fills, its muted ink labels */
  --tv-sea-fill: #BFEBD9;     --tv-sea-label: #2F5F5B;     --tv-sea-line: rgba(22, 58, 58, 0.16);
  --tv-wind-fill: #F3E6CC;    --tv-wind-label: #6F5A2A;    --tv-wind-line: rgba(111, 90, 42, 0.20);
  --tv-weather-fill: #C9D2F7; --tv-weather-label: #4B5AA6; --tv-weather-line: rgba(30, 42, 107, 0.18);
  --tv-sewage-fill: #F6C2AE;  --tv-sewage-label: #7A3E28;  --tv-sewage-line: rgba(122, 62, 40, 0.20);
}

/* ── Frame: 40px padding, three rows (header 96 · main 488 · tide 352) with 32px
   between them. The main row's right side is the 2×2 of driver tiles, whose gaps,
   padding and inner rows (label · value · secondary) are explicit tracks: each tile
   is a pseudo-element box and its value sits on the same grid, since the values
   live in sections the phone lays out differently ── */
[data-tv] body {
  block-size: 100vh;
  overflow: hidden;
  display: grid;
  /* verdict · gap · [pad · tile content · pad] · gap · [pad · tile content · pad] */
  grid-template-columns:
    calc(920 * var(--tv)) calc(32 * var(--tv))
    calc(32 * var(--tv)) calc(372 * var(--tv)) calc(32 * var(--tv))
    calc(16 * var(--tv))
    calc(32 * var(--tv)) calc(372 * var(--tv)) calc(32 * var(--tv));
  /* header · gap · [pad · label · 24 · value · 14 · secondary · the rest] · gap ·
     [the same] · gap · tide */
  grid-template-rows:
    calc(96 * var(--tv)) calc(32 * var(--tv))
    calc(24 * var(--tv)) calc(29 * var(--tv)) calc(24 * var(--tv)) calc(84 * var(--tv)) calc(14 * var(--tv)) calc(32 * var(--tv)) minmax(0, 1fr)
    calc(16 * var(--tv))
    calc(24 * var(--tv)) calc(29 * var(--tv)) calc(24 * var(--tv)) calc(84 * var(--tv)) calc(14 * var(--tv)) calc(32 * var(--tv)) minmax(0, 1fr)
    calc(32 * var(--tv)) calc(352 * var(--tv));
  padding: calc(40 * var(--tv));
  /* A screen that is not 16:9 (a laptop at 16:10, say) gets equal margins, not a
     board pushed to one edge */
  align-content: center;
  justify-content: center;
  background: var(--color-brand-deep);
  color: var(--tv-ink);
}

[data-tv] main,
[data-tv] .hero,
[data-tv] #hero-content,
[data-tv] .summary-icons,
[data-tv] [aria-labelledby="sea-heading"],
[data-tv] [aria-labelledby="wind-heading"],
[data-tv] .band__inner,
[data-tv] .data-grid:not(.tide-grid),
[data-tv] .data-item:not(.tide-row),
[data-tv] .app-footer,
[data-tv] .app-footer > .center,
[data-tv] .footer-grid,
[data-tv] .footer-brand {
  display: contents;
}

/* Nothing on the board is tapped, and the phone's search, summary, water quality,
   sewage detail, saunas, cafés and footer stay on the phone */
[data-tv] .fyi,
[data-tv] .theme-toggle,
[data-tv] .app-header__search,
[data-tv] .beach-title__place,
[data-tv] .summary-icon-col:not(:has(#sum-sewage)),
[data-tv] .summary-icon-col > *,
[data-tv] .card-label,
[data-tv] .data-key:not(.tide-type),
[data-tv] .data-value:not(#water-temp, #wind-speed, #air-temp, .tide-value),
[data-tv] #best-window-band,
[data-tv] #ea-card,
[data-tv] #overflow-card,
[data-tv] #sauna-card,
[data-tv] #cafe-card,
[data-tv] .meeting-band,
[data-tv] .footer-icon,
[data-tv] .footer-strap,
[data-tv] .footer-data {
  display: none;
}

/* Text that carries data holds one line */
[data-tv] .app-title,
[data-tv] .beach-title__name,
[data-tv] .last-updated,
[data-tv] .verdict-heading,
[data-tv] .data-value,
[data-tv] .tide-value {
  white-space: nowrap;
}

/* ── Header row: bot icon and title stack on the left; beach pill and clock on the
   right. The three share the row: the header spans it and the other two sit at its
   end, the pill set in from the clock's fixed width plus a 24px gap ── */
[data-tv] .app-header {
  grid-area: 1 / 1 / 2 / 10;
  padding: 0;
  border: 0;
  background: none;
  display: flex;
  align-items: center;
}

[data-tv] .app-header__inner {
  --content-width: none;
  --gutter: 0;
  padding: 0;
}

[data-tv] .app-brand {
  --cluster-gap: calc(24 * var(--tv));
  align-items: center;
}

[data-tv] .app-logo {
  display: none;
}

/* Lucide `bot` at 72px as an image (img/bot.svg, cyan baked in): a CSS mask
   drew nothing on the TV's browser */
[data-tv] .app-brand::before {
  content: "";
  flex-shrink: 0;
  width: calc(72 * var(--tv));
  height: calc(72 * var(--tv));
  background: url("/img/bot.svg") center / contain no-repeat;
}

[data-tv] .app-title {
  gap: calc(2 * var(--tv));
}

[data-tv] .app-title .eyebrow {
  font-size: calc(24 * var(--tv));
  font-weight: 600;
  letter-spacing: 0.14em;
  line-height: 1.2;
  color: var(--color-brand);
}

[data-tv] .app-header h1 {
  font-size: calc(40 * var(--tv));
  font-weight: 700;
  letter-spacing: -0.5px;
  line-height: 1.1;
  color: var(--tv-ink);
}

/* Beach: the name as a pill, set in from the clock's fixed width plus a 24px gap.
   A long name ellipses rather than running into the title stack */
[data-tv] .beach-title {
  grid-area: 1 / 1 / 2 / 10;
  justify-self: end;
  align-self: center;
  margin-inline-end: calc((226 + 24) * var(--tv));
  max-inline-size: calc(1000 * var(--tv));
  min-inline-size: 0;
}

[data-tv] .beach-title__name {
  padding: calc(14 * var(--tv)) calc(28 * var(--tv));
  border-radius: var(--radius);
  background: var(--color-brand);
  color: #FFFFFF;
  font-size: calc(32 * var(--tv));
  font-weight: 700;
  letter-spacing: 0.1em;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Clock: the data-updated time (the bare HH:MM in TV mode) over its label, set
   like a tide event: the value at 32px/600, the label at 32px/500 muted. The box
   is the label's width (Poppins at 32px), so the text starts a fixed distance
   from the beach pill and the label ends on the board's edge */
[data-tv] .last-updated {
  grid-area: 1 / 1 / 2 / 10;
  justify-self: end;
  align-self: center;
  width: calc(226 * var(--tv));
  text-align: start;
  font-size: calc(32 * var(--tv));
  font-weight: 600;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  color: var(--tv-ink);
}

[data-tv] .last-updated::after {
  content: "Data updated";
  display: block;
  font-weight: 500;
  color: var(--tv-muted);
}

/* ── Main row ── */
[data-tv] .verdict-panel { grid-area: 3 / 1 / 18 / 2; }

/* ── Driver tiles: the box and its label are one pseudo-element; the value (with
   its second line as ::after, from data-tv) sits in the tile's value rows ── */
[data-tv] [aria-labelledby="sea-heading"]::before,
[data-tv] [aria-labelledby="wind-heading"]::before,
[data-tv] [aria-labelledby="wind-heading"]::after,
[data-tv] main::after {
  padding: calc(24 * var(--tv)) calc(32 * var(--tv));
  background: var(--tile-fill);
  border: 1px solid var(--tile-line);
  font-size: calc(24 * var(--tv));
  font-weight: 600;
  letter-spacing: 0.1em;
  line-height: 1.2;
  text-transform: uppercase;
  color: var(--tile-label);
  white-space: nowrap;
}

[data-tv] [aria-labelledby="sea-heading"]::before  { content: "Sea";     grid-area: 3 / 3 / 10 / 6;   --tile-fill: var(--tv-sea-fill);     --tile-label: var(--tv-sea-label);     --tile-line: var(--tv-sea-line); }
[data-tv] [aria-labelledby="wind-heading"]::before { content: "Wind";    grid-area: 3 / 7 / 10 / 10;  --tile-fill: var(--tv-wind-fill);    --tile-label: var(--tv-wind-label);    --tile-line: var(--tv-wind-line); }
[data-tv] [aria-labelledby="wind-heading"]::after  { content: "Weather"; grid-area: 11 / 3 / 18 / 6;  --tile-fill: var(--tv-weather-fill); --tile-label: var(--tv-weather-label); --tile-line: var(--tv-weather-line); }
[data-tv] main::after                              { content: "Sewage";  grid-area: 11 / 7 / 18 / 10; --tile-fill: var(--tv-sewage-fill);  --tile-label: var(--tv-sewage-label);  --tile-line: var(--tv-sewage-line); }

/* The Weather and Sewage boxes are ::after, painted after their values, and the
   tone fills are opaque, so every value (and the sewage line) stacks above its box */
[data-tv] #water-temp,
[data-tv] #wind-speed,
[data-tv] #air-temp,
[data-tv] #sum-sewage,
[data-tv] .summary-icon-col:has(#sum-sewage)::after {
  z-index: 1;
}

[data-tv] #water-temp { grid-area: 6 / 4 / 9 / 5; }
[data-tv] #wind-speed { grid-area: 6 / 8 / 9 / 9; }
[data-tv] #air-temp   { grid-area: 14 / 4 / 17 / 5; }

[data-tv] #water-temp,
[data-tv] #wind-speed,
[data-tv] #air-temp {
  font-size: calc(84 * var(--tv));
  font-weight: 700;
  line-height: 1;
  letter-spacing: -2px;
  font-variant-numeric: tabular-nums;
  color: var(--tv-ink);
}

/* A feed that failed says "unavailable" at the second line's size, not the value's */
[data-tv] .error-msg {
  font-size: calc(26 * var(--tv));
  font-weight: 500;
  letter-spacing: 0;
}

[data-tv] #water-temp::after,
[data-tv] #wind-speed::after,
[data-tv] #air-temp::after,
[data-tv] .summary-icon-col:has(#sum-sewage)::after {
  content: attr(data-tv);
  display: block;
  margin-block-start: calc(14 * var(--tv));
  font-size: calc(26 * var(--tv));
  font-weight: 500;
  line-height: calc(32 * var(--tv));
  letter-spacing: 0;
  color: var(--tv-body);
  white-space: nowrap;
}

/* Sewage: the phone's summary tile, read like the other three — its Yes / No in
   the value rows, the sewage line (data-tv) under it. The tile's wrapper flattens
   so both sit on the body grid */
[data-tv] .summary-icon-col:has(#sum-sewage) {
  display: contents;
}

[data-tv] #sum-sewage {
  display: block;
  grid-area: 14 / 8 / 15 / 9;
  font-size: calc(84 * var(--tv));
  font-weight: 700;
  line-height: 1;
  letter-spacing: -2px;
  color: var(--tv-ink);
}

[data-tv] .summary-icon-col:has(#sum-sewage)::after {
  grid-area: 15 / 8 / 17 / 9;
  margin-block-start: 0;
  padding-block-start: calc(14 * var(--tv));
}

/* ── Tide row ── */
[data-tv] .tide-band { grid-area: 19 / 1 / 20 / 10; }

[data-tv] .band {
  --band-padding: calc(24 * var(--tv));
  --gutter: calc(32 * var(--tv));
  --content-width: none;
  overflow: hidden;
}

/* ── Verdict panel: filled with the status colour, so the whole panel is the
   status and the dot goes. "Dipbot says", then the date over the verdict, with the
   reasons pushed to the foot ── */
[data-tv] .verdict-panel {
  min-block-size: 0;
  overflow: hidden;
  margin-block-start: 0; /* not a .flow sibling here */
  gap: calc(20 * var(--tv));
  /* Vertical padding matches the tiles', so "Dipbot says" lines up with their labels */
  padding: calc(24 * var(--tv)) calc(56 * var(--tv));
  border-inline-start-width: 0;
}

[data-tv] .verdict-panel[data-status="go"]      { background: var(--color-go); }
[data-tv] .verdict-panel[data-status="caution"] { background: var(--color-caution); }
[data-tv] .verdict-panel[data-status="nogo"]    { background: var(--color-nogo); }

/* Go and no-go read in white; the caution yellow is too light for white (1.6:1),
   so its panel reads in ink */
[data-tv] .verdict-panel                        { --color-on-status: #FFFFFF; }
[data-tv] .verdict-panel[data-status="caution"] { --color-on-status: var(--tv-ink); }
[data-tv] .verdict-panel :is(.verdict-heading, .next-dip-date, .status-label) { color: var(--color-on-status); }

[data-tv] .status-dot {
  display: none;
}

[data-tv] .verdict-panel .eyebrow {
  font-size: calc(28 * var(--tv));
  font-weight: 600;
  letter-spacing: 0.14em;
  line-height: 1.2;
  color: color-mix(in srgb, var(--color-on-status) 80%, transparent);
}

/* The date and the verdict stack as two blocks (the <br> between them steps aside) */
[data-tv] .verdict-heading {
  font-size: calc(64 * var(--tv));
  line-height: 1;
  letter-spacing: -2px;
}

[data-tv] .verdict-heading br {
  display: none;
}

[data-tv] .next-dip-date {
  display: block;
  margin-block-end: calc(12 * var(--tv));
}

[data-tv] .status-label {
  display: block;
  white-space: nowrap;
  font-size: calc(120 * var(--tv));
}

/* "NOT RECOMMENDED." is too wide for the panel at 120px; 80px fills it */
[data-tv] .status-label.nogo {
  font-size: calc(80 * var(--tv));
}

/* The same size whatever the verdict; five reasons under a No-Go fill the panel */
[data-tv] .status-reason {
  margin-block-start: auto;
  font-size: calc(32 * var(--tv));
  font-weight: 500;
  line-height: 1.3;
  color: color-mix(in srgb, var(--color-on-status) 88%, transparent);
  white-space: pre; /* one reason per line, none wrapping */
  overflow: hidden;
  text-overflow: ellipsis; /* the sewage reasons can run past the panel */
}

/* ── Tide row: "Tide", the dip day's curve at 1840×224 and its events in one row.
   The day chips go; the board is always the dip day ── */
[data-tv] .tide-band {
  padding: 0;
  background: none;
  display: flex;
  flex-direction: column;
  gap: calc(12 * var(--tv));
}

[data-tv] .tide-band > .tide-content {
  display: contents;
}

[data-tv] #tides-content {
  --flow-space: calc(12 * var(--tv));
}

/* The phone's "Tides this week" heading reads "Tide" here. The adjust reset matters:
   with body's font-size-adjust inherited, Chrome draws font-size: 0 text at a
   readable size */
[data-tv] #tides-heading {
  display: block;
  font-size: 0;
  font-size-adjust: none;
}

[data-tv] #tides-heading::before {
  content: "Tide";
  font-size: calc(24 * var(--tv));
  font-weight: 600;
  letter-spacing: 0.1em;
  line-height: 1.2;
  text-transform: uppercase;
  color: var(--tv-label);
}

[data-tv] .tide-days {
  display: none;
}

[data-tv] .tide-curve {
  width: calc(1840 * var(--tv));
  max-inline-size: none; /* global.css caps block content at the reading measure */
  height: calc(224 * var(--tv));
}

/* Read from across the room: a 5px line, 2px axis, 24px hour labels */
[data-tv] .tide-curve__fill  { fill: rgba(201, 210, 247, 0.55); }
[data-tv] .tide-curve__line  { stroke-width: 5; }
[data-tv] .tide-axis         { stroke: rgba(22, 58, 58, 0.2); stroke-width: 2; }
[data-tv] .tide-axis-label   { font-size: 24px; fill: var(--tv-muted); }
[data-tv] .tide-pin-label    { font-size: 26px; }
[data-tv] .tide-pin-stem     { stroke-width: 6; }
[data-tv] .tide-pin-dot      { stroke-width: 6; }

/* Events under the curve, spread so each sits roughly under its point */
[data-tv] .tide-grid {
  display: flex;
  justify-content: space-between;
}

[data-tv] .tide-row {
  display: flex;
  flex-direction: row; /* the phone stacks the label over the value */
  align-items: baseline;
  gap: calc(10 * var(--tv));
  font-size: calc(32 * var(--tv));
  line-height: 1.2;
}

[data-tv] .tide-type {
  font-size: inherit;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--tv-muted);
}

[data-tv] .tide-value {
  font-size: inherit;
  font-weight: 600;
  line-height: inherit;
  font-variant-numeric: tabular-nums;
  color: var(--tv-ink);
}

[data-tv] #tides-content .text-muted-sm {
  font-size: calc(24 * var(--tv));
}
}
