/*
Theme Name: DMR.ee 2026
Theme URI: https://new.dmr.ee/
Author: DMR.ee
Description: Classic WordPress theme for the new DMR.ee staging site.
Version: 1.1.0
Requires at least: 6.0
Tested up to: 7.1
Text Domain: dmr-ee-2026
*/

:root {
  --font-sans: "Source Sans 3", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-heading: "Archivo", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
  --font-tech: "Saira Semi Condensed", "Saira", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

  /* MD3 color roles -- kept our own blue/ink palette, mapped onto
     Material's role system instead of Google's own colors.
     --teal is the legacy variable name; --blue aliases it. */
  --page: #eef2f3;
  --panel: #ffffff;
  --panel-soft: #f7f9fa;
  --ink: #172025;
  --muted: #647179;
  --line: #d9e0e3;
  --teal: #2a6f97;
  --teal-dark: #1e4f6c;
  --blue: var(--teal);
  --orange: #dc7b2f;
  --red: #ba3b46;
  --live-green: #3ecf7e;

  --md-primary: var(--teal);
  --md-on-primary: #ffffff;
  --md-primary-container: #e1eaf0;
  --md-on-primary-container: #0e2634;
  --md-surface: var(--panel);
  --md-surface-container: #f4f7f7;
  --md-surface-container-high: #eef2f2;
  --md-on-surface: var(--ink);
  --md-on-surface-variant: var(--muted);
  --md-outline: #c6cfd1;
  --md-outline-variant: #e2e8e8;

  /* MD3 state layers: semi-transparent overlays of the interactive
     element's own color, not a separate hardcoded hover color. */
  --md-state-hover: 0.08;
  --md-state-focus: 0.12;
  --md-state-press: 0.12;

  /* MD3 shape scale */
  --md-shape-xs: 4px;
  --md-shape-sm: 8px;
  --md-shape-md: 12px;
  --md-shape-lg: 16px;
  --md-shape-xl: 28px;
  --md-shape-full: 999px;

  /* MD3 elevation: dual-layer shadows (key + ambient), each level
     stronger than the last. Level 0 has no shadow. */
  --md-elevation-1: 0 1px 2px rgba(23, 32, 37, 0.14), 0 1px 3px 1px rgba(23, 32, 37, 0.08);
  --md-elevation-2: 0 1px 2px rgba(23, 32, 37, 0.16), 0 2px 6px 2px rgba(23, 32, 37, 0.1);
  --md-elevation-3: 0 4px 8px 3px rgba(23, 32, 37, 0.1), 0 1px 3px rgba(23, 32, 37, 0.18);

  --md-motion-standard: 200ms cubic-bezier(0.2, 0, 0, 1);
  --md-motion-emphasized: 300ms cubic-bezier(0.2, 0, 0, 1);

  /* Kept for any not-yet-migrated rules; new work should use the
     --md-* tokens above instead. */
  --shadow: var(--md-elevation-3);
  --shadow-soft: var(--md-elevation-1);
  --radius: var(--md-shape-md);
  --radius-sm: var(--md-shape-sm);
}

* {
  box-sizing: border-box;
}

html {
  min-height: 100%;
  background:
    linear-gradient(rgba(23, 32, 37, 0.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(23, 32, 37, 0.028) 1px, transparent 1px),
    radial-gradient(circle at 18% 0%, rgba(42, 111, 151, 0.11), transparent 32rem),
    radial-gradient(circle at 85% 12rem, rgba(220, 123, 47, 0.08), transparent 24rem),
    var(--page);
  background-size: 34px 34px, 34px 34px, auto, auto, auto;
  background-attachment: fixed;
}

body {
  margin: 0;
  min-width: 320px;
  background: transparent;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 1.02rem;
  line-height: 1.48;
  overflow-wrap: break-word;
}

a {
  color: inherit;
  text-decoration: none;
  transition: color 140ms ease;
}

a:hover {
  color: var(--teal-dark);
}

/* Headings with links */
h1 a,
h2 a,
h3 a,
h4 a,
h5 a,
h6 a {
  color: var(--ink);
}

h1 a:visited,
h2 a:visited,
h3 a:visited,
h4 a:visited,
h5 a:visited,
h6 a:visited {
  color: var(--ink);
}

h1 a:hover,
h2 a:hover,
h3 a:hover,
h4 a:hover,
h5 a:hover,
h6 a:hover {
  color: var(--teal-dark);
}

img {
  max-width: 100%;
  height: auto;
}

button,
input,
textarea,
select {
  font: inherit;
}

.site-sticky-top {
  position: sticky;
  top: 0;
  z-index: 40;
  box-shadow: 0 8px 24px rgba(23, 32, 37, 0.12);
}

/* WP's admin bar (position: fixed, top: 0, z-index: 99999) sits at the
   true viewport top. html { margin-top: 32px/46px } (injected by
   wp-includes/admin-bar.php) only affects where content starts in the
   unscrolled document -- it does NOT change what "top: 0" means for a
   position: sticky element, which sticks relative to the viewport
   itself. So on scroll, our sticky header's top:0 collides with the
   admin bar regardless of that margin.

   Static 32px/46px alone isn't enough either: some mobile browsers'
   own collapsing dynamic toolbar can make the admin bar visually
   drift from the layout viewport it's fixed to, leaving a static
   offset either overlapping it or reserving dead space once it's not
   really there. live.js measures the bar's real position on
   scroll/resize and sets --dmr-admin-bar-offset; these rules are just
   the pre-JS/no-JS fallback so there's no flash of wrong position
   before that runs. */
.admin-bar .site-sticky-top {
  top: 32px;
  top: var(--dmr-admin-bar-offset, 32px);
}

@media screen and (max-width: 782px) {
  .admin-bar .site-sticky-top {
    top: 46px;
    top: var(--dmr-admin-bar-offset, 46px);
  }
}


.site-header {
  position: relative;
  z-index: 2;
  border-bottom: 1px solid rgba(23, 32, 37, 0.08);
  background: rgba(250, 252, 252, 0.96);
  backdrop-filter: blur(16px);
}

.site-sticky-top.is-scrolled .site-header {
  border-bottom: 1px solid rgba(23, 32, 37, 0.06);
}

.header-inner,
.live-strip-inner,
.page-grid,
.content-shell {
  width: min(1180px, calc(100% - 40px));
  margin-right: auto;
  margin-left: auto;
}

.header-inner {
  display: flex;
  align-items: center;
  gap: 28px;
  min-height: 58px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-heading);
  font-weight: 800;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

.brand-mark {
  display: grid;
  place-items: center;
  width: 44px;
  height: 30px;
  border-radius: 4px 0 4px 4px;
  clip-path: polygon(
    0 0,
    calc(100% - 8.68px) 0,
    calc(100% - 8.2px) 0.04px,
    calc(100% - 7.6px) 0.18px,
    calc(100% - 7.0px) 0.45px,
    calc(100% - 6.42px) 0.94px,
    calc(100% - 0.94px) 6.42px,
    calc(100% - 0.45px) 7.0px,
    calc(100% - 0.18px) 7.6px,
    calc(100% - 0.04px) 8.2px,
    100% 8.68px,
    100% 100%,
    0 100%
  );
  background:
    linear-gradient(225deg, var(--teal) 0%, var(--teal) 5.5px, transparent 6.5px),
    linear-gradient(180deg, rgba(255, 255, 255, 0.18) 0%, transparent 60%),
    var(--ink);
  color: #ffffff !important;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  border-left: 2px solid var(--teal);
  filter: drop-shadow(0 0 1px rgba(23, 32, 37, 0.45)) drop-shadow(0 3px 8px rgba(23, 32, 37, 0.16));
}

.brand-name {
  font-size: 1.1rem;
  font-weight: 800;
}

.main-nav {
  display: flex;
  align-items: center;
  gap: 24px;
  flex: 1;
  font-family: var(--font-heading);
  font-size: 0.82rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.main-nav ul {
  display: flex;
  align-items: center;
  gap: 24px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.main-nav a {
  position: relative;
  padding: 20px 0;
}

.main-nav a::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: 14px;
  left: 0;
  height: 2px;
  background: var(--teal);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 160ms ease;
}

.main-nav a:hover::after {
  transform: scaleX(1);
}

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

.icon-button,
.login-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: #263137;
  font-weight: 700;
}

.icon-button {
  width: 36px;
  height: 36px;
  font-size: 1.45rem;
}

.login-button {
  min-height: 36px;
  padding: 0 14px;
  background: #253038;
  color: #fff;
  font-size: 0.82rem;
}

.menu-toggle {
  position: relative;
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: var(--md-shape-full);
  background: var(--md-surface-container-high);
  color: var(--ink);
  cursor: pointer;
  overflow: hidden;
  transition: background var(--md-motion-standard);
}

.menu-toggle::before {
  content: "";
  position: absolute;
  inset: 0;
  background: currentColor;
  opacity: 0;
  transition: opacity var(--md-motion-standard);
  pointer-events: none;
}

.menu-toggle:hover::before {
  opacity: var(--md-state-hover);
}

.menu-toggle:active::before {
  opacity: var(--md-state-press);
}

.menu-toggle span {
  display: block;
  width: 18px;
  height: 2px;
  border-radius: 2px;
  background: var(--ink);
  transition: transform 180ms ease, opacity 180ms ease;
}

.mobile-drawer {
  position: fixed;
  inset: 0;
  z-index: 100;
  visibility: hidden;
  pointer-events: none;
  transition: visibility 250ms ease;
}

/* WP's admin bar is z-index: 99999 -- without this, the drawer (and
   its backdrop) render underneath it when logged in, since 100 is far
   below that. Same root cause as the sticky-header/admin-bar overlap
   fixed earlier; same fix pattern (scope the override to .admin-bar). */
.admin-bar .mobile-drawer {
  z-index: 100000;
}

.mobile-drawer.is-open {
  visibility: visible;
  pointer-events: auto;
}

.mobile-drawer-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(23, 32, 37, 0.5);
  backdrop-filter: blur(4px);
  opacity: 0;
  transition: opacity 250ms ease;
}

.mobile-drawer.is-open .mobile-drawer-backdrop {
  opacity: 1;
}

.mobile-drawer-panel {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(300px, 85vw);
  background: var(--md-surface-container-high);
  border-radius: var(--md-shape-lg) 0 0 var(--md-shape-lg);
  box-shadow: var(--md-elevation-3);
  transform: translateX(100%);
  transition: transform var(--md-motion-emphasized);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}

.mobile-drawer.is-open .mobile-drawer-panel {
  transform: translateX(0);
}

.mobile-drawer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--md-outline-variant);
}

.drawer-title {
  font-family: var(--font-heading);
  font-size: 1.1rem;
  font-weight: 800;
}

.drawer-close {
  position: relative;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: var(--md-shape-full);
  background: transparent;
  color: var(--md-on-surface);
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  overflow: hidden;
  transition: background var(--md-motion-standard);
}

.drawer-close::before {
  content: "";
  position: absolute;
  inset: 0;
  background: currentColor;
  opacity: 0;
  transition: opacity var(--md-motion-standard);
  pointer-events: none;
}

.drawer-close:hover::before {
  opacity: var(--md-state-hover);
}

.drawer-close:active::before {
  opacity: var(--md-state-press);
}

.mobile-nav {
  display: flex;
  flex-direction: column;
  padding: 14px 16px;
  gap: 6px;
}

.mobile-nav ul {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.mobile-nav a {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: 0 16px;
  border-radius: var(--md-shape-full);
  color: var(--md-on-surface);
  font-family: var(--font-heading);
  font-size: 0.95rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  overflow: hidden;
  transition: background var(--md-motion-standard);
}

.mobile-nav-home {
  color: var(--md-primary);
  margin-bottom: 6px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--md-outline-variant);
}

.mobile-nav a::before {
  content: "";
  position: absolute;
  inset: 0;
  background: currentColor;
  opacity: 0;
  transition: opacity var(--md-motion-standard);
  pointer-events: none;
}

