/* ==========================================================================
   Enkefalos Solutions

   Two colours, two faces, one easing curve. The system is documented in
   docs/charter/glasswing-visual-language.md; the page in docs/landing-page-plan.md.

   Green is ink, rule and mark, plus exactly one full-bleed band. It is never
   body copy: #004235 and the body neutral #3d3d3a sit within 5% of each other
   in luminance, so at reading size green does not read as green.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Faces. Self-hosted under SIL OFL 1.1; licences sit beside the files.

   The serif ships as two pinned optical cuts rather than one variable file.
   Source Serif 4 carries an optical-size axis, and that axis alone is 46 KB
   of a 77 KB file: sixty per cent of the weight for a property with two
   useful settings on a page that has two type sizes. Pinning it twice costs
   50 KB for both cuts and keeps the effect, which is a display face with
   finer joins and a text face with sturdier ones.

   The mono carries every label, eyebrow, figure and legal line. It is most of
   why the page reads as a company that builds software rather than one that
   writes about it.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Serif Display";
  src: url("/assets/fonts/serif-display.woff2") format("woff2");
  font-weight: 250 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Serif Text";
  src: url("/assets/fonts/serif-text.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Mono";
  src: url("/assets/fonts/mono.woff2") format("woff2");
  font-weight: 400 500;
  font-style: normal;
  font-display: swap;
}

/* --------------------------------------------------------------------------
   Tokens. Roles, never values, so a theme is one attribute rather than a
   rebuild. The page ships ivory; slate is defined because a palette that
   cannot invert is not really a set of tokens.
   -------------------------------------------------------------------------- */
:root {
  /* The neutral ramp. Warm at both ends: neither white nor black appears. */
  --slate-050: #faf9f5;
  --slate-150: #f0eee6;
  --slate-200: #e8e6dc;
  --slate-300: #d1cfc5;
  --slate-500: #87867f;
  --slate-550: #73726c;
  --slate-600: #5e5d59;
  --slate-700: #3d3d3a;
  --slate-950: #141413;

  /* The Enkefalos accent, read from the logo file rather than sampled. */
  --green: #004235;
  --green-600: #0a5646;

  --display: "Serif Display", Charter, Georgia, serif;
  --read: "Serif Text", Charter, Georgia, serif;
  --label: "Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Space. Nothing off this scale. */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-8: 48px;
  --sp-9: 64px;
  --sp-11: 96px;
  --sp-12: 128px;

  /* Measure and frame. 633px is roughly 72 characters at 17px serif. */
  --measure-read: 633px;
  --measure-title: 615px;
  --container: 1400px;
  --gutter: clamp(24px, 10vw, 160px);

  --radius-control: 8px;

  /* One curve, everywhere. */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --dur-tint: 0.2s;
  --dur-reveal: 0.7s;
  --dur-enter: 0.8s;

  /* Ivory, the shipped theme. */
  --foreground-primary: var(--green);
  --foreground-body: var(--slate-700);
  --foreground-quiet: var(--slate-600);
  --background-primary: var(--slate-050);
  --background-secondary: var(--slate-150);
  --background-tertiary: var(--slate-200);
  --border-control: var(--green);
  --border-rule: var(--slate-300);
  /* The hairline that shows through a 1px grid gap, so a grid of panels
     reads as ruled rather than as boxed. */
  --hairline: rgba(0, 66, 53, 0.16);
  --hairline-on-green: rgba(250, 249, 245, 0.16);
}

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

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--background-primary);
  color: var(--foreground-body);
  font-family: var(--read);
  font-size: 17px;
  line-height: 26px;
  font-variant-numeric: proportional-nums lining-nums;
  -webkit-font-smoothing: antialiased;
}

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

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

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

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip {
  /* Clipped rather than pushed to -9999px. A far-offscreen element still
     participates in layout, which is how a skip link ends up widening the
     scrollable area of the page it exists to help. */
  position: absolute;
  top: 0;
  left: 0;
  z-index: 30;
  clip-path: inset(50%);
  background: var(--green);
  color: var(--slate-050);
  font-family: var(--label);
  font-size: 13px;
  padding: var(--sp-3) var(--sp-4);
  text-decoration: none;
}
.skip:focus {
  clip-path: none;
  left: var(--sp-3);
  top: var(--sp-3);
}

/* --------------------------------------------------------------------------
   Bands. Each is full bleed; each holds its content inside the same gutter;
   neighbours are separated by ground tone rather than by a border.
   -------------------------------------------------------------------------- */
