/*
 * The marketing site's own styles (FLO-516).
 *
 * Every colour here comes from a token. `tokens.css` is generated from
 * `src/app/tokens.css` by `npm run site:tokens` and is not edited — a value
 * that needs changing is changed at the source, so the application and this
 * site cannot disagree about what Flooa's green is.
 *
 * ## Where the design comes from, and where the Brand Book stops
 *
 * There is no demo view for a marketing page, so `AGENTS.md` sends this to
 * *"where the demo has no view, apply the Design System's principles"*.
 *
 * The Brand Book's principles are written about the **interface**: *"Flooan
 * käyttöliittymä perustuu vaaleisiin pintoihin…"*, *"Vältä tarpeetonta
 * korostamista"*. In a working interface decoration costs the user attention
 * mid-task, which is what that rule protects. **A launch page is not the
 * interface**, so the Brand Book keeps its authority over values — colour,
 * contrast, geometry — while the composition is allowed more presence than a
 * portal screen.
 *
 * ## Why the photograph is beside the text and not behind it
 *
 * A full-bleed photograph needs a scrim heavy enough to keep white type
 * legible over whatever happens to be behind the headline — a bright wall,
 * a window. That scrim then flattens the room it is protecting: the image
 * becomes a texture rather than a picture.
 *
 * Splitting the page removes the conflict instead of balancing it. The type
 * sits on a known colour and needs no shadow, and the photograph is shown at
 * full strength because nothing has to be read on top of it.
 *
 * ## The second typeface
 *
 * Cormorant Garamond italic carries the display line. That is a second face
 * in the brand and it is deliberate: the Design System specifies Inter for
 * the product, where one neutral face across tables and forms is the point,
 * and says nothing about a marketing surface. Inter sets every other word.
 *
 * ## The trap that cost the most time
 *
 * **`--flooa-font-*` are sizes, not `font` shorthands.** An early version
 * wrote `font: var(--flooa-font-body, 400 15px/1.5 system-ui)`, which
 * substitutes to `font: 13.5px` — invalid, because `font` requires a family.
 * An invalid *substituted* value does not fall back to the declaration's own
 * fallback and is not dropped either: the property is reset to its initial
 * value. The page rendered in the browser's default serif with no error
 * anywhere. Hence `font-size` and `font-weight` separately, every time.
 */

@import url("./tokens.css");

/*
 * Inter: the files `next/font` downloaded for the application itself, so the
 * marketing site and the portal cannot render in different Inters. Variable
 * faces, so one file per subset covers every weight. Provenance and the
 * subset choice are in README.md.
 */
@font-face {
  font-family: Inter;
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("./fonts/inter-latin.woff2") format("woff2");
  unicode-range: U+00-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: Inter;
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("./fonts/inter-latin-ext.woff2") format("woff2");
  unicode-range: U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329,
    U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F,
    U+A720-A7FF;
}

/* Cormorant Garamond, SIL Open Font License, self-hosted — no third-party
   request, so no visitor IP leaves for a font. */
@font-face {
  font-family: "Cormorant Garamond";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("./fonts/cormorant-italic.woff2") format("woff2");
  unicode-range: U+00-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329,
    U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /*
   * The slot `next/font` fills in the application's layout. Filling it here
   * with the same family name means `--flooa-font-family` resolves exactly as
   * it does in the portal, and the token needs no special case.
   */
  --flooa-font-inter: Inter;

  --site-serif: "Cormorant Garamond", Georgia, "Times New Roman", serif;

  /*
   * ====================================================================
   * THE ONE LINE THAT TURNS THE PHOTOGRAPH ON.
   *
   *   --site-hero: url("./images/hero.jpg");
   *
   * Leave it as `none` and the right half is a flat brand green, which is a
   * finished design rather than a gap — and no file is requested, so there
   * is no 404. The spec the file has to meet is in README.md.
   * ====================================================================
   */
  --site-hero: url("./images/hero.jpg");

  /*
   * The two measurements that are not tokens.
   *
   * The Design System's largest size is `--flooa-font-page-title: 30px`, an
   * application chrome size — a heading above a table, inside a frame, with
   * navigation competing for it. A launch page has none of that around it.
   *
   * Both clamps are **anchored at a token as their floor**, so on a phone
   * they bottom out and the page falls back to the Design System's own scale.
   * The display ceiling is lower than a full-width layout would take: the
   * line now lives in half the page, and `8vw` of the viewport is nearer
   * `16vw` of the column it actually sits in.
   */
  --site-display: clamp(var(--flooa-font-page-title), 5.4vw, 64px);
  --site-lede: clamp(var(--flooa-font-page-intro), 1.6vw, 17px);
}

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

