/*
  14 Clubs, website draft stylesheet.
  :root holds the dark tokens; html[data-theme="light"] switches to light.
  theme-init.js picks the theme (saved choice, then device setting, then light).
  Brand palette: #0D1C2B, #102437, #BFD6ED, #F2EAD9, shadow blue #426B8A.
*/

/* Font */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("../fonts/inter-latin-wght-normal.woff2") format("woff2");
}

/* Tokens */

:root {
  --font-sans: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --max-width: 1120px;
  --gutter: 16px;
  --radius-sm: 10px;
  --radius: 14px;
  --radius-lg: 18px;
  --header-height: 64px;
  --ease: cubic-bezier(.2, .7, .2, 1);

  /* Dark theme (default) */
  --bg: #0D1C2B;
  --surface: #102437;
  --surface-2: #15304A;
  --text: #F2EAD9;
  --text-muted: #A9BCCD;
  --accent: #BFD6ED;
  --accent-deep: #426B8A;
  --link: #BFD6ED;
  --link-hover: #F2EAD9;
  --border: rgba(191, 214, 237, .14);
  --border-strong: rgba(191, 214, 237, .28);
  --btn-bg: #BFD6ED;
  --btn-bg-hover: #D6E6F5;
  --btn-text: #0D1C2B;
  --tint: rgba(191, 214, 237, .07);
  --icon-bg: rgba(191, 214, 237, .09);
  --icon-fg: #BFD6ED;
  /*
    Highlight orange, added to Oliver's palette on 2026-09-21. It marks the part
    of the club a service page is about, inside his four service icons. Since
    round 20261007 it is also the hover glow in the LIGHT theme, at Oliver's
    request ("on light mode change the colour of the glow to #ff9966"); the dark
    theme keeps the brand-blue glow he called perfect.
  */
  --accent-warm: #FF9966;
  /* Hover glow. Oliver, 2026-09-21: the border around a button glows under the
     cursor. Brand blue in the dark theme; the light theme swaps in the orange.
     --glow-border is the border colour a bordered button takes on hover, so the
     border and the ring read as one glowing edge in either theme. */
  --glow: rgba(191, 214, 237, .55);
  --glow-soft: rgba(191, 214, 237, .22);
  --glow-border: var(--accent-deep);
  /* The nav links' hover colour (round 20261016). Oliver, 2026-10-16: on hover
     the words themselves take the colour of the glow, with no glow round
     them. So the colour is the glow's own hue at full strength: brand blue
     #BFD6ED here (the glow is that blue at 55%), #FF9966 in the light theme.
     Measured on the README rig over the bar's worst backdrop; the figures
     and the passing variants are in README "Round 20261016". It replaces
     round 20261011's glow-text and glow-text-shadow tokens. */
  --nav-hover: #BFD6ED;
  /*
    Glass. The bar and the phone panel are one material, so they share one tint
    and one filter; two recipes would draw a seam where the panel meets the bar.

    Round 20261007: Oliver asked for the bar to go back to a FLAT, minimal look
    (no "shaded curved" sheen, which read worst in the dark theme) and to be
    more see-through, "try 50% opacity", still frosted. So the top-edge sheen
    gradient is gone from the bar and the panel, the rim hairline is a plain
    border tone, the tint is 50% in both themes, and contrast(.28) left the
    filter: it flattened whatever was behind the bar to mid-grey, which is what
    made the old glass read as nearly opaque.

    Round 20261011: "turn the opacity down by another 15%", so the tint is
    35% in both themes, bar and panel. At a bare 35% the nav links fall under
    the 4.5:1 AA floor in both themes, so the filter now holds them up, in
    the same way in both: contrast() then brightness() leave the page's own
    tone where it is under the bar (it still reads as clear glass over the
    plain page) and pull only the far end of the range in.

    - Dark: the worst case is a white backdrop, because the loaded Google map
      is near-white and scrolls under the links: 3.49:1 at 35% with the old
      brightness(.68). contrast(.87) brightness(.59) gives 4.72:1, the margin
      .68 had at 50% (4.74:1). brightness(.55) alone would give the same over
      white, but it also darkens the bar over the navy page into a heavier band.
    - Light: the worst case is the dark hero mark, 3.26:1 at a bare 35% over
      #102437. brightness() alone would need about 2.1 and would bleach the bar
      white over everything, the sky-blue pattern included, and that is what
      Oliver wants to see through it. contrast(.65) brightness(1.16) lifts the
      darks instead and lets the sky blue through about as light as it went
      in. The mark's real pixels are #0F1F2E (mark-dark.webp, review of
      2026-10-11), a shade darker than the #102437 the round was tuned on:
      4.73:1 over it on the bar and 4.68:1 in the open phone menu.

    Both are load-bearing. Rig figures (README "The glass bar"), nav links at
    --text, worst row, desktop bar and open phone menu alike:

      round 20261007 (50% tint)   5.89 dark (mark)  4.74 dark (white)  5.05 light (#102437)
      round 20261011 (35% tint)   5.87 dark (mark)  4.72 dark (white)  4.68 light (#0F1F2E)

    Both themes keep about 0.2 over the floor. Pure black under the light bar
    reads 3.85:1, so no near-black photo area may scroll under the nav words
    in the light theme without retuning this filter.

    The catch: the AA floor, not the tint, decides how solid the glass reads.
    Measured with black and then white under the bar, the dark bar is about
    67% solid (66% before) and the light bar about 51% (50%). A bare 35% would
    read 56% and 35%, and fail AA as above. Do not thin it further without
    re-running the rig.

    The header links stay at --text; see "The glass bar" in README.md.
  */
  --glass-filter: blur(30px) saturate(180%) contrast(.87) brightness(.59);
  --glass-tint: rgba(16, 36, 55, .35);
  --glass-solid: rgba(13, 28, 43, .90);
  --glass-rim: rgba(191, 214, 237, .16);
  --glass-drop: 0 14px 34px rgba(3, 10, 18, .34);
  --menu-tint: rgba(16, 36, 55, .35);
  --menu-solid: rgba(13, 28, 43, .95);
  --edge-filter: blur(9px) saturate(160%) brightness(.78);
  --band-bg: #102437;
  --band-border: rgba(66, 107, 138, .6);
  --band-text: #F2EAD9;
  --band-muted: #A9BCCD;
  --band-eyebrow: #BFD6ED;
  --band-number: #BFD6ED;
  --band-shadow: #426B8A;
  --ph-bg: rgba(191, 214, 237, .05);
  --ph-line: rgba(191, 214, 237, .4);
  --ph-text: #BFD6ED;
  --pattern: url("../img/pattern-wide-dark.svg");
  --pattern-opacity: .9;
  --map-grid: rgba(191, 214, 237, .06);
  --focus: #BFD6ED;

  /* Form feedback. Warm red and green picked to sit beside the sand text tone. */
  --danger: #FFA8A2;
  --danger-bg: rgba(255, 168, 162, .1);
  --danger-border: rgba(255, 168, 162, .38);
  --success: #9BE0BE;
  --success-bg: rgba(155, 224, 190, .1);
  --success-border: rgba(155, 224, 190, .38);
  color-scheme: dark;
}

:root[data-theme="light"] {
  --bg: #F2EAD9;
  --surface: #FAF6EE;
  --surface-2: #EDE3D0;
  --text: #0D1C2B;
  --text-muted: #4A5B6B;
  --accent: #3A5F7C;
  --accent-soft: #BFD6ED;
  --link: #3A5F7C;
  --link-hover: #0D1C2B;
  --border: rgba(13, 28, 43, .12);
  --border-strong: rgba(13, 28, 43, .24);
  --btn-bg: #102437;
  --btn-bg-hover: #1D3C5A;
  --btn-text: #F2EAD9;
  --tint: rgba(13, 28, 43, .05);
  --icon-bg: #BFD6ED;
  --icon-fg: #102437;
  --accent-warm: #FF9966;
  /* Oliver, 2026-10-07: the light-theme glow is #ff9966. The ring is the colour
     itself, the soft spill the same #FF9966 at 45% (written out as rgba so an
     engine without color-mix() still gets a glow). */
  --glow: var(--accent-warm);
  --glow-soft: rgba(255, 153, 102, .45);
  --glow-border: var(--accent-warm);
  --nav-hover: var(--accent-warm);
  --glass-filter: blur(30px) saturate(180%) contrast(.65) brightness(1.16);
  --glass-tint: rgba(250, 246, 238, .35);
  --glass-solid: rgba(242, 234, 217, .92);
  --glass-rim: rgba(13, 28, 43, .10);
  --glass-drop: 0 14px 34px rgba(13, 28, 43, .13);
  --menu-tint: rgba(250, 246, 238, .35);
  --menu-solid: rgba(242, 234, 217, .96);
  /* brightness(1.1), not the 1.18 the brighter 20261007 bar used: the slab now
     sits at the page tone, and 1.18 drew the band as a pale line under it. */
  --edge-filter: blur(9px) saturate(160%) brightness(1.1);
  --band-bg: #BFD6ED;
  --band-border: rgba(13, 28, 43, .08);
  --band-text: #0D1C2B;
  --band-muted: #243B52;
  --band-eyebrow: #2A4A66;
  --band-number: #102437;
  --band-shadow: rgba(66, 107, 138, .5);
  --ph-bg: rgba(13, 28, 43, .035);
  --ph-line: rgba(13, 28, 43, .34);
  --ph-text: #3A5F7C;
  --pattern: url("../img/pattern-wide-light.svg");
  --pattern-opacity: 1;
  --map-grid: rgba(13, 28, 43, .05);
  --focus: #102437;

  /* Form feedback */
  --danger: #8E1B1B;
  --danger-bg: rgba(142, 27, 27, .08);
  --danger-border: rgba(142, 27, 27, .34);
  --success: #0F5D3C;
  --success-bg: rgba(15, 93, 60, .08);
  --success-border: rgba(15, 93, 60, .34);
  color-scheme: light;
}

@media (min-width: 640px) {
  :root { --gutter: 24px; }
}

@media (min-width: 1024px) {
  :root { --gutter: 32px; --header-height: 72px; }
}

/* Base */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  background-color: var(--bg);
  scroll-padding-top: calc(var(--header-height) + 16px);
}

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background-color: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: 1rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-wrap: break-word;
  transition: background-color .25s ease, color .25s ease;
}

main {
  flex: 1 0 auto;
}

