@layer reset, tokens, theme, base, components, utilities;

@layer tokens {
  /*
   * Each fallback is a local font scaled to its face's widths and line metrics, measured over the page's text
   * with fontTools, so the words barely move when the face arrives. The faces are in the page's head, which
   * knows their hashed URLs.
   */
  @font-face {
    font-family: "Fredericka fallback";
    src: local("Georgia");
    size-adjust: 107.44%;
    ascent-override: 90.98%;
    descent-override: 23.09%;
    line-gap-override: 0%;
  }

  @font-face {
    font-family: "Shantell fallback";
    src: local("Arial");
    size-adjust: 110.55%;
    ascent-override: 92.27%;
    descent-override: 28.95%;
    line-gap-override: 0%;
  }

  @font-face {
    font-family: "Caveat fallback";
    src: local("Arial");
    size-adjust: 76.43%;
    ascent-override: 125.6%;
    descent-override: 39.25%;
    line-gap-override: 0%;
  }

  :root {
    --day: #f4efe4;
    --night: #12161f;
    /* Lower is warmer. The deeper glows sit a little warmer than the pale ones, as a bulb's halo does. */
    --lamp-h: 85;
    --lamplit: oklch(90% 0.069 var(--lamp-h));
    --ink: #2b241e;
    --accent: #cc4a2b;
    --highlighter: #f6d468;
    --lit-text: oklch(91.7% 0.081 calc(var(--lamp-h) - 3));
    --lit-glow: oklch(84.4% 0.13 calc(var(--lamp-h) - 12) / 0.3);
    --lit-wash: oklch(87.6% 0.113 calc(var(--lamp-h) - 7) / 0.1);
    --font-title: "Fredericka the Great", "Fredericka fallback", Georgia, serif;
    --font-body: "Shantell Sans", "Shantell fallback", "Trebuchet MS", system-ui, sans-serif;
    --font-note: "Caveat", "Caveat fallback", "Shantell Sans", cursive;
    /*
     * good-css.com's "One fluid scale for type and space": step n runs from size-narrow x ratio-narrow^n rem
     * at --narrow rem (360px) to size-wide x ratio-wide^n rem at --wide rem (1280px), so text size moves the
     * ends. Plain numbers, since Firefox can't divide a length by a length.
     */
    --narrow: 22.5;
    --wide: 80;
    --size-narrow: 1;
    --size-wide: 1.125;
    --ratio-narrow: 1.25;
    --ratio-wide: 1.333;
    --fluid: clamp(0rem, (100vw - var(--narrow) * 1rem) / (var(--wide) - var(--narrow)), 1rem);
    --at-narrow: calc(var(--size-narrow) * (1rem - var(--fluid)));
    --at-wide: calc(var(--size-wide) * var(--fluid));
    --step--1: calc(var(--at-narrow) * pow(var(--ratio-narrow), -1) + var(--at-wide) * pow(var(--ratio-wide), -1));
    --step-0: calc(var(--at-narrow) + var(--at-wide));
    --step-1: calc(var(--at-narrow) * var(--ratio-narrow) + var(--at-wide) * var(--ratio-wide));
    --step-4: calc(var(--at-narrow) * pow(var(--ratio-narrow), 4) + var(--at-wide) * pow(var(--ratio-wide), 4));
    --step-5: calc(var(--at-narrow) * pow(var(--ratio-narrow), 5) + var(--at-wide) * pow(var(--ratio-wide), 5));
    --gutter: clamp(16px, 5vw, 72px);
    --ease-pop: cubic-bezier(0.34, 1.56, 0.64, 1);
    --ease-write: cubic-bezier(0.5, 0.1, 0.4, 1);
    --ease-swipe: cubic-bezier(0.6, 0.1, 0.3, 1);
    --t-theme: 0.6s;
    --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .32 0 0 0 0 .27 0 0 0 0 .2 0 0 0 1.6 -.72'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");
    --grain-night: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .8 0 0 0 0 .82 0 0 0 0 .9 0 0 0 1.4 -.66'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");

    @media (prefers-reduced-motion: reduce) {
      --t-theme: 0s;
    }
  }
}