html {
  scroll-behavior: smooth;
}

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

body {
  margin: 0;
  font-family: var(--flooa-font-family);
  font-size: var(--flooa-font-body);
  font-weight: var(--flooa-weight-regular);
  line-height: 1.55;
  color: var(--flooa-ink);
  background: var(--flooa-surface);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/*
 * Mobile first, and the order is deliberate: the photograph sits *above* the
 * words on a phone. It is the thing that says "furniture" before anything is
 * read, and a launch page whose first screen is a paragraph has wasted the
 * only moment it gets.
 *
 * **This was broken and the comment above was describing an intention rather
 * than the code.** The markup puts <main> before the photograph, so the grid
 * gave the words the fixed first row and the photograph everything else — the
 * reverse of what is written here. The text did not fit 38svh, and
 * `.site-stage`'s `overflow: hidden`, which exists for the lemniscate's
 * bleed, silently clipped the top of it: on a 390px phone the logo sat at
 * y -78, entirely above the viewport, with the footer cut off below.
 *
 * Nothing errored and the desktop layout was unaffected, which is why it
 * survived several rounds of review on this page.
 *
 * Two changes fix it. `.site-photo` takes `order: -1` so the photograph is
 * genuinely first on a phone, and the second row is `auto` rather than `1fr`
 * so the words get the height they need. The page may then exceed the
 * viewport and scroll, which is the correct behaviour — a phone screen is not
 * a fixed canvas.
 */
.site-split {
  min-height: 100svh;
  display: grid;
  /*
   * `34svh + 40px` rather than a rounder percentage, because the two are not the
   * same thing. The photograph is cropped from the centre, so the band's
   * height decides how much of the room survives top and bottom — the lamp at
   * one end, the rug at the other. A fixed addition moves both by the same
   * amount on every phone; a percentage gives a tall phone more room than a
   * short one, which is the opposite of what a crop wants.
   */
  grid-template-rows: calc(34svh + 40px) auto;
}

.site-photo {
  order: -1;
  /* The colour is not a "something went wrong" fallback — with --site-hero
     unset it is the design, and it also keeps the panel from flashing white
     while a large photograph decodes. */
  background-color: var(--flooa-green-primary);
  background-image: var(--site-hero);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

.site-stage {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: var(--flooa-space-l);
  padding: var(--flooa-space-2xl) var(--flooa-page-gutter);
  background: var(--flooa-green-primary);
  color: #fff;
}

/*
 * The lemniscate, which is not a decoration someone invented for this page:
 * it is the "oo" of the wordmark, lifted out of `flooa-logo-white.png` at
 * x 188–464 of the glyph band and saved as its own transparent mark. Using
 * the brand's own form rather than a drawn one means it cannot drift from
 * the logo — and it is the one shape in the identity that already says
 * "both sides, continuously", which is what the product connects.
 *
 * **Large, bleeding off the lower left, and these numbers are the owner's
 * eye rather than a rule.** The placement went through three versions and
 * the lesson is in the order of them.
 *
 * It started at `left: -14%; bottom: -18%; width: 115%`, which hid too much
 * of the form to read as a lemniscate. The fix I reached for was to shrink
 * it until it fitted entirely inside the panel — and that solved legibility
 * by spending the scale, which was the part worth keeping. The owner's
 * correction was neither: keep the big cropped mark, move it right and up,
 * take a little off the size. Three small numbers, not a different idea.
 *
 * Worth remembering, because the first instinct was to change the concept
 * when the concept was fine and the coordinates were not.
 *
 * `0.065` is the owner's level, and the contrast it implies was computed
 * rather than eyeballed. White at that alpha lifts the local ground from
 * #0f3f33 only as far as #1f4b40, where white text measures **9.76:1** and
 * the lede at 0.8 alpha measures **6.92:1** — comfortably over 4.5:1, which
 * is what lets the mark pass behind the text rather than being parked in
 * whatever space is left over. That is what pays for the scale.
 *
 * It came down from 0.10 twice, both times because the mark was reading as
 * an object rather than as the surface. The ceiling is set by contrast; the
 * floor is set by whether anyone can see it at all.
 */
.site-stage::before {
  content: "";
  position: absolute;
  left: 24%;
  bottom: -2%;
  width: min(104%, 660px);
  aspect-ratio: 276 / 139;
  /*
   * A few degrees off level. Upright it sits like a logo placed on the
   * surface; tilted it reads as part of the surface, and the tilt also stops
   * the mark's own horizon from echoing the baseline of the type above it.
   */
  rotate: -11deg;
  /*
   * SVG, because the raster was soft and that was arithmetic rather than
   * taste: the mark is 276px wide in the brand PNG and shown here at 660, so
   * every edge was interpolated across 2.4 pixels. There is no vector
   * original, so the outline was traced back out of the alpha channel with
   * potrace — see README.md for the exact command.
   */
  background: url("./images/infinity.svg") no-repeat center / contain;
  opacity: 0.065;
  pointer-events: none;
}

/* Everything in the column sits above the mark. */
.site-stage > * {
  position: relative;
  z-index: 1;
}

@media (min-width: 60rem) {
  .site-split {
    /* The text column is the narrower one. The photograph can carry more
       width than a 38ch paragraph can, and an even split leaves the type
       stranded in the middle of its own half. */
    grid-template-rows: none;
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  }

  /* Back to source order on a wide screen: the words are on the left, where
     the eye starts, and the photograph is what it arrives at. */
  .site-photo {
    order: 0;
  }

  .site-stage {
    padding: var(--flooa-space-2xl) clamp(var(--flooa-space-2xl), 6vw, 88px);
  }
}

.site-logo {
  width: clamp(112px, 13vw, 146px);
  height: auto;
  margin-bottom: var(--flooa-space-s);
  /*
   * The second half of the pair that places this column. An auto margin above
   * the first element and another above the footer split the free space
   * between them, which centres the content block *and* drops the footer to
   * the baseline. `justify-content` cannot do both at once — a single auto
   * margin would win outright and push everything to the top.
   */
  margin-top: auto;
}

/*
 * The status line above the headline. Small, letterspaced, and the only
 * uppercase on the page — it has to read as a label rather than as a second
 * heading competing with the display line.
 */
.site-eyebrow {
  margin: 0;
  font-size: var(--flooa-font-label);
  font-weight: var(--flooa-weight-medium);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  /* Not the brand green any more — on this ground it would sink. White at
     reduced strength keeps the label quiet without losing it. */
  color: rgba(255, 255, 255, 0.62);
}

h1 {
  margin: 0;
  font-family: var(--site-serif);
  font-style: italic;
  font-weight: 400;
  font-size: var(--site-display);
  /* A high-contrast serif at display size needs its leading pulled in and a
     breath of tracking; the token scale is set for 30px and below. */
  line-height: 0.95;
  letter-spacing: 0.005em;
  color: #fff;
  text-wrap: balance;
}

/*
 * The third sentence of the heading. Smaller, upright and in Inter rather
 * than the serif, so it reads as the line that explains the two before it
 * instead of competing with them. Still inside the <h1>: it is one heading,
 * set at two levels, not a heading plus a stray paragraph.
 */
.site-h1-sub {
  display: block;
  margin-top: var(--flooa-space-m);
  font-family: var(--flooa-font-family);
  font-style: normal;
  font-size: clamp(var(--flooa-font-subsection), 1.5vw, 19px);
  font-weight: var(--flooa-weight-regular);
  line-height: 1.45;
  letter-spacing: 0;
  color: rgba(255, 255, 255, 0.84);
  /*
   * Wide enough for the sentence to break at its own joint. At 26ch it split
   * as "A better way to do / furniture business", which cuts the phrase
   * mid-thought; `balance` then evens the two lines instead of leaving a
   * long one over a short one.
   */
  max-width: 34ch;
  text-wrap: balance;
}

.site-rule {
  width: 56px;
  height: 0;
  margin: var(--flooa-space-xs) 0 0;
  border: 0;
  border-top: 1px solid rgba(255, 255, 255, 0.28);
}

.site-lede {
  margin: 0;
  font-size: var(--site-lede);
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.8);
  /*
   * Set to break in three. At 38ch the sentence ran to four lines with a
   * two-word last one; this is wide enough for three even lines and still
   * inside the comfortable measure.
   *
   * `balance` rather than `pretty` because the block is short: balance evens
   * the lines across the whole paragraph, while pretty only protects the
   * last one from being orphaned.
   */
  max-width: 52ch;
  text-wrap: balance;
}

/*
 * The pilot offer. Set apart from the lede by colour and a rule rather than
 * by size: it is the sentence the page exists to deliver, and making it
 * larger would put it in competition with the headline instead of after it.
 */
.site-pilot {
  /*
   * A bigger gap above than the column's own rhythm gives. Everything up to
   * the lede describes the product; this is the ask. The break is what tells
   * the reader the subject changed, and at the shared `gap` it read as a
   * fourth paragraph of the same thought.
   */
  margin: var(--flooa-space-m) 0 0;
  padding-left: var(--flooa-space-l);
  border-left: 2px solid var(--flooa-green-active);
  font-size: var(--flooa-font-body);
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.86);
  max-width: 38ch;
  text-wrap: pretty;
}