/*
  Round 20261016: the pattern at the bottom of every page, the gradient the
  other way round (Oliver, 2026-10-16: "the sky blue pattern starts at the
  bottom and fades into the cream colour in the middle part of the page").
  One image per theme, the same file as at the top, painted once and never
  tiled or stitched: anchored to the bottom edge of the page, sized exactly
  like the top one, at .95 under the footer and fading out upwards.

  The box is half the page tall, so on a short page the fade ends near the
  middle, as he described. It never grows taller than the image itself (a
  square, max(100vw, 1280px) on a side), so on a long page the pattern covers
  the lower part of the page rather than the whole lower half; a taller fade
  would need a second copy of the image, which is the stitching he ruled out.

  body becomes the containing block and a stacking context (isolation), so the
  pseudo-element at z-index -1 paints above the body's own background and
  under everything else. isolation is not a backdrop root, so the glass bar
  still sees the page. Nothing in the footer has a background of its own; the
  footer text over the light pattern is measured in README "Round 20261016".
*/
body {
  position: relative;
  isolation: isolate;
}

body::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: -1;
  height: min(50%, max(100vw, 1280px));
  background-image: var(--pattern);
  background-position: center bottom;
  background-repeat: no-repeat;
  background-size: max(100%, 1280px) auto;
  opacity: var(--pattern-opacity);
  pointer-events: none;
  -webkit-mask-image: linear-gradient(0deg, rgba(0, 0, 0, .95) 0%, rgba(0, 0, 0, .35) 55%, transparent 100%);
  mask-image: linear-gradient(0deg, rgba(0, 0, 0, .95) 0%, rgba(0, 0, 0, .35) 55%, transparent 100%);
}

main:focus {
  outline: none;
}

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

svg {
  flex: none;
}

/*
  The form messages are shown and hidden by adding and removing the hidden
  attribute, so the attribute has to beat any layout display value a component
  sets. Nothing below styles a display on those elements either.
*/

[hidden] {
  display: none !important;
}

h1,
h2,
h3 {
  margin: 0;
  font-weight: 650;
  line-height: 1.15;
  letter-spacing: -.02em;
  text-wrap: balance;
}

p {
  margin: 0 0 1em;
}

p:last-child {
  margin-bottom: 0;
}

a {
  color: var(--link);
  text-decoration-thickness: 1px;
  text-underline-offset: .22em;
  transition: color .15s ease, background-color .15s ease, border-color .15s ease;
}

a:hover {
  color: var(--link-hover);
}

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

address {
  font-style: normal;
}

table {
  border-collapse: collapse;
}

/* Keeps a short phrase such as "100-day" on one line without the
   non-breaking hyphen character, which find-in-page and search do not
   match against an ordinary hyphen. */
.nowrap {
  white-space: nowrap;
}

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

.container {
  width: 100%;
  max-width: calc(var(--max-width) + 2 * var(--gutter));
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.icon {
  display: block;
  width: 24px;
  height: 24px;
}

.icon-sm {
  width: 20px;
  height: 20px;
}

/* Skip link */

.skip-link {
  position: absolute;
  top: -100px;
  left: 16px;
  z-index: 100;
  padding: 10px 16px;
  border-radius: var(--radius);
  background: var(--btn-bg);
  color: var(--btn-text);
  font-weight: 600;
  text-decoration: none;
}

.skip-link:focus {
  top: 12px;
  color: var(--btn-text);
}

/* Header */

/*
  The bar carries NO backdrop-filter of its own, and that is deliberate.
  An element with backdrop-filter becomes a backdrop root for its descendants,
  so a pseudo-element inside it filters the bar's own output rather than the
  page, and renders nothing at all. Verified in Chromium 153: the glass slab and
  the refraction band below the bar both go invisible when the parent filters.
  Moving the filter onto two sibling pseudo-elements of an unfiltered parent is
  what makes the band under the bar possible. Do not "tidy" the filter back onto
  .site-header.

  Geometry is unchanged from the flat bar: min-height, padding, gutters and the
  1px bottom border are all exactly as before. Only colour, filter and shadow
  differ, and a shadow does not participate in layout.
*/

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  border-bottom: 1px solid var(--glass-rim);
  background-color: var(--glass-solid);
  box-shadow: var(--glass-drop);
}

/* The bar goes edge to edge with the panel when the phone menu is open, so the
   hairline between them would read as a seam. Scoped to the breakpoint that has
   a panel at all: above 760px the toggle is display:none and there is nothing
   for the bar to join, so an aria-expanded left over from a resize must not
   strip the hairline off a desktop bar. */
@media (max-width: 759.98px) {
  .site-header:has(.nav-toggle[aria-expanded="true"]) {
    border-bottom-color: transparent;
  }
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  /* The tint moves onto ::before, so the bar must stop painting its own opaque
     fallback or the two would stack and the glass would go solid. */
  .site-header {
    background-color: transparent;
  }

  /* The glass slab: one flat tint over a frosted backdrop. Round 20261007 took
     out the top-edge sheen gradient at Oliver's request; it was what made the
     bar read as shaded and curved rather than flat. */
  .site-header::before {
    content: "";
    position: absolute;
    inset: 0;
    background-color: var(--glass-tint);
    -webkit-backdrop-filter: var(--glass-filter);
    backdrop-filter: var(--glass-filter);
  }

  /*
    Refraction. Real glass keeps bending light just past its edge, so content
    stays smeared for a few pixels below the bar and then resolves. This is the
    cue that separates glass from a plain blurred rectangle. It must not eat
    clicks on the content it sits over.
  */
  .site-header::after {
    content: "";
    position: absolute;
    top: 100%;
    right: 0;
    left: 0;
    height: 14px;
    pointer-events: none;
    -webkit-backdrop-filter: var(--edge-filter);
    backdrop-filter: var(--edge-filter);
    -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, .9), rgba(0, 0, 0, 0));
    mask-image: linear-gradient(to bottom, rgba(0, 0, 0, .9), rgba(0, 0, 0, 0));
    transition: opacity .2s ease;
  }

  /* With the panel open the band would fall inside it, so it fades out and the
     panel's own shadow takes over as the bottom edge. Same breakpoint scoping
     as the hairline rule above, and for the same reason. */
  @media (max-width: 759.98px) {
    .site-header:has(.nav-toggle[aria-expanded="true"])::after {
      opacity: 0;
    }
  }
}

/*
  Some people ask their operating system for less transparency, and a glass bar
  is exactly what that setting is about. Honour it: keep a trace of the blur so
  the bar still reads as a material, but put the tint back up to the near-opaque
  fallback so nothing shows through. This is a separate preference from reduced
  motion and is not covered by the block at the end of this file.
*/
@media (prefers-reduced-transparency: reduce) {
  /* Both selectors, because the light theme sets these tokens through
     :root[data-theme="light"], which outranks a bare :root however late it
     comes in the file. */
  :root,
  :root[data-theme="light"] {
    --glass-tint: var(--glass-solid);
    --menu-tint: var(--menu-solid);
    --glass-filter: saturate(140%);
    --edge-filter: saturate(140%);
  }
}

.header-inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  min-height: var(--header-height);
}

.brand {
  display: grid;
  margin-right: auto;
  border-radius: 6px;
}

.logo-swap {
  display: grid;
}

.logo-swap > img {
  grid-area: 1 / 1;
  transition: opacity .25s ease;
}

.theme-img-light {
  opacity: 0;
}

:root[data-theme="light"] .theme-img-dark {
  opacity: 0;
}

:root[data-theme="light"] .theme-img-light {
  opacity: 1;
}

.brand img {
  width: auto;
  height: 44px;
}

@media (min-width: 1024px) {
  .brand img { height: 52px; }
}

@media (max-width: 359.98px) {
  .brand img { height: 36px; }
}

/*
  Navigation. The no-JS baseline is a visible row that wraps onto its own line
  below 760px, so a phone without JavaScript is never left without a menu.
  With JavaScript the same list becomes the dropdown panel further down.
*/

.site-nav {
  order: 1;
  flex-basis: 100%;
  padding-bottom: 10px;
}

.site-nav ul {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/*
  Full --text, not --text-muted. A muted link was fine on the solid bar it was
  designed for; on glass thin enough to show the hero mark it fell to 4.05:1,
  under the 4.5:1 AA floor, and the thinner glass of round 20260922 would have
  taken it lower still. At full strength the same worst case measured 6.3:1
  dark and 9.7:1 light then, 5.23:1 dark and 5.11:1 light on the flat 50%
  glass of round 20261007, and 4.72:1 dark (over a white map) and 4.68:1
  light (over the hero mark, #0F1F2E) on the 35% glass of round 20261011. Do
  not put this back to --text-muted without re-running the rig described in
  README.md.

  Round 20261016: back to the type of round 20261007 at Oliver's request
  ("change the font of the words on the bar back to the same font and size as
  they were in the previous version"): Inter 500 at 15px, no tracking, the
  words as written, 8px 12px padding; 16px in the phone panel. Round 20261011
  had given them the hero eyebrow's typography (600, tracked, capitals).
*/
.site-nav a {
  display: block;
  padding: 8px 12px;
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: .9375rem;
  font-weight: 500;
  text-decoration: none;
  transition: color .15s ease;
}

/*
  Hover: the words turn the glow colour (Oliver, 2026-10-16: "fully change the
  letters/words to the same colour as the glow"), with no glow round them and
  no background box (Oliver, 2026-10-10). Hover only, like every other glow;
  keyboard focus keeps the :focus-visible outline at the top of this file.
*/
.site-nav a:hover {
  color: var(--nav-hover);
}

/* A forced palette drops the colour change, so hover would show nothing. */
@media (forced-colors: active) {
  .site-nav a:hover {
    text-decoration: underline;
  }
}

.header-tools {
  display: flex;
  align-items: center;
  gap: 4px;
}

@media (min-width: 760px) {
  .site-nav {
    order: 0;
    flex-basis: auto;
    padding-bottom: 0;
  }

  .site-nav + .header-tools {
    margin-left: 8px;
    padding-left: 12px;
    border-left: 1px solid var(--border);
  }
}

/*
  Mobile dropdown. Only when JavaScript is running, because only then can the
  toggle change aria-expanded. The button's ARIA state is the single source of
  truth for open and closed; there is no separate data attribute to keep in sync.
*/

@media (max-width: 759.98px) {
  .js .nav-toggle {
    display: inline-flex;
  }

  /*
    The panel is the same material as the bar, continued downwards, so it takes
    the same tint and the same filter and drops the hairline along its top.

    It opens like a roller blind: clip-path reveals it from the top edge down,
    so the links sit still and the bottom edge travels, which is what a blind
    does. A transform slide would drag the text down with it and a height
    animation would trigger layout on every frame. The open state clips 34px
    PAST the bottom edge because clip-path clips the box shadow too, and without
    the overshoot the panel would land with no shadow under it.

    No opacity fade: a blind does not fade, and fading while clipping reads as
    neither one thing nor the other.
  */
  .js .site-nav {
    position: absolute;
    top: 100%;
    right: 0;
    left: 0;
    z-index: 10;
    visibility: hidden;
    max-height: calc(100vh - var(--header-height));
    padding: 10px var(--gutter) 16px;
    overflow-y: auto;
    overscroll-behavior: contain;
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
    background-color: var(--menu-solid);
    box-shadow: var(--glass-drop);
    clip-path: inset(0 0 100% 0 round 0 0 var(--radius-lg) var(--radius-lg));
    transition: clip-path .34s var(--ease), visibility .34s;
  }

  @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .js .site-nav {
      background-color: var(--menu-tint);
      -webkit-backdrop-filter: var(--glass-filter);
      backdrop-filter: var(--glass-filter);
    }
  }

  .js .header-inner:has(.nav-toggle[aria-expanded="true"]) .site-nav {
    visibility: visible;
    clip-path: inset(0 0 -34px 0 round 0 0 var(--radius-lg) var(--radius-lg));
  }

  .js .site-nav ul {
    flex-direction: column;
    justify-content: flex-start;
    gap: 2px;
  }

  .js .site-nav a {
    display: flex;
    align-items: center;
    min-height: 44px;
    padding: 10px 14px;
    font-size: 1rem;
  }
}

