@charset "UTF-8";
/* =========================================================================
   Dam Lasers - main stylesheet
   =========================================================================

   Ported from the site's Squarespace build. Every colour, font weight, size
   and breakpoint below was READ OUT of the exported site.css and the Fluid
   Engine grid definitions - none of it is eyeballed. Source of record:
   _source/extracted/design-tokens.json

   A note on modern CSS, since your CSS predates most of it:

   - CUSTOM PROPERTIES  --name: value;  used as  var(--name)
     Real variables, resolved by the browser at runtime (not compiled away like
     Sass). They cascade and inherit, which is what makes the section themes
     below work: a section sets --bg/--fg once and everything inside it picks
     them up automatically.

   - FLEXBOX  (display:flex)   one-dimensional layout - a row or a column.
   - GRID     (display:grid)   two-dimensional layout - rows AND columns.
     Both replace the float/clearfix and inline-block hacks of the pre-2015 era.
     Nothing here needs a clearfix.

   - clamp(MIN, PREFERRED, MAX) picks a value that scales with the viewport but
     never leaves the bounds. It replaces most of what media queries used to do
     for type sizing, which is why there are far fewer breakpoints than you
     might expect.

   - Logical properties (padding-block, margin-inline) are the writing-mode-
     aware equivalents of top/bottom and left/right. Used where it reads better.

   Structure:
     1  Fonts
     2  Design tokens
     3  Reset and base
     4  Utilities
     5  Layout primitives
     6  Section themes
     7  Header and navigation
     8  Hero
     9  Intro
    10  Services
    11  Team
    12  Newsletter
    13  Footer
    14  Buttons
    15  Forms
    16  Contact page
    17  Generic page / 404
    18  Reduced motion and print
   ========================================================================= */


/* =========================================================================
   1. Fonts
   -------------------------------------------------------------------------
   Self-hosted Poppins. Squarespace loaded this from Adobe Typekit; Poppins is
   SIL OFL 1.1 so it is served from our own origin instead - one less
   third-party dependency and no GDPR question about hotlinking a font CDN.

   Weights are exactly the four the site uses (300 body / 400 meta /
   500 headings / 700 <strong>). No italics: the site contains none.

   font-display: swap  renders text immediately in a fallback and swaps when
   Poppins arrives, rather than leaving invisible text (FOIT).

   unicode-range splits latin from latin-ext so a visitor whose page contains
   no extended characters never downloads that file.
   ========================================================================= */