.site-pilot strong {
  font-weight: var(--flooa-weight-semibold);
  color: #fff;
}

/* ---- the pilot form, in a dialog --------------------------------------- */

/*
 * The modal is light where the page is dark, and that is the application's
 * own grammar rather than a contrast trick: Flooa's forms sit on
 * `--flooa-surface`, and this is a form. It also makes what you have typed
 * easier to read back, which is the only thing this panel is for.
 */
.site-dialog {
  width: min(32rem, calc(100vw - 2 * var(--flooa-page-gutter)));
  max-height: calc(100svh - 2 * var(--flooa-space-2xl));
  padding: 0;
  border: 1px solid var(--flooa-line);
  border-radius: var(--flooa-radius-lg);
  background: var(--flooa-surface);
  color: var(--flooa-ink);
  box-shadow: var(--flooa-shadow);
  overflow: auto;
}

.site-dialog::backdrop {
  /* The brand's own green, darkened, rather than neutral black: the page
     behind it is green, and a grey veil over it reads as a screenshot. */
  background: rgba(10, 30, 24, 0.55);
  backdrop-filter: blur(2px);
}

.site-form {
  display: flex;
  flex-direction: column;
  gap: var(--flooa-space-m);
  padding: var(--flooa-space-xl);
}

.site-dialog-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--flooa-space-m);
}