.lang-switch {
  display: flex;
  align-items: center;
  gap: 2px;
}

.lang-switch a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: var(--radius-sm);
}

.lang-switch a:hover {
  background-color: var(--tint);
}

.lang-switch img {
  display: block;
  width: 22px;
  height: 16px;
  border-radius: 2px;
  opacity: .85;
  transition: opacity .15s ease;
}

.lang-switch a:hover img {
  opacity: 1;
}

.lang-switch a[aria-current="page"] img {
  opacity: 1;
  box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
}

.theme-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin-left: 4px;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  background: transparent;
  color: var(--text);
  cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease;
}

.js .theme-toggle {
  display: inline-flex;
}

.theme-toggle:hover {
  border-color: var(--glow-border);
  background-color: var(--tint);
}

.theme-toggle .icon {
  width: 20px;
  height: 20px;
}

/*
  Phone: the toggle leaves the bar and floats just under it on the right, as a
  bare sun or moon with no button around it (Oliver, 2026-09-21).

  position: fixed works here only because .site-header deliberately carries no
  filter of its own -- a transform, filter or backdrop-filter on an ancestor
  would make it a containing block and the toggle would scroll away with the
  bar. The filter lives on .site-header::before, a sibling pseudo-element, for
  the separate reason written up at the top of the Header section. If that ever
  moves back onto .site-header, this rule breaks too.

  The bar owns z-index 50, so a fixed child of it paints above page content
  without needing a z-index of its own; 1 keeps it above the blind panel's 10
  in the same stacking context. The drop shadow is what makes a naked icon
  legible when it happens to sit over the hero mark or a pale band.
*/
@media (max-width: 759.98px) {
  .js .theme-toggle {
    position: fixed;
    top: calc(var(--header-height) + 10px);
    right: var(--gutter);
    z-index: 1;
    width: 34px;
    height: 34px;
    margin-left: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .45));
  }

  /* .js and data-theme sit on the same element, <html>, so this is one
     compound selector and not a descendant one. */
  :root.js[data-theme="light"] .theme-toggle {
    filter: drop-shadow(0 1px 3px rgba(13, 28, 43, .35));
  }

  /* No glow ring here: there is no border to glow, and a ring around a bare
     icon reads as a stray circle. The icon brightens instead. */
  .js .theme-toggle:hover {
    border-color: transparent;
    background-color: transparent;
    box-shadow: none;
    color: var(--accent);
  }

  .js .theme-toggle:hover .icon {
    transform: scale(1.08);
  }

  .theme-toggle .icon {
    width: 26px;
    height: 26px;
    transition: transform .15s var(--ease);
  }
}

.theme-toggle .icon-moon,
:root[data-theme="light"] .theme-toggle .icon-sun {
  display: none;
}

:root[data-theme="light"] .theme-toggle .icon-moon {
  display: block;
}

/* Menu button (shown only below 760px, and only with JavaScript) */

.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  margin-left: 4px;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  background: transparent;
  color: var(--text);
  cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease;
}

.nav-toggle:hover {
  border-color: var(--glow-border);
  background-color: var(--tint);
}

.nav-toggle .icon {
  width: 20px;
  height: 20px;
}

.nav-toggle .icon-close,
.nav-toggle[aria-expanded="true"] .icon-menu {
  display: none;
}

.nav-toggle[aria-expanded="true"] .icon-close {
  display: block;
}

/* Type helpers */

.eyebrow {
  margin: 0 0 16px;
  color: var(--accent);
  font-size: .75rem;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: .18em;
  text-transform: uppercase;
}

.lead {
  max-width: 36em;
  margin-top: 20px;
  color: var(--text-muted);
  font-size: clamp(1.0625rem, 1rem + .35vw, 1.25rem);
  line-height: 1.55;
}

.section-title {
  font-size: clamp(1.75rem, 1.3rem + 1.8vw, 2.5rem);
}

.section-intro {
  margin-top: 14px;
  color: var(--text-muted);
  font-size: 1.0625rem;
}

.muted {
  color: var(--text-muted);
}

/* Buttons */

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

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 48px;
  padding: 0 22px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font: inherit;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}

.btn .icon {
  width: 18px;
  height: 18px;
  transition: transform .2s var(--ease);
}

.btn:hover .icon {
  transform: translateX(3px);
}

.btn-primary {
  background-color: var(--btn-bg);
  color: var(--btn-text);
}

.btn-primary:hover {
  background-color: var(--btn-bg-hover);
  color: var(--btn-text);
}

.btn-secondary {
  border-color: var(--border-strong);
  background-color: transparent;
  color: var(--text);
}

.btn-secondary:hover {
  border-color: var(--glow-border);
  background-color: var(--tint);
  color: var(--text);
}

/*
  Hover glow. Oliver, 2026-09-21: "when the cursor hovers above a clickable
  button make the border around the button that the cursor is on glow."

  Two shadows, not one: the inset-looking 0-blur ring redraws the border in the
  glow colour so a transparent-bordered button gets an edge at all, and the
  blurred one spills it outwards. box-shadow is used rather than a border-colour
  change so the button's geometry never moves on hover.

  Scoped to :hover only. The keyboard equivalent is the :focus-visible outline
  at the top of this file, which is a stronger and more accessible signal than
  a glow, and stacking both would double-draw the edge.
*/
.btn:hover,
.pill:hover,
.card-link:hover,
.map-load:hover,
.band-link:hover,
.guarantee-note:hover,
.social-link:hover {
  box-shadow: 0 0 0 1px var(--glow), 0 0 18px 2px var(--glow-soft);
}

.theme-toggle:hover,
.nav-toggle:hover {
  box-shadow: 0 0 0 1px var(--glow), 0 0 14px 1px var(--glow-soft);
}

.btn,
.pill,
.map-load,
.band-link,
.guarantee-note,
.social-link,
.theme-toggle,
.nav-toggle {
  transition: background-color .15s ease, border-color .15s ease, color .15s ease, box-shadow .2s ease;
}

/* Placeholders */