.band {
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}
.band--ivory {
  background: var(--background-primary);
}
.band--ivory150 {
  background: var(--background-secondary);
}
.band--ivory200 {
  background: var(--background-tertiary);
}
.band--green {
  background: var(--green);
}
.band__in {
  max-width: var(--container);
  margin: 0 auto;
}

/* The recurring mark: one accent rule, identical everywhere it appears. */
.accent {
  width: 40px;
  height: 3px;
  margin: 0 auto var(--sp-5);
  background: var(--green);
  border: 0;
}
.accent--left {
  margin-left: 0;
  margin-right: auto;
}
.band--green .accent {
  background: var(--slate-050);
}

/* The eyebrow. Every section is named here, in mono capitals, and the heading
   beneath it is left free to be a sentence. This is where the capitals live
   now: the public copy style asks for headings set in capitals by the
   stylesheet, and moving that treatment onto the eyebrow satisfies it while
   letting the heading be read rather than announced. */
.eyebrow {
  font-family: var(--label);
  font-size: 11px;
  font-weight: 500;
  line-height: 16px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--foreground-primary);
  margin: 0 0 var(--sp-5);
}
.band--green .eyebrow {
  color: var(--slate-300);
}

.section-title {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(31px, 4.4vw, 48px);
  line-height: 1.08;
  /* Negative tracking at display size. A light serif set loose at 48px reads
     as a set of letters; pulled in, it reads as a phrase. */
  letter-spacing: -0.02em;
  text-wrap: balance;
  color: var(--foreground-primary);
  max-width: 20ch;
  margin: 0 0 var(--sp-9);
}
.band--green .section-title {
  color: var(--slate-050);
}

/* The mono key beside a block of prose. The label sits in its own narrow
   column at wide viewports and stacks above at narrow ones. */
.split {
  display: grid;
  grid-template-columns: minmax(0, 8rem) minmax(0, 1fr);
  gap: clamp(1.25rem, 4vw, 3.5rem);
}
@media (max-width: 820px) {
  .split {
    grid-template-columns: 1fr;
    gap: var(--sp-4);
  }
}
.split__key {
  font-family: var(--label);
  font-size: 11px;
  font-weight: 500;
  line-height: 16px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--foreground-primary);
  padding-top: 0.5rem;
}

/* --------------------------------------------------------------------------
   1. Masthead. No navigation: there is one page, and a bar linking nowhere
   is furniture. Static rather than sticky, which costs a phone nothing.
   -------------------------------------------------------------------------- */
.masthead {
  padding: var(--sp-5) var(--gutter);
  background: var(--background-primary);
}
.masthead__in {
  max-width: var(--container);
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}
.masthead__mark {
  /* The mark alone. The lockup carries a wordmark reading "Enkefalos Holdings
     Ltd", which is the legal entity and belongs in the colophon, not beside
     the trading name. alt is empty because the name is set in type alongside. */
  height: 36px;
  width: auto;
}
.masthead__name {
  font-family: var(--label);
  font-weight: 500;
  font-size: 11px;
  line-height: 20px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--foreground-primary);
}

/* --------------------------------------------------------------------------
   2. The hero photograph. Full bleed, no type over it, so no scrim is needed
   and no contrast ratio has to be re-checked when the picture changes.
   -------------------------------------------------------------------------- */
.heroshot {
  margin: 0;
}
.heroshot img {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  object-position: 50% 45%;
}
@media (min-width: 620px) {
  .heroshot img {
    aspect-ratio: 16 / 9;
  }
}
@media (min-width: 992px) {
  .heroshot img {
    aspect-ratio: 2.5 / 1;
  }
}

/* --------------------------------------------------------------------------
   3. The hero title
   -------------------------------------------------------------------------- */
.hero {
  padding-top: var(--sp-9);
  padding-bottom: var(--sp-9);
  text-align: center;
}
@media (min-width: 992px) {
  .hero {
    padding-top: var(--sp-11);
    padding-bottom: var(--sp-11);
  }
}
.hero__title {
  font-family: var(--display);
  font-weight: 250;
  font-size: clamp(40px, 7.4vw, 84px);
  line-height: 1.01;
  letter-spacing: -0.028em;
  color: var(--foreground-primary);
  text-wrap: balance;
  margin-bottom: var(--sp-5);
}
.hero__sub {
  font-family: var(--read);
  font-weight: 400;
  font-size: 17px;
  line-height: 26px;
  color: var(--foreground-body);
  max-width: 34em;
  margin: 0 auto;
  text-wrap: balance;
}
@media (min-width: 992px) {
  .hero__sub {
    font-size: 20px;
    line-height: 30px;
  }
}

