/* Lykt holding page.

   Nothing here is invented. The tokens are the Today palette from
   Cacao Website/brand/tokens.css, and the control grammar is the Lift and Hold
   system from Cacao Website/brand/ui.css, cut down to the two controls this
   page has: one button and one text field.

   The grammar, unchanged from ui.css:
   1. Radius 0. Nothing here is round.
   2. One edge: 2 px solid Nib (--ui-line), never --line.
   3. One shadow: an L of Nib down and to the left, drawn as the left and
      bottom border of a ::before bigger than the box. Zero blur, ever.
   4. One motion idea per interaction. Hover lifts up and right and grows the
      edge. Press pushes down and left and collapses it. Colour never moves.

   Two layers, never paper on paper: the warm ground and its grain are the back
   layer, every control on top is flat and sharp. */

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

@property --ui-edge { syntax: "<length>"; inherits: true; initial-value: 4px; }

:root {
  /* Painted paper palette, from brand/tokens.css. Warm, 70s, no black. */
  --c-pulp: #F7ECDD;       /* warm cream, the light ground */
  --c-nib: #2E160F;        /* deep cacao. INK ONLY. Never a ground */
  --c-husk: #6B4A3D;       /* soft ink on cream */
  --c-ingot: #4A2618;      /* poured cacao mass */
  --c-mint: #BCD8BE;       /* sage mint ground */
  --c-jungle: #1F5A4A;     /* deep jungle green: the dark ground */
  --c-marigold: #F2B233;
  --c-tangerine: #F0531C;  /* the action colour and nothing else */
  --c-criollo: #B9402B;
  --c-rose: #E98B7B;

  --canvas: var(--c-pulp);
  --ink: var(--c-nib);
  --ink-soft: var(--c-husk);
  --accent: var(--c-tangerine);
  --accent-ink: var(--c-nib);

  --font-loud: "Fraunces", "Cooper Black", Georgia, serif;
  --font-edge: "Big Shoulders Display", "Arial Narrow", "Helvetica Neue", sans-serif;
  --font-text: "Familjen Grotesk", "Helvetica Neue", Arial, sans-serif;

  --t-0: 1rem;
  --t--1: 0.875rem;
  --lead-text: 1.4;
  --measure: 48ch;

  --s-1: 0.25rem; --s-2: 0.5rem; --s-3: 0.75rem; --s-4: 1rem;
  --s-5: 1.5rem;  --s-6: 2rem;   --s-7: 3rem;    --s-8: 4rem;
  --gutter: clamp(1rem, 0.5rem + 3vw, 3rem);

  /* ui.css: Raaka's measured button curve, one overshoot, then it settles. */
  --ui-ease: cubic-bezier(0.175, 0.885, 0.32, 1.275);
  --ui-press: 150ms;
  --ui-line: var(--c-nib);
  --ui-tap: 44px;
  --ui-hit: 4px;
  --ui-bw: 2px;
}

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

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

body {
  margin: 0;
  min-height: 100svh;
  background: var(--canvas);
  color: var(--ink);
  font: 400 var(--t-0)/var(--lead-text) var(--font-text);
  /* The back layer. Two very soft washes of the house colours plus a grain,
     so the cream has weather in it rather than being a flat fill. No image. */
  background-image:
    radial-gradient(120% 70% at 12% -10%, rgba(242, 178, 51, 0.30), rgba(242, 178, 51, 0) 60%),
    radial-gradient(90% 60% at 100% 8%, rgba(233, 139, 123, 0.26), rgba(233, 139, 123, 0) 62%),
    radial-gradient(130% 80% at 50% 118%, rgba(31, 90, 74, 0.20), rgba(31, 90, 74, 0) 64%);
  background-repeat: no-repeat;
  /* These three alphas are checked, not chosen: test/shots.mjs reads the pixel
     actually painted behind every run of text on the page and refuses anything
     under 4.5:1. Raising them is allowed; shipping without re-running that is
     not. The first version of this page put a Criollo kicker over the marigold
     wash and measured 4.38:1 at 390, which is how the rule earned its place. */
}