.ph {
  display: inline-block;
  max-width: 100%;
  padding: .05em .4em;
  border: 1px dashed var(--ph-line);
  border-radius: 6px;
  background-color: var(--ph-bg);
  color: var(--ph-text);
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

.ph-box {
  display: grid;
  place-items: center;
  min-height: 160px;
  padding: 24px;
  border: 2px dashed var(--ph-line);
  border-radius: var(--radius-lg);
  background-color: var(--ph-bg);
  color: var(--ph-text);
  font-size: .9375rem;
  font-weight: 500;
  line-height: 1.5;
  text-align: center;
}

.ph-photo {
  width: 100%;
  aspect-ratio: 1 / 1;
}

/* Sections */

.section {
  padding-block: 64px;
}

.section-tight {
  padding-block: 28px;
}

@media (min-width: 900px) {
  .section { padding-block: 96px; }
  .section-tight { padding-block: 40px; }
}

.section-head {
  max-width: 40rem;
  margin-bottom: 32px;
}

@media (min-width: 900px) {
  .section-head { margin-bottom: 48px; }
}

.split {
  display: grid;
  gap: 32px;
  align-items: center;
}

.split-top {
  align-items: start;
}

@media (min-width: 860px) {
  .split {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 64px;
  }
}

/* Hero */

/*
  Round 20261011: the pattern runs all the way up under the glass bar. Oliver,
  2026-10-10: at the top of the page the light bar read cream (#F9F0DC), because
  the pattern stopped where the hero starts, below the bar, so the bar frosted
  the plain page colour. Now .hero::before reaches --hero-reach above the hero,
  under the bar, and the hero itself does not move: its box, its padding and
  everything after it keep their place, so the anchors land where they did.

  overflow: clip visible lets the reach out at the top while still clipping
  sideways; hidden on both axes would cut it off at the hero's edge. An engine
  without overflow: clip keeps the hidden line above it and simply shows the
  page colour under the bar, as before. The reach is generous rather than the
  exact bar height on purpose: the bar is taller without JavaScript on a phone
  (the links wrap onto their own rows) and under large text, and whatever pokes
  out above the page is never seen. This works only because the hero is the
  first thing in main on both home pages; do not reuse .hero lower down a page,
  or its pattern would cover the section above it.
*/
.hero {
  --hero-reach: 12rem;
  position: relative;
  isolation: isolate;
  overflow: hidden;
  overflow: clip visible;
  padding-block: 48px 56px;
}

/*
  Round 20261007: ONE image, never tiled. Oliver's new artwork is a square
  (5000x5000 in his export, a pure vector SVG here: one rect and one path, 8.5 KB
  gzipped) made wide enough that a single copy covers the whole width of a
  desktop screen, and he asked for exactly that: "Only use one image for the
  background, do not stitch multiple images together." So no-repeat, and the
  image is sized to the hero's width, but never below 1280px. The floor keeps
  the motif at the scale it has always had on phones and tablets (the old
  2000px tile painted at 640px gave the same 128px repeat), and because the
  image is square, 1280px is also taller than any hero, so the height is always
  covered too. Above 1280px the one image simply grows with the screen.

  Since round 20261011 the box starts --hero-reach above the hero (see .hero),
  so the image starts there too and the motif sits that much higher; it is a
  repeating motif, so nothing lines up with it. The fade is measured from the
  hero's own top edge, exactly as before: .95 under the bar and down to the
  hero's top, then .35 at 55% of the hero and gone at its bottom. The
  image is still taller than the box (at most 671px of hero plus the reach).
*/

.hero::before {
  content: "";
  position: absolute;
  inset: calc(-1 * var(--hero-reach)) 0 0;
  z-index: -1;
  background-image: var(--pattern);
  background-position: center top;
  background-repeat: no-repeat;
  background-size: max(100%, 1280px) auto;
  opacity: var(--pattern-opacity);
  -webkit-mask-image: linear-gradient(180deg, rgba(0, 0, 0, .95) var(--hero-reach), rgba(0, 0, 0, .35) calc(var(--hero-reach) + (100% - var(--hero-reach)) * .55), transparent 100%);
  mask-image: linear-gradient(180deg, rgba(0, 0, 0, .95) var(--hero-reach), rgba(0, 0, 0, .35) calc(var(--hero-reach) + (100% - var(--hero-reach)) * .55), transparent 100%);
}

@media (min-width: 900px) {
  .hero { padding-block: 88px 72px; }
}

/*
  Round 20261016: the pattern at the top of every other page too (Oliver,
  2026-10-16: "Yes I want the pattern on the other pages too"), done exactly
  as on the home pages. The first section in main on those pages is
  .page-hero (the service and Info pages) or .plain-page (the privacy pages
  and 404), and it gets the same pseudo-element: the same ONE image per theme,
  the same reach up under the glass bar, the same fade (.95 to the section's
  top, .35 at 55% of the way down, gone at the end). Both sections stay the
  first thing in main on every page; see the warning on .hero above.

  .page-hero fades over its own height, as the home hero does (about 330 to
  700px tall). .plain-page holds the whole privacy notice, thousands of pixels
  long, so its fade has a fixed depth instead, the top-fade token below the section's
  top, about the depth of a home hero. No overflow rule is needed here: the
  pseudo-element is never wider than the section, the background image is
  clipped to it, and sticky elements (the planners' summary) live further
  down, outside these sections, where an overflow rule could not break them.
*/
.page-hero,
.plain-page {
  --hero-reach: 12rem;
  --top-fade: 28rem;
  position: relative;
  isolation: isolate;
}

.page-hero::before,
.plain-page::before {
  content: "";
  position: absolute;
  inset: calc(-1 * var(--hero-reach)) 0 0;
  z-index: -1;
  background-image: var(--pattern);
  background-position: center top;
  background-repeat: no-repeat;
  background-size: max(100%, 1280px) auto;
  opacity: var(--pattern-opacity);
  pointer-events: none;
  -webkit-mask-image: linear-gradient(180deg, rgba(0, 0, 0, .95) var(--hero-reach), rgba(0, 0, 0, .35) calc(var(--hero-reach) + (100% - var(--hero-reach)) * .55), transparent 100%);
  mask-image: linear-gradient(180deg, rgba(0, 0, 0, .95) var(--hero-reach), rgba(0, 0, 0, .35) calc(var(--hero-reach) + (100% - var(--hero-reach)) * .55), transparent 100%);
}

.plain-page::before {
  bottom: auto;
  height: calc(var(--hero-reach) + var(--top-fade));
}

.hero + .section {
  padding-top: 40px;
}

@media (min-width: 900px) {
  .hero + .section { padding-top: 56px; }
}

.hero-grid {
  display: grid;
  gap: 48px;
  align-items: center;
}

@media (min-width: 900px) {
  .hero-grid {
    grid-template-columns: minmax(0, 1.35fr) minmax(0, .65fr);
    gap: 64px;
  }
}

.hero-title {
  font-size: clamp(2.25rem, 1.3rem + 4vw, 4rem);
  font-weight: 700;
  line-height: 1.04;
  letter-spacing: -.03em;
}

/*
  Hero mark. No box in either theme: the mark floats on the hero pattern, and
  .logo-swap cross-fades the light and dark versions of the 684x1024 artwork.
*/

.hero-art {
  display: none;
}

@media (min-width: 640px) {
  .hero-art {
    display: grid;
    place-items: center;
  }

  .hero-art .logo-swap {
    width: 100%;
    max-width: 176px;
  }

  .hero-art img {
    width: 100%;
    height: auto;
  }
}

@media (min-width: 900px) {
  .hero-art .logo-swap { max-width: 260px; }
}

.facts {
  display: grid;
  gap: 12px;
  margin: 48px 0 0;
  padding: 24px 0 0;
  border-top: 1px solid var(--border);
  list-style: none;
}

@media (min-width: 640px) {
  .facts {
    display: flex;
    flex-wrap: wrap;
    gap: 14px 32px;
  }

  .facts li {
    white-space: nowrap;
  }
}

.facts li {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: .9375rem;
  line-height: 1.35;
}

.facts .icon {
  width: 20px;
  height: 20px;
  color: var(--accent);
}

/* Service cards */

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

.card-link {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: center;
  gap: 16px;
  height: 100%;
  padding: 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background-color: var(--surface);
  color: var(--text);
  text-decoration: none;
  transition: border-color .2s ease, background-color .2s ease, transform .2s var(--ease), box-shadow .2s ease;
}

.card-link:hover {
  border-color: var(--glow-border);
  color: var(--text);
}

.card-icon {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: var(--radius);
  background-color: var(--icon-bg);
  color: var(--icon-fg);
}

.card-icon .icon {
  width: 28px;
  height: 28px;
}

.card-title {
  font-size: 1.375rem;
}

.card-text {
  margin: 4px 0 0;
  color: var(--text-muted);
  font-size: .9375rem;
  line-height: 1.5;
}

.card-arrow {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  color: var(--accent);
  transition: background-color .2s ease, border-color .2s ease, color .2s ease;
}

.card-arrow .icon {
  width: 18px;
  height: 18px;
  transition: transform .2s var(--ease);
}

.card-link:hover .card-arrow {
  border-color: var(--btn-bg);
  background-color: var(--btn-bg);
  color: var(--btn-text);
}

.card-link:hover .card-arrow .icon {
  transform: translateX(2px);
}

/*
  Home page service buttons.

  Round 20260922: Oliver replaced the four line icons with his own artwork and
  asked for them "centred on each button and bigger so they fill almost the
  entire button". The artwork is two files per service, sky blue and dark blue,
  because each icon paints in two colours (the club in the theme colour, the
  part the service is about in the highlight orange) and currentColor can only
  carry one. They cross-fade through .logo-swap, the same mechanism the
  wordmark in the header uses.

  Round 20261007: Oliver asked for NO text on the buttons. The service word now
  sits just below each button, every description is gone, and the icon is
  centred both ways inside the button and sized to fill almost all of it. The
  whole tile, button and word together, is still one link, so its accessible
  name is just the service word (the icon images have empty alt).

  The button keeps the shape the desktop card had (268x320 at full width, so
  5:6) through aspect-ratio, and the icon box is inset by a fixed share of it,
  so the icon fills the same proportion of the button at every width. The
  icons are tall and narrow (127x233 for the three clubs, 106x172 for the
  question mark), so height is what they fill; the question mark gets a larger
  inset, because a stubbier shape at the same height reads as larger than it
  is. Four across from 640px: without text the tiles no longer need the width,
  and two across at tablet widths would make each button over 400px tall.
*/

/* .cards.cards-service, not .cards-service: the generic three-column rule
   below is a single class too and sits later in the file, so an equal
   specificity would lose the cascade to it and the four buttons would wrap
   3 + 1. */
.cards.cards-service {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px 12px;
}

@media (min-width: 640px) {
  .cards.cards-service {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 24px 16px;
  }
}

/* align-content: start keeps the four buttons one size when a capitalised
   slash label wraps onto two lines (about 320 to 370px and 640 to 740px):
   the spare row height goes below the label instead of stretching the
   button, which would also widen it through its aspect ratio. */
.service-tile {
  display: grid;
  align-content: start;
  justify-items: center;
  gap: 12px;
  height: 100%;
  border-radius: var(--radius-lg);
  color: var(--text);
  text-decoration: none;
  transition: transform .2s var(--ease);
}

.service-tile:hover {
  color: var(--text);
}

.service-btn {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 5 / 6;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background-color: var(--surface);
  transition: border-color .2s ease, box-shadow .2s ease;
}

/* The icon box is the button less an even inset (5% of the height top and
   bottom, 5% of the width either side), and the icon is centred in it on
   both axes: measured in Chromium, the clubs fill 90% of the button height
   and the question mark 72%, the same 0.8 ratio the two had before. */
.service-btn .service-icon {
  position: absolute;
  inset: 5%;
  display: grid;
  place-items: center;
}

.service-btn .service-icon-wide {
  inset: 14%;
}

.service-btn .logo-swap {
  height: 100%;
  place-items: center;
}

.service-btn .service-icon img {
  width: auto;
  height: 100%;
  max-width: 100%;
  object-fit: contain;
}

/* The three club drawings sit 1.32 units left of centre inside their own
   127-unit viewBox (the art spans x 7.94 to 116.42), which measured as a
   1.5px lean at full size. Nudging by 1.04% of the image's own width puts the
   art itself dead centre. The question mark is already centred in its file.
   Re-measure if Oliver's icon files change. */
.service-btn .service-icon:not(.service-icon-wide) img {
  transform: translateX(1.04%);
}

/* Back to round 20261007 (Oliver, 2026-10-16: "change it back the same as it
   was in the previous version"): the words as written, in the shared h3
   weight. Round 20261011 had set them in capitals at 550. */
.service-label {
  margin: 0;
  font-size: 1.25rem;
  text-align: center;
}

@media (min-width: 900px) {
  .service-label { font-size: 1.5rem; }
}

/* Hover glows the button, not the word under it; keyboard focus rings the
   button for the same reason. */
.service-tile:hover .service-btn {
  border-color: var(--glow-border);
  box-shadow: 0 0 0 1px var(--glow), 0 0 18px 2px var(--glow-soft);
}

.service-tile:focus-visible {
  outline: none;
}

.service-tile:focus-visible .service-btn {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
}

@media (min-width: 720px) {
  .service-tile:hover {
    transform: translateY(-3px);
  }
}

/* Related-service icons (the compact cards on the service and Info pages). */
.service-icon {
  display: grid;
  place-items: center;
}

.service-icon .logo-swap {
  place-items: center;
}

.service-icon img {
  width: auto;
  max-width: 100%;
  object-fit: contain;
}

@media (min-width: 720px) {
  .cards {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 16px;
  }

  .card-link {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto 1fr;
    align-items: start;
    gap: 0;
    min-height: 260px;
    padding: 28px;
  }

  .card-link:hover {
    transform: translateY(-3px);
  }

  .card-title {
    margin-top: 32px;
    font-size: 1.75rem;
  }

  .card-text {
    margin-top: 8px;
  }

  .card-arrow {
    align-self: end;
    margin-top: 28px;
  }
}

/* Guarantee band */

.band {
  display: grid;
  /* minmax(0, 1fr), not the implicit auto column: a long word such as
     "kvalitetsgarantien" under WCAG text spacing would otherwise widen the
     column past the screen. The body's overflow-wrap then breaks it. */
  grid-template-columns: minmax(0, 1fr);
  gap: 20px 56px;
  padding: 36px 24px;
  border: 1px solid var(--band-border);
  border-radius: var(--radius-lg);
  background-color: var(--band-bg);
  color: var(--band-text);
}

@media (min-width: 820px) {
  .band {
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    padding: 56px 64px;
  }
}

.band-number {
  margin: 0;
  color: var(--band-number);
  font-size: clamp(5.5rem, 3.5rem + 9vw, 10.5rem);
  font-weight: 750;
  line-height: .82;
  letter-spacing: -.035em;
  text-shadow: .045em .045em 0 var(--band-shadow);
}

.band .eyebrow {
  color: var(--band-eyebrow);
}

.band-text {
  max-width: 38em;
  margin-top: 16px;
  color: var(--band-text);
  font-size: 1.0625rem;
}

.band-point {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 20px;
  color: var(--band-muted);
  font-size: .9375rem;
}

.band-point .icon {
  width: 20px;
  height: 20px;
  color: var(--band-eyebrow);
}

/*
  Clickable guarantee boxes, round 20261007. Oliver: "Make the box where it
  says 100-day quality guarantee a clickable button" that leads to the
  guarantee section on the Info page. The home band and the smaller guarantee
  note on the service pages both work the same way: the box holds one ordinary
  link ("More about the guarantee"), and that link's ::after is stretched over
  the whole box, so a click anywhere on it follows the link while the heading
  stays a heading and the link keeps a short, honest accessible name. Hover
  glows the box like every other button; keyboard focus rings the box, drawn
  on the stretched ::after so it needs no :has().
*/
.band-link,
.guarantee-note {
  position: relative;
}

.band-link:hover,
.guarantee-note:hover {
  border-color: var(--glow-border);
}

.band-more {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 22px;
  color: var(--band-text);
  font-weight: 600;
}

.band-more:hover {
  color: var(--band-text);
}

.band-more .icon {
  width: 18px;
  height: 18px;
  transition: transform .2s var(--ease);
}

.band-link:hover .band-more .icon,
.guarantee-note:hover .guarantee-note-link .icon {
  transform: translateX(3px);
}

.band-more::after,
.guarantee-note-link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius-lg);
}

