/* ==========================================================================
   FAMILY TREE DENTAL — contact.css
   The Contact page's own four sections, and nothing else.
   ---------------------------------------------------------------------------
   Loaded after style.css and system.css. Everything shared — tokens, the nav
   pill, buttons, the eyebrow, the headline mask, the hours list, the
   appointment band, the testimonial split, the clinic panel, the wave — is
   already supplied by those two and by the global components. What is here is
   only:

     01. The hero            the green field and the film beside it
     02. The play button
     03. The contact composition   the panel lifted over the form card
     04. The information panel
     05. The form
     06. The consultation
     07. The map frame
     08. Entry states        this page's own reveal channels
     09. Responsive
     10. Reduced motion

   Entry states are authored in CSS and lifted by `.no-js` — the same contract
   system.css section 04 uses, so a page that never gets its JavaScript is a
   finished page rather than an empty one.

   THE COLOUR RUNS DOWNHILL INTO THE SHARED HALF. The hero is on ivory, the
   contact section warms to pale blue, the consultation holds it and the map
   closes on #e8f2fd — which is the exact colour the appointment band beneath
   it opens with. There is no seam between this page's sections and the
   shared ones, and that is deliberate: change one end and check the other.
   ========================================================================== */


/* ==========================================================================
   01. THE HERO
   ---------------------------------------------------------------------------
   TWO PANELS, EQUAL HEIGHT. The design puts a green field and a film side by
   side; here the film is lifted a little proud of the green as a rounded card
   with its own shadow, so the pair reads as one composed band rather than as
   two halves butted together. The band is full-bleed and sits under the pill,
   so its top padding has to clear the whole of the chrome.
   ========================================================================== */
.ct-hero {
  position: relative;
  isolation: isolate;
  overflow: clip;
  padding-top: calc(var(--util-h) + var(--nav-h) + var(--hero-inset) + var(--hero-pad));
  background: var(--sv-ivory);
}

.ct-hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.06fr);
  align-items: stretch;
  /* THE BAND'S HEIGHT IS SET HERE AND NOWHERE ELSE. The <video> is taken out
     of flow below so it cannot contribute its intrinsic height — a portrait
     clip would otherwise stretch this band past the fold and push the title
     down with it. The row is as tall as this says; the film crops to fit. */
  min-height: clamp(22rem, 48vh, 31rem);
}