.mobile-nav a:hover::before {
  opacity: var(--md-state-hover);
}

.mobile-nav a:active::before {
  opacity: var(--md-state-press);
}

.live-strip {
  position: relative;
  z-index: 1;
  border-bottom: 1px solid rgba(0, 0, 0, 0.2);
  background:
    linear-gradient(90deg, rgba(42, 111, 151, 0.22), transparent 34%),
    #1a242a;
}

.live-strip-inner {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 40px;
  color: rgba(255, 255, 255, 0.72);
  font-size: 0.85rem;
}

.live-strip-inner strong {
  color: #fff;
  font-family: var(--font-mono);
}

.live-last-wrap {
  color: rgba(255, 255, 255, 0.75);
}

.socket-player-button {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 42px;
  padding: 0 22px;
  border: 0;
  border-radius: var(--md-shape-full);
  background: var(--md-primary);
  color: var(--md-on-primary) !important;
  font-family: var(--font-heading);
  font-size: 0.88rem;
  font-weight: 700;
  cursor: pointer;
  box-shadow: var(--md-elevation-1);
  transition: box-shadow var(--md-motion-standard), background var(--md-motion-standard);
  text-decoration: none !important;
  overflow: hidden;
}

.socket-player-button::before {
  content: "";
  position: absolute;
  inset: 0;
  background: currentColor;
  opacity: 0;
  transition: opacity var(--md-motion-standard);
  pointer-events: none;
}

.socket-player-button:hover,
.socket-player-button:focus-visible {
  color: var(--md-on-primary) !important;
  box-shadow: var(--md-elevation-2);
}

.socket-player-button:hover::before {
  opacity: var(--md-state-hover);
}

.socket-player-button:focus-visible::before {
  opacity: var(--md-state-focus);
}

.socket-player-button:active {
  color: var(--md-on-primary) !important;
  box-shadow: var(--md-elevation-1);
}

.socket-player-button:active::before {
  opacity: var(--md-state-press);
}

.player-play-icon {
  font-size: 0.75rem;
}

/* Active state */
.socket-player.is-active .socket-player-button {
  background: #172025;
  color: #ffffff !important;
  box-shadow: var(--md-elevation-2);
}

.socket-player.is-active .socket-player-button:hover {
  background: #253038;
  color: #ffffff !important;
}

/* Volume controls: only visible when actively listening */
.socket-player-controls {
  display: none;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border-radius: 6px;
  background: #253038;
  color: #fff;
}

.socket-player.is-active .socket-player-controls {
  display: inline-flex;
}

.volume-icon {
  font-size: 0.85rem;
  opacity: 0.85;
}

.socket-player-volume {
  width: 120px;
  accent-color: var(--teal);
  cursor: pointer;
}

/* Listener count: visible always as a clean live metadata indicator */
.socket-player-listeners {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin: 0;
  font-size: 0.8rem;
  font-family: var(--font-mono, monospace);
  font-weight: 600;
  color: #20c997;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

/* Error state: only visible on socket error */
.socket-player-error {
  display: none;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: 6px;
  background: rgba(192, 57, 43, 0.12);
  color: #c0392b;
  border: 1px solid rgba(192, 57, 43, 0.25);
  font-size: 0.82rem;
  font-weight: 700;
}

.socket-player.has-error .socket-player-error:not(:empty) {
  display: inline-flex;
}

.live-pill {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 0 9px;
  border-radius: 3px 0 3px 3px;
  clip-path: polygon(
    0 0,
    calc(100% - 8.68px) 0,
    calc(100% - 8.2px) 0.04px,
    calc(100% - 7.6px) 0.18px,
    calc(100% - 7.0px) 0.45px,
    calc(100% - 6.42px) 0.94px,
    calc(100% - 0.94px) 6.42px,
    calc(100% - 0.45px) 7.0px,
    calc(100% - 0.18px) 7.6px,
    calc(100% - 0.04px) 8.2px,
    100% 8.68px,
    100% 100%,
    0 100%
  );
  background:
    linear-gradient(225deg, rgba(255, 255, 255, 0.45) 0%, rgba(255, 255, 255, 0.45) 5px, transparent 6.5px),
    linear-gradient(180deg, rgba(255, 255, 255, 0.22) 0%, transparent 70%),
    var(--teal);
  color: #ffffff !important;
  font-family: var(--font-tech);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  filter: drop-shadow(0 0 1px rgba(30, 79, 108, 0.45)) drop-shadow(0 2px 5px rgba(42, 111, 151, 0.22));
}

.live-pill.small {
  min-height: 22px;
}

.live-pill-dot {
  display: none;
  width: 6px;
  height: 6px;
  margin-right: 6px;
  border-radius: 50%;
  background: #fff;
}

/* Active TX: the pill and state text switch from the standing teal
   "here's the live feed" identity to an on-air green "someone is
   transmitting right now" one -- a pulsing dot plus a color change,
   not just different words, so activity registers at a glance the
   same way a real on-air light would.

   Green (--live-green), matching tr.live-row-active and
   .live-summary-tx-active in the sidebar card, so "someone is
   transmitting" is ONE colour everywhere on the page. This pill was
   red until 2026-09-09, which both clashed with the card's green and
   borrowed this theme's own warning/error semantic -- an active
   transmission is the normal, desirable state, not a fault. */
.live-strip.is-active .live-pill {
  background:
    linear-gradient(225deg, rgba(255, 255, 255, 0.45) 0%, rgba(255, 255, 255, 0.45) 5px, transparent 6.5px),
    linear-gradient(180deg, rgba(255, 255, 255, 0.22) 0%, transparent 70%),
    var(--live-green);
  filter: drop-shadow(0 0 1px rgba(14, 78, 45, 0.5)) drop-shadow(0 2px 6px rgba(62, 207, 126, 0.45));
}

.live-strip.is-active .live-pill-dot {
  display: block;
  animation: live-pill-pulse 1.4s ease-in-out infinite;
}

.live-strip.is-active [data-live-state] {
  color: #7de6ab;
}

@keyframes live-pill-pulse {
  0%, 100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.4;
    transform: scale(0.72);
  }
}

@media (prefers-reduced-motion: reduce) {
  .live-strip.is-active .live-pill-dot {
    animation: none;
  }
}

.page-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 30px;
  margin-top: 28px;
  margin-bottom: 70px;
}

.feed {
  min-width: 0;
}

.section-kicker {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: #2c393f;
  font-family: var(--font-heading);
  font-size: 0.78rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.section-kicker::before {
  content: "";
  width: 22px;
  height: 3px;
  border-radius: 999px;
  background: var(--teal);
}

h1,
h2,
h3,
p {
  margin-top: 0;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: var(--font-heading);
  letter-spacing: -0.02em;
}

h1 {
  max-width: 760px;
  margin-bottom: 20px;
  font-size: clamp(2rem, 3.4vw, 3.85rem);
  line-height: 1.05;
  font-weight: 800;
  letter-spacing: -0.03em;
}

/* Full-bleed hero banner with the copy overlaid on the photo -- matches
   the old site's (www.dmr.ee, sporty theme) flexslider hero look, ported
   2026-09-04 per explicit user request ("I liked the hero images on
   featured news in old site"). The photo fills the whole card; a bottom
   gradient keeps the overlaid text readable regardless of image content.
   Shared between the front-page featured card (.featured-card) and
   single-post/page headers (.hero-banner, single.php/page.php) -- same
   treatment, reused rather than duplicated. */
.featured-card,
.hero-banner {
  position: relative;
  display: block;
  min-height: 340px;
  margin-bottom: 30px;
  overflow: hidden;
  border: none;
  border-radius: var(--md-shape-lg);
  background: var(--md-surface-container);
  box-shadow: var(--md-elevation-1);
}

/* Single-post/page hero specifically: a wide landscape banner (matches
   the old site's own hero proportions, explicit user preference) rather
   than the front-page card's taller box -- scales with the card's own
   width instead of a flat min-height, so it stays landscape at any
   viewport size down to the 680px mobile breakpoint, where the mobile
   override below takes over with its own fixed height instead.
   16/5 (3.2:1), not the initially-chosen 21/8 (2.6:1) -- checked every
   imported hero photo's real dimensions (2026-09-04): all but one of
   the old-site images ported over are already pre-cropped to ~3.16:1
   banner shape (e.g. hotspot.jpg, 960x304). A 2.6:1 box against a
   3.16:1 photo was cropping away ~44% of the image width, producing an
   extreme, unrecognizable center-zoom (found live on the HotSpotid
   page). Matching the box ratio to what the actual image set already
   is keeps object-fit:cover's crop minimal for nearly every photo. */
.hero-banner {
  min-height: 0;
  aspect-ratio: 16 / 5;
}

.hero-banner .featured-copy {
  /* .featured-copy is a normal-flow sibling of the absolutely-positioned
     .featured-media, not itself absolutely positioned -- on the
     front-page card its min-height:340px happened to match the card's
     own height, so justify-content:flex-end looked "anchored to the
     bottom" by coincidence. Once .hero-banner switched to aspect-ratio
     sizing, min-height:0 let this collapse to its own (short) content
     height and sit at the TOP of the banner instead, landing the title
     on the photo's brightest area with none of the bottom scrim behind
     it (found live 2026-09-04: title unreadable against a bright sky).
     absolute+inset:0 makes it truly fill the banner so flex-end anchors
     to the real bottom regardless of content height. */
  position: absolute;
  inset: 0;
  min-height: 0;
  /* Lighter than .featured-card's shared scrim (0.72 peak) -- reported
     live 2026-09-04 as "too dark, doesn't help": the page hero no longer
     has the ::after gradient stacking on top (removed above), and a
     stronger text-shadow on the title (below) carries more of the
     contrast job here instead of a heavy wash over the whole photo. */
  background: linear-gradient(180deg, rgba(15, 20, 24, 0) 0%, rgba(15, 20, 24, 0.22) 45%, rgba(15, 20, 24, 0.42) 100%);
}

.hero-banner .featured-copy h2,
.hero-banner .featured-copy h1 {
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.75), 0 1px 3px rgba(0, 0, 0, 0.6);
}

.featured-card::after,
.hero-banner::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(15, 20, 24, 0) 35%, rgba(15, 20, 24, 0.55) 68%, rgba(15, 20, 24, 0.88) 100%);
  z-index: 1;
}

/* .hero-banner's own .featured-copy already carries its own bottom-to-
   top scrim (see its rule above) that alone guarantees readable text --
   stacking the shared ::after gradient ON TOP of that compounded into a
   noticeably heavier shadow right behind the title than either gradient
   alone (found live 2026-09-04, reported as "too dark"). The front-page
   .featured-card keeps ::after since it doesn't have this second scrim
   layer at all. Placed after the shared rule above so it actually wins
   (equal specificity, decided by source order -- an earlier attempt at
   this override sat BEFORE the shared rule and was silently losing).*/
.hero-banner::after {
  content: none;
}

.featured-media {
  display: block;
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 0;
  box-shadow: none;
}

.featured-copy {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-end;
  min-height: 340px;
  padding: 24px 28px;
  color: #ffffff;
}

.featured-copy h2,
.featured-copy h1 {
  margin: 10px 0;
  font-size: clamp(1.55rem, 2.45vw, 2.45rem);
  line-height: 1.15;
  letter-spacing: 0;
  color: #ffffff;
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.7), 0 1px 12px rgba(0, 0, 0, 0.6);
}

.featured-copy h2 a,
.featured-copy h1 a {
  color: inherit;
}