/* The grain. One tiny SVG turbulence tile, fixed, under everything and inert.
   This is the only texture on the page and it never touches a control. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.07;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23g)'/%3E%3C/svg%3E");
}

.page {
  position: relative;
  z-index: 1;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  padding: var(--s-6) var(--gutter) var(--s-5);
  gap: var(--s-7);
}

.wrap { width: 100%; max-width: 640px; margin-inline: auto; }

/* ------------------------------------------------------------------- BRAND */

.brand { display: block; margin-inline: auto; }
.brand img { display: block; width: clamp(230px, 46vw, 340px); height: auto; }

/* ------------------------------------------------------------------- WORDS */

.middle { flex: 1 1 auto; display: flex; flex-direction: column; justify-content: center; gap: var(--s-6); }

/* On a laptop the words and the signup stand side by side, so the whole page
   is one screen with nothing to scroll for: the thing to press is visible the
   moment the page lands. Below this the column stacks and the page scrolls,
   which on a phone is what everyone expects anyway. */
@media (min-width: 1000px) {
  .middle--split {
    display: grid;
    grid-template-columns: 1.05fr 1fr;
    align-items: center;
    gap: var(--s-8);
    width: 100%;
    max-width: 1080px;
    margin-inline: auto;
  }
  .middle--split .wrap { max-width: none; margin-inline: 0; }
  .middle--split .kicker, .middle--split .h1, .middle--split .lede { text-align: left; }
  .middle--split .lede { margin-left: 0; max-width: 34ch; }
  .middle--split .h1 { font-size: clamp(2.8rem, 1.2rem + 2.6vw, 4rem); }
  /* the mark shares the headline's left edge rather than floating above the
     seam between the two columns */
  .brand { width: 100%; max-width: 1080px; }
  .brand img { margin-inline: 0; width: clamp(200px, 17vw, 250px); }
}

.kicker {
  margin: 0 0 var(--s-4);
  font: 900 var(--t--1)/1.3 var(--font-text);
  letter-spacing: .18em;
  text-transform: uppercase;
  /* Husk, not Criollo. In brand/tokens.css Criollo is a ground (.ground-criollo
     carries Pulp ink), never an ink on cream, and measured over the warm wash
     it came out at 4.38:1 at 390 px. Husk is the house's soft ink on Pulp, it
     measures 6.6:1 here, and the only colour accent on the page stays the one
     Tangerine button, which is what HOUSESTYLE asks for anyway. */
  color: var(--ink-soft);
  text-align: center;
}

.h1 {
  margin: 0;
  font-family: var(--font-edge);
  font-weight: 900;
  font-size: clamp(2.6rem, 1rem + 7vw, 5rem);
  line-height: 0.92;
  letter-spacing: 0.005em;
  text-transform: uppercase;
  text-align: center;
  text-wrap: balance;
}

.lede {
  margin: var(--s-5) auto 0;
  max-width: var(--measure);
  font-family: var(--font-loud);
  font-variation-settings: "SOFT" 40, "WONK" 1;
  font-size: clamp(1.125rem, 0.95rem + 0.7vw, 1.45rem);
  line-height: 1.35;
  text-align: center;
  text-wrap: pretty;
}

/* ------------------------------------------------------------- LYKTEBREV */

/* position + z-index are load bearing, not decoration. Every L below is a
   ::before at z-index -1, and a negative z-index child paints behind the
   nearest stacking context's background. .brev has a Mint face, so without a
   stacking context of its own every L on this panel would vanish under it, and
   the still hit pads with it. ui.css hit this exact wall at .ui-seg. */
.brev {
  position: relative;
  z-index: 0;
  background: var(--c-mint);
  border: 2px solid var(--ui-line);
  padding: var(--s-5);
}
@media (min-width: 560px) { .brev { padding: var(--s-6); } }

.brev h2 {
  margin: 0;
  font: 900 1.375rem/1.1 var(--font-edge);
  letter-spacing: .02em;
  text-transform: uppercase;
}
.brev-p { margin: var(--s-2) 0 var(--s-5); max-width: 46ch; }

.brev-form { display: grid; gap: var(--s-4); }

/* ui.css section 9: the field. A half lift under the pointer, the same L as
   every other control, and the OPPOSITE move while focused, sinking flush so
   the border reads as the one thing that answers typing. The hover trigger
   sits on the wrapper, which never moves, so the hit area cannot buzz. */