/* --- The green field ----------------------------------------------------- */
.ct-hero__panel {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  align-items: center;
  /* The right padding carries the film's overlap as well as its own gutter,
     so the copy never runs under the card that sits on top of it. */
  padding: clamp(2rem, 5vw, 4.5rem) clamp(2.5rem, 6vw, 6rem) clamp(2rem, 5vw, 4.5rem) clamp(1.25rem, 5vw, 5rem);
  /* The practice's green, deepened towards the outside edge so the type has
     a darker ground to sit on than the middle of the panel. */
  background:
    radial-gradient(120% 90% at 84% 12%, rgba(127, 227, 212, .2) 0%, rgba(127, 227, 212, 0) 62%),
    linear-gradient(152deg, #5aad38 0%, #4c9c2e 46%, #3d7f25 100%);
}

/* A single very soft wash. Flat colour across a panel this large reads as a
   swatch; this gives it a surface without giving it a pattern. */
.ct-hero__grain {
  position: absolute;
  inset: -20%;
  z-index: -1;
  background:
    radial-gradient(38% 34% at 22% 28%, rgba(255, 255, 255, .14) 0%, rgba(255, 255, 255, 0) 70%),
    radial-gradient(30% 40% at 78% 76%, rgba(6, 33, 58, .16) 0%, rgba(6, 33, 58, 0) 72%);
  pointer-events: none;
}

/* The practice's own mark, huge and nearly invisible, drifting on the
   pointer. The same device the footer uses, so the two ends of the page
   rhyme. */
.ct-hero__leaf {
  position: absolute;
  left: -5%;
  bottom: -36%;
  z-index: -1;
  width: min(28vw, 20rem);
  color: rgba(255, 255, 255, .085);
  pointer-events: none;
}
.ct-hero__leaf svg { width: 100%; height: auto; }

.ct-hero__copy {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 30rem;
  margin-left: auto;
}

/* On the green, the breadcrumb's ink has to invert. The Our Office page
   states the same three rules for the same reason; when a third page needs
   them they belong in system.css. */
.sv-crumbs--light ol,
.sv-crumbs--light li { color: rgba(255, 255, 255, .72); }
.sv-crumbs--light a { color: rgba(255, 255, 255, .82); }
.sv-crumbs--light a:hover { color: #fff; }
.sv-crumbs--light [aria-current="page"] { color: #fff; }

.ct-hero__title {
  margin-top: clamp(.9rem, 2.5vh, 1.5rem);
  font-family: var(--f-display);
  font-size: clamp(2.6rem, 1rem + 5.6vw, 4.6rem);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.022em;
  color: #fff;
}
/* The accent word is white too — a second colour on a green field is one
   colour too many. It carries the italic and nothing else. */
.ct-hero__title em { font-style: italic; color: rgba(255, 255, 255, .93); }

.ct-hero__tag {
  margin-top: clamp(.6rem, 1.6vh, 1rem);
  font-family: var(--f-display);
  font-style: italic;
  font-size: clamp(1rem, .9rem + .45vw, 1.28rem);
  color: rgba(255, 255, 255, .88);
}

/* --- The film ------------------------------------------------------------ */
.ct-hero__stage {
  position: relative;
  z-index: 1;
  display: flex;
  /* Pulled back over the green so the card overlaps the field it sits on.
     Negative margins are written as a positive clamp times -1 — a clamp with
     three negative values is legal but reads backwards. */
  margin-left: calc(clamp(1.25rem, 3vw, 3.25rem) * -1);
  /* THE THREE INSETS ARE ONE NUMBER. Top, right and bottom come off the same
     clamp, so the card floats by the same amount on every side it is not
     overlapping — an inset that differs by a few pixels per edge is the
     difference between a composed card and a misplaced one. */
  padding: clamp(.75rem, 1.6vw, 1.75rem);
  padding-left: 0;
}

.ct-hero__frame {
  position: relative;
  flex: 1;
  margin: 0;
  overflow: hidden;
  /* It floats on three sides and overlaps on the fourth, so it is drawn as a
     card: one radius, all four corners. */
  border-radius: var(--sv-r-xl);
  background: #06213a;
  box-shadow: 0 30px 70px rgba(11, 45, 82, .22);
}
/* ABSOLUTE, so the clip's own dimensions never drive the band's height — see
   the note on .ct-hero__inner. The frame is sized by the grid row and the
   film covers whatever that turns out to be. */
.ct-hero__video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  will-change: transform;
}
/* A quarter-stop of depth in the corners, so the white play glyph has
   something to sit against whatever the footage is doing. */
.ct-hero__grade {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    radial-gradient(120% 90% at 50% 50%, rgba(6, 33, 58, 0) 42%, rgba(6, 33, 58, .34) 100%);
  pointer-events: none;
}


/* ==========================================================================
   02. THE PLAY BUTTON
   ---------------------------------------------------------------------------
   Authored `hidden` and revealed by contact.js, which removes the video's own
   `controls` in the same breath. So the native controls are what a visitor
   without JavaScript gets — a working player either way, never a decorative
   button that does nothing.

   The Our Office page has its own copy of this control in office.css. The two
   are deliberately not shared yet: promoting them to system.css means one
   class name across two pages' markup and two scripts, and that is a change
   worth making the day a THIRD page needs a player — not before.
   ========================================================================== */
.ct-play {
  position: absolute;
  top: 50%; left: 50%;
  z-index: 2;
  display: grid;
  place-items: center;
  width: clamp(4.5rem, 8vw, 6rem);
  aspect-ratio: 1;
  padding: 0;
  color: #fff;
  /* Dark rather than light: a white scrim disappears against the bright
     ceilings and lit walls this clip is full of, while a dark one holds the
     white glyph at every frame of the footage. */
  background: rgba(6, 33, 58, .42);
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  box-shadow: 0 10px 30px rgba(6, 33, 58, .3);
  -webkit-backdrop-filter: blur(8px) saturate(140%);
  backdrop-filter: blur(8px) saturate(140%);
  transform: translate(-50%, -50%);
  transition: background var(--t-fast) var(--ease-out),
              transform var(--t-med) var(--ease-out);
}
.ct-play:hover { background: rgba(6, 33, 58, .56); transform: translate(-50%, -50%) scale(1.05); }
.ct-play:focus-visible { outline: 2px solid #fff; outline-offset: 4px; }

/* The ring breathes only while the film is paused — once it is playing the
   button is a pause control, and a pulsing pause control is a distraction. */
.ct-play__ring {
  position: absolute;
  inset: -6px;
  border: 1px solid rgba(255, 255, 255, .5);
  border-radius: 50%;
  animation: ct-pulse 3.4s var(--ease-out) infinite;
}
@keyframes ct-pulse {
  0%, 100% { transform: scale(1);    opacity: .55; }
  50%      { transform: scale(1.09); opacity: .18; }
}

/* One element, two glyphs. A triangle drawn with borders becomes two bars by
   swapping the border for a box-shadow — no icon swap, no second element. */
.ct-play__mark {
  width: 0; height: 0;
  margin-left: .28em;
  border-top: .58rem solid transparent;
  border-bottom: .58rem solid transparent;
  border-left: .95rem solid currentColor;
}
.ct-play[aria-pressed="true"] .ct-play__ring { animation: none; opacity: .3; }
.ct-play[aria-pressed="true"] .ct-play__mark {
  width: .28rem; height: 1.1rem;
  margin-left: 0;
  border: 0;
  background: currentColor;
  box-shadow: .48rem 0 0 currentColor;
  transform: translateX(-.24rem);
}

/* Present for a reader, absent for a viewer — the button is a circle. */
.ct-play__label {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}


/* ==========================================================================
   03. THE CONTACT COMPOSITION
   ---------------------------------------------------------------------------
   THE OVERLAP IS THE DESIGN. The information panel lifts off the form card's
   left edge and a hairline is drawn between the two, so what a visitor sees
   is one object with two halves rather than two cards that happen to be near
   each other. The card's left padding carries the overlap, which is why the
   two numbers below are written from the same clamp.
   ========================================================================== */
.ct-connect {
  position: relative;
  isolation: isolate;
  overflow: clip;
  padding-block: clamp(3.5rem, 9vh, 6.5rem) clamp(3rem, 8vh, 5.5rem);
  background: linear-gradient(180deg, var(--sv-ivory) 0%, #f4f9ff 46%, #eef5fd 100%);
}

.ct-connect__wash {
  position: absolute;
  top: -18%;
  right: -12%;
  z-index: -1;
  width: min(60vw, 44rem);
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
              rgba(127, 227, 212, .18) 0%,
              rgba(110, 178, 244, .12) 46%,
              rgba(110, 178, 244, 0) 70%);
  filter: blur(18px);
  pointer-events: none;
}

.ct-connect__inner {
  position: relative;
  width: var(--sv-shell);
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, .82fr) minmax(0, 1.18fr);
  align-items: start;
}

/* THERE IS NO DRAWN CONNECTOR BETWEEN THE TWO HALVES, and that is a decision
   rather than an omission. A hairline run from the panel into the card either
   crosses the form's own labels or has to be positioned against a column edge
   it cannot see — and the overlap, with the panel's shadow falling across the
   card beneath it, already says the two are one object. A second device
   saying the same thing is the visual noise this page is built to avoid. */


/* ==========================================================================
   04. THE INFORMATION PANEL
   ---------------------------------------------------------------------------
   The practice's deep blue, and it sits ABOVE the form card in the stack —
   which is what the negative margin needs to be legible rather than clipped.
   ========================================================================== */
.ct-panel {
  position: relative;
  z-index: 2;
  isolation: isolate;
  overflow: hidden;
  /* Lifted and pushed right, so it hangs over the card's corner. */
  margin-top: clamp(1.25rem, 3vw, 2.75rem);
  margin-right: calc(clamp(1.5rem, 3.4vw, 4rem) * -1);
  padding: clamp(1.6rem, 3vw, 2.6rem) clamp(1.5rem, 2.8vw, 2.4rem);
  border-radius: var(--sv-r-lg);
  color: var(--sv-on-dark);
  background:
    radial-gradient(90% 70% at 82% 6%, rgba(127, 227, 212, .26) 0%, rgba(127, 227, 212, 0) 58%),
    linear-gradient(158deg, #2b8ad8 0%, #1a72d6 34%, #0f4f96 72%, #0e4f5e 100%);
  box-shadow: 0 26px 60px rgba(11, 45, 82, .26);
}

.ct-panel__wash {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(40% 30% at 12% 88%, rgba(255, 255, 255, .16) 0%, rgba(255, 255, 255, 0) 70%);
  pointer-events: none;
}
.ct-panel__leaf {
  position: absolute;
  right: -14%;
  bottom: -18%;
  z-index: -1;
  width: min(60%, 15rem);
  color: rgba(255, 255, 255, .08);
  pointer-events: none;
}
.ct-panel__leaf svg { width: 100%; height: auto; }

.ct-panel__block + .ct-panel__block { margin-top: clamp(1.4rem, 3vw, 2rem); }

/* The hairline between blocks. Drawn rather than bordered so it can stop
   short of both edges and read as a rule instead of as a division. */
.ct-panel__block + .ct-panel__block::before {
  content: "";
  display: block;
  width: 100%;
  height: 1px;
  margin-bottom: clamp(1.4rem, 3vw, 2rem);
  background: linear-gradient(90deg, rgba(255, 255, 255, .34) 0%, rgba(255, 255, 255, .06) 100%);
}

.ct-panel__label {
  font-family: var(--f-ui);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .78);
}
.ct-panel__label + .ct-panel__line,
.ct-panel__label + .sv-hours { margin-top: .75rem; }

.ct-panel__line {
  display: flex;
  align-items: flex-start;
  gap: .65rem;
  font-family: var(--f-sans);
  font-size: clamp(.95rem, .92rem + .18vw, 1.06rem);
  line-height: 1.62;
  color: #fff;
  font-variant-numeric: var(--nums-ui);
}
.ct-panel__icon { flex: none; width: 20px; height: 20px; margin-top: .12em; color: var(--c-mint); }
.ct-panel__icon svg { width: 20px; height: 20px; }

/* The design's own "(Map)", kept word for word and given the affordance the
   word alone does not have. */
.ct-panel__map {
  margin-left: .35rem;
  font-family: var(--f-ui);
  font-size: .84em;
  color: rgba(255, 255, 255, .9);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: .22em;
  text-decoration-color: rgba(255, 255, 255, .45);
  transition: color var(--t-fast) var(--ease-out),
              text-decoration-color var(--t-fast) var(--ease-out);
}
.ct-panel__map:hover { color: var(--c-mint); text-decoration-color: currentColor; }

.ct-panel__phone {
  position: relative;
  font-size: clamp(1.1rem, 1rem + .5vw, 1.4rem);
  font-family: var(--f-ui);
  font-weight: 500;
  letter-spacing: .01em;
  color: #fff;
  transition: color var(--t-fast) var(--ease-out);
}
.ct-panel__phone::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -.18em;
  height: 1px;
  background: var(--c-mint);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--t-med) var(--ease-out);
}
.ct-panel__phone:hover { color: var(--c-mint); }
.ct-panel__phone:hover::after { transform: scaleX(1); }

