/**
 * Component styles for the public site.
 *
 * Tailwind utilities handle layout in the templates; this file holds the few
 * repeated components and everything that needs a real selector - focus rings,
 * dropdown hover behaviour, and the logical properties that make Arabic and
 * Urdu mirror correctly without a second stylesheet.
 *
 * Colours come from the CSS custom properties emitted by theme_service.php, so
 * changing a colour in the admin panel changes them here too.
 */

/* ------------------------------------------------------------ typography - */

body {
  font-family: Roboto, system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, .font-heading {
  font-family: Montserrat, system-ui, sans-serif;
  font-weight: 700;
  letter-spacing: -0.015em;
}

/* Montserrat and Roboto have no Arabic or Urdu coverage, so those languages
   fall back to a system Arabic stack rather than rendering in a substituted
   face with mismatched metrics. */
[lang="ar"] h1, [lang="ar"] h2, [lang="ar"] h3, [lang="ar"] h4,
[lang="ur"] h1, [lang="ur"] h2, [lang="ur"] h3, [lang="ur"] h4,
html[dir="rtl"] h1, html[dir="rtl"] h2, html[dir="rtl"] h3, html[dir="rtl"] h4 {
  font-family: "Segoe UI", Tahoma, "Noto Naskh Arabic", system-ui, sans-serif;
  letter-spacing: 0;
}

html[dir="rtl"] body {
  font-family: "Segoe UI", Tahoma, "Noto Naskh Arabic", system-ui, sans-serif;
}

/* Arabic gets a geometric face that sits closer to Montserrat's weight. */
html[lang="ar"] body,
html[lang="ar"] h1, html[lang="ar"] h2, html[lang="ar"] h3, html[lang="ar"] h4 {
  font-family: "Noto Kufi Arabic", "Segoe UI", Tahoma, system-ui, sans-serif;
}

/* Nastaliq is a sloping script: its glyphs need far more vertical room than
   Latin or Naskh, and it looks broken at normal line-height. */
html[lang="ur"] body,
html[lang="ur"] h1, html[lang="ur"] h2, html[lang="ur"] h3, html[lang="ur"] h4 {
  font-family: "Noto Nastaliq Urdu", "Jameel Noori Nastaleeq", "Segoe UI", Tahoma, system-ui, sans-serif;
}

html[lang="ur"] body {
  line-height: 2.1;
}

html[lang="ur"] h1, html[lang="ur"] h2, html[lang="ur"] h3, html[lang="ur"] h4 {
  line-height: 1.9;
}

/* ---------------------------------------------------------- skip link ---- */

.skip-link {
  position: absolute;
  inset-inline-start: -9999px;
  top: 0;
  z-index: 100;
  padding: 0.75rem 1.25rem;
  background: var(--tg-primary);
  color: var(--tg-ink);
  font-weight: 600;
}

.skip-link:focus {
  inset-inline-start: 0;
}

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

/* The header floats transparently over the banner and turns solid on scroll.
   Every page opens with a dark hero or banner, so light-on-transparent is
   always legible at the top of the page. */

.site-head {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 50;
  transition: background-color 0.25s ease, box-shadow 0.25s ease;
}

/* Scrolled, the bar is white and see-through rather than solid.
   0.78 at the client's request, down from 0.88. That is close to the floor:
   over the darkest band the bar composites to #C9D3CE, where the nav text is
   5.9:1 and the gold active rule 3.4:1 against a 3.0 minimum. 0.70 puts that
   rule at 2.9:1 and fails, so this cannot go much further while the marker
   stays gold. The blur is what stops body text reading as noise underneath. */
.site-head.is-scrolled {
  background: rgb(255 255 255 / 0.78);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  backdrop-filter: blur(14px) saturate(140%);
  box-shadow: 0 6px 24px -16px rgba(22, 33, 28, 0.45);
}

/* Without backdrop-filter the blur cannot hide what is behind, so the bar
   leans more opaque instead. */
@supports not (backdrop-filter: blur(1px)) {
  .site-head.is-scrolled {
    background: rgb(255 255 255 / 0.94);
  }
}

.site-topbar {
  border-bottom: 1px solid rgba(255, 255, 255, 0.16);
  color: rgba(255, 255, 255, 0.85);
  overflow: hidden;
  max-height: 3rem;
  transition: max-height 0.25s ease, opacity 0.2s ease, border-color 0.25s ease;
}

/* Collapse the utility bar once scrolling starts, so the header stays compact. */
.site-head.is-scrolled .site-topbar {
  max-height: 0;
  opacity: 0;
  border-color: transparent;
}

.site-header {
  border-bottom: 1px solid transparent;
  transition: border-color 0.25s ease;
}

.site-head.is-scrolled .site-header {
  border-bottom-color: var(--tg-border);
}

/* Logo swap: light over the banner, dark over white. */
.site-logo {
  display: block;
  width: auto;
  object-fit: contain;
}

.site-logo--dark {
  display: none;
}

/* Light logo over the hero, dark logo once the bar turns white. */
.site-head.is-scrolled .site-logo--light {
  display: none;
}

.site-head.is-scrolled .site-logo--dark {
  display: block;
}

.nav-link {
  display: inline-flex;
  align-items: center;
  padding: 0.5rem 0.75rem;
  border-radius: 0.5rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.88);
  transition: color 0.15s ease, background-color 0.15s ease;
  white-space: nowrap;
}

.nav-link:hover {
  color: #fff;
  background: rgba(255, 255, 255, 0.12);
}

/* Gold text would be 3.9:1 on the translucent bar over a light section, so
   the active page is marked with a gold rule under white text instead: the
   text keeps its contrast and the marker is a graphic, which needs only 3:1. */
.nav-link.is-active {
  color: #fff;
  position: relative;
}