@font-face {
  font-family: 'Poppins'; font-style: normal; font-weight: 300; font-display: swap;
  src: url('../fonts/poppins-300-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Poppins'; font-style: normal; font-weight: 300; font-display: swap;
  src: url('../fonts/poppins-300-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Poppins'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('../fonts/poppins-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Poppins'; font-style: normal; font-weight: 400; font-display: swap;
  src: url('../fonts/poppins-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Poppins'; font-style: normal; font-weight: 500; font-display: swap;
  src: url('../fonts/poppins-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Poppins'; font-style: normal; font-weight: 500; font-display: swap;
  src: url('../fonts/poppins-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Poppins'; font-style: normal; font-weight: 700; font-display: swap;
  src: url('../fonts/poppins-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Poppins'; font-style: normal; font-weight: 700; font-display: swap;
  src: url('../fonts/poppins-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}


/* =========================================================================
   2. Design tokens
   ========================================================================= */

:root {
  /* --- Colour. Squarespace theme palette, converted from HSL. ------------ */
  --c-black:        #000000;                 /* theme "black"      */
  --c-dark:         hsl(0 0% 20.78%);        /* theme "dark"      = #353535 */
  --c-dark-bold:    hsl(0 0% 11.37%);        /* theme "dark-bold" = #1D1D1D */
  --c-white:        #ffffff;
  --c-light-accent: hsl(45 11.76% 86.67%);   /* #E1DFD9 warm off-white      */
  --c-accent:       hsl(241.8 64.88% 40.2%); /* #2824A9 deep indigo         */

  /* Set per section in §6; everything inside a section reads these. */
  --bg: var(--c-dark-bold);
  --fg: var(--c-white);

  /* --- Type ------------------------------------------------------------- */
  --font: 'Poppins', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --fw-body:    300;   /* Light   */
  --fw-meta:    400;   /* Regular */
  --fw-heading: 500;   /* Medium  */
  --fw-bold:    700;   /* Bold - <strong> only */

  --lh-body:    1.8;
  --lh-heading: 1.4;
  --lh-meta:    1.2;

  /*
   * Squarespace sizes are fixed rem values (h1 4rem etc). Fixed 4rem = 64px is
   * far too large on a 360px-wide phone, and Squarespace handled that with its
   * own responsive machinery. clamp() does the same job in one declaration:
   * never below the first value, never above the last, fluid in between.
   * The MAX of each clamp is the exact Squarespace value.
   */
  --fs-h1:     clamp(2.5rem, 1.30rem + 5.2vw, 4rem);
  --fs-h2:     clamp(1.9rem, 1.34rem + 2.4vw, 2.8rem);
  --fs-h3:     clamp(1.6rem, 1.25rem + 1.5vw, 2.2rem);
  --fs-h4:     1.2rem;
  --fs-large:  clamp(1.1rem, 0.98rem + 0.5vw, 1.4rem);
  --fs-body:   1rem;
  --fs-small:  0.9rem;

  /* --- Layout ----------------------------------------------------------- */
  --max-width: 1500px;   /* Squarespace maxPageWidth */
  --gutter:    6vw;      /* Squarespace mobile site gutter, used throughout */

  /*
   * Logo height. Squarespace's tweak is a flat 200px; the clamp keeps that as
   * the desktop value while scaling down on narrow screens.
   * At 1440px wide, 14vw = 201.6px, so the 200px ceiling applies - matching the
   * live site exactly.
   *
   * WHY THE min(..., 23vh) WAS ADDED
   *   The logo scales with viewport WIDTH but consumes viewport HEIGHT, and
   *   those are independent. A wide-but-short window therefore got the LARGEST
   *   logo and had the LEAST room for it. Since the header is fixed, that space
   *   never scrolls away. Measured before this change:
   *       1366x768  laptop            219px header = 28.5% of the screen
   *       844x390   phone landscape   135px header = 34.6%
   *   At 844x390 the entire viewport was header + hero title, with no body
   *   content visible at all.
   *
   *   23vh is chosen so the cap is INERT on normal displays: at 1440x900 it
   *   evaluates to 207px, above the 200px ceiling, so the live-site value is
   *   preserved exactly. It only starts to bite as the viewport gets shorter.
   *   min() inside clamp() keeps this continuous - no jump on resize.
   *
   *   TRADE-OFF, ACCEPTED DELIBERATELY: on a short viewport this no longer
   *   matches Squarespace pixel-for-pixel (at 1440x800 the live site shows
   *   200px, this shows 184px). That divergence is the point of the fix.
   */
  --logo-h: clamp(40px, min(14vw, 23vh), 140px);

  /*
   * DERIVED, not guessed. The header is the logo plus its vertical padding,
   * and since 2026-08-17 that padding is itself a fraction of the logo (see
   * .site-header below), so one multiplication describes the whole thing:
   *
   *     header = logo + 2 x (logo x 0.08) = logo x 1.16
   *
   * Written as logo + max(12px, logo x 0.16) so it tracks the same 6px floor
   * the padding has. Computing it means the value used for scroll-padding and
   * for the hero's top offset can never drift out of sync with the actual
   * rendered header - which it did while this was a hand-tuned clamp.
   *
   * THIS IS NOW THE ONLY --header-h IN THE FILE. The mobile and short-viewport
   * blocks used to redefine it (and the padding) as well; they no longer need
   * to, because both branches change only --logo-h and everything else follows.
   */
  --header-h: calc(var(--logo-h) + max(12px, calc(var(--logo-h) * 0.16)));

  /* --- Rhythm ----------------------------------------------------------- */
  --space-section: clamp(3.5rem, 2rem + 6vw, 7rem);
  --space-lg: 2.5rem;
  --space-md: 1.5rem;
  --space-sm: 0.75rem;

  --radius: 0px;   /* Squarespace used square corners throughout */
  --transition: 200ms ease;
}


/* =========================================================================
   3. Reset and base
   ========================================================================= */

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

html {
  /* Squarespace base font size. Never set this in px without a good reason -
     it overrides the user's browser font-size preference. 100% respects it. */
  font-size: 100%;
  -webkit-text-size-adjust: 100%;

  /* Anchor links must not land underneath the fixed header. */
  scroll-padding-top: var(--header-h);
  scroll-behavior: smooth;
}

body {
  margin: 0;
  font-family: var(--font);
  font-weight: var(--fw-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--fg);
  background-color: var(--c-dark-bold);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;

  /* Belt and braces against a stray wide element causing sideways scroll. */
  overflow-x: hidden;
}

h1, h2, h3, h4, h5, h6 {
  margin: 0 0 var(--space-md);
  font-weight: var(--fw-heading);
  line-height: var(--lh-heading);
  letter-spacing: 0;
}

h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); }
h4 { font-size: var(--fs-h4); }

p { margin: 0 0 var(--space-md); }
p:last-child { margin-bottom: 0; }

strong, b { font-weight: var(--fw-bold); }

a { color: inherit; text-decoration: underline; text-underline-offset: 0.2em; }
a:hover { text-decoration-thickness: 2px; }

img, picture, video, svg { display: block; max-width: 100%; height: auto; }

/* The browser's default is `margin: 1em 40px`, which indents every figure away
 * from the text beside it - and doubles up on a gallery, whose images are
 * figures inside a figure. Element selector, so core's block stylesheet (which
 * targets .wp-block-image and friends) still wins where it has an opinion. */
figure { margin: 0 0 var(--space-md); }

ul, ol { margin: 0; padding: 0; }
li { list-style: none; }

/*
 * Visible focus for keyboard users, on everything, always.
 * :focus-visible fires for keyboard navigation but NOT for mouse clicks, so
 * this does not produce the outline-on-click that people used to remove
 * with `outline: none` - which broke keyboard accessibility entirely.
 */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 3px solid var(--c-light-accent);
  outline-offset: 3px;
}


/* =========================================================================
   4. Utilities
   ========================================================================= */

/*
 * Visually hidden but still read by screen readers. Do NOT use
 * display:none or visibility:hidden for this - both remove the element from
 * the accessibility tree as well as the screen.
 */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Keyboard users get a skip link as the first focusable thing on the page. */
.skip-link {
  position: absolute;
  top: -100%;
  left: var(--space-md);
  z-index: 999;
  padding: var(--space-sm) var(--space-md);
  background: var(--c-light-accent);
  color: var(--c-black);
  text-decoration: none;
}
.skip-link:focus { top: var(--space-md); }

/*
 * Honeypot. Hidden from people, present in the DOM for bots.
 * Positioned off-screen rather than display:none because some bots skip
 * display:none fields specifically to dodge this trick.
 */
.honeypot {
  position: absolute !important;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}


/* =========================================================================
   5. Layout primitives
   ========================================================================= */

.site-main { display: block; }

/* A full-bleed band of colour. Squarespace: "full-bleed-section". */
.section {
  position: relative;
  padding-block: var(--space-section);
  padding-inline: var(--gutter);
  background-color: var(--bg);
  color: var(--fg);
}

/* The content column inside a band, capped at the Squarespace max width. */
.section__inner {
  width: 100%;
  max-width: var(--max-width);
  margin-inline: auto;
}

.section__inner--narrow { max-width: 46rem; }


/* =========================================================================
   6. Section themes
   -------------------------------------------------------------------------
   Each theme sets --bg and --fg; everything nested inherits them. This is the
   custom-property equivalent of Squarespace's [data-section-theme] blocks, and
   the values are taken from that CSS verbatim.
   ========================================================================= */

.section--dark-bold { --bg: var(--c-dark-bold); --fg: var(--c-white); }
.section--dark      { --bg: var(--c-dark);      --fg: var(--c-white); }
.section--black     { --bg: var(--c-black);     --fg: var(--c-white); }


/* =========================================================================
   7. Header and navigation
   -------------------------------------------------------------------------
   Squarespace config, read from the live page:
     desktop layout : brandingCenter
     mobile  layout : logoLeftNavRight, doubleLineHamburger, fade overlay
     theme          : "dark" (#353535), NOT transparent
     fixed          : true

   ON THE LOGO HEIGHT
   Squarespace's tweak is header-logo-height: 200px (mobile max 70px). Taken
   literally that is a fixed header over 220px tall, which eats a third of a
   laptop viewport.

   The old "FLAG FOR LAYOUT REVIEW: worth deciding whether to keep 200px at
   all" was ANSWERED on 2026-08-17: the owner chose to lower the ceiling to
   140px. So this is now a deliberate departure from the live site on wide
   screens - measured, 1440x900 goes 216px -> 162px of fixed header. Mobile
   (70px) and short-viewport (80px) ceilings are unchanged.

   ON THE VERTICAL PADDING
   Squarespace's header-vert-padding is 1vw desktop / 6vw mobile. The mobile
   figure was reproduced faithfully and turned out to be a bug in disguise:
   6vw doubled is 12vw, and the mobile logo is ALSO 12vw, so the mobile header
   was exactly 50% padding at every width - and 57% at 767px, where the logo
   hits its 70px ceiling but the padding keeps growing. Worse, the header got
   TALLER as the viewport narrowed: 123px at 768px, 162px at 767px, because
   collapsing to the hamburger dropped 38px of logo and added 77px of padding.

   Replaced with a padding that is a fraction of the logo, so the proportion is
   fixed everywhere instead of being three unrelated width-based numbers. 0.08
   was chosen because it is what the DESKTOP ratio already was (14.4px against
   a 187px logo at 1440), so wide screens barely moved.
   ========================================================================= */

.site-header {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 100;

  --bg: var(--c-dark);
  --fg: var(--c-white);
  background-color: var(--bg);
  color: var(--fg);

  /* A fraction of the logo, not of the viewport width - see "ON THE VERTICAL
   * PADDING" above. max() picks the LARGER of the two, so 6px is a floor for
   * the smallest phones, not a cap. This single declaration replaces the old
   * 1vw desktop / 6vw mobile / 2vh short-viewport trio. */
  padding-block: max(6px, calc(var(--logo-h) * 0.08));
  padding-inline: var(--gutter);
}

/*
 * brandingCenter, as Squarespace actually renders it:
 *   NAV (left)  |  LOGO (centre)  |  ACTIONS (right)
 * all on ONE row, vertically centred.
 *
 * An earlier revision read "brandingCenter" as "logo centred on its own row
 * with the nav beneath it", which is a different layout and did not match the
 * live site.
 *
 * Three equal columns rather than flex space-between: with 1fr auto 1fr the
 * logo stays optically centred in the viewport regardless of how wide the nav
 * or the social icons happen to be. space-between would drift the logo left or
 * right as those change.
 */
.site-header__inner {
  max-width: var(--max-width);
  margin-inline: auto;

  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-md);
}

/*
 * Explicit column placement so source order (branding first, for a sensible
 * reading and focus order) does not dictate visual order.
 *
 * `grid-row: 1` ON ALL THREE IS LOAD-BEARING, not decoration.
 * A grid item given an explicit COLUMN but no explicit ROW is still placed by
 * the auto-placement algorithm, which walks forward and never goes back. So
 * with branding (column 2) first in source and nav (column 1) second, the nav
 * cannot be placed in row 1 - the cursor is already past column 2 - and grid
 * silently pushes it to row 2. The result is a two-row header with the nav
 * dropped underneath the logo, which is exactly the layout this was meant to
 * replace. Pinning every item to row 1 removes the ambiguity.
 */
.site-header__branding { grid-row: 1; grid-column: 2; display: flex; justify-content: center; }
.site-nav             { grid-row: 1; grid-column: 1; justify-self: start; }
.site-header__actions { grid-row: 1; grid-column: 3; justify-self: end; }

.site-header__logo-link { display: block; line-height: 0; }

.site-header__logo {
  height: var(--logo-h);   /* 200px ceiling = the Squarespace value */
  width: auto;
}

/* Hamburger: hidden on desktop, shown under the breakpoint (§7 media query). */
.site-header__toggle {
  display: none;
  position: absolute;
  inset-inline-end: 0;
  inset-block-start: 50%;
  transform: translateY(-50%);

  width: 44px;  /* 44px is the minimum comfortable touch target */
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
}

.site-header__toggle-bar {
  display: block;
  width: 26px;
  height: 1px;
  margin: 5px auto;
  background: currentColor;
  transition: transform var(--transition), opacity var(--transition);
}

/* Hamburger morphs into a close (X) when the menu is open. */
.site-header__toggle[aria-expanded="true"] .site-header__toggle-bar:first-child {
  transform: translateY(3px) rotate(45deg);
}
.site-header__toggle[aria-expanded="true"] .site-header__toggle-bar:last-child {
  transform: translateY(-3px) rotate(-45deg);
}

.site-nav {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
}

.site-nav__list {
  display: flex;
  align-items: center;
  gap: var(--space-lg);
}

/* Nav links.
 *
 * Matched by DESCENDANT (.site-nav__list a), not by class. wp_nav_menu() only
 * accepts a class for the <ul> (menu_class); the <li> and <a> it generates get
 * WordPress's own classes, so a .site-nav__link rule silently stops applying
 * the moment a menu is assigned in Appearance > Menus - which is exactly what
 * happened here. The links kept the global `a` underline, lost the weight and
 * size, and the current-page indicator disappeared.
 *
 * The descendant selector covers BOTH the generated menu and the hardcoded
 * fallback in header.php, so the header cannot regress that way again. Scoped
 * to .site-nav__list, because .site-nav also contains the social icons and the
 * language switcher on mobile, which have their own styling.
 *
 * WordPress puts aria-current="page" on the current menu item by itself, so
 * the indicator below needs no extra wiring.
 */
.site-nav__list a {
  font-weight: var(--fw-meta);
  font-size: var(--fs-body);
  line-height: var(--lh-meta);
  text-decoration: none;
  padding-block: 0.35em;
  border-block-end: 1px solid transparent;
  transition: border-color var(--transition);
}
.site-nav__list a:hover,
.site-nav__list a[aria-current="page"] { border-block-end-color: currentColor; }
/* The global a:hover thickens the underline; there is no underline here. */
.site-nav__list a:hover { text-decoration: none; }

/* -------------------------------------------------------------------------
   Submenu (desktop only - the mobile overlay renders it as an indented list).

   WHY THIS EXISTS: the header row physically cannot hold the service pages.
   Measured on the real site - 4 items need 1150px, 5 need 1440px, and 6 do
   not fit below 1920px at all (they wrap onto three lines and push the logo
   up to 256px off centre). A submenu costs the row no width.

   NO JAVASCRIPT. :focus-within opens the panel for keyboard users the moment
   focus reaches a child link, which is why tabbing through the menu works
   without an aria-expanded button to maintain. Touch devices never see this
   at all: below 1200px the header is the overlay menu, where the submenu is
   just more items in the column.
   ------------------------------------------------------------------------- */

/* Positioning context for the panel. Only the item that has children needs
   it, so an ordinary item is left completely untouched.

   `display: flex` is not decoration either. The link is inline, so its
   vertical padding overflows the line box and the <li> ends up SHORTER than
   the link it contains - measured, 3px shorter. `inset-block-start: 100%`
   is relative to the <li>, so the panel would start 3px high and cover the
   link's hover underline. Making the item a flex container gives it the
   link's real height, and 100% then means exactly "below the link". */
.site-nav__list .menu-item-has-children {
  position: relative;
  display: flex;
  align-items: center;
}

/*
 * The affordance, drawn with borders rather than written as text.
 *
 * A character in `content` would be one more string in the page - and this
 * theme's rule is that anything rendered is translatable, so it would arrive
 * in the worksheet as a row saying "translate this triangle". A border
 * triangle is invisible to TranslatePress and to screen readers alike.
 */
.site-nav__list .menu-item-has-children > a::after {
  content: "";
  display: inline-block;
  margin-inline-start: 0.45em;
  vertical-align: 0.15em;
  border: 0.3em solid transparent;
  border-block-start-color: currentColor;
  border-block-end: 0;
  transition: transform var(--transition);
}
.site-nav__list .menu-item-has-children:hover > a::after,
.site-nav__list .menu-item-has-children:focus-within > a::after {
  transform: translateY(1px);
}

.site-nav__list .sub-menu {
  position: absolute;
  /* Flush against the bottom of the parent link, so there is no dead gap for
     the pointer to cross on its way down - a hover menu with a gap in it
     closes as you reach for it. */
  inset-block-start: 100%;
  /* Pull left by the panel's own padding so the child labels line up with the
     parent label rather than being indented by it. */
  inset-inline-start: calc(var(--space-md) * -1);

  /* max-content, so a label decides the panel width and never wraps. */
  min-width: max-content;

  display: flex;
  flex-direction: column;
  gap: var(--space-sm);

  margin: 0;
  padding: var(--space-md);
  list-style: none;

  /* Same dark as the header bar, so the panel reads as an extension of it
     rather than a floating card. The shadow is what separates it from the
     page content it covers. */
  background-color: var(--c-dark);
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.35);

  /*
   * ⚠ HIDDEN WITH opacity ALONE - NOT visibility:hidden, NOT display:none.
   *
   * Both of those remove the links from the tab order, and this panel is
   * opened by :focus-within - i.e. BY a child link receiving focus. A link
   * that cannot be focused can never open the panel that is hiding it, so
   * the keyboard route silently disappears and the submenu becomes
   * mouse-only. Measured before this was fixed: calling .focus() on a child
   * left the panel at visibility:hidden, because the call did nothing.
   *
   * So the links stay focusable at all times, and the first Tab into them
   * reveals the panel. Screen readers announce them for the same reason,
   * which is what we want - they are real navigation.
   *
   * pointer-events:none is then REQUIRED, not a nicety: the panel overlaps
   * the page below the header, and an invisible box that still swallows
   * clicks is worse than no panel at all.
   */
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition);
}

.site-nav__list .menu-item-has-children:hover > .sub-menu,
.site-nav__list .menu-item-has-children:focus-within > .sub-menu {
  opacity: 1;
  /* Re-enabled so the pointer can travel down into the panel without the
     hover being lost on the way. */
  pointer-events: auto;
}

.site-social { display: flex; align-items: center; gap: var(--space-md); }
.site-social__link { display: block; color: inherit; opacity: 0.85; transition: opacity var(--transition); }
.site-social__link:hover { opacity: 1; }
.site-social__icon { width: 20px; height: 20px; }

/*
 * The same social list is emitted three times from one helper: in the desktop
 * header bar, inside the mobile overlay menu, and in the footer. Only one of
 * the first two is ever visible - display:none also removes the hidden copy
 * from the accessibility tree, so nothing is announced twice.
 */
.site-social--overlay { display: none; }

/*
 * Language switcher (EN | NL).
 *
 * Same three-copies-one-helper arrangement as .site-social above: a desktop
 * copy inside .site-header__actions and a mobile copy inside the overlay, with
 * the unused one display:none.
 *
 * The desktop copy sits alongside the social icons, so .site-header__actions
 * is made a flex row - it previously held a single child and needed no layout
 * of its own.
 */
.site-header__actions { display: flex; align-items: center; gap: var(--space-md); }

.site-lang {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
  font-weight: 400;            /* the "meta" weight, per the type scale */
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/*
 * The divider is a BORDER, not a "|" text node, so a screen reader never
 * announces a stray pipe between the two language names.
 * ':not(:first-child)' rather than 'x + x' so it also works if a third
 * language is ever added.
 */
.site-lang__item:not(:first-child) {
  border-inline-start: 1px solid currentColor;
  padding-inline-start: 0.5rem;
}

.site-lang__link {
  color: inherit;
  text-decoration: none;
  opacity: 0.7;
  border-block-end: 1px solid transparent;   /* reserve the underline space */
  transition: opacity var(--transition);
}
.site-lang__link:hover,
.site-lang__link[aria-current="true"] { opacity: 1; border-block-end-color: currentColor; }

.site-lang--overlay { display: none; }

/*
 * Push page content clear of the fixed header.
 * The hero opts out (§8) because its video is meant to sit behind the header.
 */
.site-main > .section:first-child:not(.hero) { padding-block-start: calc(var(--header-h) + var(--space-section)); }


/* =========================================================================
   8. Hero
   ========================================================================= */

/*
 * HERO HEIGHT IS CONTENT-DRIVEN, NOT FULL VIEWPORT.
 *
 * The section's config is section-height--custom with customSectionHeight: 0,
 * and a screenshot of the live site confirms it: the hero is roughly 265px
 * tall at 1440 wide - just the title, the tagline and some padding.
 *
 * An earlier revision used `min-height: 100svh`, which produced a full-screen
 * hero. That looks dramatic but is NOT what the current site does, and the
 * brief is to reproduce the site in its current form.
 *
 * min-height here is a floor for very small text scaling only; the real height
 * comes from the content plus the section padding.
 */
.hero {
  display: grid;
  place-items: center;

  /* ~76px above and below the content, measured off the live site at 1440. */
  padding-block: clamp(2rem, 5vw, 4.75rem);
  padding-block-start: calc(var(--header-h) + clamp(2rem, 5vw, 4.75rem));
  overflow: hidden;
  text-align: center;
}

.hero__media { position: absolute; inset: 0; z-index: 0; }

.hero__video {
  width: 100%;
  height: 100%;

  /* Fill the box, cropping rather than distorting - like background-size:cover
     but for a real <video>/<img> element. */
  object-fit: cover;
  object-position: center;
}

/*
 * NO SCRIM on the hero: the section's imageOverlayOpacity is 0.0, i.e. the
 * video plays at full strength. The 0.3 value an earlier revision used here
 * belongs to the SERVICES section, which has no background media anyway.
 */

.hero__content { position: relative; z-index: 1; }

/*
 * OUTLINE THE HERO TEXT.
 *
 * The hero deliberately has no scrim (see above), so white text sits directly
 * on the footage. That was comfortable against the old poster, whose bright
 * areas were broad and soft; the current one has thin, hard-edged beams with
 * clipped-white cores (measured: peak luma 230 -> 255) that cut straight
 * through the glyphs. Mean brightness is essentially unchanged, so darkening
 * the image would not fix it - the problem is local contrast, not exposure.
 *
 * Four 1px offsets draw a thin dark outline around every glyph; the wider,
 * softer fifth layer lifts the whole line off the background without reading
 * as a drop shadow. Order matters only in that later shadows paint UNDER
 * earlier ones, so the tight outline stays crisp on top of the soft halo.
 *
 * text-shadow rather than -webkit-text-stroke: a stroke is painted centred on
 * the glyph outline, so half of it falls INSIDE the letter and thins the
 * strokes. The tagline is Poppins 300 - it cannot afford that. (paint-order,
 * which would fix it, is still not dependable for HTML text.)
 *
 * Scoped to the hero: it is the only place text sits on top of media.
 */
.hero__title,
.hero__tagline {
  text-shadow:
     0   1px 1px rgba(0, 0, 0, 0.6),
     0  -1px 1px rgba(0, 0, 0, 0.6),
     1px  0   1px rgba(0, 0, 0, 0.6),
    -1px  0   1px rgba(0, 0, 0, 0.6),
     0   0  12px rgba(0, 0, 0, 0.5);
}

.hero__title {
  margin-bottom: var(--space-sm);

  /* The heading is wrapped in <strong> in the Squarespace source, so it renders
     at 700 rather than the 500 headings normally use. Applied as a weight here
     rather than adding a semantically empty <strong> to the markup. */
  font-weight: var(--fw-bold);

  /* Rendered in caps by CSS, not typed in caps, so screen readers announce
     "Dam Lasers" instead of spelling out D-A-M. */
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/*
 * No max-width. On the live site the tagline sits on ONE line at 1440px wide;
 * a 34ch cap forced it to wrap onto two, which was visibly wrong.
 */
.hero__tagline {
  font-size: var(--fs-large);
  font-weight: var(--fw-body);
  margin-inline: auto;
}


/* =========================================================================
   9. Intro
   ========================================================================= */

/*
 * ~977px measured off the live site at 1440 wide (content-width--wide).
 * 60rem/960px was close but wrapped the paragraph a word early.
 */
.intro__inner { max-width: 61rem; margin-inline: auto; text-align: center; }

.intro__text {
  font-size: var(--fs-large);
  font-weight: var(--fw-body);
  margin: 0;
}

/* "Dam Lasers" is bold and "safe" is underlined in the source copy. */
.intro__text strong { font-weight: var(--fw-bold); }
.intro__text u { text-decoration: underline; text-underline-offset: 0.15em; }


/* =========================================================================
   10. Services
   ========================================================================= */

.services__heading { text-align: center; margin-bottom: var(--space-lg); }

/*
 * Three equal columns.
 * repeat(3, 1fr) = three tracks each taking one equal share of the free space.
 * 1fr is a grid-only unit meaning "one fraction of what's left"; it is what
 * replaced percentage-width floats.
 */
.services__list {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  /* Live cards are separated by ~17px, not the 40px --space-lg gives. */
  gap: var(--space-md);
  margin-bottom: var(--space-lg);
}

.services__item { text-align: center; }

.services__media { margin-bottom: var(--space-md); }

.services__media img {
  width: 100%;
  /*
   * Force a consistent card shape. The three source images have different
   * native ratios (3:2, 16:9 and 1:1), and without this the row would look
   * ragged. object-fit crops to fill rather than squashing.
   */
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

/* Squarespace per-section type override: title 1.2rem, description 0.9rem. */
.services__title {
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);   /* bold on the live site */
  margin-bottom: var(--space-sm);
}

.services__desc {
  font-size: var(--fs-small);
  margin: 0;
}

.services__custom { text-align: center; margin-bottom: var(--space-md); }
.services__cta { text-align: center; margin: 0; }


/* =========================================================================
   11. Team
   ========================================================================= */

.team__heading {
  text-align: center;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);   /* <strong>TEAM</strong> in the source */
  margin-bottom: var(--space-lg);
}

.team__list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-lg);
  max-width: 60rem;
  margin-inline: auto;
}

.team__item { text-align: center; }

.team__media { margin-bottom: var(--space-md); }

/*
 * RECTANGULAR, NOT CIRCULAR.
 * An earlier revision applied border-radius: 50% because both source files are
 * square. The live site renders them as plain rectangles with square corners -
 * confirmed on screenshot - so the circle was an invention, not a reproduction.
 */
.team__media img {
  width: 100%;
  max-width: 420px;   /* ~425px measured on the live site */
  margin-inline: auto;
  aspect-ratio: 1;
  object-fit: cover;
}

.team__name {
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);   /* <strong> in the source */
  margin-bottom: 0.25rem;
}

.team__role {
  font-size: var(--fs-small);
  font-weight: var(--fw-meta);
  line-height: var(--lh-meta);
  margin: 0;
}


/* =========================================================================
   12. Newsletter
   ========================================================================= */

/*
 * The newsletter section has a BACKGROUND IMAGE (20150531_015859.jpg) under a
 * 0.35 black overlay. An earlier revision missed this entirely and, worse,
 * used the same file as a services card image, where it does not belong.
 *
 * `isolation: isolate` creates a new stacking context so the z-index values
 * below only compete with each other, not with the fixed header.
 */
.newsletter { position: relative; overflow: hidden; isolation: isolate; }

.newsletter__bg { position: absolute; inset: 0; z-index: -1; }

.newsletter__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* imageOverlayOpacity: 0.35 */
.newsletter__scrim { position: absolute; inset: 0; background: rgb(0 0 0 / 0.35); }

.newsletter .section__inner { position: relative; z-index: 1; }

/*
 * Squarespace block-edf034ef97e932acd394: 6% padding on all sides,
 * background --lightAccent. That is a light card on a dark section, so the
 * text colour has to flip - hence --fg is overridden here, not just --bg.
 */
.newsletter__card {
  --bg: var(--c-light-accent);
  --fg: var(--c-black);

  max-width: 46rem;
  margin-inline: auto;
  padding: 6%;
  background-color: var(--bg);
  color: var(--fg);
  text-align: center;
}

.newsletter__heading { margin-bottom: var(--space-sm); }
.newsletter__intro   { margin-bottom: var(--space-md); }

.newsletter__row {
  display: flex;
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
}

/*
 * flex: 1 1 auto lets the input absorb the spare width while the button keeps
 * its natural size. min-width:0 is the fix for the classic flexbox trap where
 * a flex item refuses to shrink below its content's intrinsic width and
 * overflows its container.
 */
.newsletter__input {
  flex: 1 1 auto;
  min-width: 0;
}

.newsletter__submit { flex: 0 0 auto; }

.newsletter__privacy {
  font-size: var(--fs-small);
  line-height: var(--lh-body);
  margin: 0;
}


/* =========================================================================
   13. Footer
   ========================================================================= */

.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-lg);
}