/* --- The hours, on the dark ground ---------------------------------------
   The markup is the clinic panel's, so site.js marks today's row here too.
   Only the colours are re-stated; nothing about the structure is repeated. */
.ct-hours .sv-hours__row {
  padding: .46rem 0;
  font-size: clamp(.82rem, .8rem + .12vw, .89rem);
  border-bottom-color: rgba(255, 255, 255, .18);
}
.ct-hours .sv-hours__row span:first-child { color: rgba(255, 255, 255, .78); }
.ct-hours .sv-hours__row span:last-child  { color: #fff; }
.ct-hours .sv-hours__row--closed span:last-child { color: rgba(255, 255, 255, .6); }
.ct-hours .sv-hours__row.is-today span:first-child { color: #fff; }
.ct-hours .sv-hours__row.is-today::before { background: var(--c-mint); }


/* ==========================================================================
   05. THE FORM
   ---------------------------------------------------------------------------
   White, quiet, and generous. The card carries the overlap in its left
   padding — the same clamp .ct-panel pulls itself right by — so the two
   numbers cannot drift apart.
   ========================================================================== */
.ct-form-card {
  position: relative;
  z-index: 1;
  padding: clamp(1.9rem, 3.6vw, 3.4rem);
  padding-left: calc(clamp(1.5rem, 3.4vw, 4rem) + clamp(1.9rem, 3.6vw, 3.4rem));
  background: rgba(255, 255, 255, .88);
  border: 1px solid var(--sv-card-brd);
  border-radius: var(--sv-r-lg);
  box-shadow: var(--sh-md);
  -webkit-backdrop-filter: blur(20px) saturate(165%);
  backdrop-filter: blur(20px) saturate(165%);
}

.ct-field + .ct-field { margin-top: clamp(1.5rem, 3vh, 2.1rem); }

.ct-field__label {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .4rem;
  cursor: pointer;
}
.ct-field__name {
  font-family: var(--f-ui);
  font-size: .95rem;
  font-weight: 500;
  color: var(--c-blue-900);
}
/* The practice's own parenthetical. Quieter than the label but never grey on
   grey — it carries a real instruction. */
.ct-field__note {
  font-family: var(--f-ui);
  font-size: .78rem;
  font-weight: 400;
  line-height: 1.45;
  color: var(--c-ink-faint);
}

/* THE BOX IS THE UNDERLINE'S CONTAINER, not a border on the input itself:
   the resting rule and the focus rule are two elements, so one can be drawn
   across the other without either moving. */
.ct-field__box {
  position: relative;
  display: block;
  margin-top: .5rem;
  border-bottom: 1px solid rgba(120, 170, 225, .5);
  transition: border-color var(--t-fast) var(--ease-out);
}
.ct-field__rule {
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--c-blue-500), var(--c-mint));
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--t-med) var(--ease-out);
}