/* --------------------------------------------------------------------------
   4. The statement
   -------------------------------------------------------------------------- */
.statement {
  padding-top: var(--sp-11);
  padding-bottom: var(--sp-11);
  text-align: center;
}
.statement p {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(23px, 3.6vw, 38px);
  line-height: 1.2;
  letter-spacing: -0.018em;
  color: var(--foreground-primary);
  max-width: 20em;
  margin: 0 auto;
  text-wrap: balance;
}

/* --------------------------------------------------------------------------
   5. What we do. One panel at a time, advanced by the reader.
   Nothing autoplays: content on a timer takes the page away mid-sentence and
   then needs a pause control to give it back.
   -------------------------------------------------------------------------- */
.services {
  padding-top: var(--sp-11);
  padding-bottom: var(--sp-11);
}
.carousel {
  display: flex;
  flex-direction: column;
  gap: var(--sp-5);
  max-width: 900px;
  margin: 0 auto;
}
.carousel__controls {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-3);
}
.carousel__count {
  font-family: var(--label);
  font-size: 11px;
  line-height: 16.5px;
  letter-spacing: 0.1em;
  color: var(--foreground-quiet);
  font-variant-numeric: tabular-nums;
  margin-right: var(--sp-2);
}
.carousel__count [data-carousel-index] {
  color: var(--foreground-primary);
  font-weight: 600;
}
.carousel__btn {
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: transparent;
  border: 1px solid var(--border-control);
  border-radius: var(--radius-control);
  color: var(--foreground-primary);
  cursor: pointer;
  transition: background var(--dur-tint) var(--ease);
}
.carousel__btn:hover:not(:disabled) {
  background: var(--background-secondary);
}
.carousel__btn:active:not(:disabled) {
  background: var(--background-tertiary);
}
.carousel__btn:disabled {
  opacity: 0.4;
  cursor: default;
}
.carousel__stage {
  display: grid;
}
.panel {
  grid-area: 1 / 1;
}
.panel img {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  margin-bottom: var(--sp-5);
}
.panel h3 {
  font-family: var(--label);
  font-weight: 500;
  font-size: 11px;
  line-height: 20px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--foreground-primary);
  margin-bottom: var(--sp-3);
}
.panel p {
  font-family: var(--read);
  font-size: 17px;
  line-height: 26px;
  color: var(--foreground-body);
  max-width: var(--measure-read);
}
/* Without script every panel is visible and stacked. Only under .js does one hide. */
.js .panel + .panel {
  margin-top: 0;
}
.js .panel {
  opacity: 0;
}
.js .panel[data-active="true"] {
  opacity: 1;
  transform: translateX(0);
  transition:
    opacity var(--dur-enter) var(--ease),
    transform var(--dur-enter) var(--ease);
}
.js .panel[data-enter="prev"] {
  transform: translateX(-24px);
}
.js .panel[data-enter="next"] {
  transform: translateX(24px);
}

/* --------------------------------------------------------------------------
   6. How we work. The numerals are the only ones on the page that carry
   information rather than decorating a list: these are stages, in order.
   -------------------------------------------------------------------------- */
.approach {
  padding-top: var(--sp-11);
  padding-bottom: var(--sp-11);
}
/* A hairline grid: the 1px gap is the rule. Cheaper than a border on every
   cell, and it never doubles at the joins the way borders do. */
.approach__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1px;
  background: var(--hairline-on-green);
  border: 1px solid var(--hairline-on-green);
  grid-template-columns: 1fr;
}
@media (min-width: 760px) {
  .approach__grid {
    grid-template-columns: repeat(3, 1fr);
  }
}
.approach__step {
  background: var(--green);
  padding: clamp(1.5rem, 3vw, 2.2rem) clamp(1.4rem, 2.4vw, 1.9rem) clamp(1.8rem, 3.4vw, 2.4rem);
  transition: background var(--dur-tint) var(--ease);
}
.approach__step:hover {
  background: var(--green-600);
}
.approach__n {
  display: block;
  font-family: var(--display);
  font-weight: 250;
  font-size: clamp(30px, 3.4vw, 40px);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--slate-050);
  margin-bottom: var(--sp-4);
}
.approach .accent {
  background: var(--slate-050);
}
.approach__step h3 {
  font-family: var(--label);
  font-weight: 500;
  font-size: 11px;
  line-height: 20px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--slate-300);
  margin-bottom: var(--sp-3);
}
.approach__step p {
  font-family: var(--read);
  font-size: 16px;
  line-height: 25px;
  color: var(--slate-200);
}