/*
 * These render at h4 size (1.2rem) to match the Squarespace original, but are
 * marked up as <h2>. Heading LEVEL is a document-structure signal for
 * assistive tech; heading SIZE is presentation. Using <h4> here purely to get
 * 1.2rem would skip from h2 to h4 and break the outline.
 */
.site-footer__heading {
  font-size: var(--fs-h4);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: var(--space-sm);
}

.site-footer__detail {
  font-size: var(--fs-small);
  line-height: var(--lh-body);
  margin: 0 0 0.35rem;
}

.site-social--footer { justify-content: flex-start; }


/* =========================================================================
   14. Buttons
   ========================================================================= */

.button {
  display: inline-block;
  padding: 0.9em 1.8em;
  font-family: inherit;
  font-size: var(--fs-body);
  font-weight: var(--fw-meta);
  line-height: var(--lh-meta);
  text-align: center;
  text-decoration: none;
  border: 1px solid currentColor;
  border-radius: var(--radius);
  cursor: pointer;
  transition: background-color var(--transition), color var(--transition);
}

/*
 * Squarespace computes primaryButtonBackgroundColor = white AND
 * primaryButtonTextColor = white for the dark-bold theme, i.e. white on white.
 * That is a quirk of their generated palette, not a design intent, so the
 * text colour here is the section background instead - which is what the
 * "dark" theme's button resolves to and is legible.
 */