.ct-field__input {
  display: block;
  width: 100%;
  /* 46px of target at the smallest step, which is a comfortable thumb. */
  min-height: 46px;
  padding: .55rem .25rem .6rem;
  font-family: var(--f-ui);
  font-size: 1rem;
  line-height: 1.5;
  color: var(--c-ink);
  background: transparent;
  border: 0;
  border-radius: 0;
  font-variant-numeric: var(--nums-ui);
  transition: background var(--t-fast) var(--ease-out);
}
.ct-field__input:focus { outline: none; }
.ct-field__input::placeholder { color: transparent; }

/* Focus is stated three ways — the rule draws, the resting border darkens and
   the field takes the faintest tint — because one of the three has to survive
   forced colours, and the tint is what makes the ACTIVE field findable when a
   visitor looks back at the form after reading something else. */
.ct-field__box:focus-within { border-bottom-color: var(--c-blue-500); }
.ct-field__box:focus-within .ct-field__rule { transform: scaleX(1); }
.ct-field__box:focus-within .ct-field__input { background: rgba(214, 232, 251, .28); }

.ct-field__input--area {
  min-height: 6.5rem;
  resize: vertical;
  font-family: var(--f-sans);
  line-height: 1.65;
}

/* The error state. The message is the BROWSER'S OWN, in the visitor's own
   language — contact.js copies it out of validationMessage rather than
   writing a second set of English sentences. */