.featured-copy p {
  max-width: 560px;
  color: rgba(255, 255, 255, 0.95);
  font-size: 1rem;
  line-height: 1.55;
  background: rgba(15, 20, 24, 0.25);
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
  padding: 2px 7px;
  /* The excerpt is trimmed (wp_trim_words' own "…" marks the cut),
     but on a photo backdrop that ellipsis alone reads as an abrupt
     stop -- fade the last line out instead so it visually trails off. */
  -webkit-mask-image: linear-gradient(180deg, #000 75%, transparent 100%);
  mask-image: linear-gradient(180deg, #000 75%, transparent 100%);
}

.featured-copy .meta-row {
  color: rgba(255, 255, 255, 0.82);
}

.featured-copy .meta-row time,
.featured-copy .updated-note {
  background: rgba(15, 20, 24, 0.45);
  padding: 3px 9px;
  border-radius: var(--md-shape-full);
}

.featured-copy .updated-note {
  color: #bcdcf2;
  opacity: 1;
}

.featured-copy .external-note {
  color: #bcdcf2;
  background: rgba(15, 20, 24, 0.45);
  padding: 3px 9px;
  border-radius: var(--md-shape-full);
}

.featured-copy .external-link,
.featured-copy .external-link:link,
.featured-copy .external-link:visited {
  border-color: rgba(255, 255, 255, 0.55);
  background: rgba(15, 20, 24, 0.35);
  color: #ffffff !important;
}

.featured-copy .external-link:hover,
.featured-copy .external-link:focus-visible {
  color: #ffffff !important;
}

.meta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 600;
}

.meta-row time {
  font-family: var(--font-mono);
  font-size: 0.76rem;
  font-weight: 500;
}

.tag,
.post-categories a {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding: 0 10px;
  border-radius: var(--md-shape-full);
  border: 1px solid var(--md-outline-variant);
  background: var(--md-surface-container-high);
  color: var(--md-on-surface);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.tag-important,
.tag-important:link,
.tag-important:visited {
  border-color: rgba(255, 255, 255, 0.12);
  background: #253038;
  color: #ffffff !important;
}

.tag-tech {
  background: #d7ece9;
  color: #0b615a;
}

.tag-event {
  background: #e8e1d7;
  color: #7a461f;
}

.tag-link {
  background: #dce8ef;
  color: #1f587a;
}

/* Forum-feed source tags (page-foorumid-blogid.php) -- distinct colors
   per source domain so the two feeds are visually distinguishable at a
   glance, same pattern as .tag-tech/.tag-event/.tag-link above. */
.tag-source-hamfoorum {
  background: #f0e3d6;
  color: #8a4b12;
}

.tag-source-esqtc {
  background: #e1e0f5;
  color: #3d3a91;
}

.modified-note {
  font-style: italic;
  opacity: 0.8;
}

.updated-note {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--teal-dark);
  opacity: 0.85;
}

.external-note {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--blue);
  font-family: var(--font-heading);
  font-size: 0.78rem;
  font-weight: 700;
}

.entry-content a.is-external-link {
  display: inline;
  padding-right: 15px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232a6f97' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E");
  background-position: right center;
  background-repeat: no-repeat;
  background-size: 11px 11px;
}

.entry-content a.is-external-link:hover {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%231e4f6c' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E");
}

.entry-content a.is-external-link:has(img),
.entry-content a.is-external-link.wp-block-button__link {
  padding-right: 0;
  background-image: none;
}

.ext-icon {
  display: inline-flex;
  align-items: center;
  margin-left: 5px;
  vertical-align: middle;
}

.ext-icon::before {
  content: "";
  display: inline-block;
  width: 12px;
  height: 12px;
  background-color: currentColor;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E");
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: contain;
  mask-size: contain;
}

.card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 16px;
}

.mobile-live-summary {
  display: none;
}

.primary-link,
.listen-button,
.wp-block-button__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 42px;
  border-radius: var(--md-shape-full);
  padding: 0 22px;
  background: var(--md-primary);
  color: var(--md-on-primary) !important;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.88rem;
  letter-spacing: -0.01em;
  box-shadow: var(--md-elevation-1);
  transition: box-shadow var(--md-motion-standard), transform var(--md-motion-standard);
  text-decoration: none !important;
  overflow: hidden;
}

.primary-link::before,
.listen-button::before,
.wp-block-button__link::before {
  content: "";
  position: absolute;
  inset: 0;
  background: currentColor;
  opacity: 0;
  transition: opacity var(--md-motion-standard);
  pointer-events: none;
}

.primary-link:link,
.primary-link:visited,
.listen-button:link,
.listen-button:visited,
.wp-block-button__link:link,
.wp-block-button__link:visited {
  color: var(--md-on-primary) !important;
}

.primary-link:hover,
.primary-link:focus-visible,
.listen-button:hover,
.listen-button:focus-visible,
.wp-block-button__link:hover,
.wp-block-button__link:focus-visible {
  color: var(--md-on-primary) !important;
  box-shadow: var(--md-elevation-2);
}

.primary-link:hover::before,
.listen-button:hover::before,
.wp-block-button__link:hover::before {
  opacity: var(--md-state-hover);
}

.primary-link:focus-visible::before,
.listen-button:focus-visible::before,
.wp-block-button__link:focus-visible::before {
  opacity: var(--md-state-focus);
}

.primary-link:active,
.listen-button:active,
.wp-block-button__link:active {
  color: var(--md-on-primary) !important;
  box-shadow: var(--md-elevation-1);
}

.primary-link:active::before,
.listen-button:active::before,
.wp-block-button__link:active::before {
  opacity: var(--md-state-press);
}

.external-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 42px;
  border-radius: var(--md-shape-full);
  padding: 0 22px;
  border: 1px solid var(--md-outline);
  background: transparent;
  color: var(--md-on-surface) !important;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.88rem;
  letter-spacing: -0.01em;
  transition: background var(--md-motion-standard);
  text-decoration: none !important;
  overflow: hidden;
}

.external-link::before {
  content: "";
  position: absolute;
  inset: 0;
  background: currentColor;
  opacity: 0;
  transition: opacity var(--md-motion-standard);
  pointer-events: none;
}

.external-link:link,
.external-link:visited {
  color: var(--md-on-surface) !important;
}

.external-link:hover,
.external-link:focus-visible {
  color: var(--md-on-surface) !important;
}

.external-link:hover::before {
  opacity: var(--md-state-hover);
}

.external-link:focus-visible::before {
  opacity: var(--md-state-focus);
}

.external-link:active {
  color: var(--md-on-surface) !important;
}

.external-link:active::before {
  opacity: var(--md-state-press);
}

.toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 16px;
}

.toolbar h2,
.quick-links h2,
.side-card h2,
.topics h2 {
  display: inline-flex;
  align-items: center;
  margin-bottom: 0;
  font-family: var(--font-heading);
  font-size: 0.92rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.toolbar h2::before,
.quick-links h2::before,
.side-card h2::before,
.topics h2::before {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--teal);
  box-shadow: 0 0 6px rgba(42, 111, 151, 0.7);
  margin-right: 8px;
}

.filter-tabs {
  display: flex;
  gap: 6px;
  overflow-x: auto;
}

.filter-tabs a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  border-radius: var(--md-shape-full);
  padding: 0 14px;
  border: 1px solid var(--md-outline-variant);
  background: var(--md-surface);
  color: var(--md-on-surface-variant);
  font-family: var(--font-heading);
  font-size: 0.78rem;
  font-weight: 700;
  white-space: nowrap;
  transition: background var(--md-motion-standard), color var(--md-motion-standard), border-color var(--md-motion-standard);
}

.filter-tabs a:hover {
  background: var(--md-surface-container-high);
  color: var(--ink);
}

.filter-tabs:not(.has-active-state) a:first-child,
.filter-tabs:not(.has-active-state) a:first-child:link,
.filter-tabs:not(.has-active-state) a:first-child:visited {
  border-color: var(--md-primary);
  background: var(--md-primary-container);
  color: var(--md-on-primary-container) !important;
}

.filter-tabs:not(.has-active-state) a:first-child:hover {
  background: var(--md-primary-container);
  color: var(--md-on-primary-container) !important;
}

.filter-tabs a.is-active,
.filter-tabs a.is-active:link,
.filter-tabs a.is-active:visited {
  border-color: var(--md-primary);
  background: var(--md-primary-container);
  color: var(--md-on-primary-container) !important;
}

.filter-tabs a.is-active:hover {
  background: var(--md-primary-container);
  color: var(--md-on-primary-container) !important;
}

.filter-tabs.range-tabs {
  margin-top: 10px;
  margin-bottom: 22px;
}

.event-month-group {
  margin-top: 26px;
}

.event-month-group:first-of-type {
  margin-top: 20px;
}

.event-month-heading {
  margin: 0 0 10px;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  padding-bottom: 6px;
  border-bottom: 1px solid var(--md-outline-variant);
}

.event-empty-hint {
  margin-top: 4px;
  color: var(--muted);
  font-size: 0.86rem;
}

.notice-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}

.event-strip {
  margin-top: 30px;
}

.event-strip-header {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 18px;
  margin-bottom: 14px;
}

.event-strip-header h2 {
  margin-bottom: 4px;
  font-size: 1rem;
  font-weight: 900;
  text-transform: uppercase;
}

.event-strip-header p {
  max-width: 520px;
  margin-bottom: 0;
  color: var(--muted);
  font-size: 0.9rem;
}

.event-strip-footer,
.notice-grid-footer {
  display: flex;
  justify-content: center;
  margin-top: 18px;
}

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

/* Same cards as the front-page "Tulevased üritused" strip, but placed
   directly inside an article's own .entry-content flow instead of
   under a .event-strip-header -- needs its own top margin since there
   is no section heading here to provide one. */
.event-grid-inline {
  margin-top: 24px;
}

.event-card {
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr);
  gap: 12px;
  min-height: 118px;
  padding: 12px;
  border: none;
  border-radius: var(--md-shape-md);
  background: var(--md-surface-container);
  box-shadow: var(--md-elevation-1);
  transition: box-shadow var(--md-motion-standard), transform var(--md-motion-standard);
}

.event-card:hover {
  box-shadow: var(--md-elevation-2);
  transform: translateY(-1px);
}

.event-card-day {
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 1px;
  padding: 8px 4px;
  border-radius: var(--md-shape-sm);
  background: var(--md-primary-container);
  color: var(--md-on-primary-container);
}

/* The day block is a fixed 52px column, too narrow for a full
   weekday name ("Neljapäev") at any viewport -- so the single-letter
   short form is the default, not a mobile-only fallback. The real
   full date lives in the adjacent <time>; this block is aria-hidden
   decoration, so nothing is lost semantically. */
.event-card-weekday {
  font-size: 0;
}

.event-card-weekday::before {
  content: attr(data-short);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-weight: 800;
  letter-spacing: 0.03em;
  opacity: 0.9;
}

.event-card-daynum {
  font-family: var(--font-heading);
  font-size: 1.5rem;
  font-weight: 800;
  line-height: 1.05;
}

.event-card-month {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 700;
  text-transform: uppercase;
  opacity: 0.75;
}

.event-card-type-icon {
  display: block;
  width: 15px;
  height: 15px;
  margin-bottom: 2px;
  opacity: 0.85;
}

.event-card-type-icon svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* Per-event_type accent on the day block, one pair per taxonomy term
   (background/on-color, same MD3 "container" pattern as --md-primary-
   container/--md-on-primary-container elsewhere). New terms added
   later from wp-admin fall back to the neutral default day-block
   styling until a matching modifier is added here. */
.event-card--dmr .event-card-day {
  background: var(--md-primary-container);
  color: var(--md-on-primary-container);
}

.event-card--vhf-uhf .event-card-day {
  background: #dcede0;
  color: #14532b;
}

.event-card--hf .event-card-day {
  background: #f6e3d1;
  color: #7a4210;
}

.event-card--aprs .event-card-day {
  background: #e6dcf3;
  color: #4b2680;
}

.event-card--voistlus .event-card-day {
  background: #f4dbdd;
  color: #7a1f28;
}

.event-card--erau-uritus .event-card-day {
  background: #dde2e6;
  color: #33444f;
}

.event-card-body {
  min-width: 0;
}