/* One deviation from ui.css, and it is a bug fix rather than a restyle: an
   <input> renders no ::before in any browser, so ui.css's L on .ui-field-input
   never paints. The L moves to the wrapper, which is exactly the input's box
   (the label is a sibling, outside it). That also makes it a pinned shadow in
   the Lift and Hold sense: the L stays put and the plate lifts off it. */
.ui-field { --ui-edge: 4px; display: block; position: relative; }
.ui-field::before {
  content: "";
  position: absolute;
  top: 0; right: 0;
  left: calc(-1px - var(--ui-edge));
  bottom: calc(-1px - var(--ui-edge));
  border-left: var(--ui-edge) solid var(--ui-line);
  border-bottom: var(--ui-edge) solid var(--ui-line);
  z-index: -1;
  pointer-events: none;
  transition: --ui-edge var(--ui-press) var(--ui-ease);
}
.ui-field::after { content: ""; position: absolute; inset: 0; z-index: -1; background: transparent; }
@media (hover: hover) and (pointer: fine) {
  .ui-field:hover::after { inset: -2px -2px 0 0; }
  .ui-field:hover { --ui-edge: 8px; }
}
.ui-field:focus-within { --ui-edge: 0px; }
.ui-field-label {
  display: block;
  font: 900 1rem/1.5 var(--font-text);
  letter-spacing: .125em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: var(--s-1);
}
.ui-field-input {
  --ui-edge: 4px;
  position: relative;
  display: block; width: 100%; min-height: var(--ui-tap);
  padding: 10px 14px;
  font: 400 1rem/1.4 var(--font-text);
  background: var(--c-pulp); color: var(--c-nib);
  border: 2px solid var(--ui-line); border-radius: 0;
  transform: translate(0, 0);
  transition: transform var(--ui-press) var(--ui-ease),
              --ui-edge var(--ui-press) var(--ui-ease);
}
@media (hover: hover) and (pointer: fine) {
  .ui-field:hover .ui-field-input:not(:focus) { transform: translate(2px, -2px); }
}
.ui-field-input:focus { transform: translate(-2px, 2px); }
.ui-field-input::placeholder { color: var(--c-husk); }
.ui-field-input:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }

/* The honeypot. Off screen rather than display:none, because some autofillers
   skip hidden fields and some bots read the style. Never focusable. */
.honey {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%);
  white-space: nowrap;
}

/* The consent row. NOT pre-checked: markedsforingsloven needs an active opt in,
   so the box ships empty and the submit is refused without it. */
.consent { display: flex; gap: var(--s-3); align-items: flex-start; }
.consent-box {
  flex: 0 0 auto;
  width: 24px; height: 24px;
  margin: 0;
  accent-color: var(--c-jungle);
}
.consent-box:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }
.consent label { font-size: var(--t--1); line-height: 1.45; }

/* ui.css section 2: the button. Fill is the ground's own --accent, so Tangerine
   with Nib text, and the one action colour on the page is this one. */
.ui-btn {
  --ui-edge: 4px;
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  gap: .6em;
  min-height: var(--ui-tap); min-width: var(--ui-tap);
  padding: 14px 22px;
  font: 900 1rem/1.2 var(--font-text);
  letter-spacing: .125em;
  text-transform: uppercase;
  color: var(--accent-ink);
  background: var(--accent);
  border: 2px solid var(--ui-line);
  border-radius: 0;
  text-decoration: none;
  cursor: pointer;
  transform: translate(0, 0);
  transition: transform var(--ui-press) var(--ui-ease),
              --ui-edge var(--ui-press) var(--ui-ease);
}
.ui-btn--block { display: flex; width: 100%; }
.ui-btn::before {
  content: "";
  position: absolute;
  top: 0; right: 0;
  left: calc(-1px - var(--ui-edge));
  bottom: calc(-1px - var(--ui-edge));
  border-left: var(--ui-edge) solid var(--ui-line);
  border-bottom: var(--ui-edge) solid var(--ui-line);
  z-index: -1;
  pointer-events: none;
}

/* THE STILL HIT PAD (ui.css w8). A transform moves the box for hit testing as
   well as for paint, so a control that lifts on hover buzzes at the edge the
   lift moves away from. This pad stands still and covers the union of the
   resting box and the lifted one. */