.ct-field.is-invalid .ct-field__box { border-bottom-color: #d7513f; }
.ct-field.is-invalid .ct-field__rule {
  background: #d7513f;
  transform: scaleX(1);
}
.ct-field__err {
  margin-top: .5rem;
  font-family: var(--f-ui);
  font-size: .82rem;
  line-height: 1.45;
  color: #b8402f;
}

/* The spam trap: gone for a person, present for a bot. Not `display: none` —
   some bots skip what is not rendered. */
.ct-form__trap {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

.ct-form__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem 1.25rem;
  margin-top: clamp(1.8rem, 4vh, 2.6rem);
}
.ct-form__submit {
  letter-spacing: .08em;
  text-transform: uppercase;
}
.ct-form__submit[disabled] { opacity: .6; cursor: progress; }

.ct-form__status {
  font-family: var(--f-ui);
  font-size: .86rem;
  line-height: 1.5;
  color: var(--c-ink-faint);
}
.ct-form__status.is-error { color: #b8402f; }

/* --- The confirmation ---------------------------------------------------- */
.ct-form__done { text-align: center; padding-block: clamp(1.5rem, 4vh, 3rem); }
.ct-form__done-mark { display: inline-grid; place-items: center; color: var(--c-blue-500); }
.ct-form__done-mark svg { width: 52px; height: 52px; }
.ct-form__done-title {
  margin-top: .9rem;
  font-family: var(--f-display);
  font-size: clamp(1.5rem, 1.2rem + 1vw, 2rem);
  font-weight: 400;
  color: var(--c-blue-900);
}
.ct-form__done-text {
  max-width: 34ch;
  margin: .6rem auto 0;
  font-size: .98rem;
  line-height: 1.65;
  color: var(--c-ink-soft);
}
.ct-form__done-text a { color: var(--c-blue-700); text-decoration: underline; text-underline-offset: .18em; }
.ct-form__again {
  margin-top: 1.4rem;
  background: none;
  border: 0;
  cursor: pointer;
}


/* ==========================================================================
   06. THE CONSULTATION
   ---------------------------------------------------------------------------
   One centred column in a lot of air. Centred is right for a heading and a
   single paragraph — but the measure is held to ~64ch so the lines never
   become the long centred ribbons that make a page hard to read.
   ========================================================================== */
.ct-consult {
  position: relative;
  padding-block: clamp(3.5rem, 10vh, 7rem) clamp(2rem, 5vh, 3.25rem);
  background: linear-gradient(180deg, #eef5fd 0%, #f4f9ff 100%);
  text-align: center;
}

.ct-consult__inner {
  width: min(54rem, 100% - 2 * var(--gutter));
  margin-inline: auto;
}

/* The drawn rule that stands in for an eyebrow. There is no label in the
   design above this heading and inventing one would be inventing content —
   so the section opens with a mark instead of a word. */
.ct-consult__rule {
  display: block;
  width: 62px;
  height: 2px;
  margin: 0 auto clamp(1.2rem, 3vh, 1.9rem);
  border-radius: 2px;
  background: linear-gradient(90deg, var(--c-mint), var(--c-blue-500));
  transform: scaleX(0);
  transform-origin: 50% 50%;
}

.ct-consult__title {
  font-family: var(--f-display);
  font-size: clamp(1.95rem, 1.1rem + 2.7vw, 3.1rem);
  font-weight: 400;
  line-height: 1.1;
  letter-spacing: -0.018em;
  color: var(--c-blue-700);
}
.ct-consult__title em { font-style: italic; color: var(--c-blue-500); }

.ct-consult__text {
  max-width: 64ch;
  margin: clamp(1.2rem, 3vh, 1.9rem) auto 0;
  font-size: clamp(.98rem, .95rem + .18vw, 1.08rem);
  line-height: 1.78;
  color: var(--c-ink-soft);
}


/* ==========================================================================
   07. THE MAP FRAME
   ---------------------------------------------------------------------------
   The frame is the design; what fills it is data. It closes on #e8f2fd, which
   is the colour the appointment band below opens with — the two sections meet
   with no seam, and that is why the gradient's last stop is not a round
   number somebody liked.
   ========================================================================== */
.ct-map {
  position: relative;
  padding-block: clamp(1.5rem, 4vh, 2.5rem) clamp(3.5rem, 9vh, 6rem);
  background: linear-gradient(180deg, #f4f9ff 0%, #eef5fd 55%, #e8f2fd 100%);
}

.ct-map__inner {
  width: var(--sv-shell);
  margin-inline: auto;
}

.ct-map__frame {
  position: relative;
  margin: 0;
  overflow: hidden;
  /* 21:9 on a desktop is a map you can read a neighbourhood from without it
     taking the whole screen. The ratio is re-set on narrow widths below. */
  aspect-ratio: 21 / 9;
  border: 1px solid var(--sv-card-brd);
  border-radius: var(--sv-r-lg);
  background: linear-gradient(140deg, #e4eefb 0%, #dbe9f7 52%, #e9f3fd 100%);
  box-shadow: 0 14px 34px rgba(11, 45, 82, .1);
  transition: box-shadow var(--t-slow) var(--ease-out);
}
.ct-map__frame:hover { box-shadow: 0 22px 54px rgba(11, 45, 82, .16); }

.ct-map__embed {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  /* A stop of saturation off, so a third party's colours sit inside this
     page's palette instead of shouting over it. Restored on hover, when the
     map is the thing being used rather than the thing being passed. */
  filter: saturate(.82) contrast(1.02);
  transition: filter var(--t-slow) var(--ease-out);
}
.ct-map__frame:hover .ct-map__embed { filter: none; }

/* The holding state, used only when config.php carries no embed. Decoration
   and nothing else: no streets, no pin, nothing that could be mistaken for a
   map of somewhere. */
.ct-map__holding {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: rgba(26, 114, 214, .16);
}
.ct-map__holding svg { width: min(22%, 9rem); height: auto; }
.ct-map__rings {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(closest-side circle at 50% 50%, rgba(255, 255, 255, .5) 98%, transparent 100%) 50% 50% / 26% 46% no-repeat,
    radial-gradient(closest-side circle at 50% 50%, rgba(255, 255, 255, .32) 98%, transparent 100%) 50% 50% / 44% 78% no-repeat,
    radial-gradient(closest-side circle at 50% 50%, rgba(255, 255, 255, .2) 98%, transparent 100%) 50% 50% / 64% 112% no-repeat;
  pointer-events: none;
}


/* ==========================================================================
   08. ENTRY STATES — this page's own reveal channels
   ---------------------------------------------------------------------------
   system.css section 04 already hides [data-sv] and [data-sv-mask] on every
   page. These are the channels that only exist here. Same contract: the start
   state is authored in CSS so nothing flashes, and `.no-js` returns every one
   of them to its finished state.
   ========================================================================== */
[data-ct-mask]  { clip-path: inset(0% 0% 100% 0%); }
[data-ct-map]   { clip-path: inset(0% 0% 100% 0%); }
[data-ct-panel] { clip-path: inset(0% 100% 0% 0%); }
[data-ct-card]  { opacity: 0; }
[data-ct-row]   { opacity: 0; }

.no-js [data-ct-mask],
.no-js [data-ct-map]   { clip-path: none; }
.no-js [data-ct-panel] { clip-path: none; }
.no-js [data-ct-card],
.no-js [data-ct-row]   { opacity: 1; }
.no-js .ct-consult__rule { transform: none; }


/* ==========================================================================
   09. RESPONSIVE
   ---------------------------------------------------------------------------
   Checked at 1440, 1280, 1024, 768, 480, 390 and 360. The two structural
   changes are the hero becoming a stack and the contact composition losing
   its overlap — below 980px the panel and the card are simply two cards, one
   above the other, because an overlap needs width to be an overlap rather
   than a collision.
   ========================================================================== */
@media (max-width: 1180px) {
  .ct-hero__inner    { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .ct-hero__copy     { max-width: 26rem; }
  .ct-connect__inner { grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); }
}

@media (max-width: 980px) {
  /* The hero becomes a stack: the green field, then the film under it. */
  .ct-hero__inner  { grid-template-columns: minmax(0, 1fr); min-height: 0; }
  .ct-hero__copy   { max-width: none; margin-left: 0; }
  .ct-hero__panel  { padding: clamp(2.25rem, 7vw, 3.5rem) clamp(1.25rem, 5vw, 3rem); }
  .ct-hero__leaf   { left: -14%; bottom: -42%; width: min(46vw, 18rem); }
  .ct-hero__stage  { margin-left: 0; padding: 0; }
  .ct-hero__frame  { aspect-ratio: 16 / 10; border-radius: var(--sv-r-lg) var(--sv-r-lg) 0 0; }

  /* ONE COLUMN, INFORMATION FIRST. The markup is already in that order, so
     nothing is reordered here — the overlap is simply given back as a gap. */
  .ct-connect__inner { grid-template-columns: minmax(0, 1fr); gap: clamp(1.5rem, 4vw, 2.5rem); }
  .ct-panel     { margin: 0; }
  .ct-form-card { padding: clamp(1.6rem, 4.5vw, 2.4rem); }
}

@media (max-width: 700px) {
  .ct-map__frame { aspect-ratio: 4 / 3; }
}

@media (max-width: 560px) {
  .ct-hero__title   { font-size: clamp(2.4rem, 1rem + 9vw, 3.4rem); }
  .ct-consult       { padding-block: clamp(2.75rem, 8vh, 4rem) clamp(1.5rem, 4vh, 2.25rem); }
  .ct-consult__text { text-align: left; }

  /* Two blocks of the panel can share a row at this width without either
     losing its measure, which keeps the hours in view beside the number. */
  .ct-panel      { padding: clamp(1.4rem, 5vw, 1.9rem) clamp(1.25rem, 4.5vw, 1.6rem); }
  .ct-form-card  { padding: clamp(1.4rem, 5vw, 1.9rem) clamp(1.25rem, 4.5vw, 1.6rem); }
  .ct-form__foot { flex-direction: column; align-items: stretch; }
  .ct-form__submit { width: 100%; }
  .ct-form__status { text-align: center; }
}

@media (max-width: 400px) {
  .ct-play        { width: 4rem; }
  .ct-map__frame  { aspect-ratio: 1 / 1; }
  .ct-panel__line { font-size: .95rem; }
}


/* ==========================================================================
   10. REDUCED MOTION
   ---------------------------------------------------------------------------
   contact.js returns before building a single timeline when the query
   matches, so what is left here is the motion it does not own: the play
   button's pulse, the map's filter and the hover transitions. The FORM keeps
   its focus transitions — a focus ring that appears instantly is not motion
   anybody asked to be spared.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  [data-ct-mask],
  [data-ct-map]   { clip-path: none; }
  [data-ct-panel] { clip-path: none; }
  [data-ct-card],
  [data-ct-row]   { opacity: 1; }
  .ct-consult__rule { transform: none; }

  .ct-play__ring { animation: none; }
  .ct-hero__video { will-change: auto; }
  .ct-map__embed,
  .ct-map__frame { transition: none; }
  .ct-map__embed { filter: none; }
}