.band-more:focus-visible,
.guarantee-note-link:focus-visible {
  outline: none;
}

.band-more:focus-visible::after,
.guarantee-note-link:focus-visible::after {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
}

/*
  The guarantee section on the Info page, the one those boxes lead to. Same
  band look as the home page box, so the visitor sees they have arrived at
  the thing they clicked; it is not itself a link.
*/
.guarantee-points {
  display: grid;
  gap: 12px;
  margin: 22px 0 0;
  padding: 0;
  list-style: none;
}

.guarantee-points li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  color: var(--band-text);
  line-height: 1.5;
}

.guarantee-points .icon {
  width: 20px;
  height: 20px;
  margin-top: 2px;
  color: var(--band-eyebrow);
}

.guarantee-points strong {
  font-weight: 650;
}

.band .ph {
  color: var(--band-text);
}

.band-fineprint {
  margin-top: 20px;
  color: var(--band-muted);
  font-size: .9375rem;
}

/* Oliver's three exceptions to the guarantee (10 October 2026), set as fine
   print under the points: present and plain, but not the headline. */
.band-fineprint > p {
  margin: 0;
}

.guarantee-exceptions {
  display: grid;
  gap: 4px;
  margin: 6px 0 0;
  padding-left: 1.25em;
  line-height: 1.5;
}

/* Steps */

.steps {
  display: grid;
  gap: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: step;
}

@media (min-width: 640px) {
  .steps {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 16px;
  }
}