.button--primary {
  background-color: var(--c-white);
  color: var(--c-dark-bold);
  border-color: var(--c-white);
}
.button--primary:hover { background-color: var(--c-light-accent); border-color: var(--c-light-accent); }

.button--secondary {
  background-color: transparent;
  color: inherit;
}
.button--secondary:hover { background-color: var(--c-white); color: var(--c-dark-bold); }

.button:disabled,
.button[disabled] { opacity: 0.45; cursor: not-allowed; }


/* =========================================================================
   15. Forms
   ========================================================================= */

.form-field { margin-bottom: var(--space-md); }

/* A <fieldset> carries a browser default border and padding; strip them. */
.form-field--group { border: 0; padding: 0; margin-inline: 0; }

.form-label,
legend.form-label {
  display: block;
  font-size: var(--fs-small);
  font-weight: var(--fw-meta);
  line-height: var(--lh-meta);
  margin-bottom: 0.4rem;
  padding: 0;   /* legend has default inline padding */
}

.form-sublabel {
  display: block;
  font-size: var(--fs-small);
  opacity: 0.8;
  margin-bottom: 0.25rem;
}

/* Squarespace renders required fields as the label followed by "(required)",
   not an asterisk. Muted so it reads as a hint rather than an error. */
.form-required {
  font-weight: var(--fw-body);
  opacity: 0.7;
}