/* One heading line, no kicker above it — the house rule for a modal here. */
.site-dialog-head h2 {
  margin: 0;
  font-size: var(--flooa-font-section-title);
  font-weight: var(--flooa-weight-semibold);
  line-height: 1.3;
  color: var(--flooa-ink);
}

.site-dialog-close {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 36px, the Design System's compact control height: small enough not to
     compete with the heading, big enough to hit. */
  width: var(--flooa-control-height-compact);
  height: var(--flooa-control-height-compact);
  margin: -6px -6px 0 0;
  border: 0;
  border-radius: var(--flooa-radius-sm);
  background: transparent;
  color: var(--flooa-muted);
  cursor: pointer;
}

.site-dialog-close:hover {
  background: var(--flooa-canvas);
  color: var(--flooa-ink);
}

.site-dialog-close:focus-visible {
  outline: 2px solid var(--flooa-green-800);
  outline-offset: 2px;
}

.site-dialog-close svg {
  width: 16px;
  height: 16px;
}

.site-dialog-lede {
  margin: 0;
  font-size: var(--flooa-font-body);
  line-height: 1.55;
  color: var(--flooa-muted);
}

.site-row {
  display: grid;
  gap: var(--flooa-space-m);
}

@media (min-width: 30rem) {
  .site-row {
    grid-template-columns: 1fr 1fr;
  }
}