.event-card h3 {
  margin: 8px 0 0;
  font-size: 1.08rem;
  line-height: 1.2;
}

.event-card time {
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 750;
}

.event-card time[data-event-countdown] {
  color: var(--teal-dark);
  font-weight: 800;
}

.event-card p {
  margin: 6px 0 0;
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.45;
}

.event-grid-archive {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-top: 18px;
}

.event-grid-archive .event-card {
  min-height: 0;
  padding: 14px;
}

.notice-card {
  display: grid;
  grid-template-columns: 58px minmax(0, 1fr);
  gap: 14px;
  min-height: 150px;
  padding: 14px;
  border: none;
  border-radius: var(--md-shape-md);
  background: var(--md-surface-container);
  box-shadow: var(--md-elevation-1);
}

.notice-card.has-thumb {
  grid-template-columns: 128px minmax(0, 1fr);
}

.notice-date {
  display: grid;
  align-content: center;
  justify-items: center;
  width: 58px;
  height: 58px;
  border-radius: var(--md-shape-sm);
  background: var(--md-primary-container);
  color: var(--md-on-primary-container) !important;
}

.notice-date strong {
  font-family: var(--font-mono);
  font-size: 1.35rem;
  font-weight: 700;
  line-height: 1;
}

.notice-date span {
  color: var(--md-on-primary-container);
  opacity: 0.75;
  font-family: var(--font-heading);
  font-size: 0.68rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.notice-card h3 {
  margin: 9px 0 7px;
  font-size: 1.22rem;
  line-height: 1.2;
}

.notice-card p {
  margin-bottom: 0;
  color: #3e4a52;
  font-size: 1.02rem;
  line-height: 1.48;
}

.notice-card,
.event-card,
.quick-grid a,
.side-card,
.featured-card {
  transition: box-shadow var(--md-motion-standard), transform var(--md-motion-standard);
}

.notice-card:hover,
.event-card:hover,
.quick-grid a:hover,
.side-card:hover,
.featured-card:hover {
  box-shadow: var(--md-elevation-2);
  transform: translateY(-2px);
}

.notice-thumb {
  width: 100%;
  min-height: 118px;
  object-fit: cover;
  border-radius: 7px;
}

.inline-link {
  display: inline-flex;
  margin-top: 10px;
  color: var(--teal-dark);
  font-size: 0.9rem;
  font-weight: 850;
}

.dmr-field {
  display: grid;
  gap: 7px;
}

.dmr-field input {
  width: 100%;
}

.dmr-field p {
  margin: 0;
  color: #646970;
  font-size: 12px;
}

/* Single column: the heading block now carries an intro sentence, so
   the old 220px side column would squeeze it into a narrow strip. */
.quick-links {
  display: block;
  margin-top: 36px;
}

.quick-links.quick-links-bottom {
  margin-top: 28px;
  padding-top: 24px;
  border-top: 1px solid var(--md-outline-variant);
}

.quick-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 10px;
}

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

.quick-links-header {
  margin-bottom: 14px;
}

.quick-links-header p {
  max-width: 560px;
  margin: 6px 0 0;
  color: var(--muted);
  font-size: 0.9rem;
}

.quick-grid a {
  position: relative;
  padding: 16px;
  padding-right: 34px;
  border: none;
  border-radius: var(--md-shape-md);
  background: var(--md-surface-container);
  box-shadow: var(--md-elevation-1);
}

/* All guide cards share the same treatment: neutral surface,
   a top accent stripe, and a matching icon tile. The primary
   card keeps a slightly heavier border to stay first. */
.quick-grid a.quick-primary {
  border-top: 4px solid var(--md-primary);
}

.quick-grid a.quick-primary .quick-icon {
  background: var(--md-primary-container);
  color: var(--md-on-primary-container);
}

/* Reuses VHF/UHF (green), HF (amber), and Tech/Firmware (indigo) pairs. */
.quick-grid a.quick-accent-vhf {
  border-top: 3px solid #dcede0;
}

.quick-grid a.quick-accent-vhf .quick-icon {
  background: #dcede0;
  color: #14532b;
}

.quick-grid a.quick-accent-hf {
  border-top: 3px solid #f6e3d1;
}

.quick-grid a.quick-accent-hf .quick-icon {
  background: #f6e3d1;
  color: #7a4210;
}

.quick-grid a.quick-accent-firmware {
  border-top: 3px solid #e0e7ff;
}

.quick-grid a.quick-accent-firmware .quick-icon {
  background: #e0e7ff;
  color: #3730a3;
}

.quick-grid strong,
.quick-grid .quick-desc {
  display: block;
}

.quick-grid .quick-desc {
  margin-top: 6px;
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.45;
}

.quick-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  margin-bottom: 10px;
  border-radius: var(--md-shape-sm);
  background: var(--md-primary-container);
  color: var(--md-on-primary-container);
}

.quick-icon svg {
  width: 19px;
  height: 19px;
  display: block;
}

/* Arrow affordance so the cards read as entry points, not just
   labelled boxes. Nudges on hover alongside the shared card lift. */
.quick-grid a::after {
  content: "→";
  position: absolute;
  top: 16px;
  right: 14px;
  color: var(--teal);
  font-size: 1rem;
  font-weight: 700;
  opacity: 0.55;
  transition: transform var(--md-motion-standard), opacity var(--md-motion-standard);
}

.quick-grid a:hover::after {
  opacity: 1;
  transform: translateX(2px);
}

.sidebar {
  min-width: 0;
}

.side-card {
  margin-bottom: 16px;
  padding: 14px;
  border: none;
  border-radius: var(--md-shape-md);
  background: var(--md-surface-container);
  box-shadow: var(--md-elevation-1);
}

/* Deliberately breaks from the calm white side-card look -- this is
   the one block on the page showing something actually live, so it
   gets its own "radio console" identity: dark surface, glowing teal
   accents, monospace readouts, like an actual receiver display rather
   than an editorial card. */
.live-card {
  /* Named container so the last-heard table can respond to the CARD's
     own width -- it is ~290px in the desktop sidebar at every viewport
     from 1024px up, which no viewport media query can distinguish from
     a wide screen. See the @container rule by the table's cell padding. */
  container: live-card / inline-size;
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(120% 140% at 0% 0%, rgba(42, 111, 151, 0.35), transparent 55%),
    #1a2229;
  border: 1px solid rgba(143, 188, 217, 0.14);
  color: #fff;
}

.live-card .side-heading h2 {
  color: #fff;
}

.live-card .side-heading .live-pill.small {
  background:
    linear-gradient(225deg, rgba(255, 255, 255, 0.45) 0%, rgba(255, 255, 255, 0.45) 5px, transparent 6.5px),
    linear-gradient(180deg, rgba(255, 255, 255, 0.22) 0%, transparent 70%),
    var(--teal);
  filter: drop-shadow(0 0 1px rgba(30, 79, 108, 0.45)) drop-shadow(0 2px 6px rgba(42, 111, 151, 0.4));
}

.live-card .side-heading .live-pill.small .live-pill-dot {
  display: block;
  animation: live-pill-pulse 1.4s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .live-card .side-heading .live-pill.small .live-pill-dot {
    animation: none;
  }
}

.live-card .socket-player-button {
  background: var(--teal);
  box-shadow: 0 0 0 1px rgba(143, 188, 217, 0.3), 0 6px 20px rgba(42, 111, 151, 0.35);
}

.live-card .socket-player-button:hover {
  background: #327fac;
}

.live-card .volume-icon {
  filter: brightness(1.4);
}

.live-card .socket-player-volume {
  accent-color: var(--teal);
}

.live-card .live-table-toggle {
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}

.live-card .live-summary-time {
  color: rgba(255, 255, 255, 0.5);
}

.live-card .live-summary-callsign {
  color: #fff;
  font-weight: 800;
}

.live-card .live-summary-name {
  color: rgba(255, 255, 255, 0.6);
}

.live-card .live-summary-stats {
  color: rgba(255, 255, 255, 0.5);
}

/* Collapsed-view equivalent of tr.live-row-active below -- a pulsing
   green glow (not a boxed badge -- tried "ON AIR" pill first, 2026-09-07,
   swapped same day for a more stylish glow per direct feedback) so an
   active transmission reads as "still going" whether the table is
   expanded or collapsed to its one-line summary. Green rather than
   this theme's own --teal/--orange/--red -- avoids reading as this
   site's warning/error semantic colors, and matches the conventional
   on-air/recording indicator color other broadcast UIs use. */
.live-card .live-summary-tx-active {
  color: var(--live-green);
  font-weight: 800;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  text-shadow: 0 0 10px rgba(62, 207, 126, 0.65);
}

.live-card .live-summary-tx-active::before {
  content: "";
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--live-green);
  box-shadow: 0 0 0 4px rgba(62, 207, 126, 0.25), 0 0 12px 2px rgba(62, 207, 126, 0.7);
  animation: live-tx-pulse 1.2s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .live-card .live-summary-tx-active::before {
    animation: none;
  }
}

.live-card .live-summary-empty {
  color: rgba(255, 255, 255, 0.55);
}

.live-card .live-table-toggle-icon {
  background-color: rgba(255, 255, 255, 0.5);
}

.live-card .live-table-toggle:hover .live-table-toggle-icon,
.live-card .live-table-toggle:focus-visible .live-table-toggle-icon {
  background-color: #8fbcd9;
}

.live-card th {
  color: rgba(255, 255, 255, 0.45);
  border-bottom-color: rgba(255, 255, 255, 0.1);
}

.live-card td {
  border-bottom-color: rgba(255, 255, 255, 0.08);
}

.live-card td:first-child {
  color: rgba(255, 255, 255, 0.4);
}

/* Callsign is the identity that matters most at a glance -- plain white,
   the strongest text color this dark card has, rather than the softer
   blue tint it had before. TX duration steps back to a quieter tone in
   its place -- it's supporting detail, not the thing a reader scans
   the table for. Selected by class, not nth-child position: a relay
   row's callsign cell spans two column-slots (colspan="2", no separate
   name), which shifts every later cell's nth-child index on that row
   specifically -- a positional selector would apply the wrong rule to
   the wrong logical column on exactly that row. */
.live-card .live-cell-callsign {
  color: #fff;
}

.live-card .live-cell-name:not(.live-cell-callsign) {
  color: rgba(255, 255, 255, 0.6);
}

.live-card .live-cell-tx {
  color: rgba(255, 255, 255, 0.55);
  font-weight: 500;
}

.live-card tbody tr:nth-child(even) {
  background: rgba(255, 255, 255, 0.03);
}

.live-card tbody tr:hover {
  background: rgba(143, 188, 217, 0.08);
}

.live-card tbody tr:first-child {
  box-shadow: inset 3px 0 0 var(--teal);
}

.live-card td[data-severity] {
  color: rgba(255, 255, 255, 0.4);
}

.live-card a {
  color: inherit;
}

.side-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}

.side-heading a {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 5px;
  border: 1px solid rgba(23, 32, 37, 0.08);
  background: #e8edf0;
  color: #263137;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 700;
  text-decoration: none !important;
  transition: background 140ms ease, border-color 140ms ease;
}

.side-heading a:hover {
  background: #d8e2e6;
  border-color: #b0bec5;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.78rem;
}

th,
td {
  padding: 8px 4px;
  border-bottom: 1px solid #e8edf0;
  text-align: left;
  white-space: nowrap;
}

table tbody td:first-child {
  font-family: var(--font-mono);
  font-weight: 800;
  color: var(--teal-dark);
  letter-spacing: 0.02em;
}

/* Analog-repeater relay rows put a long label ("ES3DMRA Märjamaa",
   "ES1DMR Tallinn") in the callsign cell (name is empty for those rows)
   -- th/td's own nowrap above was forcing the whole live table wide
   enough to trigger horizontal scroll in the sidebar just for this one
   column. Letting both the callsign and name cells wrap instead keeps
   the table's real width in check; the other columns (time/TX/Loss/BER)
   are all naturally short and stay nowrap. No max-width -- most rows are
   a short plain callsign, and constraining every row's width to fit the
   rare long label would wrap those unnecessarily. */