.nav-link.is-active::after {
  content: "";
  position: absolute;
  left: 0.75rem;
  right: 0.75rem;
  bottom: 0.15rem;
  height: 2px;
  border-radius: 2px;
  /* The lighter gold, not --tg-primary: the primary measures 2.6:1 against the
     translucent bar over a white section, under the 3:1 a graphic needs. */
  background: var(--tg-on-dark);
}

/* The scrolled bar is light, so the links invert to dark ink. Unscrolled they
   stay white over the hero - that state is unchanged. */
.site-head.is-scrolled .nav-link {
  color: var(--tg-secondary);
}

/* Ink rather than the gold: --tg-primary-dark is 4.2:1 against this bar over
   a dark band, just under the 4.5:1 small text needs. */
.site-head.is-scrolled .nav-link:hover {
  color: var(--tg-ink);
  background: rgba(22, 33, 28, 0.06);
}

.site-head.is-scrolled .nav-link.is-active {
  color: var(--tg-ink);
}

/* On the light bar the marker needs the darker gold: --tg-primary is 1.9:1
   there, well under the 3:1 a graphic needs. */
.site-head.is-scrolled .nav-link.is-active::after {
  background: var(--tg-primary-dark);
}

/* Language button and mobile toggle follow the same two states. */
.lang-button,
.menu-toggle {
  border-color: rgba(255, 255, 255, 0.35);
  color: rgba(255, 255, 255, 0.9);
}

.lang-button:hover,
.menu-toggle:hover {
  border-color: #fff;
  color: #fff;
}

.site-head.is-scrolled .lang-button,
.site-head.is-scrolled .menu-toggle {
  border-color: var(--tg-border);
  color: var(--tg-secondary);
}

.site-head.is-scrolled .lang-button:hover,
.site-head.is-scrolled .menu-toggle:hover {
  border-color: var(--tg-primary-dark);
  color: var(--tg-primary-dark);
}

/* The mobile menu is a white panel, so its controls always use dark ink. */
.mobile-menu .menu-toggle {
  border-color: var(--tg-border);
  color: var(--tg-secondary);
}

/* --------------------------------------------------------- dropdowns ----- */

.nav-dropdown {
  position: relative;
}

.nav-dropdown-panel {
  position: absolute;
  inset-inline-start: 0;
  top: calc(100% + 0.25rem);
  z-index: 60;
  min-width: 15rem;
  padding: 0.5rem;
  border: 1px solid var(--tg-border);
  border-radius: 0.75rem;
  background: #fff;
  box-shadow: 0 18px 40px -22px rgba(22, 33, 28, 0.5);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
}

.nav-dropdown-panel--right {
  inset-inline-start: auto;
  inset-inline-end: 0;
}

.nav-dropdown:hover .nav-dropdown-panel,
.nav-dropdown:focus-within .nav-dropdown-panel,
.nav-dropdown-panel.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.nav-dropdown-link {
  display: block;
  padding: 0.5rem 0.75rem;
  border-radius: 0.5rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--tg-secondary);
}

.nav-dropdown-link:hover {
  background: var(--tg-surface-alt);
  color: var(--tg-primary-dark);
}

.nav-dropdown-link.is-active {
  background: var(--tg-primary);
  color: var(--tg-ink);
}

/* Structure only. Colour belongs to the header's two states, set above and in
   the .is-scrolled rules - this block used to re-declare `color` and
   `border-color` here, and being later in the file it silently overrode the
   white treatment, leaving "EN" dark on the transparent header. Same trap as
   .menu-toggle vs xl:hidden. */
.lang-button {
  padding: 0.45rem 0.7rem;
  border-width: 1px;
  border-style: solid;
  border-radius: 0.5rem;
  font-size: 0.8rem;
  font-weight: 600;
}

.site-head.is-scrolled .lang-button:hover {
  border-color: var(--tg-primary-dark);
  color: var(--tg-primary-dark);
}

/* ------------------------------------------------------- mobile menu ----- */

.menu-toggle {
  /* No `display` here on purpose. site.css loads after tailwind.css, and at
     equal specificity a `display: inline-flex` in this file beat Tailwind's
     `xl:hidden` - media queries add no specificity - so the hamburger stayed
     visible next to the full desktop nav. Display now lives in the markup. */
  width: 2.75rem;
  height: 2.75rem;
  border: 1px solid var(--tg-border);
  border-radius: 0.5rem;
  color: var(--tg-secondary);
}

.menu-toggle:hover {
  border-color: var(--tg-primary-dark);
  color: var(--tg-primary-dark);
}

.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 80;
  overflow-y: auto;
  background: #fff;
}

.mobile-menu[hidden] {
  display: none;
}

.mobile-link {
  display: block;
  padding: 0.7rem 0.9rem;
  border-radius: 0.5rem;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--tg-secondary);
}

.mobile-link:hover {
  background: var(--tg-surface-alt);
  color: var(--tg-primary-dark);
}

.mobile-link.is-active {
  background: var(--tg-primary);
  color: var(--tg-ink);
}

.mobile-link--child {
  margin-inline-start: 1rem;
  font-size: 0.875rem;
  font-weight: 500;
}

/* ------------------------------------------------------------ buttons ---- */

.btn-primary,
.btn-secondary,
.btn-ghost {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* "Get a Callback" was breaking across two lines in the header. A button
     label is a single unit; let the button grow instead of the text wrapping. */
  white-space: nowrap;
  gap: 0.5rem;
  padding: 0.7rem 1.35rem;
  border-radius: 0.5rem;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.2;
  transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}

.btn-primary {
  background: var(--tg-primary);
  color: var(--tg-ink);
}

.btn-primary:hover {
  background: var(--tg-primary-dark);
  transform: translateY(-1px);
}

.btn-secondary {
  background: var(--tg-accent);
  color: #fff;
}

.btn-secondary:hover {
  background: var(--tg-accent-dark);
  color: #fff;
  transform: translateY(-1px);
}

.btn-ghost {
  border: 1px solid rgba(255, 255, 255, 0.6);
  color: #fff;
}

.btn-ghost:hover {
  background: rgba(255, 255, 255, 0.12);
}

