/* stopscroll.ing
 *
 * It doesn't suggest, it instructs. Colour is sampled from vitreous enamel signage —
 * flat, saturated, no gradient anywhere. Archivo's width axis does the condensing, the
 * way a real sign is condensed to fit its plate. Each draw repaints the plate a
 * different enamel colour (data-draw on the section), so a new answer is felt without
 * any motion.
 *
 * Each component gets one class on its root and is styled from the inside by element,
 * so the markup carries structure rather than names for it. Loaded after reset.css,
 * which opens the `reset` layer.
 */

@layer reset, tokens, base, layout, components;

/* Archivo, self-hosted. Two subsets: the browser only fetches latin-ext if a glyph in
   that range actually appears, so carrying it costs nothing until someone types one. */
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 400 800;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url("/assets/archivo-latin-591a8b69.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: "Archivo";
  font-style: normal;
  font-weight: 400 800;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url("/assets/archivo-latin-ext-a0aa4f2d.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;
}

@layer tokens {
  :root {
    --chalk: oklch(0.955 0.010 95);
    --ground: light-dark(oklch(0.960 0.005 95), oklch(0.155 0.010 165));
    --ink: light-dark(oklch(0.200 0.020 165), oklch(0.940 0.010 95));
    --ink-soft: light-dark(oklch(0.470 0.020 165), oklch(0.700 0.010 95));
    --hair: light-dark(oklch(0.800 0.012 165), oklch(0.330 0.012 165));

    --enamel-green: oklch(0.400 0.075 165);
    --enamel-red: oklch(0.470 0.180 28);
    --enamel-blue: oklch(0.380 0.095 250);
    --enamel-oxide: oklch(0.465 0.125 55);
    --field: var(--enamel-green);

    --display: "Archivo", system-ui, sans-serif;

    --measure: 54rem;
    --pad: clamp(1.1rem, 4vw, 2.5rem);
  }

  [data-draw="1"] { --field: var(--enamel-red); }
  [data-draw="2"] { --field: var(--enamel-blue); }
  [data-draw="3"] { --field: var(--enamel-oxide); }
}

@layer base {
  body {
    background: var(--ground);
    color: var(--ink);
    font-family: var(--display);
  }

  a { color: inherit; }

  summary { cursor: pointer; }
}

@layer layout {
  header,
  footer,
  .compose,
  .list,
  .auth,
  .policy {
    width: min(100% - (var(--pad) * 2), var(--measure));
    margin-inline: auto;
  }

  header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding-block: 1.25rem;
  }

  main {
    display: grid;
    gap: 2.5rem;
  }

  footer {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    padding-block: 2.5rem 4rem;
    color: var(--ink-soft);
    font-size: 0.875rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
  }
}