.live-cell-name {
  white-space: normal;
}

th {
  color: #3d4950;
  font-weight: 900;
}

table tbody tr:hover {
  background: var(--md-surface-container-high);
}

/* 6 columns (Aeg/Kutsung/Nimi/TX/Loss/BER, matching the old dmr.ee live
   table) in a narrow sidebar card. Previously an auto-layout table with
   nowrap cells, sized by content and left to overflow into its own
   horizontal-scroll container when too wide -- content-driven width is
   inherently a few px of sub-pixel rounding away from overflowing at
   SOME desktop width even after tightening padding (reported live:
   "still shows horizontal scrollbar... doesn't seem it needs any
   scrolling", reproduced at 1280px viewport, 1px over). table-layout:
   fixed with explicit percentage widths (below) makes the table's
   total width mathematically bound to exactly 100% of its container
   regardless of content or rounding -- structurally cannot overflow,
   so there is nothing left for horizontal scroll to be a fallback
   for. Long content in Kutsung/Nimi truncates with an ellipsis
   instead (see .live-cell-name and the callsign cell below). */
.live-card table {
  /* Horizontal cell padding, declared once: the <col> widths below each
     add it back on top of their own ch content budget, so the two can
     never drift apart. Narrow screens override it (see the media query
     near the end of this file) to buy back width for the columns
     themselves rather than shrinking any column's content budget. */
  --live-cell-px: 4px;
  table-layout: fixed;
  width: 100%;
  font-family: var(--font-mono);
  font-size: 0.76rem;
  font-variant-numeric: tabular-nums;
}

/* Aeg/TX/Loss/BER are fixed-shape content (HH:MM, N.Ns/TX, N%) -- sized
   in `ch` to exactly what they need, so they can never be squeezed
   below a readable width. Kutsung/Nimi are the variable-length fields
   and share whatever space is left, in `table-layout: fixed`'s
   remaining-space-by-percentage-ratio behavior for columns not given
   an absolute width. Previously all 6 columns were pure percentages
   of the container -- correct at this component's original ~320px
   sidebar width, but reported live (2026-09-07, during a net, several
   users on their own screens): "cuts time, ber and other columns" --
   confirmed the percentage math produces sub-30px columns for TX/Loss/
   BER at some real narrower effective widths (a slightly narrower
   sidebar layout, browser zoom, a scrollbar eating into the container),
   at which point overflow:hidden + ellipsis genuinely clips real
   digits, not just decorative overflow. `ch` widths hold their needed
   pixel size regardless of the container's own width -- Kutsung/Nimi
   are what shrink first if the container is ever narrower than their
   own minimum, which is the right column to sacrifice (name text is
   the only one of these six fields with genuine truncation tolerance).

   Sized via the <colgroup>'s own <col> elements (sidebar-live.php), NOT
   nth-child position on td/th -- found live (2026-09-08): a relay row
   ("ES1DMR Tallinn", no separate name) has only 5 <td> elements with a
   colspan="2" on its callsign cell, so td:nth-child(3) on THAT row was
   structurally the TX cell, not Nimi -- every value after the colspan
   visibly shifted one column left. A <col> targets a column SLOT
   directly and is completely unaffected by how many actual <td>s any
   given row has or where a colspan falls. */
.live-col-time {
  width: calc(6ch + var(--live-cell-px) * 2);
}

/* Kutsung is fixed-shape too (a callsign is at most 6-7 chars), so it is
   sized in ch like the numeric columns rather than as a percentage --
   at narrow widths a percentage handed it and Nimi far more room than
   either needs while Aeg/Loss/BER were squeezed below their own content
   and clipped. Nimi is now the ONLY flexible column: it takes whatever
   is left and is the one field with real truncation tolerance.

   Every ch value adds the 8px of horizontal cell padding explicitly.
   The <col> resolves ch against the TABLE's font-size (0.76rem), but
   the text is rendered at the cell's own smaller font-size -- so a bare
   ch width is not a safe content measure, and the +8px keeps padding
   from eating into it either way. */
.live-col-callsign {
  width: calc(7ch + var(--live-cell-px) * 2);
}

/* The only flexible column: it absorbs whatever the five fixed-shape
   columns above do not take. The 6ch floor keeps its own header
   ("Nimi") from being the thing that ellipsizes at the narrowest real
   container (the 290px desktop sidebar) -- under table-layout: fixed a
   min-width on the cells is ignored, the floor has to be on the <col>
   width itself, so it is expressed as a max() rather than `auto`.
   Long names still wrap to two lines there, the intended sacrifice. */
.live-col-name {
  width: max(calc(7ch + var(--live-cell-px) * 2), 20%);
}



/* A real transmission can run into triple digits of seconds
   (BrandMeister's own max-call-duration cutoff is ~90-125s, see
   CLAUDE.md's "long-transmission truncation" item), so "123.4s" plus
   the live pulsing-dot indicator is the real worst case. */
.live-col-tx {
  width: calc(7ch + var(--live-cell-px) * 2);
}

/* Header text ("Loss") is wider than the values ("0%", "7%") -- size
   for the header, it is what clipped to "Lo..." at 375px. */
.live-col-loss {
  width: calc(4.5ch + var(--live-cell-px) * 2);
}

.live-col-ber {
  width: calc(5ch + var(--live-cell-px) * 2);
}

.live-card td,
.live-card th {
  padding: 8px var(--live-cell-px);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The card is narrow by CONTAINER, not viewport: the desktop sidebar is
   ~290px at every viewport from 1024px up, so a media query cannot
   reach it (the 380px one near the end of this file is viewport-based
   and only covers real phones). A container query on the card itself
   catches BOTH cases with one rule. Trimming the horizontal cell
   padding is what buys the width back -- it is worth more here than
   shaving any column's ch budget, which would put content back at risk
   of the ellipsis this whole block exists to prevent. */
@container live-card (max-width: 340px) {
  .live-card table {
    --live-cell-px: 2px;
  }
}

/* A long node-relay label ("ES3DMRA Märjamaa") in the callsign cell
   (colspan="2" across Kutsung+Nimi when there is no separate name --
   sidebar-live.php) wraps instead of ellipsis-truncating: with
   table-layout: fixed the spanned cell gets both columns' combined
   width, so wrapping to two lines reads better than cutting the label
   off. Every other column stays single-line/ellipsis. Must come after
   the rule above in source order to win on equal specificity.

   overflow-wrap: normal overrides body's own global overflow-wrap:
   break-word (this file's ~line 99) specifically here -- that global
   rule exists for normal prose, but on a short label like this it let
   the browser break MID-CALLSIGN when the wrap-to-two-lines case above
   didn't have a space to break at (reported live: "can the callsigns
   not wrap by letters"). word-break: keep-all is the same guarantee
   for browsers/scripts where overflow-wrap alone isn't the deciding
   property. A real callsign has no natural break point, so this is
   never a real fit problem -- word wrapping happens at the space
   before "Märjamaa", never inside "ES3DMRA" itself. */
.live-card .live-cell-name {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  overflow-wrap: normal;
  word-break: keep-all;
}

/* Column order is Aeg/Kutsung/Nimi/TX/Loss/BER -- the callsign (2nd
   column) is the identity that matters at a glance, so it carries the
   bold/branded treatment; the timestamp is de-emphasized instead of
   competing with it for attention. Colors for this dark card are
   defined once, up near the rest of its palette -- only the shared
   structural bits (weight) live here. */
.live-card td:first-child,
.live-card th:first-child {
  font-weight: 500;
}

.live-card .live-cell-callsign,
.live-card th:nth-child(2) {
  font-weight: 800;
}

/* Loss/BER severity, matching the old theme's warn(>=1%)/error(>=5%)
   thresholds -- a clean 0% is a positive signal worth quieting down,
   a marginal reading worth a nudge, a bad one worth a real flag.
   Two tiers carry more signal than a flat clean-vs-not split. */
.live-card td[data-severity="warn"] {
  color: var(--orange);
  font-weight: 700;
}

.live-card td[data-severity="error"] {
  color: var(--red);
  font-weight: 700;
}

/* Brief highlight on a genuinely new top row -- both the full table's
   row and the collapsed summary line use the same animation, so
   activity registers whichever state the card is in. */
@keyframes live-row-new-flash {
  0% {
    background-color: rgba(143, 188, 217, 0.35);
  }
  100% {
    background-color: transparent;
  }
}

.live-card tr.live-row-new {
  animation: live-row-new-flash 1.8s ease-out;
}

.live-summary-row.live-row-new {
  animation: live-row-new-flash 1.8s ease-out;
  border-radius: 6px;
}

@media (prefers-reduced-motion: reduce) {
  .live-card tr.live-row-new,
  .live-summary-row.live-row-new {
    animation: none;
  }
}

/* Sustained, not a one-shot flash -- stays on this row for as long as
   the transmission is genuinely still active (row.tx === "TX"), unlike
   live-row-new-flash above which fires once on new activity and fades
   in under 2s. The "TX" text label alone in a small cell was easy to
   miss at a glance; this makes an active transmission visible from the
   whole row, not just its TX cell.
   Strengthened 2026-09-07 (reported live: not noticeable enough) --
   first pass used --teal-dark (low contrast) plus a boxed "ON AIR"
   pill; swapped the pill same day for a pulsing green glow per direct
   feedback ("more stylish"). Green rather than this theme's own
   --teal/--orange/--red -- reads as a live/on-air/recording indicator
   without borrowing the site's own warning/error semantic colors. */
/* Deliberately stronger than a hover tint: this is the one row on the
   page reporting something happening RIGHT NOW, and at the sidebar
   card's size a 0.1 alpha wash (what this was until 2026-09-09) barely
   separated it from the striped rows around it. The gradient fades the
   tint out to the right so the green reads as coming FROM the live
   edge marker rather than as a flat highlighted band, and the row's
   own text is lifted to full white -- the surrounding rows are
   deliberately dimmer, so brightness carries the distinction as well
   as hue (a green tint alone is weak for the ~8% of men with red-green
   colour vision deficiency). */
/* Written as `tbody tr.live-row-active` (not `tr.live-row-active`) so
   it out-specifies `.live-card tbody tr:first-child`'s teal edge bar
   and `.live-card tbody tr:hover`'s neutral wash, both of which sit
   earlier in this file at equal specificity. The active row IS
   normally the first row, so without this the teal bar simply won on
   source order and the green edge marker never showed. */
.live-card tbody tr.live-row-active {
  background: linear-gradient(
    90deg,
    rgba(62, 207, 126, 0.26) 0%,
    rgba(62, 207, 126, 0.16) 45%,
    rgba(62, 207, 126, 0.09) 100%
  );
  box-shadow: inset 3px 0 0 var(--live-green), inset 0 0 22px rgba(62, 207, 126, 0.12);
}

.live-card tbody tr.live-row-active td {
  color: #fff;
}

/* Keep the active row's own tint under the pointer -- the hover rules
   above would otherwise swap in a neutral surface and momentarily hide
   the one thing this row exists to signal. */
.live-card tbody tr.live-row-active:hover {
  background: linear-gradient(
    90deg,
    rgba(62, 207, 126, 0.32) 0%,
    rgba(62, 207, 126, 0.2) 45%,
    rgba(62, 207, 126, 0.12) 100%
  );
}

.live-card tr.live-row-active .live-cell-tx {
  color: var(--live-green);
  font-weight: 800;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  text-shadow: 0 0 10px rgba(62, 207, 126, 0.65);
}

@keyframes live-tx-pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.45;
  }
}

.live-card tr.live-row-active .live-cell-tx::before {
  content: "";
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--live-green);
  box-shadow: 0 0 0 4px rgba(62, 207, 126, 0.25), 0 0 12px 2px rgba(62, 207, 126, 0.7);
  animation: live-tx-pulse 1.2s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .live-card tr.live-row-active .live-cell-tx::before {
    animation: none;
  }
}