.form-help {
  font-size: var(--fs-small);
  opacity: 0.75;
  margin: 0.3rem 0 0;
}

.form-input,
.form-select,
.form-textarea {
  width: 100%;
  padding: 0.75em 0.9em;

  /* Form controls do NOT inherit font by default - they fall back to the
     browser's UI font unless told otherwise. */
  font-family: inherit;
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  line-height: var(--lh-meta);

  color: var(--c-black);
  background-color: var(--c-white);
  border: 1px solid rgb(0 0 0 / 0.25);
  border-radius: var(--radius);
}

.form-textarea { resize: vertical; min-height: 8rem; }

/* Two-up first/last name, collapsing to stacked on narrow screens. */
.form-field__split {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-sm);
}

.form-checklist { display: grid; gap: 0.5rem; }

.form-checklist__item {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
}

.form-checkbox {
  flex: 0 0 auto;
  width: 1.1rem;
  height: 1.1rem;
  margin-block-start: 0.35rem;
  accent-color: var(--c-accent);   /* tints the native control */
}

.form-choice-label {
  font-size: var(--fs-small);
  line-height: var(--lh-body);
  cursor: pointer;
}

/* "Not connected yet" banner, shown until stage 4 supplies credentials. */
.form-notice {
  padding: var(--space-md);
  margin-bottom: var(--space-md);
  border: 1px solid currentColor;
  font-size: var(--fs-small);
  line-height: var(--lh-body);
}