.ui-btn::after { content: ""; position: absolute; inset: 0; z-index: -1; background: transparent; }
@media (hover: hover) and (pointer: fine) {
  .ui-btn:hover::after {
    inset: calc(var(--ui-hit) - var(--ui-bw)) calc(var(--ui-hit) - var(--ui-bw))
           calc(-1 * (var(--ui-hit) + var(--ui-bw))) calc(-1 * (var(--ui-hit) + var(--ui-bw)));
  }
  .ui-btn:hover { --ui-edge: 8px; transform: translate(4px, -4px); }
}
.ui-btn:active::after {
  inset: calc(-1 * (var(--ui-hit) + var(--ui-bw))) calc(-1 * (var(--ui-hit) + var(--ui-bw)))
         calc(var(--ui-hit) - var(--ui-bw)) calc(var(--ui-hit) - var(--ui-bw));
}
.ui-btn:active { --ui-edge: 0px; transform: translate(-4px, 4px); }
/* Unlike Raaka, keyboard focus does not look pressed. It stays put and shows
   an authored ring, which their site does not have at all. */
.ui-btn:focus-visible { transform: translate(0, 0); --ui-edge: 4px; outline: 3px solid var(--ink); outline-offset: 3px; }
.ui-btn[disabled] {
  background: var(--c-pulp); color: var(--c-husk); border-color: var(--c-husk);
  cursor: not-allowed; transform: translate(0, 0); --ui-edge: 0px;
}
.ui-btn[disabled]::before { border-color: var(--c-husk); }

/* The status line. It only ever says something after a real answer, so an
   empty one takes no height and no screen reader time. */
.brev-msg { margin: 0; font-size: var(--t--1); line-height: 1.45; font-weight: 700; }
.brev-msg:empty { display: none; }
.brev-msg[data-state="ok"] { color: var(--c-jungle); }
.brev-msg[data-state="feil"] { color: var(--c-criollo); }

.brev-fine { margin: 0; font-size: 0.8125rem; line-height: 1.5; color: var(--c-husk); }
.brev-fine a { color: inherit; }

/* -------------------------------------------------------------------- FOOT */

.foot {
  display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-5);
  align-items: baseline; justify-content: center;
  font-size: var(--t--1);
  color: var(--ink-soft);
}
.foot p { margin: 0; }
.foot a { color: var(--ink); text-decoration-thickness: 2px; text-underline-offset: 3px; }
.foot a:focus-visible { outline: 3px solid var(--ink); outline-offset: 3px; }

/* ------------------------------------------------------------- PROSE PAGES */

.prose { max-width: 68ch; margin-inline: auto; }
.prose h1 {
  margin: 0 0 var(--s-5);
  font: 900 clamp(2rem, 1.2rem + 3vw, 3rem)/0.96 var(--font-edge);
  text-transform: uppercase;
}
.prose h2 {
  margin: var(--s-7) 0 var(--s-3);
  font: 700 1.25rem/1.2 var(--font-loud);
}
.prose p, .prose li { max-width: 68ch; }
.prose ul { padding-left: 1.2em; }
.prose li { margin-bottom: var(--s-2); }
/* Ingot, not Criollo, for the same measured reason as the kicker: Criollo over
   the warm wash came out at 4.46:1 here. A link is told apart by its underline,
   which is the affordance anyway, and now by a ratio that passes. */
.prose a { color: var(--c-ingot); text-underline-offset: 3px; }
.prose .back { display: inline-block; margin-top: var(--s-7); }

.skip {
  position: absolute; left: -9999px;
  background: var(--c-pulp); color: var(--ink);
  border: 2px solid var(--ui-line); padding: var(--s-3) var(--s-4);
  z-index: 10;
}
.skip:focus { left: var(--gutter); top: var(--s-3); }

/* ---------------------------------------------------------- REDUCED MOTION */
/* ui.css: the lift goes, the edge stays. Pressing still answers, nothing
   travels. */
@media (prefers-reduced-motion: reduce) {
  .ui-btn, .ui-field-input, .ui-field::before { transition: none; }
  .ui-btn:hover, .ui-btn:active,
  .ui-field:hover .ui-field-input:not(:focus), .ui-field-input:focus { transform: translate(0, 0); }
}