.btn-outline {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 1.1rem;
  border: 1px solid var(--tg-border);
  border-radius: 0.5rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--tg-secondary);
  transition: border-color 0.15s ease, color 0.15s ease;
}

.btn-outline:hover {
  border-color: var(--tg-primary-dark);
  color: var(--tg-primary-dark);
}

/* Arrow glyphs inside buttons must point the other way in RTL. */
html[dir="rtl"] .btn-primary svg,
html[dir="rtl"] .btn-secondary svg,
html[dir="rtl"] .btn-outline svg,
html[dir="rtl"] .link-arrow svg {
  transform: scaleX(-1);
}

/* ----------------------------------------------------------- sections ---- */

.section {
  padding-block: 4rem;
}

@media (min-width: 768px) {
  .section {
    padding-block: 5.5rem;
  }
}

.section-kicker {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--tg-primary-dark);
}

.section-kicker--light {
  color: var(--tg-on-dark);
}

/* The "01 / OUR SERVICES" counter that runs down the page. */
.section-number {
  font-family: Montserrat, system-ui, sans-serif;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0;
  color: var(--tg-accent);
}

.section-number-divider {
  margin-inline: 0.15rem 0.35rem;
  /* Was --tg-border, a divider-line grey: 1.30:1 on white, so the slash between
     "01" and the kicker was invisible. This is the muted text token. */
  color: var(--tg-muted);
  font-weight: 400;
}

/* Hero video fades in once it can actually play, so a slow connection shows
   the poster image rather than a black rectangle. */
video[data-hero-video].is-ready {
  opacity: 1;
}

.section-title {
  margin-top: 0.75rem;
  font-size: clamp(1.75rem, 3vw, 2.5rem);
  font-weight: 700;
  line-height: 1.15;
  color: var(--tg-ink);
}

.section-title--light {
  color: #fff;
}

/* The About block is set noticeably larger than the rest of the page, as on
   the reference site, so it reads as the site's statement of who we are. */
.section-title--xl {
  font-size: clamp(2.1rem, 4.4vw, 3.5rem);
  line-height: 1.06;
  letter-spacing: -0.01em;
}

.section-lead {
  margin-top: 1rem;
  font-size: 1rem;
  line-height: 1.7;
  color: var(--tg-body);
}

.section-lead--xl {
  font-size: 1.0625rem;
  line-height: 1.8;
}

@media (min-width: 1024px) {
  .section-lead--xl {
    font-size: 1.15rem;
  }
}

.section-lead--light {
  color: rgba(255, 255, 255, 0.82);
}

/* Gold rule under headings, mirrored in RTL. */
.title-rule::after {
  content: "";
  display: block;
  width: 3.5rem;
  height: 3px;
  margin-top: 1rem;
  border-radius: 2px;
  background: var(--tg-accent);
}

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

.card {
  display: flex;
  flex-direction: column;
  height: 100%;
  padding: 1.75rem;
  border: 1px solid var(--tg-border);
  border-radius: 1rem;
  background: #fff;
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}

.card:hover {
  transform: translateY(-3px);
  border-color: var(--tg-primary-light);
  box-shadow: 0 18px 38px -24px rgba(8, 26, 18, 0.45);
}

.card-title {
  margin-top: 1.1rem;
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--tg-ink);
}

.card-text {
  margin-top: 0.6rem;
  font-size: 0.9rem;
  line-height: 1.65;
  color: var(--tg-body);
}

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--tg-primary-dark);
}

.link-arrow:hover {
  color: var(--tg-primary-dark);
}

/* ---------------------------------------------------------------- hero --- */

.hero {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  /* The colour behind the video while it loads, and if it fails to play.
     Must be a DARK band tone, not --tg-primary-dark: since gold became the
     primary that token is #856916, which showed a mustard block under the
     white hero text at 4.26:1. */
  background: var(--tg-band-green, #0a3720);
}

.hero-slide {
  position: absolute;
  inset: 0;
  transition: opacity 0.8s ease;
}

.hero-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, rgba(6, 37, 22, 0.92) 0%, rgba(14, 74, 42, 0.82) 45%, rgba(20, 102, 58, 0.45) 100%);
}

html[dir="rtl"] .hero-overlay {
  background: linear-gradient(260deg, rgba(6, 37, 22, 0.92) 0%, rgba(14, 74, 42, 0.82) 45%, rgba(20, 102, 58, 0.45) 100%);
}

/* -------------------------------------------- hero video overlay --------- *
 * The reference site darkens its hero video with a single layer at
 * opacity .37. These are pitched around that: enough contrast under white
 * text on the left, light enough that the footage still reads on the right.
 *
 * Written here rather than as Tailwind gradient utilities because the opacity
 * values needed (.16-.72) are not on Tailwind's opacity scale, so
 * `to-brand-900/28` and friends were silently never generated.
 */
.hero-video-overlay {
  position: absolute;
  inset: 0;
  /* Weighted to the text column rather than raised everywhere. The footage is
     a bright daytime aerial - white cloud and glass - where the old even
     overlay left the heading at 2.8:1 on its brightest pixels. The right-hand
     side stays light so the picture still reads. */
  background:
    linear-gradient(to bottom, rgba(6, 37, 22, 0.55) 0%, rgba(6, 37, 22, 0) 22%),
    linear-gradient(
      100deg,
      rgba(8, 26, 18, 0.72) 0%,
      rgba(8, 26, 18, 0.62) 38%,
      rgba(6, 37, 22, 0.34) 72%,
      rgba(6, 37, 22, 0.22) 100%
    );
}

.hero-video-overlay--light {
  background:
    linear-gradient(to bottom, rgba(6, 37, 22, 0.4) 0%, rgba(6, 37, 22, 0) 22%),
    linear-gradient(
      100deg,
      rgba(6, 37, 22, 0.55) 0%,
      rgba(6, 37, 22, 0.44) 38%,
      rgba(6, 37, 22, 0.22) 72%,
      rgba(6, 37, 22, 0.14) 100%
    );
}