.site-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  /* Without this a grid child refuses to shrink below its input's default
     width, and the two columns overflow their track instead of sharing it. */
  min-width: 0;
}

.site-field label {
  font-size: var(--flooa-font-label);
  font-weight: var(--flooa-weight-medium);
  color: var(--flooa-muted);
}

.site-optional {
  font-weight: var(--flooa-weight-regular);
  color: var(--flooa-muted-2);
}

.site-field input,
.site-field textarea {
  width: 100%;
  min-height: var(--flooa-control-height);
  padding: 10px var(--flooa-space-m);
  border: 1px solid var(--flooa-line-strong);
  border-radius: var(--flooa-radius-sm);
  background: var(--flooa-surface);
  color: var(--flooa-ink);
  /* Inherited rather than assumed: a form control does not take the page's
     font on its own, and leaving it would set the one part of the page a
     visitor types into in the browser's default UI face. */
  font-family: inherit;
  font-size: var(--flooa-font-control);
  line-height: 1.5;
  transition: border-color 140ms ease, box-shadow 140ms ease;
}

.site-field textarea {
  resize: vertical;
}

/*
 * 16px on touch, and it is not a style choice.
 *
 * iOS Safari zooms the page when a field is focused whose font is smaller
 * than 16px, on the reasoning that the text would be too small to read. It
 * then does not zoom back out, so the visitor is left on a magnified page
 * they have to pinch their way out of mid-form.
 *
 * The other fix — `user-scalable=no` in the viewport meta — is the one to
 * avoid: it stops the zoom by taking pinch-zoom away from the whole page,
 * which is somebody's accessibility setting, not ours to switch off.
 *
 * `pointer: coarse` keeps the desktop form at the Design System's control
 * size, where no browser zooms anything.
 */
@media (pointer: coarse) {
  .site-field input,
  .site-field textarea {
    font-size: 16px;
  }
}

.site-field input:hover,
.site-field textarea:hover {
  border-color: var(--flooa-muted-2);
}

.site-field input:focus,
.site-field textarea:focus {
  outline: none;
  border-color: var(--flooa-green-active);
  box-shadow: 0 0 0 3px var(--flooa-green-soft);
}

/*
 * The honeypot, hidden four ways. `display: none` alone is the one a bot
 * checks for, so the field stays in the layout and is simply unreachable:
 * off-canvas, zero-sized, and out of the tab order via the attribute.
 */
.site-trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.site-submit {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--flooa-control-height);
  padding: 0 var(--flooa-space-xl);
  border: 0;
  border-radius: var(--flooa-radius-md);
  background: var(--flooa-green-primary);
  color: var(--flooa-surface);
  font-family: inherit;
  font-size: var(--flooa-font-control);
  font-weight: var(--flooa-weight-medium);
  cursor: pointer;
  transition: background 140ms ease;
}

.site-submit:hover:not(:disabled) {
  background: var(--flooa-green-active);
}

.site-submit:focus-visible {
  outline: 2px solid var(--flooa-green-800);
  outline-offset: 3px;
}