.contact-form--pending { opacity: 0.75; }


/* =========================================================================
   16. Contact page
   ========================================================================= */

/*
 * Section 1: heading OVER a background image (h1nbi85y.jpeg, overlay 0.15).
 * Not a separate image band - the type sits on the photograph.
 */
.contact-hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  padding-block-start: calc(var(--header-h) + var(--space-section));
  text-align: center;
}

.contact-hero__bg { position: absolute; inset: 0; z-index: -1; }

.contact-hero__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

.contact-hero__scrim { position: absolute; inset: 0; background: rgb(0 0 0 / 0.15); }

.contact-hero__inner { max-width: 52rem; margin-inline: auto; position: relative; }
.contact-hero__intro { font-size: var(--fs-large); margin-bottom: var(--space-md); }
.contact-hero__sub   { font-size: var(--fs-large); margin: 0; }

/*
 * Section 2: photo LEFT, form RIGHT.
 * Measured off the live site at 1440: the image occupies roughly the left 42%
 * and the form the right 42%, with a wide gutter between them.
 */
.contact-form-section__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 6vw, 6rem);
  align-items: start;
}

.contact-form-section__media img {
  width: 100%;
  /* Tall portrait crop, as on the live site. */
  aspect-ratio: 3 / 4;
  object-fit: cover;
}

.contact-form__submit { width: 100%; }


/* =========================================================================
   17. Generic page and 404
   ========================================================================= */

.page-content { padding-block-start: calc(var(--header-h) + var(--space-section)); }

/* Interior page heading.
 *
 * Deliberately NOT --fs-h1. That 4rem is the Squarespace HOME hero size, and
 * this template is new - Squarespace had no interior-page equivalent - so there
 * is nothing to match. At 4rem inside a 46rem (736px) column, Poppins Medium
 * fits roughly 20 characters per line, so every real page title wrapped and
 * usually stranded its last word alone on line two. --fs-h2 tops out at 2.8rem.
 *
 * text-wrap: balance (2023; all current browsers) asks the browser to even out
 * the lines of a heading that still wraps, instead of filling each line greedily
 * and leaving a short remainder - so two lines come out similar in length rather
 * than one full line plus an orphan. Browsers that do not know the property just
 * wrap the old way, so no fallback is needed. It deliberately only works on
 * short blocks (browsers cap it at a few lines), which is what a heading is.
 *
 * The 404 heading is a different class (.error-404__title) and is unaffected:
 * it is two short words, already centred by .error-404__inner, and the hero
 * scale suits that page.
 */
.page-content__title {
  margin-bottom: var(--space-lg);
  font-size: var(--fs-h2);
  text-align: center;
  text-wrap: balance;
}

/* Editor-driven content.
 *
 * Core's block stylesheet does the structural work (gallery grid, columns,
 * alignment) and is enqueued only on templates that call the_content() - see
 * damlasers_request_renders_blocks() in functions.php. What is left to this
 * theme is everything that assumes a LIGHT background, because core's defaults
 * do: hairline borders, muted caption greys and quote rules all disappear on
 * #1D1D1D. The rules below restate those in the site's own palette.
 *
 * Headings and lists come first; they predate the block work.
 */
.prose h2 { margin-block-start: var(--space-lg); }
.prose h3 { margin-block-start: var(--space-md); }
.prose h4 { margin-block-start: var(--space-md); }
.prose ul, .prose ol { margin: 0 0 var(--space-md) 1.25rem; }
.prose li { list-style: disc; margin-bottom: 0.35rem; }
.prose ol li { list-style: decimal; }

/* Never let the last child add trailing space inside the section padding. */
.prose > *:last-child { margin-bottom: 0; }

/* Images and galleries -------------------------------------------------- */

.prose figure { margin-block: var(--space-lg); }
/* Gallery images are figures inside a figure; the outer one owns the spacing. */
.prose .wp-block-gallery figure { margin-block: 0; }

/* Height ceiling for block media.
 *
 * Core sizes block media by WIDTH ONLY:
 *     .wp-block-image img   { height: auto; max-width: 100%; width: auto }
 *     .wp-block-video video { height: auto; width: 100% }
 * so the height is whatever the aspect ratio implies, and nothing caps it.
 * Measured on the Previz page: a 1080x1920 portrait phone video renders
 * 1308px tall in this 46rem column - taller than a laptop viewport, and 1049px
 * against the 375px height of a landscape phone. 16:9 material is fine at
 * ~414px; it is tall sources that break, and nothing stopped the next one.
 *
 * --media-max-h is the single lever. The block-style presets registered in
 * functions.php do nothing but set it to a different value on the <figure>,
 * and it inherits down to the img/video - which is why they need no
 * !important and cannot fight the rule below.
 *
 * svh = "small viewport height": the viewport WITH a mobile browser's URL bar
 * showing, i.e. the smallest it ever gets. Plain vh measures it with the bar
 * hidden, so a vh cap can still overflow on first paint - exactly the case
 * this rule exists for. No vh fallback is given deliberately: a custom
 * property holding an unknown unit is invalid at computed-value time, which
 * resets max-height to none rather than falling back, so the usual two-
 * declaration trick would fail open. svh predates text-wrap: balance, which
 * this stylesheet already relies on.
 */
.prose { --media-max-h: 70svh; }

.prose .is-style-media-short    { --media-max-h: 45svh; }
.prose .is-style-media-tall     { --media-max-h: 90svh; }
.prose .is-style-media-uncapped { --media-max-h: none; }