.hero-video-overlay--dark {
  background:
    linear-gradient(to bottom, rgba(6, 37, 22, 0.6) 0%, rgba(6, 37, 22, 0) 22%),
    linear-gradient(
      100deg,
      rgba(6, 37, 22, 0.85) 0%,
      rgba(6, 37, 22, 0.74) 38%,
      rgba(6, 37, 22, 0.5) 72%,
      rgba(6, 37, 22, 0.36) 100%
    );
}

html[dir="rtl"] .hero-video-overlay,
html[dir="rtl"] .hero-video-overlay--light,
html[dir="rtl"] .hero-video-overlay--dark {
  background-image: linear-gradient(
    260deg,
    var(--tg-hero-a) 0%,
    var(--tg-hero-b) 45%,
    var(--tg-hero-c) 100%
  );
}

.hero-video-overlay {
  --tg-hero-a: rgba(6, 37, 22, 0.55);
  --tg-hero-b: rgba(6, 37, 22, 0.4);
  --tg-hero-c: rgba(6, 37, 22, 0.28);
}

.hero-video-overlay--light {
  --tg-hero-a: rgba(6, 37, 22, 0.4);
  --tg-hero-b: rgba(6, 37, 22, 0.26);
  --tg-hero-c: rgba(6, 37, 22, 0.16);
}

.hero-video-overlay--dark {
  --tg-hero-a: rgba(6, 37, 22, 0.72);
  --tg-hero-b: rgba(6, 37, 22, 0.58);
  --tg-hero-c: rgba(6, 37, 22, 0.44);
}

/* --------------------------------------------------------------- stats --- */

.stat-value {
  font-family: Montserrat, system-ui, sans-serif;
  font-size: clamp(2rem, 4vw, 3rem);
  font-weight: 700;
  line-height: 1;
  color: var(--tg-accent);
  /* Last-resort guard. The size modifiers below are what actually keep these
     inside their columns, but a figure nobody predicted should wrap rather
     than print on top of its neighbour. */
  min-width: 0;
  overflow-wrap: anywhere;
}

/* Sizes chosen by length in tg_stat_size_class(). "30+" can afford 3rem;
   "3354/RWP" at that size is wider than its grid track. */
.stat-value--sm {
  font-size: clamp(1.5rem, 2.6vw, 2.05rem);
}

.stat-value--xs {
  font-size: clamp(1.2rem, 2vw, 1.6rem);
  letter-spacing: -0.01em;
}

/* ----------------------------------------------------------- rich text --- */

.prose-tg {
  font-size: 1rem;
  line-height: 1.75;
  color: var(--tg-body);
}

.prose-tg h2,
.prose-tg h3,
.prose-tg h4 {
  margin-top: 2rem;
  margin-bottom: 0.75rem;
  color: var(--tg-ink);
  font-weight: 700;
}

.prose-tg h2 { font-size: 1.5rem; }
.prose-tg h3 { font-size: 1.25rem; }
.prose-tg h4 { font-size: 1.05rem; }

.prose-tg p { margin-bottom: 1rem; }

.prose-tg ul,
.prose-tg ol {
  margin-bottom: 1rem;
  padding-inline-start: 1.4rem;
}

.prose-tg ul { list-style: disc; }
.prose-tg ol { list-style: decimal; }
.prose-tg li { margin-bottom: 0.4rem; }

/* Everything else the admin editor can produce: nested lists, a rule,
   underline and strikethrough. Paragraph alignment arrives as an inline
   text-align that the sanitiser has already validated. */
.prose-tg ul ul { list-style: circle; }
.prose-tg ol ol { list-style: lower-alpha; }
.prose-tg li > ul,
.prose-tg li > ol { margin: 0.4rem 0 0; }

.prose-tg hr {
  margin: 1.75rem 0;
  border: 0;
  border-top: 1px solid var(--tg-border);
}

.prose-tg u { text-decoration: underline; text-underline-offset: 2px; }
.prose-tg s { text-decoration: line-through; }

.prose-tg a {
  color: var(--tg-primary-dark);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.prose-tg blockquote {
  margin: 1.25rem 0;
  padding-inline-start: 1rem;
  border-inline-start: 3px solid var(--tg-accent);
  color: var(--tg-ink);
  font-style: italic;
}

.prose-tg img {
  max-width: 100%;
  height: auto;
  border-radius: 0.75rem;
}

.prose-tg table {
  width: 100%;
  margin-bottom: 1rem;
  border-collapse: collapse;
}

.prose-tg th,
.prose-tg td {
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--tg-border);
  text-align: start;
}

.prose-tg th {
  background: var(--tg-surface-alt);
  font-weight: 600;
  color: var(--tg-ink);
}

/* ------------------------------------------------------------- process --- */

.step-number {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 999px;
  background: var(--tg-primary);
  color: var(--tg-ink);
  font-family: Montserrat, system-ui, sans-serif;
  font-size: 1.1rem;
  font-weight: 600;
}

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

.field-label {
  display: block;
  margin-bottom: 0.35rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--tg-ink);
}