/* Compact one-line stand-in for the full table while it's collapsed
   -- same fields (time/callsign/name/TX/loss/BER) as the table's own
   first row, so collapsing the table doesn't lose the at-a-glance
   "who's on right now" info. */
.live-summary-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding: 10px 0 2px;
  font-family: var(--font-mono);
  font-size: 0.78rem;
}

.live-summary-row[hidden] {
  display: none;
}

.live-summary-time {
  color: var(--muted);
}

.live-summary-callsign {
  font-weight: 700;
}

.live-summary-callsign a {
  color: inherit;
}

.live-summary-name {
  color: var(--muted);
}

.live-summary-stats {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
  color: var(--muted);
}

.live-summary-stats span[data-severity="warn"] {
  color: var(--orange);
  font-weight: 700;
}

.live-summary-stats span[data-severity="error"] {
  color: var(--red);
  font-weight: 700;
}

.live-summary-empty {
  color: var(--muted);
  font-size: 0.82rem;
}

.listen-button {
  width: 100%;
  margin-top: 14px;
}

.live-card .socket-player {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 14px;
  margin: 14px 0;
}

/* The collapse trigger sits directly on the summary row it controls
   (not up in the heading, far from the content it affects) -- wraps
   .live-summary-row plus the chevron as one clickable button, right
   above .live-table-scroll. Button-element resets on top of the flex
   row layout. */
.live-table-toggle {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  border: none;
  background: none;
  padding: 8px 0;
  margin: 0;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.live-table-toggle-icon {
  flex: none;
  display: inline-block;
  width: 20px;
  height: 20px;
  margin-left: auto;
  background-color: var(--muted);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: contain;
  mask-size: contain;
  transition: transform var(--md-motion-standard, 0.2s ease), background-color 140ms ease;
}

.live-table-toggle:hover .live-table-toggle-icon,
.live-table-toggle:focus-visible .live-table-toggle-icon {
  background-color: var(--teal-dark);
}

.live-table-toggle[aria-expanded="false"] .live-table-toggle-icon {
  transform: rotate(-90deg);
}

/* No horizontal scroll: table-layout: fixed above bounds the table to
   exactly 100% of this container's width by construction, so there is
   nothing left to scroll to. overflow-x was previously auto, with a
   live.js-measured edge-fade hint for when it actually triggered --
   both removed together (reported live: a real, if rare, 1px overflow
   still slipped past content-driven auto-layout width even after
   tightening padding, since content width vs. container width is
   fundamentally a rounding race that fixed-percentage widths don't
   have). If a column's content is genuinely too long for its
   percentage share, it now truncates (ellipsis) or wraps
   (.live-cell-name) instead of ever showing a scrollbar. */
.live-table-scroll {
  position: relative;
  overflow: hidden;
  max-height: 2000px;
  transition: max-height 0.25s ease, margin-top 0.25s ease;
}

.live-table-scroll[hidden] {
  display: block;
  max-height: 0;
  margin-top: 0;
}

.status-map {
  position: relative;
  height: 190px;
  overflow: hidden;
  border-radius: 7px;
  background: var(--panel-soft);
  isolation: isolate;
  z-index: 0;
}

/* Leaflet's own z-index stack (tiles < markers < tooltips < popups <
   controls, values up to 1000) needs to stay intact for popups to paint
   above markers -- previously this was flattened to z-index:auto across
   every pane, which fell back to DOM order and made popups render behind
   later-added markers. .status-map's isolation:isolate (below) already
   contains Leaflet's z-index values within the map, so nothing here can
   escape and cover the sticky header/drawer -- no page-level override
   needed any more. */

.status-map .leaflet-control-attribution {
  background: rgba(255, 255, 255, 0.82);
  color: var(--muted);
  font-size: 0.6rem;
}

.status-map .leaflet-control-attribution a {
  color: var(--ink);
}

.status-map .leaflet-control-zoom a {
  background: #fff;
  color: var(--ink);
  border-color: var(--line);
}

.status-map .leaflet-control-zoom a:hover {
  background: var(--panel-soft);
}

.status-map .leaflet-tooltip {
  background: #fff;
  color: var(--ink);
  border: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  padding: 3px 7px;
}

.status-map .leaflet-tooltip-top:before {
  border-top-color: #fff;
}

.map-marker-wrap {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
}

.map-label {
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-top: -6px;
  padding: 1px 4px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.88);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 700;
  line-height: 1.3;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: 0 1px 2px rgba(23, 32, 37, 0.2);
}

.map-label.is-not-working {
  color: var(--muted);
  background: rgba(255, 255, 255, 0.72);
}

.map-antenna {
  position: relative;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2px solid transparent;
  background: #fff;
  box-shadow: 0 1px 3px rgba(23, 32, 37, 0.35);
  color: var(--teal);
  cursor: pointer;
  transition: transform 0.12s ease, border-color 0.12s ease;
}

/* Icon color = protocol family. A thin matching border (not a wide glow
   -- tried a box-shadow halo first, too heavy/thick) keeps the same
   information at a smaller footprint. .warning/.is-not-working below
   still override this when they apply, since those are rarer, more
   urgent states than "which protocol does this normally run". */
.map-antenna-dmr {
  color: var(--teal);
  border-color: rgba(42, 111, 151, 0.45);
}

.map-antenna-tetra {
  color: #6a4c93;
  border-color: rgba(106, 76, 147, 0.45);
}

.map-antenna-echolink {
  color: #1e7a5f;
  border-color: rgba(30, 122, 95, 0.45);
}

.map-antenna-beacon {
  color: #a6472e;
  border-color: rgba(166, 71, 46, 0.45);
}

.map-antenna-simplex {
  color: var(--muted);
  border-color: rgba(19, 30, 34, 0.25);
}

.map-antenna-fm {
  color: #8a7a1f;
  border-color: rgba(138, 122, 31, 0.45);
}

/* Analog (FM) capability, independent of protocol color above -- a
   repeater that's digital-primary but ALSO reachable in plain FM (most
   of the DMR fleet) gets its border tinted amber instead of its own
   protocol color, rather than overriding the icon color entirely or
   squeezing in a small hard-to-read corner badge (tried first, dropped
   -- illegible at marker scale). A second box-shadow ring was tried
   next and also dropped -- read as a thick glow, not a border. Amber
   specifically so it never matches a protocol color or the warning/
   offline states below. */
.map-antenna-fm-ring {
  border-color: #d39b18;
}

.map-antenna:hover {
  transform: scale(1.12);
}

.map-cluster-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
}

.map-spider-center {
  display: block;
  width: 8px;
  height: 8px;
  margin: 3px;
  border-radius: 50%;
  background: var(--teal);
  border: 2px solid #fff;
  box-shadow: 0 1px 3px rgba(23, 32, 37, 0.35);
  cursor: pointer;
}

.map-cluster {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--teal);
  box-shadow: 0 0 0 5px rgba(42, 111, 151, 0.24), 0 1px 3px rgba(23, 32, 37, 0.35);
  color: #fff;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 800;
  cursor: pointer;
  transition: transform 0.12s ease;
}

.map-cluster:hover {
  transform: scale(1.12);
}

.map-antenna svg {
  width: 13px;
  height: 13px;
  display: block;
}

.map-antenna.warning {
  color: var(--orange);
  border-color: rgba(220, 123, 47, 0.5);
}

.map-antenna.is-not-working {
  background: rgba(19, 30, 34, 0.06);
  color: var(--muted);
  border-color: rgba(19, 30, 34, 0.2);
  opacity: 0.65;
}

.status-map .leaflet-popup-content-wrapper {
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(23, 32, 37, 0.2);
}

.status-map .leaflet-popup-content {
  margin: 10px 12px;
  font-family: var(--font-sans);
}

.map-popup {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 150px;
}

.map-popup-badges {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 1px;
}

.map-popup-type {
  display: inline-flex;
  align-items: center;
  padding: 1px 5px;
  border-radius: 4px;
  background: rgba(19, 30, 34, 0.06);
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* Same protocol palette as the map marker's own border color
   (.map-antenna-* in the map section below) -- a chip and the marker it
   describes should read as the same color, in both the list's type tags
   and the popup's badge row. A DMR+FM repeater shows both its "DMR" and
   "FM" chips (see typeLabelsFor/dmr_ee_2026_repeater_type_labels), each
   colored on its own rather than only the marker showing FM via a
   border tint. */
.status-type-tag-dmr,
.map-popup-type-dmr {
  background: rgba(42, 111, 151, 0.12);
  color: var(--teal-dark);
}

.status-type-tag-tetra,
.map-popup-type-tetra {
  background: rgba(106, 76, 147, 0.12);
  color: #6a4c93;
}

.status-type-tag-echolink,
.map-popup-type-echolink {
  background: rgba(30, 122, 95, 0.12);
  color: #1e7a5f;
}

.status-type-tag-beacon,
.map-popup-type-beacon {
  background: rgba(166, 71, 46, 0.12);
  color: #a6472e;
}

.status-type-tag-simplex,
.map-popup-type-simplex {
  background: rgba(19, 30, 34, 0.06);
  color: var(--muted);
}

.status-type-tag-fm,
.map-popup-type-fm {
  background: rgba(211, 155, 24, 0.16);
  color: #8a6a10;
}

.map-popup-callsign {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.82rem;
  color: var(--ink);
  text-decoration: none;
}

.map-popup-callsign:hover {
  color: var(--teal);
}

.map-popup-city {
  font-size: 0.74rem;
  color: var(--muted);
}

.map-popup-status {
  font-family: var(--font-tech);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--teal-dark);
}

.map-popup-status.attention {
  color: #a35a1e;
}

.map-popup-fields {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 10px;
  margin: 4px 0 0;
}

.map-popup-fields div {
  display: flex;
  align-items: baseline;
  gap: 4px;
}

.map-popup-fields dt {
  font-family: var(--font-mono);
  font-size: 0.58rem;
  font-weight: 800;
  letter-spacing: 0.03em;
  color: var(--muted);
}

.map-popup-fields dd {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  color: #172025;
  font-variant-numeric: tabular-nums;
}

.map-popup-more-link {
  margin-top: 4px;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--teal);
  text-decoration: none;
}

.map-popup-more-link:hover {
  text-decoration: underline;
}

.status-row-group.is-map-active {
  background: rgba(42, 111, 151, 0.08);
}

.status-list {
  margin: 14px 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid rgba(19, 30, 34, 0.08);
}

.status-row-group {
  border-bottom: 1px solid rgba(19, 30, 34, 0.08);
}

.status-row-group.is-not-working {
  opacity: 0.6;
}

.status-not-working {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  width: fit-content;
  margin-top: 1px;
  padding: 1px 6px;
  border-radius: 4px;
  background: rgba(19, 30, 34, 0.08);
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.status-list-empty {
  color: var(--muted);
  font-size: 0.86rem;
  padding: 8px 2px;
}

.status-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 2px;
}

.status-toggle-icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid rgba(19, 30, 34, 0.18);
  background: none;
  color: var(--muted);
  font-family: var(--font-heading);
  font-size: 0.68rem;
  font-style: italic;
  font-weight: 700;
  cursor: pointer;
  transition: transform 0.15s ease, background 0.15s ease, color 0.15s ease;
}

.status-toggle-icon:hover,
.status-toggle-icon:focus-visible {
  border-color: var(--teal);
  color: var(--teal);
}

.status-toggle-icon[aria-expanded="true"] {
  background: var(--teal);
  border-color: var(--teal);
  color: #fff;
  transform: rotate(180deg);
}

.status-detail {
  padding: 2px 2px 12px 68px;
  animation: status-detail-in 0.15s ease;
}