.prose .wp-block-image img,
.prose .wp-block-video video {
  max-height: var(--media-max-h);

  /* Core sets width:100% on video. Capping the height of a stretched box
   * SQUASHES it - the aspect ratio is not preserved unless the width is free
   * to shrink too. width:auto does that; margin-inline re-centres the now
   * narrower box in the column. */
  display: block;
  width: auto;
  max-width: 100%;
  margin-inline: auto;
}

/* Galleries are exempt. They lay their images out in a grid with proportions
 * of their own, and capping each image individually makes the rows ragged.
 * Gallery images are .wp-block-image figures nested inside .wp-block-gallery,
 * so they match the rule above and have to be reset here - equal specificity,
 * later in the file wins. */
.prose .wp-block-gallery img {
  max-height: none;
  width: 100%;
}

.prose figcaption {
  margin-block-start: 0.6rem;
  font-size: var(--fs-small);
  line-height: var(--lh-meta);
  /* Core sets a dark grey here, which is close to invisible on this palette. */
  color: var(--c-light-accent);
  opacity: 0.75;
  text-align: center;
}

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

.prose .wp-block-details {
  margin-block: var(--space-md);
  border-block-end: 1px solid rgba(225, 223, 217, 0.25);
}
.prose .wp-block-details summary {
  cursor: pointer;
  padding-block: 0.6rem;
  font-weight: var(--fw-meta);
  /* list-style is reset to none globally, which also removes the disclosure
   * triangle - ::marker puts it back on the <summary> itself. */
  list-style: revert;
}
.prose .wp-block-details summary::marker { color: var(--c-light-accent); }
.prose .wp-block-details[open] summary { margin-bottom: var(--space-sm); }

/* Quotes and tables ------------------------------------------------------ */

.prose blockquote {
  margin: var(--space-lg) 0;
  padding-inline-start: var(--space-md);
  border-inline-start: 2px solid var(--c-accent);
}
.prose blockquote cite {
  display: block;
  margin-block-start: var(--space-sm);
  font-size: var(--fs-small);
  font-style: normal;   /* the site uses no italics anywhere */
  opacity: 0.75;
}

.prose table {
  width: 100%;
  margin-block: var(--space-lg);
  border-collapse: collapse;
}
.prose th, .prose td {
  padding: 0.5rem 0.75rem;
  text-align: start;
  border: 1px solid rgba(225, 223, 217, 0.25);
}
.prose th { font-weight: var(--fw-meta); }

/* Wide content must scroll in its own box rather than push the page sideways;
 * body carries overflow-x: hidden, which would otherwise just clip it. */
.prose .wp-block-table { overflow-x: auto; }

.error-404__inner { text-align: center; }
.error-404__text  { font-size: var(--fs-large); margin-bottom: var(--space-lg); }

.error-404__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  justify-content: center;
}


/* =========================================================================
   18. Responsive
   -------------------------------------------------------------------------
   TWO breakpoints, and they are deliberately different numbers:

     1200px  THE HEADER ONLY. Below it the header collapses to the overlay
             menu. See "WHY THE HEADER SWITCHES AT 1200" immediately below.
     767px   EVERYTHING ELSE. Squarespace's own mobile breakpoint
             (system_mobile, maxWidth 767px), taken from the exported form
             block's design fields, so the content layout switches at the same
             width the current site does.

   They were one 767px block until 2026-08-18. If you widen the 767px block to
   cover the header again, the services grid, team grid, contact form and
   footer will all collapse to one column on a 1100px laptop.

   Note how few rules are needed: the clamp()-based type scale and the
   auto-fitting grids handle most of the range without a query.
   =========================================================================

   WHY THE HEADER SWITCHES AT 1200, NOT 767
   ----------------------------------------
   Because the desktop header could not fit its own nav, and had not been
   measured below 1440. Measured on the live site, four items
   (Home | Laser Shows | Portfolio | Contact):

     1150px and up   fits on one line, logo centred
     1120px          "Laser Shows" WRAPS to two lines
      900px          wraps, and the logo is pushed 39px off centre
      768px          wraps, logo squeezed to 106px and 88px off centre

   So the band 768-1140px - tablets, small laptops, any half-screen window -
   was rendering a broken header, and had been since stage 3. The desktop row
   genuinely cannot be made to fit there: at 768px the four labels need 395px
   against 253px of column, so no gap or font tweak reaches it.

   1200 rather than 1150 leaves headroom for the submenu caret, which adds
   12px and would otherwise put the threshold at 1180.
   ========================================================================= */

@media (max-width: 1199px) {

  /* Mobile header: logoLeftNavRight. Logo left, hamburger right, menu in a
     full-screen overlay.

     No padding-block here any more. Squarespace's mobile-header-vert-padding
     was 6vw, which made the header half padding at every width - see "ON THE
     VERTICAL PADDING" in section 7. The base rule's logo-proportional padding
     applies unchanged, so only --logo-h below needs a mobile value. */

  /* Collapse the 3-column desktop grid back to a simple row. */
  .site-header__inner {
    display: flex;
    flex-direction: row;
    justify-content: flex-start;
    align-items: center;
    position: relative;
  }

  .site-header__branding { justify-content: flex-start; }

  /* The desktop social cluster is replaced by the copy inside the overlay. */
  .site-header__actions { display: none; }
  .site-social--overlay { display: flex; justify-content: center; }

  /* Language switcher moves into the overlay with the social icons. */
  .site-lang--overlay {
    display: flex;
    justify-content: center;
    margin-block-start: var(--space-md);
  }

  /* header-mobile-logo-max-height: 70px. --header-h needs no mobile value any
     more: it is derived from --logo-h, so changing the logo here is enough. */
  :root { --logo-h: clamp(40px, 12vw, 70px); }

  .site-header__toggle { display: block; }

  /*
   * The overlay menu.
   * visibility+opacity rather than display:none, because display cannot be
   * transitioned - the fade would not happen. visibility:hidden still removes
   * it from the tab order and the accessibility tree, which display:none also
   * does but instantly.
   */
  .site-nav {
    position: fixed;
    inset: 0;
    z-index: 90;

    /*
     * !! THESE TWO LINES ARE WHAT MAKES THE OVERLAY COVER THE SCREEN !!
     *
     * The desktop rule sets `justify-self: start` to keep the nav at the left
     * of the header's grid. That alignment STILL APPLIES here: this element is
     * a child of the grid container, so even as a fixed box it is aligned
     * within its grid area rather than stretched across the viewport - and
     * `justify-self: start` then shrink-wraps it to the width of its widest
     * link.
     *
     * Measured on a 390px screen before this fix: position:fixed and top/left
     * both 0 as intended, but width 111.5px - a narrow strip down the left
     * edge with the page showing beside it, instead of a full-screen menu.
     * `inset: 0` looks like it should prevent that and does not.
     *
     * Stretch on both axes to override the inherited alignment.
     */
    justify-self: stretch;
    align-self: stretch;

    flex-direction: column;

    /*
     * Two declarations on purpose, not a duplicate.
     *
     * The menu is twice as long since the submenu was added, and a centred
     * flex column that overflows pushes its first items off the TOP of the
     * scroll container, where they cannot be scrolled back to - the classic
     * way a long overlay menu loses its first entry on a landscape phone.
     * `safe center` centres only while everything fits and falls back to
     * start-alignment when it does not. A browser that does not know the
     * keyword ignores the second declaration and keeps plain centring.
     */
    justify-content: center;
    justify-content: safe center;

    /* And it has to be able to scroll at all - 8 items plus the social row
       and the switcher do not fit a 375px-tall phone held sideways. */
    overflow-y: auto;

    gap: var(--space-lg);

    background-color: var(--c-dark);
    padding: var(--gutter);

    opacity: 0;
    visibility: hidden;
    transition: opacity var(--transition), visibility var(--transition);
  }

  .site-nav.is-open { opacity: 1; visibility: visible; }

  .site-nav__list { flex-direction: column; gap: var(--space-lg); }
  /* Descendant selector for the same reason as the desktop rule above. */
  .site-nav__list a { font-size: var(--fs-h4); }

  /*
   * The submenu, unpacked.
   *
   * Everything the desktop panel does is undone here: it is not a hover
   * panel, it is simply more items in the column. That is the whole reason
   * the overlay can hold a menu the header row cannot - it grows downwards,
   * and now scrolls if it has to.
   */
  /*
   * ⚠ Undo the desktop rule's `display: flex`, which is a ROW.
   *
   * That rule exists to give the item the link's true height for positioning
   * the panel. Left alone here it would put the link and its four children
   * side by side across the overlay.
   *
   * ⚠ `align-items: center`, NOT stretch. The overlay's list centres each
   * item, and every other item shrink-wraps to its own text - so a stretched
   * child fills the width of the WIDEST link in the group and its label then
   * sits left inside that box. Measured at 390px: the four submenu labels and
   * their parent all left-aligned at x=164 while Home, Portfolio and Contact
   * were centred on x=225. Centring here makes each link shrink-wrap, which
   * is what the other items were doing all along.
   */
  .site-nav__list .menu-item-has-children {
    flex-direction: column;
    align-items: center;
  }

  .site-nav__list .sub-menu {
    position: static;
    opacity: 1;
    pointer-events: auto;

    display: flex;
    flex-direction: column;
    /* Centred for the same reason as the parent item above - the panel is a
       flex item too, so its own children would otherwise stretch. */
    align-items: center;
    gap: var(--space-md);

    margin: var(--space-md) 0 0;
    padding: 0;
    list-style: none;

    background: none;
    box-shadow: none;
  }

  /* Children a step down from their parent, so the grouping is readable
     without indentation - which would fight the centred column. */
  .site-nav__list .sub-menu a { font-size: var(--fs-body); }

  /* No affordance needed when the items are already on screen. */
  .site-nav__list .menu-item-has-children > a::after { display: none; }

  /* Keep the toggle above the overlay so it can close it. */
  .site-header__toggle { z-index: 95; }
}