.field-input,
.field-select,
.field-textarea {
  width: 100%;
  padding: 0.65rem 0.85rem;
  border: 1px solid var(--tg-border);
  border-radius: 0.5rem;
  background: #fff;
  font-size: 0.95rem;
  color: var(--tg-ink);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.field-input:focus,
.field-select:focus,
.field-textarea:focus {
  outline: none;
  border-color: var(--tg-primary-dark);
  box-shadow: 0 0 0 3px rgba(20, 102, 58, 0.15);
}

/* --------------------------------------------------------------- misc ---- */

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

/* Slate rather than the brand green. A full-width green slab competed with the
   gold that now leads the palette, and green-on-green was what made the
   headings unreadable in the first place. */
/* The footer was a dark slate band with white text. The client asked for a
   light one with black text, which inverts every colour in it - hence the
   footer-* classes in the layout: the markup carries structure, this carries
   colour, so the next re-theme is a change here alone.

   Ratios on the #F4F7F5 ground, all well past their thresholds:
     ink headings   15.1:1      body copy      7.0:1
     links          8.9:1       gold icons     4.9:1  (needs 3:1)

   Note --tg-muted (#7C8B83) is 3.4:1 here and is deliberately NOT used for
   text; the "muted" tone in this footer is --tg-body. */
.site-footer {
  background: var(--tg-surface-alt);
  color: var(--tg-body);
  border-top: 1px solid var(--tg-border);
}

.footer-link {
  color: var(--tg-secondary);
  transition: color 0.15s ease;
}

.footer-link:hover {
  color: var(--tg-primary-dark);
}

.footer-heading {
  font-family: Montserrat, system-ui, sans-serif;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--tg-ink);
}

.footer-muted {
  color: var(--tg-body);
}

.footer-muted a {
  color: var(--tg-secondary);
}

.footer-muted a:hover {
  color: var(--tg-primary-dark);
}

/* Rules and edges. On slate these were white at 15%, which is invisible on a
   pale ground. */
.footer-rule {
  border-color: var(--tg-border);
}

.footer-copy {
  color: var(--tg-body);
}

/* The licence pill: a gold wash rather than the old dark-green chip. */
/* Ink on the gold wash, not gold on gold: --tg-primary-dark over this tint
   composites to 4.27:1, just under the 4.5 small text needs. Same rule as the
   gold button - the gold is the fill, the ink is the text. */
.footer-badge {
  background: rgba(201, 162, 39, 0.16);
  border: 1px solid rgba(133, 105, 22, 0.35);
  color: var(--tg-ink);
}

.footer-icon {
  color: var(--tg-primary-dark);
}

.footer-social {
  border-color: var(--tg-border);
  color: var(--tg-secondary);
  background: #fff;
}

.footer-social:hover {
  border-color: var(--tg-primary-dark);
  background: var(--tg-primary);
  /* Ink on gold, never white on gold - white on #C9A227 is 2.4:1. */
  color: var(--tg-ink);
}

.footer-cert {
  border-color: var(--tg-border);
  background: #fff;
  color: var(--tg-secondary);
}

.whatsapp-fab {
  position: fixed;
  inset-inline-end: 1.25rem;
  bottom: 1.25rem;
  z-index: 70;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 999px;
  background: #25d366;
  color: #fff;
  box-shadow: 0 12px 28px -10px rgba(37, 211, 102, 0.7);
  transition: transform 0.15s ease;
}

.whatsapp-fab:hover {
  transform: scale(1.06);
}

:focus-visible {
  outline: 2px solid var(--tg-primary);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    transition-duration: 0.001ms !important;
  }
}

/* ---------------------------------------------------- section backgrounds ---
 * Set per section from the admin panel (Appearance > Section background).
 * tg_render_section() wraps the section in .tg-bg--<style>; clearing the inner
 * section's own background is what lets one setting restyle all section types
 * without every partial having to know about it.
 */
.tg-bg > section {
  background-color: transparent !important;
  background-image: none !important;
}