@media (min-width: 1000px) {
  .steps { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.step {
  padding: 24px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  counter-increment: step;
}

.step::before {
  content: counter(step, decimal-leading-zero);
  display: block;
  margin-bottom: 24px;
  color: var(--accent);
  font-size: .8125rem;
  font-weight: 600;
  letter-spacing: .18em;
}

.step-title {
  margin-bottom: 8px;
  font-size: 1.125rem;
}

.step p {
  color: var(--text-muted);
  font-size: .9375rem;
}

/* About */

.about-photo {
  max-width: 460px;
}

@media (min-width: 860px) {
  .about-photo { justify-self: end; }
}

/*
  "Coming soon" stand-in for the workshop photo, round 20261007. Oliver asked
  for the two placeholders in "The workshop" to read "coming soon", so the
  dashed draft box became a finished-looking card in the same square footprint:
  the flat mark, swapped per theme like the header logo, over a small label.
  It is no longer a placeholder (no .ph-box), because the words are now his.
*/
.soon-box {
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 20px;
  width: 100%;
  aspect-ratio: 1 / 1;
  padding: 24px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background-color: var(--surface);
  text-align: center;
}

.soon-box .logo-swap {
  width: 84px;
}

.soon-box img {
  display: block;
  width: 100%;
  height: auto;
}

.soon-label {
  margin: 0;
  color: var(--accent);
  font-size: .8125rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
}

/* Info lists (location, contact) */

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

.section-list {
  margin-top: 32px;
}

.info-list li {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}

.info-icon {
  display: grid;
  flex: none;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 12px;
  background-color: var(--icon-bg);
  color: var(--icon-fg);
}

.info-icon .icon {
  width: 20px;
  height: 20px;
}

.info-body {
  padding-top: 7px;
  line-height: 1.5;
}

.info-body strong {
  font-weight: 600;
}

.directions {
  margin-top: 32px;
}

.directions-label {
  margin: 0 0 12px;
  color: var(--text-muted);
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
}

.pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 42px;
  padding: 0 14px;
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  color: var(--text);
  font-size: .9375rem;
  font-weight: 500;
  text-decoration: none;
}

.pill:hover {
  border-color: var(--glow-border);
  background-color: var(--tint);
  color: var(--text);
}

.pill .icon {
  width: 16px;
  height: 16px;
  color: var(--accent);
}

/* Map (click to load; Google renders its own attribution inside the frame) */

.map {
  position: relative;
  min-width: 0;
  height: 340px;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background-color: var(--surface);
}

@media (min-width: 640px) {
  .map { height: 400px; }
}

@media (min-width: 860px) {
  .map {
    height: auto;
    min-height: 460px;
    align-self: stretch;
  }
}

.map-placeholder {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 14px;
  padding: 24px;
  background-image:
    linear-gradient(var(--map-grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--map-grid) 1px, transparent 1px);
  background-position: center center;
  background-size: 28px 28px;
  text-align: center;
}

.map-pin {
  display: grid;
  place-items: center;
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background-color: var(--icon-bg);
  color: var(--icon-fg);
}

.map-pin .icon {
  width: 28px;
  height: 28px;
}

.map-address {
  margin: 0;
  font-weight: 600;
}

.map-note {
  display: none;
  max-width: 30ch;
  margin: 0;
  color: var(--text-muted);
  font-size: .8125rem;
  line-height: 1.45;
}

.map-load {
  display: none;
}

.js .map-load {
  display: inline-flex;
}

.js .map-note {
  display: block;
}

.js .map-nojs {
  display: none;
}

.map-frame {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Contact */

/*
  Social links reuse the Directions pills. A pill is 42px tall against a 40px
  icon, so the 7px lead-in that aligns plain text with the icon has to go when
  the row holds pills instead.
*/

.info-list .pills {
  min-width: 0;
}

.info-body:has(.pills) {
  padding-top: 0;
}

/*
  Contact form. Fields borrow the card surface, border, radius and font tokens
  so they read as part of the site. The max-width keeps the line length sane in
  the wide two-column layout; on a phone the form just fills its column.
*/

.contact-form {
  display: grid;
  gap: 20px;
  width: 100%;
  max-width: 34rem;
}

.form-intro {
  margin: 0;
  color: var(--text-muted);
  font-size: .9375rem;
}

.field {
  display: grid;
  gap: 6px;
  min-width: 0;
}

.field > label {
  font-size: .9375rem;
  font-weight: 500;
}

.contact-form input[type="text"],
.contact-form input[type="email"],
.contact-form textarea {
  width: 100%;
  min-width: 0;
  min-height: 44px;
  padding: 10px 14px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background-color: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 1rem;
  line-height: 1.5;
  transition: border-color .15s ease, box-shadow .15s ease;
}

.contact-form textarea {
  min-height: 150px;
  resize: vertical;
}

.contact-form input[type="text"]:hover,
.contact-form input[type="email"]:hover,
.contact-form textarea:hover {
  border-color: var(--accent-deep);
}

/* The global :focus-visible ring is the focus indicator; the border follows it. */

.contact-form input:focus-visible,
.contact-form textarea:focus-visible {
  border-color: var(--accent-deep);
}

.contact-form .is-invalid {
  border-color: var(--danger);
  box-shadow: inset 0 0 0 1px var(--danger);
}

.field-error {
  margin: 0;
  color: var(--danger);
  font-size: .8125rem;
  line-height: 1.45;
}

/* Checkbox row: box and label on one line, box level with the first line of text. */

.field-check {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  min-height: 44px;
  padding-block: 8px;
}

.field-check input[type="checkbox"] {
  flex: none;
  width: 20px;
  height: 20px;
  margin: 3px 0 0;
  accent-color: var(--accent);
  cursor: pointer;
}

.field-check > label {
  margin: 0;
  font-weight: 400;
  cursor: pointer;
}

/*
  Honeypot. Not display:none and not hidden, because a bot that reads the CSS
  skips those; this one is a real, laid-out field parked off-screen. People
  never see it, aria-hidden keeps it out of the accessibility tree and
  tabindex="-1" in the markup keeps it off the tab order.
*/

.hp {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/*
  Turnstile. The widget is about 300x65 and loads only once the visitor starts
  filling the form, so the slot reserves the height up front and the page does
  not jump. site.js adds .is-loaded once the widget has rendered.
*/

.turnstile-slot {
  display: flex;
  align-items: center;
  min-height: 70px;
  max-width: 300px;
}

.turnstile-note {
  margin: 0;
  color: var(--text-muted);
  font-size: .8125rem;
  line-height: 1.45;
}

.turnstile-slot.is-loaded .turnstile-note {
  display: none;
}

.contact-form .btn {
  justify-self: start;
}

.form-status {
  margin: 0;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background-color: var(--tint);
  font-size: .9375rem;
  line-height: 1.5;
}

.form-status.is-success {
  border-color: var(--success-border);
  background-color: var(--success-bg);
  color: var(--success);
}

.form-status.is-error {
  border-color: var(--danger-border);
  background-color: var(--danger-bg);
  color: var(--danger);
}

/* Service pages */

.page-hero {
  padding-block: 32px 48px;
}

@media (min-width: 900px) {
  .page-hero { padding-block: 48px 72px; }
}

/* Breadcrumb (service and privacy pages) */

.breadcrumb {
  margin-bottom: 28px;
}

.breadcrumb ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px 12px;
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--text-muted);
  font-size: .9375rem;
  font-weight: 500;
}

.breadcrumb li {
  display: inline-flex;
  align-items: center;
  gap: 12px;
}

.breadcrumb li + li::before {
  content: "";
  width: 6px;
  height: 6px;
  border-top: 1.5px solid currentColor;
  border-right: 1.5px solid currentColor;
  opacity: .7;
  transform: rotate(45deg);
}

.breadcrumb a {
  display: inline-block;
  padding: 6px 0;
  color: var(--text-muted);
  text-decoration: none;
}

.breadcrumb a:hover {
  color: var(--text);
  text-decoration: underline;
}

.breadcrumb [aria-current="page"] {
  color: var(--text);
}

.page-hero-grid {
  display: grid;
  /* minmax(0, 1fr) for the same reason as .band: "Køllehodebytte" as a page
     title under WCAG text spacing must not widen the column at 320px. */
  grid-template-columns: minmax(0, 1fr);
  gap: 40px;
  align-items: center;
}

@media (min-width: 860px) {
  .page-hero-grid {
    grid-template-columns: minmax(0, 1.2fr) minmax(0, .8fr);
    gap: 64px;
  }
}

.page-title {
  font-size: clamp(2.25rem, 1.4rem + 3.4vw, 3.5rem);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -.03em;
}

.confirm-note {
  margin-top: 12px;
  font-size: .875rem;
}

.task-list {
  display: grid;
  gap: 0;
  margin: 24px 0 0;
  padding: 0;
  list-style: none;
  counter-reset: task;
}

.task-list li {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding: 14px 0;
  border-top: 1px solid var(--border);
  counter-increment: task;
}

.task-list li:last-child {
  border-bottom: 1px solid var(--border);
}

.task-list li::before {
  content: counter(task);
  display: inline-grid;
  flex: none;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 1px solid var(--border-strong);
  border-radius: 9px;
  color: var(--accent);
  font-size: .8125rem;
  font-weight: 600;
  transform: translateY(-1px);
}

.price-card,
.info-card {
  padding: 28px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background-color: var(--surface);
}

@media (min-width: 900px) {
  .price-card { padding: 32px; }
}

.card-heading {
  font-size: 1.375rem;
}

.price-table {
  width: 100%;
  margin-top: 16px;
  font-size: .9375rem;
}

.price-table th,
.price-table td {
  padding: 14px 0;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
}

.price-table thead th {
  padding-top: 0;
  color: var(--text-muted);
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
}

.price-table tbody th {
  font-weight: 500;
}

.price-table td,
.price-table thead th:last-child {
  padding-left: 16px;
  text-align: right;
  white-space: nowrap;
}

.price-table tbody tr:last-child > * {
  border-bottom: 0;
  padding-bottom: 0;
}

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

@media (min-width: 760px) {
  .info-grid {
    grid-template-columns: minmax(0, 1.8fr) minmax(0, 1fr);
    align-items: stretch;
  }

  .info-grid .ph-photo {
    grid-column: 2;
    grid-row: 1 / span 2;
    align-self: center;
  }
}

.info-card .info-icon {
  margin-bottom: 20px;
}

/* Two equal cards side by side (the "How we work" section on the Info page). */
.feature-grid {
  display: grid;
  gap: 16px;
}

@media (min-width: 760px) {
  .feature-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.info-card h3 {
  margin-bottom: 8px;
  font-size: 1.125rem;
}

.info-card p {
  color: var(--text-muted);
}

.job-groups {
  display: grid;
  gap: 28px;
  margin-top: 28px;
}

@media (min-width: 560px) {
  .job-groups { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.job-groups h3 {
  margin-bottom: 12px;
  font-size: 1.125rem;
}

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

.check-list li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  line-height: 1.45;
}

.check-list .icon {
  width: 18px;
  height: 18px;
  margin-top: 2px;
  color: var(--accent);
}

.after-list {
  margin-top: 28px;
  color: var(--text-muted);
}

.guarantee-note {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 16px 20px;
  align-items: start;
  padding: 24px;
  border: 1px solid var(--band-border);
  border-radius: var(--radius-lg);
  background-color: var(--band-bg);
  color: var(--band-text);
}

@media (min-width: 760px) {
  .guarantee-note {
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    padding: 28px 32px;
  }
}

.guarantee-note .info-icon {
  background-color: transparent;
  border: 1px solid var(--band-border);
  color: var(--band-eyebrow);
}

.guarantee-note-title {
  margin: 0 0 4px;
  font-size: 1.125rem;
  font-weight: 650;
  letter-spacing: -.01em;
}

.guarantee-note p:not(.guarantee-note-title) {
  color: var(--band-muted);
}

.guarantee-note-link {
  grid-column: 2;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--band-text);
  font-weight: 600;
}

.guarantee-note-link:hover {
  color: var(--band-text);
}

.guarantee-note-link .icon {
  width: 18px;
  height: 18px;
}

/* nowrap only where the link has a column of its own. Under 760px it sits
   in the text column, and at 320px "More about the guarantee" plus the arrow
   is wider than that column, so there it may wrap (WCAG 1.4.10 Reflow). */
@media (min-width: 760px) {
  .guarantee-note-link {
    grid-column: auto;
    white-space: nowrap;
  }
}

.cta-band {
  padding: 40px 24px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background-color: var(--surface);
  text-align: center;
}

@media (min-width: 900px) {
  .cta-band { padding: 64px; }
}

.cta-band .section-intro {
  max-width: 36em;
  margin-inline: auto;
}

.cta-band .actions {
  justify-content: center;
}

/* Questions and answers (home and service pages) */

.faq-list {
  display: grid;
  column-gap: 64px;
}

@media (min-width: 860px) {
  .faq-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.faq-item {
  padding: 20px 0 22px;
  border-top: 1px solid var(--border);
}

.faq-item h3 {
  margin-bottom: 8px;
  font-size: 1.125rem;
}

.faq-item p {
  color: var(--text-muted);
}

/* Other services (compact cards on service pages) */

.related-head {
  margin-bottom: 24px;
}

/*
  Related-service cards at the foot of a service page. They carry Oliver's
  artwork too, small, so the site speaks one icon language for the four
  services rather than two. Sizing is by height for the same reason as the big
  buttons: the artwork is much taller than it is wide.
*/
.cards-compact .service-icon {
  width: auto;
  padding-bottom: 0;
}

.cards-compact .service-icon img {
  height: 54px;
}

@media (min-width: 720px) {
  /* Three cards on a service page, four on the Information page. Four stay
     two by two at every width (see the 900px block); the site already
     depends on :has() for the header, so this adds no new requirement. */
  .cards-compact {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .cards-compact .service-icon img {
    height: 66px;
  }

  .cards-compact .service-icon-wide img {
    height: 58px;
  }

  .cards-compact .card-link {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-rows: none;
    align-items: center;
    gap: 16px;
    min-height: 0;
    padding: 20px 24px;
  }

  .cards-compact .card-title {
    margin-top: 0;
    font-size: 1.25rem;
  }

  .cards-compact .card-text {
    margin-top: 4px;
  }

  .cards-compact .card-arrow {
    align-self: center;
    margin-top: 0;
  }
}

@media (min-width: 900px) {
  .cards-compact {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  /* Four across left about 110px for each title, which broke words in the
     middle ("replaceme/nt", "Køllehodeb/ytte") at every desktop width, so
     four cards stay two by two. The :has() test keeps them from going 3 + 1.
     The long title words also carry soft hyphens in the markup, for the
     three-across service pages at 900 to 1000px. */
  .cards-compact:has(> li:nth-child(4)) {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Privacy and 404 */

.plain-page {
  padding-block: 48px 96px;
}

@media (min-width: 900px) {
  .plain-page { padding-block: 72px 128px; }
}

.plain-page .ph-box {
  min-height: 280px;
  margin-top: 32px;
}

.notfound-number {
  margin: 0 0 32px;
  color: var(--accent);
  font-size: clamp(4.5rem, 3rem + 7vw, 8rem);
  font-weight: 750;
  line-height: .9;
  letter-spacing: -.05em;
  text-shadow: .045em .045em 0 var(--band-shadow);
}

.notfound-grid {
  display: grid;
  gap: 40px;
}

@media (min-width: 760px) {
  .notfound-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 64px;
  }
}

.notfound-grid h1,
.notfound-grid h2 {
  margin-bottom: 12px;
  font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2rem);
}

.notfound-grid p {
  color: var(--text-muted);
}

.notfound-grid .actions {
  margin-top: 24px;
}

/* Footer */

.site-footer {
  margin-top: 32px;
  border-top: 1px solid var(--border);
  color: var(--text-muted);
  font-size: .875rem;
}

.footer-top {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  padding-block: 40px 28px;
  text-align: center;
}

.footer-brand {
  display: flex;
  align-items: center;
  gap: 14px;
  color: var(--text-muted);
  text-decoration: none;
}

.footer-brand:hover {
  color: var(--text);
}

.footer-brand img {
  width: auto;
  height: 40px;
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px 24px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.footer-links a {
  display: inline-block;
  padding: 6px 0;
  color: var(--text);
  font-weight: 500;
  text-decoration: none;
}

.footer-links a:hover {
  color: var(--accent);
}

/*
  Social media strip, round 20260922. Oliver asked for Instagram and TikTok to
  come out of "Get in touch" and sit in their own small section at the bottom,
  with the real app logos rather than the line icons that were there before.

  Its own band between .footer-top and .footer-bottom, so it reads as a
  separate section rather than another footer link row.
*/
.footer-social {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding-block: 22px;
  border-top: 1px solid var(--border);
  text-align: center;
}

.footer-social-title {
  margin: 0;
  color: var(--text-muted);
  font-size: .8125rem;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
}

.social-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.social-link {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 42px;
  padding: 0 16px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  color: var(--text);
  font-size: .9375rem;
  font-weight: 500;
  text-decoration: none;
}

.social-link:hover {
  border-color: var(--glow-border);
  background-color: var(--tint);
  color: var(--text);
}

/* The brand glyphs are solid shapes, not the stroked set the rest of the site
   uses, so they need fill and no stroke. */
.social-link .icon-brand {
  width: 20px;
  height: 20px;
  fill: currentColor;
  stroke: none;
}

@media (min-width: 760px) {
  .footer-social {
    flex-direction: row;
    justify-content: space-between;
    gap: 16px;
    text-align: left;
  }
}

.footer-bottom {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding-block: 20px 28px;
  border-top: 1px solid var(--border);
  font-size: .8125rem;
  text-align: center;
}

.footer-bottom:first-child {
  border-top: 0;
}

.footer-bottom p {
  margin: 0;
}

.footer-legal > span {
  white-space: nowrap;
}

.footer-bottom a {
  color: var(--text-muted);
}

.footer-bottom a:hover {
  color: var(--text);
}

.footer-bottom .ph {
  color: inherit;
}

@media (min-width: 760px) {
  .footer-top {
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 24px;
    padding-block: 40px 32px;
    text-align: left;
  }

  .footer-bottom {
    flex-direction: row;
    justify-content: space-between;
    gap: 24px;
    text-align: left;
  }

  .footer-credit {
    text-align: right;
  }
}

/* Motion */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }

  .card-link:hover,
  .service-tile:hover,
  .btn:hover .icon,
  .band-link:hover .band-more .icon,
  .guarantee-note:hover .guarantee-note-link .icon,
  .card-link:hover .card-arrow .icon {
    transform: none;
  }

  /* The blind is a clip-path transition, so the blanket transition-duration
     above already reduces it to an instant reveal. It is listed here so the
     next person does not go looking for a rule that neutralises it. */
}

/* === Re-grip configurator (20261007) === */
/*
  Re-grip planner on grip.html and no/grep.html, Oliver's change list of
  2026-10-07, items 10 to 12. Behaviour and the URL contract are in site.js
  (initRegrip, initRegripPrefill). Everything here reuses the existing tokens:
  the steps sit on the price-card surface, the summary on the guarantee band,
  the chips take the button colours when chosen and the shared hover glow, so
  a token change elsewhere (the light-theme glow, say) reaches them too.

  Without JavaScript the planner cannot hand anything over, so .regrip-app and
  its intro are hidden and .regrip-nojs points at the contact form instead. Both key off the
  .js class theme-init.js sets in <head>, as the map button does, so the page
  does not jump when site.js runs.
*/

.regrip-head {
  margin-bottom: 24px;
}

.regrip-intro {
  display: none;
}

.js .regrip-intro {
  display: block;
}

.js .regrip-nojs {
  display: none;
}

/* A sentence elsewhere on the page that points at the planner (the own-grips
   FAQ answer) is shown only when the planner itself is. */
.regrip-js-only {
  display: none;
}

.js .regrip-js-only {
  display: inline;
}

.regrip-app {
  display: none;
}

.js .regrip-app {
  display: grid;
  gap: 16px;
  align-items: start;
}

@media (min-width: 900px) {
  .js .regrip-app {
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
    gap: 24px;
  }
}

.regrip-steps {
  padding: 4px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background-color: var(--surface);
}

@media (min-width: 640px) {
  .regrip-steps { padding: 4px 28px; }
}

/* A fieldset keeps a min-content width by default, which would push the
   fourteen chips past a phone screen; min-inline-size undoes that. */
.regrip-step {
  min-inline-size: 0;
  margin: 0;
  padding: 24px 0;
  border: 0;
}

.regrip-step + .regrip-step {
  border-top: 1px solid var(--border);
}

/* Floated so the legend joins the normal flow inside the fieldset instead of
   sitting on its border; the clear below puts the content back under it. */
.regrip-legend {
  float: left;
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  margin: 0 0 16px;
  padding: 0;
  font-size: 1.125rem;
  font-weight: 650;
  line-height: 1.3;
  letter-spacing: -.01em;
}

.regrip-legend + * {
  clear: left;
}

.regrip-note .regrip-legend {
  float: none;
  margin-bottom: 4px;
}

.regrip-num {
  display: inline-grid;
  flex: none;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 1px solid var(--border-strong);
  border-radius: 9px;
  color: var(--accent);
  font-size: .8125rem;
  font-weight: 600;
}

.regrip-optional {
  color: var(--text-muted);
  font-size: .9375rem;
  font-weight: 400;
}

.regrip-hint {
  margin: 8px 0 0;
  color: var(--text-muted);
  font-size: .875rem;
  line-height: 1.5;
}

/* Chips: a real radio button inside each label, visually hidden but still
   focusable, so arrow keys, Tab and screen readers get the native group. */
.regrip-choices {
  display: grid;
  gap: 8px;
}

/* Seven by two at every width: on a 390px phone that still leaves each chip
   about 40 by 44 pixels, well above the 24px minimum target size. */
.regrip-clubs {
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
}

@media (min-width: 640px) {
  .regrip-clubs { gap: 8px; }
}

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

@media (min-width: 560px) {
  .regrip-tape { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.regrip-chip {
  position: relative;
  display: block;
  min-width: 0;
  cursor: pointer;
}

.regrip-chip input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

.regrip-chip span {
  display: grid;
  place-items: center;
  align-content: center;
  min-height: 44px;
  padding: 6px 4px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  font-variant-numeric: tabular-nums;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease, box-shadow .2s ease;
}

.regrip-chip-tall span {
  min-height: 56px;
  padding-inline: 8px;
}

.regrip-tag {
  display: block;
  margin-top: 3px;
  color: var(--text-muted);
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
}

.regrip-chip:hover span {
  border-color: var(--glow-border);
  box-shadow: 0 0 0 1px var(--glow), 0 0 18px 2px var(--glow-soft);
}

.regrip-chip input:focus-visible + span {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
}

.regrip-chip input:checked + span {
  border-color: var(--btn-bg);
  background-color: var(--btn-bg);
  color: var(--btn-text);
}

.regrip-chip input:checked + span .regrip-tag {
  color: inherit;
}

/* Grips: the selector, then the own-grips tick box that bypasses it. */
.regrip-label {
  display: block;
  margin-bottom: 6px;
  font-size: .9375rem;
  font-weight: 500;
}

.regrip-select,
.regrip-note textarea {
  display: block;
  width: 100%;
  min-width: 0;
  min-height: 44px;
  padding: 10px 14px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background-color: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: 1rem;
  line-height: 1.5;
}

.regrip-select:disabled {
  background-color: var(--tint);
  color: var(--text-muted);
  opacity: 1;
  cursor: not-allowed;
}

.regrip-grip .regrip-select {
  transition: opacity .2s ease;
}

/* Dimmed further when the visitor has their own grips. A disabled control is
   exempt from the text contrast rule, and the hint under it says in full
   contrast why it is not needed. */
.regrip-grip.is-bypassed .regrip-select {
  opacity: .55;
}

.regrip-own {
  margin-top: 16px;
}

.regrip-own-hint {
  margin-top: 0;
  padding-left: 32px;
}

.regrip-note textarea {
  margin-top: 12px;
  min-height: 96px;
  resize: vertical;
  transition: border-color .15s ease;
}

.regrip-note textarea:hover {
  border-color: var(--accent-deep);
}

.regrip-note textarea:focus-visible {
  border-color: var(--accent-deep);
}

/* Summary: the guarantee band's colours, sticky beside the steps on wide
   screens so the estimate stays in view while the chips are changed. */
.regrip-summary {
  display: grid;
  gap: 0;
  padding: 24px;
  border: 1px solid var(--band-border);
  border-radius: var(--radius-lg);
  background-color: var(--band-bg);
  color: var(--band-text);
}

@media (min-width: 640px) {
  .regrip-summary { padding: 28px; }
}

@media (min-width: 900px) {
  .regrip-summary {
    position: sticky;
    top: calc(var(--header-height) + 24px);
  }
}

.regrip-summary-title {
  font-size: 1.375rem;
}

.regrip-list {
  margin: 16px 0 0;
}

.regrip-list > div {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 4px 16px;
  padding: 12px 0;
  border-bottom: 1px solid var(--band-border);
}

.regrip-list dt {
  color: var(--band-muted);
  font-size: .9375rem;
}

.regrip-list dd {
  margin: 0;
  font-size: .9375rem;
  font-weight: 600;
  text-align: right;
}

.regrip-total {
  margin-top: 20px;
}

.regrip-total p {
  margin: 0;
}

.regrip-total-label {
  color: var(--band-eyebrow);
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
}

.regrip-total-value {
  margin-top: 4px;
  color: var(--band-number);
  font-size: clamp(2rem, 1.6rem + 1.6vw, 2.5rem);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}

.regrip-total-detail {
  margin-top: 4px;
  color: var(--band-muted);
  font-size: .9375rem;
}

.regrip-fine {
  margin: 12px 0 0;
  color: var(--band-muted);
  font-size: .875rem;
  line-height: 1.5;
}

.regrip-cta {
  width: 100%;
  margin-top: 20px;
}

.regrip-after {
  margin: 10px 0 0;
  color: var(--band-muted);
  font-size: .8125rem;
  line-height: 1.45;
  text-align: center;
}

/* Home page: the note site.js puts at the top of the contact form when it
   has filled in the message from the planner. */
.regrip-prefill-note {
  margin: 0;
  padding: 12px 14px;
  border: 1px solid var(--border-strong);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  background-color: var(--tint);
  color: var(--text);
  font-size: .9375rem;
  line-height: 1.5;
}

/* Windows high contrast and other forced palettes drop background colours,
   which is all that marks a chosen chip; give it the system highlight. */
@media (forced-colors: active) {
  .regrip-chip input:checked + span {
    forced-color-adjust: none;
    border-color: Highlight;
    background-color: Highlight;
    color: HighlightText;
  }
}

/* The only motion here is transitions, which the reduce block above already
   shortens to nothing. Nothing is animated on scroll. */
/* === end re-grip configurator === */

/* === Re-shafting planner (20261011) === */
/*
  Re-shafting planner on shaft.html and no/skaft.html, Oliver's change list of
  2026-10-10 ("make it the same as the plan your re-grip one, just for
  shafts"). It wears the re-grip planner's classes (.regrip-app, the steps
  card, the chips, the select, the note, the summary and the no-JS swap), so
  this block only adds what that planner does not have: the club buttons and
  their chooser, the nine-button length row, and the per-club lines in the
  summary. Behaviour and the URL contract are in site.js (initReshaft,
  initReshaftPrefill).

  The steps card is a size container. The club grid, the chooser and the
  length row lay out by the width they really get, which in the two-column
  layout from 900px is narrower than on a tablet in one column.
*/

.reshaft-steps {
  container: reshaft / inline-size;
}

.reshaft-hint,
.regrip-step-hint {
  margin: 0 0 14px;
}

/* Step 2: one button per club. Dashed while the club is not chosen yet,
   solid once it is, filled while its chooser is open. */
.reshaft-clubs {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}

@container reshaft (min-width: 400px) {
  .reshaft-clubs { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@container reshaft (min-width: 560px) {
  .reshaft-clubs { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.reshaft-club,
.reshaft-option {
  display: grid;
  place-items: center;
  min-width: 0;
  min-height: 48px;
  margin: 0;
  padding: 8px 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background-color: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: .9375rem;
  font-weight: 600;
  line-height: 1.25;
  text-align: center;
  /* Only ever needed under WCAG text spacing, where a long word such as
     "Hybrider" would otherwise push out of a narrow button. */
  overflow-wrap: anywhere;
  cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease, box-shadow .2s ease;
}

.reshaft-club {
  border-style: dashed;
}

.reshaft-club.is-chosen {
  border-style: solid;
  border-color: var(--accent);
}

.reshaft-club[aria-expanded="true"],
.reshaft-option[aria-current="true"] {
  border-style: solid;
  border-color: var(--btn-bg);
  background-color: var(--btn-bg);
  color: var(--btn-text);
}

.reshaft-club:hover,
.reshaft-option:hover {
  border-color: var(--glow-border);
  box-shadow: 0 0 0 1px var(--glow), 0 0 18px 2px var(--glow-soft);
}

/* The chooser opens under the club buttons, for one club at a time. */
.reshaft-chooser {
  margin-top: 12px;
  padding: 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background-color: var(--tint);
}

@container reshaft (min-width: 460px) {
  .reshaft-chooser { padding: 16px; }
}

.reshaft-chooser-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
}

.reshaft-chooser-title {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-size: 1rem;
  font-weight: 650;
  line-height: 1.3;
}

.reshaft-tool {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 4px;
  min-height: 36px;
  margin: 0;
  padding: 6px 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background-color: transparent;
  color: var(--text);
  font: inherit;
  font-size: .875rem;
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
  transition: border-color .15s ease, box-shadow .2s ease;
}

.reshaft-tool .icon {
  width: 18px;
  height: 18px;
}

/* Close keeps to the right when a narrow card wraps it under Back. */
.reshaft-close {
  margin-inline-start: auto;
}

.reshaft-tool:hover {
  border-color: var(--glow-border);
  box-shadow: 0 0 0 1px var(--glow), 0 0 18px 2px var(--glow-soft);
}

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

.reshaft-option {
  min-height: 44px;
  padding: 6px 4px;
  font-size: 1rem;
}

@container reshaft (min-width: 360px) {
  .reshaft-cats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@container reshaft (min-width: 600px) {
  .reshaft-cats { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

/* The nine wedges sit three by three on a phone, five and four on a wider
   card and in one row from 480px; on a phone five across left too little
   room for "60°" under WCAG text spacing. */
.reshaft-wedges,
.reshaft-hybrids {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.reshaft-woods {
  grid-template-columns: repeat(5, minmax(0, 1fr));
}

.reshaft-irons {
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

@container reshaft (min-width: 360px) {
  .reshaft-wedges { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

@container reshaft (min-width: 420px) {
  .reshaft-irons { grid-template-columns: repeat(8, minmax(0, 1fr)); }
}

@container reshaft (min-width: 480px) {
  .reshaft-wedges { grid-template-columns: repeat(9, minmax(0, 1fr)); }
}

/*
  Step 4, the length. Two layouts over the same nine radios, which stay ONE
  native radio group (name="reshaft-len") in Oliver's order, so the state, the
  summary and the hand-off are the same either way.

  From a 460px row: nine buttons in ONE row, Standard in the middle (Oliver,
  2026-10-10: "All 9 buttons should be in a row"). The type scales with the
  row (cqi) so the widest label always fits, up to 16px; Standard gets 1.4
  shares. The Shorter and Longer buttons are not shown, and the two
  .reshaft-len-set wrappers are display: contents, so the nine labels are the
  grid's own items.

  Below a 460px row (Oliver, 2026-10-16: "only having 3 buttons instead in
  this order: Shorter, Standard and Longer"): Shorter, Standard and Longer in
  one row. Standard is the standard radio itself. Shorter and Longer are
  disclosure buttons (aria-expanded, aria-controls) that open a second row
  with the four minus or the four plus options; opening one closes the other.
  A closed row is hidden visually only, so its radios stay in the group: Tab
  still reaches the chosen one and the arrow keys still walk all nine, and a
  radio that takes focus in a closed row opens it (site.js). When the chosen
  length sits in a closed row, its button is filled and shows the value.
  Round 20261011 stood the nine labels on their side here instead.
*/
.reshaft-length {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr)) minmax(0, 1.4fr) repeat(4, minmax(0, 1fr));
  gap: 4px;
}

.reshaft-len-set {
  display: contents;
}

.reshaft-dir {
  display: none;
}

.reshaft-length .regrip-chip {
  display: grid;
}

.reshaft-length .regrip-chip span {
  min-height: 48px;
  padding: 6px 2px;
  font-size: clamp(.75rem, calc((100cqi - 72px) / 32), 1rem);
  overflow-wrap: anywhere;
}

@container reshaft (max-width: 459.98px) {
  .reshaft-length {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 6px;
  }

  .reshaft-dir {
    position: relative;
    display: grid;
    place-items: center;
    align-content: center;
    grid-row: 1;
    min-width: 0;
    min-height: 48px;
    margin: 0;
    padding: 6px 4px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background-color: transparent;
    color: var(--text);
    font: inherit;
    font-size: .9375rem;
    font-weight: 600;
    line-height: 1.2;
    text-align: center;
    overflow-wrap: anywhere;
    cursor: pointer;
    transition: background-color .15s ease, border-color .15s ease, color .15s ease, box-shadow .2s ease;
  }

  .reshaft-dir[data-reshaft-dir="shorter"] { grid-column: 1; }
  .reshaft-dir[data-reshaft-dir="longer"] { grid-column: 3; }

  .reshaft-length .reshaft-len-std {
    grid-row: 1;
    grid-column: 2;
  }

  .reshaft-length .regrip-chip span {
    font-size: .9375rem;
  }

  /* The open row: four options under the three buttons. */
  .reshaft-len-set {
    display: grid;
    grid-row: 2;
    grid-column: 1 / -1;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 6px;
  }

  .reshaft-length .reshaft-len-set .regrip-chip span {
    min-height: 44px;
  }

  /* A closed row is hidden from sight only: its radios stay focusable and in
     the group, and site.js opens the row when one of them takes focus. */
  .reshaft-len-set.is-folded {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .reshaft-dir:hover {
    border-color: var(--glow-border);
    box-shadow: 0 0 0 1px var(--glow), 0 0 18px 2px var(--glow-soft);
  }

  /* Open: a solid 2px accent edge. */
  .reshaft-dir[aria-expanded="true"] {
    border-color: var(--accent);
    border-width: 2px;
    padding: 5px 3px;
  }

  /* The chosen length is behind this button: filled like a chosen chip, with
     the value under the word. */
  .reshaft-dir.has-choice {
    border-color: var(--btn-bg);
    background-color: var(--btn-bg);
    color: var(--btn-text);
  }

  .reshaft-dir-pick {
    display: block;
    margin-top: 2px;
    font-size: .8125rem;
    font-weight: 600;
  }

  .reshaft-dir-pick:empty {
    display: none;
  }
}

@media (forced-colors: active) {
  .reshaft-dir.has-choice {
    forced-color-adjust: none;
    border-color: Highlight;
    background-color: Highlight;
    color: HighlightText;
  }
}

/* Summary: the club count, then one line per club under it. */
.reshaft-summary .regrip-list > div:first-child {
  padding-bottom: 6px;
  border-bottom: 0;
}

.reshaft-set-row {
  padding-top: 0;
}

.regrip-list .reshaft-set-row dd {
  width: 100%;
  font-weight: 400;
  text-align: left;
}

.reshaft-set {
  display: grid;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.reshaft-set li {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0 12px;
  font-size: .875rem;
  line-height: 1.45;
}

.reshaft-set-club {
  color: var(--band-muted);
}

.reshaft-set-value {
  font-weight: 600;
  text-align: right;
}

.reshaft-set-value.is-empty {
  color: var(--band-muted);
  font-weight: 400;
}

/* Forced palettes drop the fill that marks the open club and the current
   choice; give both the system highlight. A chosen club keeps its solid
   border, thickened, against the dashed border of one not chosen yet. The
   length chips are .regrip-chip and already covered above. */
@media (forced-colors: active) {
  .reshaft-club[aria-expanded="true"],
  .reshaft-option[aria-current="true"] {
    forced-color-adjust: none;
    border-color: Highlight;
    background-color: Highlight;
    color: HighlightText;
  }

  .reshaft-club.is-chosen {
    border-width: 2px;
  }
}

/* Transitions only, which the reduced-motion block shortens to nothing; the
   chooser is shown and hidden with the hidden attribute, never animated. */
/* === end re-shafting planner === */