@media (max-width: 767px) {

  /* Single column everywhere. */
  .services__list,
  .team__list,
  .form-field__split,
  .contact-form-section__grid { grid-template-columns: 1fr; }

  /* The photo becomes a wide banner above the form rather than a tall column. */
  .contact-form-section__media img { aspect-ratio: 16 / 10; }

  .newsletter__row { flex-direction: column; }
  .newsletter__submit { width: 100%; }

  .newsletter__card { padding: 8% 6%; }

  .site-footer__inner { flex-direction: column; }

  /* No hero height override needed: the hero is content-sized at every width,
     matching the live site. The earlier 80svh rule existed only to tame the
     full-viewport hero that has now been removed. */
}

/* Stop the body scrolling behind the open overlay menu. */
body.menu-open { overflow: hidden; }


/* =========================================================================
   19. Reduced motion and print
   ========================================================================= */

/*
 * Respect the OS-level "reduce motion" setting. For a site whose hero is a
 * 40-second looping video of moving laser beams this matters more than usual:
 * that is exactly the kind of content that triggers vestibular symptoms and,
 * with high-contrast strobing colour, is a migraine risk.
 *
 * site.js also pauses the video and shows the poster instead - CSS alone
 * cannot stop playback.
 */


/* =========================================================================
   SHORT VIEWPORTS - phones held sideways
   =========================================================================

   MUST STAY BELOW the max-width:767px block above. Same specificity, so
   source order decides, and this has to win over BOTH the desktop rules and
   the mobile ones.

   Why both: a landscape phone straddles the 767px breakpoint, and each side
   gets it wrong in its own way. As originally measured:
     844x390 (iPhone 14 landscape) -> above 767px, so DESKTOP rules:
         logo 14vw = 118px, padding 2vw   -> 135px header = 34.6% of screen
     667x375 (iPhone SE landscape) -> below 767px, so MOBILE rules:
         logo capped 70px, but padding is 12vw = 80px -> 150px = 40.0%
   The mobile branch was the worse of the two, and for a different reason: its
   PADDING was width-based, which is enormous on a wide, short screen.

   This block originally fixed both by switching to height-relative units for
   the logo AND the padding. Since 2026-08-17 only the LOGO half is still here:
   the padding is a fraction of --logo-h everywhere, so it follows on its own.
   Result is unchanged - a consistent ~20% of viewport height instead of 35-40%
   (measured after the change: 844x390 -> 70px, 667x375 -> 58px, both identical
   to before).

   The 500px ceiling is deliberately below the ~600px of small laptops, so this
   only catches devices that are genuinely short. There is a discontinuity at
   exactly 500px, which is acceptable: phones do not resize continuously across
   it, and the alternative (folding it into the clamp above) would have forced
   the desktop logo below its 200px live-site value.
   ========================================================================= */
@media (max-height: 500px) {
  :root {
    /* 16vh -> 62px at 390px tall. The 44px floor keeps the wordmark legible;
       the 80px ceiling stops a tall-ish landscape tablet growing it again. */
    --logo-h: clamp(44px, 16vh, 80px);
  }

  /*
   * This block used to need TWO more rules: its own --header-h (calc(logo +
   * 4vh)) and its own `.site-header { padding-block: 2vh }`, which had to be
   * kept numerically in agreement by hand. Both are gone since 2026-08-17.
   *
   * The reason they were needed is worth keeping, because it is the failure
   * this design removes: --header-h does NOT drive the header's own size. It
   * is a DERIVED MIRROR of it, consumed by the hero's padding-block-start and
   * by scroll-padding-top. While the real padding was hardcoded per breakpoint
   * the two could drift, and they did - at 667x375 the mobile rule still
   * applied padding-block:6vw (40px a side, 140px header) while --header-h
   * claimed 75px, so the hero reserved 65px too little and the fixed header
   * sat on top of the "DAM LASERS" title.
   *
   * Now the padding and the mirror are both expressed as a fraction of
   * --logo-h, so overriding --logo-h alone moves all three together and they
   * cannot disagree. Anything this block once fixed, it still fixes.
   */
}


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

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

  .hero__video { display: none; }

  /* Fall back to the poster image as a static background. */
  .hero__media {
    background-image: url('../img/laser-show-hero-poster-1920.jpg');
    background-size: cover;
    background-position: center;
  }
}

@media print {
  .site-header, .site-nav, .hero__media, .form-notice { display: none !important; }
  body { color: #000; background: #fff; }
  .section { padding-block: 1rem; }
}