/*
 * In flight it stays legible rather than fading out: the visitor has just
 * handed over their details and needs to see that something is happening,
 * which a ghosted control does not say.
 */
.site-submit:disabled {
  cursor: progress;
  opacity: 0.72;
}

.site-form-status {
  margin: 0;
  min-height: 1.2em;
  font-size: var(--flooa-font-meta);
  line-height: 1.5;
  color: var(--flooa-muted);
}

.site-form-status[data-kind="error"] {
  color: #b4291d;
}

@media (prefers-reduced-motion: reduce) {
  .site-field input,
  .site-field textarea,
  .site-submit {
    transition: none;
  }
}

.site-action {
  display: inline-flex;
  align-items: center;
  gap: var(--flooa-space-s);
  margin-top: var(--flooa-space-s);
  align-self: flex-start;
  border: 0;
  cursor: pointer;
  min-height: var(--flooa-control-height);
  padding: 0 var(--flooa-space-xl);
  border-radius: var(--flooa-radius-md);
  background: var(--flooa-surface);
  color: var(--flooa-green-primary);
  font-size: var(--flooa-font-control);
  font-weight: var(--flooa-weight-medium);
  text-decoration: none;
  transition:
    background 140ms ease,
    transform 140ms ease,
    box-shadow 140ms ease;
}

.site-action:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: var(--flooa-shadow);
}

/*
 * In flight. It stays legible rather than fading out: the visitor has just
 * handed over their details and needs to see that something is happening,
 * which a ghosted control does not say.
 */
.site-action:disabled {
  cursor: progress;
  opacity: 0.72;
  transform: none;
}

.site-action:focus-visible {
  /* Keyboard focus is a Design System responsibility, not a nicety. */
  outline: 2px solid #fff;
  outline-offset: 3px;
}

.site-action svg {
  width: 14px;
  height: 14px;
  flex: none;
}

/*
 * Pinned to the foot of the column rather than trailing the button.
 *
 * `margin-top: auto` eats the free space below the content, which has two
 * effects: the footer sits on the baseline of the panel where a footer
 * belongs, and the block above it is no longer pushed off-centre by having a
 * footer attached to its bottom. Without this the text group reads as
 * floating slightly high with a caption stuck to it.
 */
footer {
  margin-top: auto;
  padding-top: var(--flooa-space-2xl);
  font-size: var(--flooa-font-meta);
  /*
   * 0.55 measured 4.73:1 against this ground — over the 4.5:1 line, but by
   * less than a rounding error at 12px, and the footer carries the only
   * email address on the page. 0.66 puts it at 5.9:1 with no visible cost to
   * the hierarchy.
   */
  color: rgba(255, 255, 255, 0.66);
}

footer p {
  margin: 0;
}

footer a {
  color: inherit;
}

@media (prefers-reduced-motion: reduce) {
  .site-action {
    transition: none;
  }
}

/*
 * The entrance. One sequence on load and nothing after it — the page does not
 * scroll and has nothing to reveal, so anything continuous would be motion
 * for its own sake.
 *
 * Every element animates *from* an offset with `backwards`, so a browser that
 * never runs the animation still shows the finished page rather than an
 * invisible one — the failure mode where a page ships parked at opacity 0.
 */
@media (prefers-reduced-motion: no-preference) {
  .site-stage > * {
    animation: site-rise 700ms cubic-bezier(0.22, 0.61, 0.36, 1) backwards;
  }

  .site-photo {
    animation: site-fade 900ms ease backwards;
  }

  .site-logo {
    animation-delay: 80ms;
  }
  h1 {
    animation-delay: 180ms;
  }
  .site-rule {
    animation-delay: 320ms;
  }
  .site-lede {
    animation-delay: 380ms;
  }
  .site-action {
    animation-delay: 470ms;
  }
  footer {
    animation-delay: 560ms;
  }

  @keyframes site-rise {
    from {
      opacity: 0;
      transform: translateY(16px);
    }
  }

  @keyframes site-fade {
    from {
      opacity: 0;
    }
  }
}