.tg-bg--white  { background-color: var(--tg-surface, #fff); }
.tg-bg--light  { background-color: var(--tg-surface-alt, #f4f7f5); }

/* Deliberately NOT --tg-primary/--tg-secondary: those are brand roles that can
   be swapped from the admin panel, and a full-width band in gold would be
   unreadable. These two are fixed deep tones. */
.tg-bg--brand  { background-color: var(--tg-band-green, #0a3720); }
.tg-bg--dark   { background-color: var(--tg-band-slate, #2c353d); }

.tg-bg--image {
  position: relative;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* The overlay sits between the photo and the content. */
.tg-bg--image::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(10, 55, 32, 0.55);
}

/* ---------------------------------------------- feathered photo edges ---- *
 * A photograph that stops dead against the next section reads as pasted on.
 * These fade the band into the page background over its top and bottom edges,
 * so the picture dissolves instead of ending on a ruled line.
 *
 * Deliberately NOT `mask-image` on the section: a mask applies to the whole
 * element including its children, so the headline and cards would fade out
 * near the edges too. Two gradient overlays affect only what is behind them.
 *
 * The fade colour is the page background, so a blended section wants light
 * neighbours - which is how the homepage is ordered.
 */
.tg-bg--blend > .tg-bg-feather-top,
.tg-bg--blend > .tg-bg-feather-bottom {
  content: "";
  position: absolute;
  inset-inline: 0;
  height: 5.5rem;
  pointer-events: none;
  z-index: 1;
}

.tg-bg--blend > .tg-bg-feather-top {
  top: 0;
  background: linear-gradient(to bottom, var(--tg-surface, #fff) 0%, rgba(255, 255, 255, 0) 100%);
}

.tg-bg--blend > .tg-bg-feather-bottom {
  bottom: 0;
  background: linear-gradient(to top, var(--tg-surface, #fff) 0%, rgba(255, 255, 255, 0) 100%);
}

/* The section's own content must sit above the feathers. */
.tg-bg--blend > section {
  position: relative;
  z-index: 2;
}


.tg-bg--overlay-none::before  { background: transparent; }
.tg-bg--overlay-light::before { background: rgba(10, 55, 32, 0.35); }
/* Same value as the default above, stated explicitly so the class is not a
   no-op that only works by relying on the base rule. */
.tg-bg--overlay-medium::before { background: rgba(10, 55, 32, 0.55); }
.tg-bg--overlay-heavy::before { background: rgba(10, 55, 32, 0.75); }

.tg-bg--image > section {
  position: relative;
  z-index: 1;
}

/* On a dark ground the section's own text colours would be unreadable, so they
 * are overridden here rather than in each of the 18 partials. */
.tg-bg--brand,
.tg-bg--dark,
.tg-bg--image {
  color: #fff;
}

/* Text colour on a dark band is handled by ordinary inheritance from the
   container above, NOT by forcing `color: inherit` onto every tag.
   That earlier approach outranked utility classes like `text-white/80` and,
   worse, whitened the labels of the enquiry form - which sits on a white card
   inside the dark band, so they became white on white.

   The model instead is: the band sets white, and any explicitly light surface
   inside it resets to ink. */
.tg-bg--brand :is(.bg-white, .bg-slateink-100, .bg-brand-50),
.tg-bg--dark :is(.bg-white, .bg-slateink-100, .bg-brand-50),
.tg-bg--image :is(.bg-white, .bg-slateink-100, .bg-brand-50) {
  color: var(--tg-ink);
}

/* Components that hardcode a dark colour still need telling. Scoped away from
   light cards for the same reason as above. */
.tg-bg--brand .card:not(.card--photo) .card-title,
.tg-bg--dark .card:not(.card--photo) .card-title,
.tg-bg--image .card:not(.card--photo) .card-title {
  color: #fff;
}

.tg-bg--brand .card:not(.card--photo) .card-text,
.tg-bg--dark .card:not(.card--photo) .card-text,
.tg-bg--image .card:not(.card--photo) .card-text {
  color: rgba(255, 255, 255, 0.82);
}

/* The blanket `color: inherit` above flattens everything on a dark band to
   white, including the details that carry the accent. These put the accent
   back, in the light gold that actually reads on a dark ground. Declared after
   the inherit rule so they win at equal specificity. */
.tg-bg--brand :is(.section-number, .section-kicker, .stat-value, .link-arrow),
.tg-bg--dark :is(.section-number, .section-kicker, .stat-value, .link-arrow),
.tg-bg--image :is(.section-number, .section-kicker, .stat-value, .link-arrow) {
  color: var(--tg-on-dark);
}

/* The short rule under a heading is a fill, not text. */
.tg-bg--brand .btn-outline,
.tg-bg--dark .btn-outline,
.tg-bg--image .btn-outline {
  border-color: rgba(255, 255, 255, 0.55);
  color: #fff;
}

.tg-bg--brand .title-rule::after,
.tg-bg--dark .title-rule::after,
.tg-bg--image .title-rule::after {
  background: var(--tg-primary);
}

/* Supporting copy on a dark band. Full-strength white is harsh over a
   paragraph, so the lead steps back a little. */
.tg-bg--brand .section-lead,
.tg-bg--dark .section-lead,
.tg-bg--image .section-lead {
  color: rgba(255, 255, 255, 0.78);
}

/* A section placed on a dark band from the admin panel does not know to pass
   the template's "light" flag, so these make the dark treatment automatic. */
.tg-bg--brand .section-title,
.tg-bg--dark .section-title,
.tg-bg--image .section-title {
  color: #fff;
}

/* Cards on a dark band. The first version used rgba(255,255,255,.08) over a
   background photo and the cards were effectively invisible - no readable edge,
   no separation from the picture behind them. Darkening instead of lightening,
   with a real border and a blur, gives them their shape back. */
.tg-bg--brand :is(.card, .stat-card, .quote-card):not(.card--photo),
.tg-bg--dark :is(.card, .stat-card, .quote-card):not(.card--photo),
.tg-bg--image :is(.card, .stat-card, .quote-card):not(.card--photo) {
  background: rgba(8, 26, 18, 0.62);
  border-color: rgba(255, 255, 255, 0.28);
  backdrop-filter: blur(3px);
  box-shadow: 0 18px 40px -24px rgba(0, 0, 0, 0.85);
}

.tg-bg--brand .card:not(.card--photo):hover,
.tg-bg--dark .card:not(.card--photo):hover,
.tg-bg--image .card:not(.card--photo):hover {
  background: rgba(8, 26, 18, 0.74);
  border-color: rgba(255, 255, 255, 0.45);
}

/* Icon chips need to lift off the darkened card, not blend into it. */
.tg-bg--brand .card-icon,
.tg-bg--dark .card-icon,
.tg-bg--image .card-icon {
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
}

/* ------------------------------------------------ cards with a photo ----- *
 * Each card carries its own background image. The scrim is what keeps the
 * text readable regardless of how busy the photograph is, and it deepens on
 * hover so the card still responds to the pointer.
 */
.card--photo {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background-color: var(--tg-band-green, #0a3720);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  border-color: rgba(255, 255, 255, 0.3);
}

.card--photo::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    180deg,
    rgba(8, 26, 18, 0.62) 0%,
    rgba(8, 26, 18, 0.82) 100%
  );
  transition: background 0.2s ease;
}

.card--photo:hover::before {
  background: linear-gradient(
    180deg,
    rgba(8, 26, 18, 0.72) 0%,
    rgba(8, 26, 18, 0.9) 100%
  );
}

.card--photo .card-title,
.card--photo .card-text,
.card--photo .link-arrow {
  color: #fff;
}

.card--photo .card-text {
  color: rgba(255, 255, 255, 0.86);
}

.card--photo .card-icon {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}



/* Hero text sits over moving footage, so individual frames can put a white
   cloud directly behind a letter. A soft shadow costs nothing and keeps the
   worst frames legible without darkening the whole picture. */
.hero h1,
.hero .section-kicker,
.hero p {
  text-shadow: 0 1px 12px rgba(4, 20, 12, 0.55);
}

/* ------------------------------------------------- content protection ---- *
 * Client request: discourage casual copying and image saving.
 *
 * This is a deterrent, not a control. Selection can be re-enabled from
 * devtools in one line, and the page source is downloaded before any of this
 * runs. See the matching note in static/js/site.js.
 *
 * The exemptions below are not optional niceties - without them the job
 * application form cannot be filled in, and an employer cannot copy the phone
 * number the page is asking them to ring.
 */

body {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  /* Stops the iOS "save image / copy" sheet on a long press. */
  -webkit-touch-callout: none;
}

/* Anything a visitor has to type into, or legitimately needs to copy. */
input,
textarea,
select,
[contenteditable="true"],
address,
a[href^="tel:"],
a[href^="mailto:"],
.allow-copy,
[data-allow-copy] {
  -webkit-user-select: text;
  -moz-user-select: text;
  -ms-user-select: text;
  user-select: text;
  -webkit-touch-callout: default;
}

/* Images: no drag-to-desktop, no long-press menu. */
img,
video {
  -webkit-user-drag: none;
  -khtml-user-drag: none;
  -moz-user-drag: none;
  -o-user-drag: none;
  user-drag: none;
  -webkit-touch-callout: none;
}

/* Selection must stay visible where it is allowed, or fields look broken. */
input::selection,
textarea::selection,
.allow-copy ::selection {
  background: rgba(201, 162, 39, 0.28);
}

/* Keyboard users still need to see where they are. Never remove this: the
   protection rules above must not cost the site its focus indicators. */
:focus-visible {
  outline: 2px solid var(--tg-primary, #c9a227);
  outline-offset: 2px;
}

/* ===================================================== homepage v3 ======= *
 * Hero message rotation, the references marquee, the reworked card icon,
 * star ratings and the right-click notice.
 * ======================================================================== */

/* ------------------------------------------------ hero message slides ---- *
 * The first slide stays in normal flow so the hero keeps the height of its
 * tallest message; the rest are absolutely positioned on top of it. Without
 * that the section would resize on every rotation and the page would jump.
 */
.hero-slides {
  position: relative;
}

.hero-slides.is-rotating > .hero-slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
  transform: translateY(14px);
  transition: opacity 0.55s ease, transform 0.55s ease, visibility 0.55s;
  pointer-events: none;
}

/* The first child also holds the block open, so it is never taken out of flow. */
.hero-slides.is-rotating > .hero-slide:first-child {
  position: relative;
}

.hero-slides.is-rotating > .hero-slide.is-active {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  pointer-events: auto;
}

/* Someone who has asked for less motion gets the text swapped, not slid. */
@media (prefers-reduced-motion: reduce) {
  .hero-slides.is-rotating > .hero-slide {
    transition: opacity 0.2s linear;
    transform: none;
  }
}

.hero-dot {
  width: 2.25rem;
  height: 0.25rem;
  border-radius: 999px;
  border: 0;
  padding: 0;
  cursor: pointer;
  background: rgba(255, 255, 255, 0.4);
  transition: background-color 0.2s ease, width 0.2s ease;
}

.hero-dot:hover {
  background: rgba(255, 255, 255, 0.7);
}

.hero-dot.is-active {
  width: 3.25rem;
  background: var(--tg-primary);
}

/* --------------------------------------------------- references marquee -- *
 * The track holds the logo row twice and slides left by exactly 50%, so the
 * duplicate lands where the original started and the loop has no seam.
 */
.logo-marquee {
  position: relative;
  overflow: hidden;
  /* Fade the ends so logos enter and leave rather than being clipped. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
}

.logo-marquee-track {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  width: max-content;
  animation: tg-marquee 46s linear infinite;
}

.logo-marquee:hover .logo-marquee-track,
.logo-marquee:focus-within .logo-marquee-track {
  animation-play-state: paused;
}

@keyframes tg-marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* A moving row is exactly what prefers-reduced-motion is for. Stop it and let
   the row scroll by hand instead of removing the logos altogether. */
@media (prefers-reduced-motion: reduce) {
  .logo-marquee-track {
    animation: none;
  }
  .logo-marquee {
    overflow-x: auto;
  }
}

/* `display: contents` on the aria-hidden wrapper so the duplicate tiles are
   laid out by the flex track exactly like the originals. */
.logo-marquee-track .contents {
  display: contents;
}

/* --------------------------------------------------------- logo tiles ---- */

.logo-tile {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  /* Enlarged at the client's request. Most tiles carry a company NAME rather
     than a mark, so the extra width is what stops the longer ones wrapping to
     three cramped lines. */
  width: 16.5rem;
  height: 9rem;
  padding: 1.25rem 1.5rem;
  border: 1px solid var(--tg-border);
  border-radius: 1rem;
  background: #fff;
  transition: border-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease;
}

.logo-tile img {
  max-height: 100%;
  max-width: 100%;
  width: auto;
  object-fit: contain;
  transition: filter 0.35s ease, opacity 0.35s ease;
}

/* Grey until hovered, which is what makes a wall of mismatched brand colours
   read as one row. The whole tile is the hover target, not just the image. */
.logo-tile--grey img {
  filter: grayscale(1);
  opacity: 0.55;
}

.logo-tile--grey:hover img,
.logo-tile--grey:focus-visible img {
  filter: grayscale(0);
  opacity: 1;
}

.logo-tile:hover,
.logo-tile:focus-visible {
  border-color: var(--tg-primary);
  box-shadow: 0 16px 34px -22px rgba(22, 33, 28, 0.5);
  transform: translateY(-2px);
}

.logo-tile-name {
  font-family: Montserrat, system-ui, sans-serif;
  font-size: 0.95rem;
  line-height: 1.35;
  font-weight: 600;
  text-align: center;
  color: var(--tg-secondary);
}

/* Flag tiles: picture above, country name below, inside the same fixed tile
   so the countries strip and the references strip move as one design. */
.logo-tile--flag {
  flex-direction: column;
  gap: 0.6rem;
}

.logo-tile--flag img {
  max-height: 4.5rem;
  border-radius: 3px;
  /* A hairline edge, because several flags are white along one side -
     Japan, South Korea, the top half of Poland - and on a white tile the
     flag simply loses its outline. */
  box-shadow: 0 0 0 1px rgba(22, 33, 28, 0.14);
}

.logo-tile-caption {
  font-family: Montserrat, system-ui, sans-serif;
  font-size: 0.85rem;
  font-weight: 600;
  line-height: 1.25;
  text-align: center;
  color: var(--tg-secondary);
}

/* In the static grid the tile should fill its cell rather than keep the fixed
   slider width. */
.grid > .logo-tile {
  width: 100%;
}

/* ------------------------------------------------------ card icon v2 ----- *
 * The old chip was a flat grey square that sat in the corner looking bolted
 * on. This is a ring instead: the icon keeps the brand gold, the ring gives it
 * a defined edge at any card background, and the fill arrives on hover so the
 * card has somewhere to go.
 */
/* Icon and heading on one row. `align-items: flex-start` rather than centre:
   a two-line heading should grow downwards from the icon, not drag the icon
   down with it and leave the row looking lopsided. */
.card-head {
  display: flex;
  align-items: flex-start;
  gap: 0.85rem;
}

/* The icon must not shrink when the heading is long, and must not stretch to
   the heading's height - both of which flex does by default. */
.card-head .card-icon {
  flex: 0 0 auto;
  margin-top: -0.15rem;
}

/* The heading no longer needs its own top margin: the row provides the spacing
   and .card-head sits directly under the card padding. */
.card-head .card-title {
  margin-top: 0;
}

.card-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 999px;
  border: 1.5px solid var(--tg-primary);
  background: transparent;
  color: var(--tg-primary-dark);
  transition: background-color 0.25s ease, color 0.25s ease,
              border-color 0.25s ease, transform 0.25s ease;
}

.card:hover .card-icon,
.card:focus-within .card-icon {
  background: var(--tg-primary);
  border-color: var(--tg-primary);
  color: var(--tg-ink);
  transform: translateY(-2px);
}

/* On a dark band or a photo card the ring and glyph go light, since the gold
   ink loses its edge against a dark ground. */
.tg-bg--brand .card-icon,
.tg-bg--dark .card-icon,
.tg-bg--image .card-icon,
.card--photo .card-icon {
  border-color: var(--tg-on-dark);
  background: rgba(255, 255, 255, 0.08);
  color: var(--tg-on-dark);
}

.tg-bg--brand .card:hover .card-icon,
.tg-bg--dark .card:hover .card-icon,
.tg-bg--image .card:hover .card-icon,
.card--photo:hover .card-icon {
  background: var(--tg-primary);
  border-color: var(--tg-primary);
  color: var(--tg-ink);
}

/* --------------------------------------------------------- star rating --- */

.star-rating {
  display: inline-flex;
  gap: 0.2rem;
}

.star {
  width: 1.15rem;
  height: 1.15rem;
  fill: rgba(255, 255, 255, 0.22);
}

.star--on {
  fill: var(--tg-primary);
}

/* ---------------------------------------------------- video testimonial -- */

.testimonial-video {
  margin-bottom: 1.35rem;
  border-radius: 0.9rem;
  overflow: hidden;
  background: rgba(8, 26, 18, 0.55);
  /* 4:3 rather than 16:9, and `contain` rather than `cover`. The client's
     reviews are a mix of phone video shot in portrait (476x848) and landscape
     interviews (1024x576); `cover` on a 16:9 box cropped the portrait ones
     down to a strip of someone's chin. Letterboxing is the lesser evil when
     the shape of the source is out of our hands. */
  aspect-ratio: 4 / 3;
}

.testimonial-video video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* ------------------------------------------------------ review slider ---- *
 * Native scroll-snap does the sliding, so touch and trackpad swipe work with
 * no JS at all; the script only drives the arrows and the dots. Deliberately
 * NOT auto-advancing - these cards hold videos, and moving the carousel out
 * from under someone who is watching one is hostile.
 */
.review-slider {
  position: relative;
}

.review-track {
  display: flex;
  gap: 1.5rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding-bottom: 0.5rem;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.review-track::-webkit-scrollbar {
  display: none;
}

.review-card {
  flex: 0 0 100%;
  scroll-snap-align: start;
}

@media (min-width: 768px) {
  .review-card { flex-basis: calc((100% - 1.5rem) / 2); }
}

@media (min-width: 1024px) {
  .review-card { flex-basis: calc((100% - 3rem) / 3); }
}

.review-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.25rem;
  margin-top: 2rem;
}

.review-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.45);
  color: #fff;
  transition: background-color 0.2s ease, border-color 0.2s ease, opacity 0.2s ease;
}

.review-arrow:hover {
  background: var(--tg-primary);
  border-color: var(--tg-primary);
  /* Ink on gold. White on #C9A227 is 2.4:1. */
  color: var(--tg-ink);
}

.review-arrow[disabled] {
  opacity: 0.35;
  cursor: default;
}

.review-arrow[disabled]:hover {
  background: none;
  border-color: rgba(255, 255, 255, 0.45);
  color: #fff;
}

.review-dots {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.review-dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.35);
  transition: background-color 0.2s ease, transform 0.2s ease;
}

.review-dot.is-active {
  background: var(--tg-on-dark);
  transform: scale(1.35);
}

@media (prefers-reduced-motion: reduce) {
  .review-track { scroll-behavior: auto; }
  .review-dot { transition: none; }
}

/* ------------------------------------------------------ right-click note - *
 * A short notice instead of nothing happening. Without it a blocked
 * right-click just feels like the page is broken.
 */
.tg-notice {
  position: fixed;
  left: 50%;
  bottom: 2rem;
  z-index: 200;
  transform: translate(-50%, 1rem);
  max-width: min(90vw, 26rem);
  padding: 0.85rem 1.25rem;
  border-radius: 0.75rem;
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: var(--tg-band-slate, #2c353d);
  color: #fff;
  font-size: 0.875rem;
  font-weight: 500;
  text-align: center;
  box-shadow: 0 20px 48px -24px rgba(0, 0, 0, 0.75);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s;
}

.tg-notice.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}

@media (prefers-reduced-motion: reduce) {
  .tg-notice {
    transition: opacity 0.15s linear;
    transform: translate(-50%, 0);
  }
}