@layer components {
  header {
    /* The wordmark is the only anchor that is a direct child; the rest live in the nav. */
    > a {
      font-size: 1.375rem;
      font-weight: 800;
      font-stretch: 70%;
      text-transform: uppercase;
      letter-spacing: 0.02em;
      text-decoration: none;
    }

    nav {
      display: flex;
      gap: 1rem;
      font-size: 0.875rem;
      text-transform: uppercase;
      letter-spacing: 0.08em;
    }

    details {
      position: relative;

      > div {
        position: absolute;
        inset-inline-end: 0;
        z-index: 10;
        display: grid;
        gap: 0.25rem;
        width: max-content;
        padding: 0.5rem;
        background: var(--ground);
        border: 2px solid var(--ink);
      }

      button { padding: 0.5rem; }
    }
  }

  /* Full-bleed plate. Deliberately not centred — a sign is set to its own margin. */
  .answer {
    display: flex;
    flex-direction: column;
    /* Fixed height, so the buttons stay under the thumb as you flick through draws instead
       of walking down the page behind a longer answer. The floor has to clear the tallest
       headline the tiers can produce, plus the line under it on a first visit (507px at
       full size), or the content wins and the plate goes back to changing size per draw. */
    min-height: clamp(32rem, 58svh, 34rem);
    background: var(--field);
    color: var(--chalk);
    padding-block: clamp(1.75rem, 6vw, 4rem);
    /* Bleeds to the viewport edge, but its contents line up with the column below it. */
    padding-inline: max(var(--pad), calc((100% - var(--measure)) / 2));

    /* The phrase leading up to the activity is the qualifier, the activity is the
       destination. Weight and tint carry that, at one width — mixing widths mid-sentence
       read as a mistake rather than a decision. 60% is as dim as the qualifier can go
       and still clear 3:1 on every plate at this size. */
    /* One auto here and one on .actions splits the slack the fixed height leaves in two,
       which centres the headline (and the line under it) however many lines it runs to. */
    h1 {
      margin-block-start: auto;
      font-weight: 400;
      font-stretch: 62%;
      line-height: 0.95;
      letter-spacing: -0.005em;
      text-transform: uppercase;
      text-wrap: balance;
      color: color-mix(in oklab, var(--chalk) 60%, var(--field));
    }

    /* The plate does not grow, so the type gives. Which tier applies is decided by the
       length of the whole headline, in ApplicationHelper#headline_size. */
    &[data-length="short"] h1 { font-size: clamp(2.75rem, 10vw, 6.5rem); }
    &[data-length="medium"] h1 { font-size: clamp(2.25rem, 8vw, 5.5rem); }
    &[data-length="long"] h1 { font-size: clamp(1.75rem, 6vw, 4rem); }

    strong {
      font-weight: 800;
      color: var(--chalk);
    }

    /* Direct children only, so anything added inside a block later opts in rather than
       inheriting this by accident. Not opacity: this is body-size text, so it has to
       hold 4.5:1 on every plate. */
    > p {
      max-width: 44ch;
      margin-block-start: 1.25rem;
      font-size: 1.125rem;
      text-wrap: pretty;
      color: color-mix(in oklab, var(--chalk) 92%, var(--field));
    }

    /* auto, not a fixed margin: the buttons sit on the bottom of the plate whatever the
       headline above them does. */
    .actions {
      display: flex;
      flex-wrap: wrap;
      gap: 0.5rem;
      margin-block-start: auto;
      padding-block-start: 2rem;
    }

    button {
      padding: 0.75rem 1.25rem;
      min-height: 44px;
      font-size: 0.9375rem;
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: 0.06em;
      border: 2px solid var(--chalk);

      &.primary {
        background: var(--chalk);
        color: var(--field);
      }
    }
  }

  footer nav {
    display: flex;
    gap: 1rem;
  }

  .compose h2,
  .list h2,
  .auth h1,
  .policy h2 {
    font-size: 0.875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--ink-soft);
    padding-block-end: 0.5rem;
    border-block-end: 2px solid var(--ink);
    margin-block-end: 0.75rem;
  }

  /* Direct child, so the error list inside the form doesn't pick this up. */
  .compose > p {
    /* Wide enough that the whole thing sits on one line where there is room, rather
       than breaking mid-clause. It still wraps on a narrow screen. */
    max-width: 76ch;
    margin-block-end: 0.75rem;
    font-size: 1.0625rem;
    color: var(--ink-soft);
    text-wrap: pretty;
  }

  .compose .row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
  }

  :is(.compose, .auth) input:not([type="submit"]) {
    flex: 1 1 16rem;
    padding: 0.75rem;
    font-size: 1.0625rem;
    min-height: 44px;
    border: 2px solid var(--hair);

    &:focus-visible { border-color: var(--ink); }
  }

  :is(.compose, .auth) input[type="submit"] {
    padding: 0.75rem 1.25rem;
    min-height: 44px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    background: var(--ink);
    color: var(--ground);
  }

  /* Departure board: one row per line, hairline rules, ruthless alignment. */
  .list {
    li {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 1rem;
      min-height: 3rem;
      border-block-end: 1px solid var(--hair);
      font-size: 1.1875rem;
      font-stretch: 82%;
      text-transform: uppercase;
      letter-spacing: 0.02em;
    }

    /* A glyph rather than a word, so it is sized to be tappable and to sit on the
       optical centre of the row instead of on a text baseline. */
    button {
      display: grid;
      place-items: center;
      width: 44px;
      min-height: 44px;
      font-size: 1.5rem;
      line-height: 1;
      color: var(--ink-soft);

      &:hover, &:focus-visible { color: var(--enamel-red); }
    }
  }

  /* Sign in, sign up, password reset. Same vocabulary as the compose row. */
  .auth {
    padding-block: 1.5rem;

    form {
      display: grid;
      gap: 0.75rem;
      max-width: 26rem;
    }

    .links {
      display: grid;
      gap: 0.5rem;
      margin-block-start: 1.5rem;
      font-size: 0.875rem;
      text-transform: uppercase;
      letter-spacing: 0.08em;
    }
  }

  /* Prose, set narrower than the column it sits in so the lines stay readable. The section
     headings borrow the compose/list vocabulary, so the page reads as the same signage. */
  .policy {
    padding-block: 1.5rem;

    h1 {
      font-size: clamp(2rem, 6vw, 3rem);
      font-weight: 800;
      font-stretch: 70%;
      line-height: 1;
      text-transform: uppercase;
    }

    h2 { margin-block-start: 2.5rem; }

    p, li {
      font-size: 1.0625rem;
      line-height: 1.5;
      text-wrap: pretty;
    }

    p { margin-block-end: 0.75rem; }

    ul {
      display: grid;
      gap: 0.5rem;
      margin-block-end: 1.25rem;
      padding-inline-start: 1.25rem;
      list-style: disc;
    }

    .updated {
      margin-block: 0.5rem 2rem;
      font-size: 0.875rem;
      text-transform: uppercase;
      letter-spacing: 0.08em;
      color: var(--ink-soft);
    }
  }

  /* Validation errors, beside the field that produced them. */
  .errors {
    padding: 0.75rem;
    margin-block-end: 0.75rem;
    background: var(--enamel-red);
    color: var(--chalk);
    font-size: 0.9375rem;
  }

  /* Fixed to the bottom corner: never scrolled past, and out of the way of the answer
     plate and the field they were typing in when it fired. */
  .flashes {
    position: fixed;
    inset-block-end: var(--pad);
    inset-inline-end: var(--pad);
    z-index: 50;
    display: grid;
    gap: 0.5rem;
    justify-items: end;
    /* Shrink-wraps its message, but never crowds a narrow screen edge to edge. */
    max-width: min(24rem, calc(100% - (var(--pad) * 2)));
    pointer-events: none;

    p {
      padding: 0.6rem 0.85rem;
      font-size: 0.8125rem;
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: 0.08em;
      text-wrap: balance;
      background: var(--ink);
      color: var(--ground);
    }

    /* The role already says which kind of message this is, so there is no second
       class to keep in step with it. Notices dismiss themselves; alerts stay, because
       auto-hiding something that went wrong is a bad trade. Under reduced motion the
       reset collapses the duration but not the delay, so it jump-cuts instead. */
    [role="status"] { animation: flash-dismiss 300ms ease-in 6s forwards; }

    [role="alert"] {
      background: var(--enamel-red);
      color: var(--chalk);
    }
  }

  @keyframes flash-dismiss {
    to { opacity: 0; visibility: hidden; }
  }
}