@keyframes status-detail-in {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.status-detail dl {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  margin: 0 0 8px;
}

.status-detail dl div {
  display: flex;
  align-items: baseline;
  gap: 5px;
}

.status-detail dt {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  color: var(--muted);
}

.status-detail dd {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 600;
  color: #172025;
  font-variant-numeric: tabular-nums;
}

.status-detail p {
  margin: 0 0 6px;
  font-size: 0.8rem;
  line-height: 1.45;
  color: #39464e;
}

.status-detail p:last-child {
  margin-bottom: 0;
}

.status-detail-note {
  color: var(--muted);
}

.status-detail-bm-link {
  display: inline-block;
  margin-top: 2px;
  color: var(--teal);
  font-size: 0.78rem;
  font-weight: 700;
  text-decoration: none;
}

.status-detail-bm-link:hover,
.status-detail-bm-link:focus-visible {
  text-decoration: underline;
}

.status-badge {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 58px;
  padding: 2px 6px;
  border-radius: 4px;
  font-family: var(--font-tech);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-align: center;
}

.status-badge.online {
  background: rgba(42, 111, 151, 0.12);
  color: #235e80;
}

.status-badge.attention {
  background: rgba(220, 123, 47, 0.14);
  color: #a35a1e;
}

.status-badge.unknown {
  background: rgba(19, 30, 34, 0.08);
  color: var(--muted);
}

.status-info {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1 1 auto;
}

.status-callsign {
  font-family: var(--font-mono);
  font-weight: 800;
  font-size: 0.86rem;
  color: #172025;
  letter-spacing: 0.01em;
  text-decoration: none;
  width: fit-content;
}

a.status-callsign:hover,
a.status-callsign:focus-visible {
  color: var(--teal);
  text-decoration: underline;
}

.status-city {
  font-size: 0.72rem;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.net-card {
  border-color: rgba(255, 255, 255, 0.1);
  background:
    linear-gradient(135deg, rgba(42, 111, 151, 0.18), transparent 44%),
    #253038;
  color: #fff;
}

.net-card p {
  color: rgba(255, 255, 255, 0.74);
}

.net-card .inline-link {
  color: #8fbcd9;
}

.net-card h2 {
  display: flex;
  align-items: center;
  gap: 7px;
}

.net-card-type-icon {
  display: block;
  width: 16px;
  height: 16px;
  flex: none;
  color: #8fbcd9;
}

.net-card-type-icon svg {
  width: 100%;
  height: 100%;
  display: block;
}

.discord-widget-wrap {
  display: flex;
  justify-content: center;
  padding: 4px 0 2px;
}

.discord-widget-wrap iframe {
  max-width: 360px;
  border-radius: 8px;
}

.net-time {
  margin: 8px 0 0;
  color: #fff !important;
  font-family: var(--font-heading);
  font-size: 2.7rem;
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1;
}

.net-date {
  margin: 2px 0 0 !important;
  color: rgba(255, 255, 255, 0.6) !important;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}

.net-countdown {
  display: block;
  margin-top: 8px;
}

.net-countdown:not(:empty) {
  display: inline-flex;
  padding: 3px 9px;
  border-radius: var(--md-shape-full);
  background: rgba(143, 188, 217, 0.18);
  color: #8fbcd9;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 800;
}

.topics {
  padding: 2px 0;
}

.topic-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

.topic-list a {
  display: inline-flex;
  align-items: center;
  min-height: 30px;
  border-radius: 6px;
  border: 1px solid rgba(23, 32, 37, 0.08);
  padding: 0 10px;
  background: #ffffff;
  color: #344149;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  font-weight: 700;
  box-shadow: 0 2px 6px rgba(23, 32, 37, 0.04);
  transition: border-color 140ms ease, box-shadow 140ms ease, transform 140ms ease;
}

.topic-list a:hover {
  border-color: #b0bec5;
  box-shadow: 0 4px 12px rgba(23, 32, 37, 0.08);
  transform: translateY(-1px);
}

.content-shell {
  margin-top: 34px;
  margin-bottom: 70px;
}

.content-card {
  max-width: 860px;
  padding: clamp(22px, 4vw, 44px);
  border-radius: var(--md-shape-lg);
  border: none;
  background: var(--md-surface-container);
  box-shadow: var(--md-elevation-1);
}

.content-card h1 {
  font-size: clamp(2rem, 4vw, 3.8rem);
}

/* Break the hero banner out of .content-card's own padding so the photo
   reaches the card's real edges instead of being inset -- the card's
   clamp(22px,4vw,44px) padding is meant for text content, not a full-
   bleed image. Negative-margins the hero back out to the card boundary,
   matching the padding value exactly at each breakpoint. */
.content-card > .hero-banner {
  margin: clamp(-44px, -4vw, -22px) clamp(-44px, -4vw, -22px) 24px;
  border-radius: var(--md-shape-lg) var(--md-shape-lg) 0 0;
}

.content-card > .hero-banner .featured-copy h1 {
  font-size: clamp(1.55rem, 2.45vw, 2.45rem);
}

/* single.php: the tag/date/external-link row used to sit inside the
   hero overlay (white-on-photo); moved below the hero/title to match
   page.php's simpler title-only hero (explicit user request, 2026-09-04:
   "maybe news should also show only title on the hero and other info
   after hero" -- was the other way around, decided in the end to make
   news match pages rather than pages match news). Needs its own bottom
   margin here since it now sits directly above .entry-content. */
.content-card > .meta-row {
  margin-bottom: 18px;
}

.entry-content {
  color: #344149;
  font-size: 1.1rem;
  line-height: 1.48;
  overflow-wrap: anywhere;
}

.entry-content p {
  margin-top: 0;
  margin-bottom: 0.95em;
}

.entry-content ul,
.entry-content ol {
  margin-top: 0;
  margin-bottom: 0.95em;
  padding-left: 1.4em;
  line-height: 1.48;
}

.entry-content li {
  margin-bottom: 0.25em;
}

.related-notices {
  margin-top: 36px;
  padding-top: 28px;
  border-top: 1px solid var(--md-outline-variant);
}

.entry-content a {
  color: var(--teal-dark);
  font-weight: 750;
}

.entry-content img {
  border-radius: var(--radius);
}

.entry-content .wp-block-table {
  margin: 22px 0 28px;
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  box-shadow: 0 8px 24px rgba(23, 32, 37, 0.06);
}

.entry-content .wp-block-table table {
  min-width: 720px;
  font-size: 0.86rem;
}

.entry-content .wp-block-table table,
.live-card table {
  font-variant-numeric: tabular-nums;
}

.entry-content .wp-block-table th,
.entry-content .wp-block-table td {
  padding: 10px 12px;
  vertical-align: top;
}

.entry-content .wp-block-table tr:first-child td,
.entry-content .wp-block-table thead th {
  background: #253038;
  color: #fff;
  font-weight: 900;
}

.entry-content .wp-block-table tr:nth-child(even):not(:first-child) td {
  background: #f8fafb;
}

.entry-content s {
  color: #849098;
}

.entry-content mark.has-vivid-red-color {
  color: var(--red) !important;
  font-weight: 850;
}

.archive-list {
  display: grid;
  gap: 16px;
  max-width: 860px;
}

.search-form {
  display: flex;
  gap: 10px;
  margin: 22px 0;
}

.search-form label {
  flex: 1;
}

.search-field {
  width: 100%;
  min-height: 44px;
  border: 1px solid var(--md-outline);
  border-radius: var(--md-shape-full);
  padding: 0 18px;
  background: var(--md-surface);
  color: var(--ink);
}

.search-submit {
  position: relative;
  min-height: 44px;
  border: 0;
  border-radius: var(--md-shape-full);
  padding: 0 22px;
  background: var(--md-primary);
  color: var(--md-on-primary);
  font-family: var(--font-heading);
  font-weight: 700;
  cursor: pointer;
  box-shadow: var(--md-elevation-1);
  transition: box-shadow var(--md-motion-standard);
  overflow: hidden;
}

.search-submit::before {
  content: "";
  position: absolute;
  inset: 0;
  background: currentColor;
  opacity: 0;
  transition: opacity var(--md-motion-standard);
  pointer-events: none;
}

.search-submit:hover {
  box-shadow: var(--md-elevation-2);
}

.search-submit:hover::before {
  opacity: var(--md-state-hover);
}

.search-submit:active::before {
  opacity: var(--md-state-press);
}

.socket-player {
  display: grid;
  gap: 12px;
  max-width: 480px;
  margin: 24px 0;
}

.socket-player.live-strip-player {
  display: block;
  max-width: none;
  margin: 0 0 0 auto;
}

.socket-player-button.is-playing,
.socket-player-button.is-tx {
  background: var(--ink);
}

.socket-player-button.is-error {
  background: var(--red);
}

.socket-player-volume {
  display: none;
  width: 100%;
}

.socket-player.is-active .socket-player-volume {
  display: block;
}

.socket-player-status {
  color: var(--muted);
  font-size: 0.9rem;
}

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

.footer-inner {
  display: grid;
  grid-template-columns: minmax(220px, 1fr) minmax(0, 2fr);
  gap: 20px;
  width: min(1180px, calc(100% - 40px));
  margin: 0 auto;
  padding: 26px 0;
  color: var(--muted);
  font-family: var(--font-sans);
  font-size: 0.88rem;
  line-height: 1.5;
}

.footer-inner strong {
  color: var(--ink);
  font-weight: 600;
  display: inline-block;
  margin-bottom: 2px;
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 10px 20px;
  font-weight: 600;
  font-size: 0.9rem;
}

.footer-links a {
  color: #344149;
  text-decoration: none;
  transition: color 0.15s ease;
}

.footer-links a:hover {
  color: var(--teal);
  text-decoration: underline;
}

@media (max-width: 980px) {
  .header-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    min-height: 50px;
    padding: 8px 0;
  }

  .brand {
    gap: 8px;
    flex-shrink: 0;
  }

  .main-nav {
    display: none;
  }

  .login-button {
    display: none;
  }

  .menu-toggle {
    display: flex;
  }

  .header-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
    flex-shrink: 0;
  }

  .icon-button {
    width: 36px;
    height: 36px;
    font-size: 1.35rem;
  }

  .page-grid {
    display: flex;
    flex-direction: column;
  }

  /* Below the two-column breakpoint, .sidebar's own DOM position (after
     .feed) would otherwise bury the live last-heard table under every
     news card -- display:contents promotes its children to be direct
     flex items of .page-grid so .live-card can be pulled to the very
     top (right under the listen button in the sticky strip) via
     order, while the rest of the sidebar (repeaters, net event) stays
     below the news feed as before. */
  .sidebar {
    display: contents;
  }

  .live-card {
    order: -1;
    margin-bottom: 24px;
  }

  .feed {
    order: 1;
  }

  .side-card:not(.live-card) {
    order: 2;
  }
}