@layer theme {
  :root {
    color-scheme: light;
    --background-color-page: light-dark(var(--day), var(--night));
    --text-color: light-dark(var(--ink), oklch(96.3% 0.02 var(--lamp-h)));
    --text-color-muted: light-dark(#615a4f, oklch(80.8% 0.028 var(--lamp-h)));
    --focus-outline-color: light-dark(#1e5bc6, oklch(88.5% 0.118 calc(var(--lamp-h) - 2)));
    --font-family: var(--font-body);
  }

  :root[data-theme="dark"] {
    color-scheme: dark;
  }
}

@layer base {
  /* -y only: overscroll-behavior-x would also stop a horizontal swipe going back. */
  html {
    overscroll-behavior-y: none;

    @media (any-pointer: coarse) {
      overscroll-behavior-y: auto;
    }
  }

  /* the folio keeps its own gutters */
  body {
    padding-inline: 0;
  }
}

@layer components {
  .folio {
    position: relative;
    isolation: isolate;
    display: flex;
    flex-direction: column;
    min-block-size: 100svh;
    overflow: clip;
    font-size: var(--step-0);

    &::after {
      content: "";
      position: fixed;
      inset: 0;
      z-index: 30;
      background-image: var(--grain);
      opacity: 0.5;
      pointer-events: none;
    }
  }

  :root[data-theme="dark"] .folio::after {
    background-image: var(--grain-night);
    opacity: 0.3;
  }

  .folio__main {
    flex: 1;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-content: center;
    gap: clamp(0.25rem, 2vw, 1.5rem);
    inline-size: min(100%, 110rem);
    margin-inline: auto;
    padding-block: clamp(28px, 6vw, 56px) 0;
    padding-inline: var(--gutter);
  }

  /*
   * Here, not on the folio: a colour animating there is inherited by the whole drawing, restyling it each
   * frame.
   */
  .intro {
    position: relative;
    z-index: 10;
    container-type: inline-size;
    transition: color var(--t-theme);
  }

  /*
   * "One Lamp" is 4.963em of Fredericka's advances (fontTools; the subset has no kerning) plus 8 x 0.01em of
   * letter spacing: 5.04em, so 19cqi leaves 4% to spare.
   */
  .intro__title {
    font-family: var(--font-title);
    font-weight: 400;
    font-size: min(var(--step-5), 19cqi);
    line-height: 0.95;
    letter-spacing: 0.01em;
    text-wrap: balance;
  }

  .intro__line {
    margin-block-start: 0.8em;
    max-inline-size: 15em;
    font-size: var(--step-1);
    line-height: 1.42;
    text-wrap: pretty;
  }

  .intro__contact {
    margin-block-start: 1.2em;
  }

  .lit {
    position: relative;
    white-space: nowrap;
    color: light-dark(currentColor, var(--lit-text));
    text-shadow: 0 0 0.35em light-dark(transparent, var(--lit-glow));

    &::before {
      content: "";
      position: absolute;
      z-index: -1;
      inset-block: 0.18em 0.04em;
      inset-inline: -0.24em -0.22em;
      background: var(--highlighter);
      opacity: 0.75;
      mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 40' preserveAspectRatio='none'%3E%3Cpath d='M6 9C40 4 120 7 194 3c3 9 2 22 3 32-60 3-128 1-190 3C3 28 4 18 6 9z'/%3E%3C/svg%3E") no-repeat center / 100% 100%;
      rotate: -1.2deg;
      transform-origin: 0 50%;
    }
  }

  :root[data-theme="dark"] .lit::before {
    inset-block: -0.3em;
    inset-inline: -0.45em;
    background: radial-gradient(closest-side, var(--lit-wash), transparent);
    mask: none;
    opacity: 1;
    rotate: 0deg;
  }

  .mail {
    position: relative;
    display: inline-flex;
    max-inline-size: 100%;
    align-items: center;
    gap: 0.55em;
    padding-block: 0.25em;
    border-radius: 4px;
    font-size: var(--step-0);
    font-weight: 500;
    text-decoration: none;
  }

  .mail__icon {
    flex: none;
    inline-size: 1.6em;
    block-size: auto;
    overflow: visible;

    path {
      fill: none;
      stroke: currentColor;
      stroke-width: 1.6;
      stroke-linecap: round;
      stroke-linejoin: round;
    }
  }

  /* A flex item's minimum is its min-content: zero lets the address shrink, and break-word wrap it. */
  .mail__text {
    position: relative;
    min-inline-size: 0;
    overflow-wrap: break-word;

    &::after {
      content: "";
      position: absolute;
      inset-inline: -0.1em;
      inset-block-end: -0.32em;
      block-size: 0.42em;
      background: var(--accent);
      mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 14' preserveAspectRatio='none'%3E%3Cpath d='M3 9C48 5 92 10 140 7s110-3 156-1' fill='none' stroke='%23000' stroke-width='3.4' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat center / 100% 100%;
    }
  }

  .scene {
    position: relative;
    margin-inline: calc(-1 * var(--gutter));
  }

  /* the words sit above the drawing here, so the wall above the light stays dark */
  .scene__lamp {
    --dark-above: 1;
  }

  /* One cell, sized by the longer caption, so switching moves nothing. */
  .scene__caption {
    position: relative;
    z-index: 10;
    display: grid;
    justify-content: center;
    margin-block-start: 0.1rem;
    color: var(--text-color-muted);
    font-family: var(--font-note);
    font-weight: 500;
    font-size: var(--step-1);
  }

  :is(.scene__day, .scene__night) {
    grid-area: 1 / 1;
  }

  .scene__night {
    visibility: hidden;
  }

  :root[data-theme="dark"] {
    .scene__day {
      visibility: hidden;
    }

    .scene__night {
      visibility: visible;
    }
  }

  .foot {
    position: relative;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    inline-size: min(100%, 110rem);
    margin-inline: auto;
    padding-block: 1rem 1.25rem;
    padding-inline: var(--gutter);
    color: var(--text-color-muted);
    font-size: var(--step--1);
  }

  .sound {
    display: grid;
    place-items: center;
    inline-size: 2.75rem;
    block-size: 2.75rem;
    border: 0;
    border-radius: 50%;
    background: none;
    color: inherit;
    cursor: pointer;
    touch-action: manipulation;

    @media (hover: hover) {
      &:hover {
        color: var(--text-color);
      }
    }
  }

  .sound__icon {
    inline-size: 1.5rem;
    overflow: visible;

    path {
      fill: none;
      stroke: currentColor;
      stroke-width: 1.6;
      stroke-linecap: round;
      stroke-linejoin: round;
    }
  }

  .sound__on {
    .sound[aria-pressed="false"] & {
      display: none;
    }
  }

  .sound__off {
    .sound[aria-pressed="true"] & {
      display: none;
    }
  }

  @media (min-width: 600px) {
    .scene {
      inline-size: min(100%, 64rem);
      margin-inline: 0;
      justify-self: center;
    }
  }

  @media (min-width: 60rem) and (orientation: landscape), (orientation: landscape) and (max-height: 32rem) {
    .folio__main {
      grid-template-columns: minmax(0, 5fr) minmax(0, 9fr);
      align-items: center;
      column-gap: clamp(1rem, 3vw, 3rem);
      padding-block-start: 2rem;
    }

    .intro {
      padding-block-end: 4rem;
    }

    .scene {
      inline-size: min(100%, calc((100svh - 6rem) * 1000 / 700));
    }

    .scene__lamp {
      --dark-above: 0;
    }
  }

  @media (orientation: landscape) and (max-height: 32rem) {
    .folio__main {
      grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
      padding-block-start: 1rem;
    }

    .intro {
      padding-block-end: 0;
    }

    .intro__title {
      font-size: min(var(--step-4), 19cqi);
    }

    :is(.intro__line, .mail) {
      font-size: var(--step-0);
    }

    .scene {
      inline-size: min(100%, calc(100svh - 3rem));
    }
  }

  @media (prefers-reduced-motion: no-preference) {
    .intro__title {
      animation: write 1.3s var(--ease-write) 0.15s both;
    }

    /*
     * A wipe, not a fade: Chrome counts text painted at opacity 0 as the largest paint only when it repaints,
     * which here was the end of the draw-in (LCP 2.6s at night).
     */
    .intro__line {
      animation: write-down 0.9s var(--ease-write) 1s backwards;
    }

    .intro__contact {
      animation: write-down 0.5s var(--ease-write) 1.65s backwards;
    }

    :is(.scene__caption, .foot) {
      animation: appear 0.8s ease 1.8s both;
    }

    :root[data-drawing] .lit::before {
      animation: swipe 0.5s var(--ease-swipe) 1.9s both;
    }

    :root[data-theme="dark"] {
      &[data-drawing] .lit {
        text-shadow: none;

        &::before {
          animation: none;
          opacity: 0;
        }
      }

      &:not([data-drawing]) .lit::before {
        animation: lit 1s linear both;
      }
    }

    @media (hover: hover) {
      .mail:hover {
        .mail__text::after {
          animation: swipe 0.55s var(--ease-swipe);
        }

        .mail__icon {
          animation: envelope 0.5s ease;
        }
      }
    }
  }

  @keyframes write {
    from {
      clip-path: inset(-30% 100% -30% -5%);
    }

    to {
      clip-path: inset(-30% -5% -30% -5%);
    }
  }

  @keyframes write-down {
    from {
      clip-path: inset(-1em -1em calc(100% + 1em) -1em);
      transform: translateY(0.4em);
    }

    to {
      clip-path: inset(-1em);
    }
  }

  @keyframes appear {
    from {
      opacity: 0;
    }
  }

  @keyframes swipe {
    from {
      clip-path: inset(0 100% 0 0);
    }
  }

  /* the same flicker as lamp.css's bulb-on */
  @keyframes lit {
    0%,
    22% {
      opacity: 0;
    }

    24% {
      opacity: 1;
    }

    29%,
    37% {
      opacity: 0.12;
    }

    40% {
      opacity: 1;
    }

    47% {
      opacity: 0.55;
    }

    53%,
    100% {
      opacity: 1;
    }
  }

  @keyframes envelope {
    30% {
      transform: rotate(-8deg) translateY(-2px);
    }

    65% {
      transform: rotate(5deg);
    }
  }
}