/* --------------------------------------------------------------------------
   7. The green band. The only area use of the brand colour.
   -------------------------------------------------------------------------- */
.closing {
  padding-top: var(--sp-12);
  padding-bottom: var(--sp-12);
  text-align: center;
}
.closing p {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(23px, 3.6vw, 38px);
  line-height: 1.22;
  letter-spacing: -0.018em;
  color: var(--slate-050);
  max-width: 24em;
  margin: 0 auto;
  text-wrap: balance;
}

/* --------------------------------------------------------------------------
   8. Enquiries. Inverted from the band above: ivory ground, green ink.
   -------------------------------------------------------------------------- */
.enquiries {
  padding-top: var(--sp-11);
  padding-bottom: var(--sp-11);
  text-align: center;
}
.enquiries__label {
  font-family: var(--label);
  font-weight: 500;
  font-size: 11px;
  line-height: 18px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--foreground-quiet);
  margin-bottom: var(--sp-3);
}
.enquiries__mail {
  font-family: var(--display);
  font-weight: 300;
  font-size: clamp(21px, 3vw, 32px);
  line-height: 1.3;
  letter-spacing: -0.02em;
  color: var(--foreground-primary);
  /* Marked always, not on hover: every heading on this ground is also green,
     so colour cannot be what distinguishes the link. A ruled underline sits
     further from the baseline than text-decoration and does not collide with
     the descenders in the address. */
  display: inline-block;
  text-decoration: none;
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 0.28em;
  transition: border-color var(--dur-tint) var(--ease);
}
.enquiries__mail:hover {
  border-bottom-color: var(--green);
}

/* --------------------------------------------------------------------------
   9. Colophon. The only place the registered entity is named.
   -------------------------------------------------------------------------- */
.colophon {
  padding-top: var(--sp-8);
  padding-bottom: var(--sp-8);
}
.colophon__in {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5) var(--sp-8);
}
.colophon__mark {
  height: 36px;
  width: auto;
}
.colophon p {
  font-family: var(--label);
  font-size: 12px;
  line-height: 21px;
  letter-spacing: 0.04em;
  color: var(--foreground-quiet);
  max-width: 46ch;
}

/* --------------------------------------------------------------------------
   Scroll progress. A 2px rule across the top, driven by an animation timeline
   where the browser supports one and by script where it does not. It is the
   only ornament on the page and it earns its place by saying how much is left.
   -------------------------------------------------------------------------- */
.progress {
  position: fixed;
  inset: 0 0 auto 0;
  height: 2px;
  z-index: 20;
  background: var(--green);
  transform: scaleX(0);
  transform-origin: 0 50%;
}
@supports (animation-timeline: scroll()) {
  .progress {
    animation: progress-grow linear both;
    animation-timeline: scroll(root block);
  }
}
@keyframes progress-grow {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}
@media (prefers-reduced-motion: reduce) {
  /* The bar reports position rather than decorating it, so it stays; only the
     smoothing goes. */
  .progress {
    transition: none;
  }
}

/* --------------------------------------------------------------------------
   Motion. Two ideas, one curve, and nothing else moves.
   The hidden state lives here rather than in the markup, so a page whose
   script never ran is a page without animation instead of a blank one.
   -------------------------------------------------------------------------- */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(16px);
}
.js [data-reveal].is-revealed {
  opacity: 1;
  transform: translateY(0);
  transition:
    opacity var(--dur-reveal) var(--ease),
    transform var(--dur-reveal) var(--ease);
}

@media (prefers-reduced-motion: reduce) {
  /* The hidden state is removed outright rather than shortened. An element
     that still travels 16px quickly is still an element that travels. */
  .js [data-reveal],
  .js [data-reveal].is-revealed {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .js .panel,
  .js .panel[data-active="true"] {
    transform: none !important;
    transition: none !important;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

@media print {
  .carousel__controls,
  .progress,
  .skip {
    display: none;
  }
  .js .panel,
  .js [data-reveal] {
    opacity: 1;
    transform: none;
  }
}