@media (max-width: 680px) {
  .header-inner,
  .live-strip-inner,
  .page-grid,
  .content-shell,
  .footer-inner {
    width: min(100% - 24px, 1180px);
  }

  .header-inner {
    min-height: 48px;
    padding: 6px 0;
  }

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

  .brand-mark {
    width: 36px;
    height: 26px;
    font-size: 0.72rem;
  }

  .live-strip-inner {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 34px;
    padding: 4px 0;
    font-size: 0.78rem;
  }

  .live-last-wrap {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    color: rgba(255, 255, 255, 0.75);
  }

  .live-card .socket-player {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
  }

  .live-card .socket-player-button {
    width: 100%;
    min-height: 40px;
    justify-content: center;
  }

  .socket-player.is-active .socket-player-controls {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    box-sizing: border-box;
  }

  .live-card .socket-player-volume {
    flex: 1;
    max-width: 200px;
  }

  h1 {
    margin-bottom: 14px;
    font-size: 2.2rem;
  }

  /* Article body lists (e.g. "Korduvad loendused ja ümarlauad") have
     no custom li spacing at all -- each bullet's apparent gap is
     really just 2-line wrapping at the desktop-sized 40px indent/
     1.03rem font, not margin. Narrower indent and a touch smaller
     type let more bullets stay on one line, so the list reads as
     tighter without adding negative margins on top of zero. */
  .entry-content ul,
  .entry-content ol {
    padding-left: 22px;
  }

  .entry-content li {
    font-size: 0.95rem;
    line-height: 1.4;
  }

  /* Notice-card list items (the "Viimased teated" list under the
     featured article) carry generous desktop padding/min-height that
     reads as loose/oversized on an ordinary phone width, not just the
     very smallest screens -- found at a real 375px width, distinct
     from the more aggressive tightening under the 380px tier below. */
  .notice-grid {
    gap: 12px;
  }

  .notice-card {
    grid-template-columns: 50px minmax(0, 1fr);
    gap: 12px;
    min-height: 0;
    padding: 12px;
  }

  .notice-card.has-thumb {
    grid-template-columns: 108px minmax(0, 1fr);
  }

  .notice-date {
    width: 50px;
    height: 50px;
  }

  .notice-date strong {
    font-size: 1.15rem;
  }

  .notice-card h3 {
    margin: 7px 0 5px;
    font-size: 1.12rem;
  }

  .notice-card p {
    font-size: 0.94rem;
    line-height: 1.45;
  }

  .mobile-live-summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 18px;
    padding: 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: #253038;
    color: #fff;
  }

  .mobile-live-summary div {
    display: grid;
    gap: 5px;
  }

  .mobile-live-summary span:not(.live-pill) {
    color: rgba(255, 255, 255, 0.72);
    font-size: 0.82rem;
  }

  .mobile-live-summary a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 38px;
    border-radius: var(--md-shape-full);
    padding: 0 16px;
    background: var(--md-primary);
    color: var(--md-on-primary) !important;
    font-weight: 700;
    white-space: nowrap;
    box-shadow: var(--md-elevation-1);
    text-decoration: none !important;
  }

  .featured-card,
  .featured-copy {
    min-height: 280px;
  }

  /* .hero-banner stays landscape on mobile too (explicit user preference)
     instead of inheriting the front-page card's boxier fixed min-height.
     Not as wide as desktop's 16/5 -- at mobile widths that would make
     the banner very short in absolute pixels (cramping the overlaid
     text); not 16/9 either (the original choice here cropped the
     ~3.16:1 photo set too aggressively, see desktop rule's comment
     above). Widened again from 2/1 to 12/5 (2.4:1) -- 2/1 was STILL
     cropping ~25% off the width of the same ~3.16:1 banner-shaped photo
     set (found live 2026-09-04 on /alustamine/'s circuit-board photo,
     same class of issue as hotspot.jpg on desktop) -- 12/5 keeps the
     hero from getting too squat on a narrow screen while cropping this
     photo set noticeably less. */
  .hero-banner {
    min-height: 0;
    aspect-ratio: 12 / 5;
  }

  .featured-copy {
    padding: 18px 18px;
  }

  .featured-copy p {
    font-size: 0.92rem;
  }

  .toolbar,
  .event-strip-header,
  .footer-inner {
    align-items: flex-start;
    flex-direction: column;
  }

  .footer-inner {
    grid-template-columns: 1fr;
  }

  .footer-links {
    justify-content: flex-start;
  }

  .search-form {
    flex-direction: column;
  }

  .notice-grid,
  .event-grid,
  .quick-grid,
  .sidebar {
    grid-template-columns: 1fr;
  }

  /* Cards are full-width here, so the icon stacked above the title
     wastes a whole row. Put it beside the text instead. */
  .quick-grid a {
    display: grid;
    grid-template-columns: 34px minmax(0, 1fr);
    align-items: start;
    column-gap: 12px;
  }

  .quick-icon {
    margin-bottom: 0;
    grid-row: span 2;
  }

  .notice-card,
  .notice-card.has-thumb {
    grid-template-columns: 1fr;
  }

  .notice-date {
    width: 100%;
    height: 44px;
    grid-auto-flow: column;
    justify-content: start;
    padding-left: 12px;
    gap: 6px;
  }

  .notice-thumb {
    min-height: 138px;
  }

  th,
  td {
    white-space: normal;
  }
}

/* Foorumid/Blogid page: content-shell's default .content-card
   (max-width 860px, meant for prose) left the wider .notice-grid
   feed below it looking visually disconnected/misaligned on desktop.
   Widen the intro card to match, and let the grid collapse to a
   single column when there isn't enough room for two reasonably
   sized cards instead of a fixed 2-up split that left short cards
   with a lot of empty space. */
.forum-page .forum-intro {
  max-width: none;
}

/* /repiiterid/ has no sidebar (a full-width map needs the room), so
   collapse .page-grid's fixed 1fr/320px split to a single column instead
   of leaving an empty reserved sidebar gutter. This alone wasn't enough
   -- found live 2026-09-04: .page-grid's base rule (shared with
   .header-inner/.content-shell/.footer-inner) also caps width at
   min(1180px, 100% - 40px) with auto margins, a site-wide "readable
   content column" convention that's the wrong fit for a data-dense
   map/list page and reproduced the same "empty space" symptom the
   display:block fix above was meant to remove, just centered instead
   of in a dead grid column. Let this page use the fuller viewport width
   instead, still leaving a small side margin, not the 1180px content cap. */
.repeaters-page.page-grid {
  display: block;
  width: min(1600px, calc(100% - 40px));
}

/* .content-card's own max-width: 860px (style.css ~line 3044) is meant
   for prose readability -- the wrong fit for a map/list card, and left
   the same empty-space symptom the two fixes above already addressed
   for the page container itself, just one level deeper. */
.repeaters-map-card.content-card {
  max-width: none;
}

/* This page stacks two .content-cards directly (the intro article, then
   the map/filter/list card) with nothing else between them in .feed --
   .feed itself has no gap between children, so with zero margin the two
   cards' rounded corners touched directly and the filter row/pills near
   the top of the second card visually collided with the first card's
   bottom edge. */
.repeaters-map-card {
  margin-top: 20px;
  padding: 14px;
}

.repeaters-filter-row {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 10px;
}

.repeaters-filter-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: 999px;
  border: 1px solid rgba(42, 111, 151, 0.3);
  background: rgba(42, 111, 151, 0.08);
  color: var(--teal-dark);
  font-family: var(--font-sans);
  font-size: 0.8rem;
  font-weight: 700;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease;
}

.repeaters-filter-toggle:hover,
.repeaters-filter-toggle:focus-visible {
  background: rgba(42, 111, 151, 0.16);
  border-color: rgba(42, 111, 151, 0.5);
}

.repeaters-filter-toggle[aria-pressed="true"] {
  background: var(--teal);
  border-color: var(--teal);
  color: #fff;
}

.repeaters-type-filter-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 12px;
}

.repeaters-type-filter-pill {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid rgba(19, 30, 34, 0.15);
  background: #fff;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.repeaters-type-filter-pill:hover,
.repeaters-type-filter-pill:focus-visible {
  border-color: rgba(42, 111, 151, 0.4);
  color: var(--teal-dark);
}

.repeaters-type-filter-pill[aria-pressed="true"] {
  background: var(--teal);
  border-color: var(--teal);
  color: #fff;
}

.repeaters-map-full {
  height: 460px;
}

.repeaters-list-full {
  margin-top: 16px;
  columns: 2;
  column-gap: 24px;
}

.repeaters-list-full .status-row-group {
  break-inside: avoid;
}

.status-type-tag {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 58px;
  padding: 2px 6px;
  border-radius: 4px;
  background: rgba(19, 30, 34, 0.06);
  color: var(--muted);
  font-family: var(--font-tech);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-align: center;
  text-transform: uppercase;
}

/* Pushed to the row's trailing edge (was leading, before the status
   badge) -- with a variable number of chips per repeater (1 for DMR-
   only, 2 for DMR+FM), leading placement meant every row's callsign
   started at a different horizontal position depending on chip count.
   Trailing placement keeps the badge+callsign block's position fixed so
   callsigns line up down the list, and moves the chips to the flexible
   side where a variable width doesn't disturb anything else. */
.status-type-tags {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
}

/* Full-page list: detail is expanded by default (more room than the
   sidebar), so the "i" toggle is redundant there and only needed once
   space gets tight on narrow screens. */
.status-row-group-page .status-toggle-icon-mobile-only {
  display: none;
}

@media (max-width: 680px) {
  .repeaters-map-full {
    /* Was 320px -- too short for autoPan to fully fit a popup opened on
       a marker near the top edge, even with the popup's coverage/note
       text moved out to the list below. More height gives autoPan
       actual room to work with instead of hitting the map's own edge. */
    height: 420px;
  }

  .repeaters-list-full {
    columns: 1;
  }

  .status-row-group-page .status-toggle-icon-mobile-only {
    display: inline-flex;
  }

  .status-row-group-page .status-detail[hidden] {
    display: none;
  }
}

@media (min-width: 681px) {
  .status-row-group-page .status-detail {
    display: block !important;
  }
}

/* Links to the actual sites these feed items come from -- explicit user
   request 2026-09-04: link to the main site (hamfoorum.ee, esqtc.erau.ee),
   not the RSS feed URLs themselves (DMR_EE_2026_FORUM_FEEDS in
   functions.php), since a reader has no use for a raw feed URL. */
.forum-sources {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--md-outline-variant);
  font-size: 0.88rem;
}

.forum-sources-label {
  color: var(--muted);
  font-weight: 600;
}

.forum-sources a {
  color: var(--teal-dark);
  font-weight: 700;
  text-decoration: none;
}

.forum-sources a:hover {
  text-decoration: underline;
}

.forum-feed-grid {
  margin-top: 26px;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  align-items: start;
}

/* Genuinely small phones (iPhone 5/SE-class, ~320-375px) -- verified
   at a real 320px width that the live table's 6 columns (Aeg/Kutsung/
   Nimi/TX/Loss/BER) squeeze tight enough to need horizontal scroll,
   and that the page generally feels oversized end to end at this
   width (h1, card padding, notice-card titles), not just that one
   table. Tightens padding/font-size across the page rather than only
   the live card. */
@media (max-width: 380px) {
  h1 {
    margin-bottom: 10px;
    font-size: 1.7rem;
  }

  .section-kicker {
    font-size: 0.7rem;
  }

  .content-shell {
    margin-top: 20px;
    margin-bottom: 44px;
  }

  .page-grid {
    margin-top: 18px;
    margin-bottom: 44px;
  }

  .side-card {
    padding: 11px;
  }

  .notice-card {
    grid-template-columns: 46px minmax(0, 1fr);
    gap: 10px;
    min-height: 0;
    padding: 11px;
  }

  .notice-card.has-thumb {
    grid-template-columns: 96px minmax(0, 1fr);
  }

  .notice-date {
    width: 46px;
    height: 46px;
  }

  .notice-date strong {
    font-size: 1.05rem;
  }

  .notice-card h3 {
    margin: 6px 0 5px;
    font-size: 1rem;
  }

  .notice-card p {
    font-size: 0.82rem;
  }

  .tag {
    padding: 2px 8px;
    font-size: 0.66rem;
  }

  /* Buy back width for the columns themselves by trimming the
     horizontal cell padding rather than any column's ch budget -- 2px
     per cell edge is 24px across the six columns, which is what lets
     Nimi keep a real gap next to TX at the narrowest widths. The <col>
     widths track this automatically (they add --live-cell-px back). */
  .live-card table {
    font-size: 0.68rem;
    --live-cell-px: 2px;
  }

  .live-card td,
  .live-card th {
    padding: 7px var(--live-cell-px);
    font-size: 0.65rem;
  }

  .live-summary-row {
    gap: 3px 7px;
    font-size: 0.7rem;
  }

  .socket-player-button {
    padding: 0 16px;
    font-size: 0.82rem;
  }

  .featured-copy {
    padding: 18px 18px;
  }

  .featured-copy h2,
  .featured-copy h1 {
    font-size: 1.35rem;
  }

  .featured-copy p {
    font-size: 0.9rem;
  }
}
