/* ===========================================================================
   Vivid Memoir Hub

   One stylesheet, hand written, no build step. The token block below is the
   contents of docs/brand-tokens/ flattened into one file: those files use
   @import, and four extra round trips on a phone on a train is a worse deal
   than keeping two copies in step. If a token changes there, change it here.

   Everything after the tokens is DESIGN.md section 2 for the marketing
   components the hub reuses, and section 6 for the seven the hub adds.

   Rules that shape almost every line of this file, from DESIGN.md section 4:
   radius is 2px and never above 4px (the floating dock is the one stated
   exception at 15px), there are no shadows, nothing is below 11px,
   Instrument Sans is never running text, --hair is decorative and
   --line-ctrl is anything operable, and 44px is the floor on a tap target.
   =========================================================================== */

/* --- Tokens. docs/brand-tokens/colors.css ------------------------------- */
:root {
  --ink: #0E110D;               /* page ground */
  --band: #141914;              /* every other section */
  --card: #1D231C;              /* cards, fields, image containers */

  --bone: #EFEAE0;              /* primary text */
  --bone-dim: #969C8F;          /* secondary text */

  --gold: #DBAE5B;              /* brand: logo, numerals, eyebrows, links */
  --gold-deep: #BA944D;         /* button borders, icons, bullets */

  --hair: rgba(219, 174, 91, .15);      /* decorative rules only */
  --line-ctrl: rgba(219, 174, 91, .58); /* borders on anything operable */

  /* Not in the marketing system. The hub has form errors, and colour alone
     is never the message: the icon and the text carry it too. */
  --alarm: #E09A78;

  /* docs/brand-tokens/typography.css */
  --display: "Bodoni Moda", Didot, "Times New Roman", serif;
  --body: "Newsreader", Georgia, serif;
  --util: "Instrument Sans", "Segoe UI", system-ui, sans-serif;

  --d1: clamp(40px, 8.2vw, 94px);
  --d2: clamp(28px, 4.2vw, 52px);
  --d3: clamp(21px, 2.3vw, 29px);
  --fs-body: 17px;
  --fs-label: 11px;
  --fs-label-lg: 12px;

  --lh-display: 1.06;
  --lh-body: 1.78;
  --lh-prose: 1.85;
  --lh-label: 1.45;

  --track-display: -.02em;
  --track-label: .18em;

  /* docs/brand-tokens/spacing.css */
  --maxw: 1240px;
  --pad: 20px;
  --sec: 72px;

  /* docs/brand-tokens/effects.css */
  --r: 2px;
  --r-max: 4px;
  --r-circle: 50%;

  --slow: cubic-bezier(.16, .72, .24, 1);   /* the only easing in the system */
  --dur-micro: .45s;
  --dur-reveal: .8s;
  --dur-image: 1.1s;

  --focus-ring: 2px solid var(--gold);
  --focus-offset: 3px;

  color-scheme: dark;
}

@media (min-width: 480px) { :root { --pad: 24px; --sec: 88px; } }
@media (min-width: 760px) { :root { --pad: 48px; --sec: 120px; --fs-body: 18px; } }
@media (max-width: 759px) { :root { --dur-reveal: .45s; } }

/* --- Base. docs/brand-tokens/base.css ----------------------------------- */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

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

body {
  font-family: var(--body);
  font-weight: 300;
  background: var(--ink);
  color: var(--bone);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
}

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

a { color: var(--gold); text-underline-offset: 3px; text-decoration-thickness: 1px; }

button { font: inherit; cursor: pointer; color: inherit; }

::selection { background: var(--gold); color: var(--ink); }

/* Newsreader sets oldstyle figures by default, so numerals sit lower and
   narrower than the text around them. Anything counted or aligned needs
   this. */
.nums, input, td, time { font-variant-numeric: lining-nums tabular-nums; }

:where(a, button, input, textarea, select, summary, label):focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
  border-radius: var(--r);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

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

.skip {
  position: absolute;
  left: -9999px;
  z-index: 70;
}
.skip:focus {
  left: var(--pad);
  top: 10px;
  padding: 12px 18px;
  background: var(--card);
  border: 1px solid var(--line-ctrl);
  border-radius: var(--r);
}

/* --- Type roles. Three, never mixed. ------------------------------------ */
h1, h2, h3, .d1, .d2, .d3 {
  font-family: var(--display);
  font-weight: 400;
  letter-spacing: var(--track-display);
  line-height: var(--lh-display);
}

h1, .d1 { font-size: clamp(30px, 5vw, 46px); }
h2, .d2 { font-size: var(--d3); line-height: 1.18; }
h3, .d3 { font-size: var(--d3); line-height: 1.18; }

/* Italic on one word of a heading at most. Rule 5. */
h1 em, h2 em, h3 em, .d1 em, .d2 em, .d3 em { font-style: italic; }
h1 em { color: var(--gold); }

.names {
  font-family: var(--display);
  font-weight: 400;
  font-style: italic;
  font-size: clamp(34px, 9vw, 52px);
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
}

p { margin-bottom: 16px; }
p:last-child { margin-bottom: 0; }

.prose { color: var(--bone-dim); line-height: var(--lh-prose); }
.dim { color: var(--bone-dim); }

/* Labels only. Never running text. Rule 1. */
.eyebrow, .label, .cap, label {
  display: block;
  font-family: var(--util);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--track-label);
  line-height: var(--lh-label);
  text-transform: uppercase;
  color: var(--bone-dim);
}

.eyebrow { color: var(--gold); letter-spacing: .26em; }
.cap-gold { color: var(--gold); }
.cap-deep { color: var(--gold-deep); letter-spacing: .16em; }

.num { font-variant-numeric: lining-nums tabular-nums; }

/* A margin note in the photographer's own hand: the coverage caution, the
   album caution. Display italic in the deeper gold, never a boxed callout. */
.note-i {
  font-family: var(--display);
  font-style: italic;
  font-size: 16px;
  line-height: 1.5;
  color: var(--gold-deep);
}

/* --- The plain page. Sign-in, backoffice, anything without the tab bar. -- */
.wrap { max-width: 42rem; margin: 0 auto; padding: 40px var(--pad) 80px; }

header.page {
  padding-bottom: 24px;
  border-bottom: 1px solid var(--hair);
  margin-bottom: 32px;
}
header.page .eyebrow { margin-bottom: 6px; }

section { margin-bottom: 40px; }

.card {
  background: var(--card);
  border: 1px solid var(--hair);
  border-radius: var(--r);
  padding: 18px 20px;
  margin-bottom: 12px;
}

footer.page {
  margin-top: 56px;
  padding-top: 24px;
  border-top: 1px solid var(--hair);
  display: flex;
  gap: 16px;
  align-items: center;
  flex-wrap: wrap;
}

ul.plain { list-style: none; }

.notice {
  padding: 14px 16px;
  margin-bottom: 24px;
  background: var(--band);
  border-left: 2px solid var(--gold-deep);
  border-radius: var(--r);
}
.notice code { font-size: 14px; word-break: break-all; }

/* ===========================================================================
   DESIGN.md section 6: the seven components the hub adds
   =========================================================================== */

/* --- The shell -----------------------------------------------------------
   The dock floats over the content, so the content pads for it rather than
   the page reserving a strip. 120px: the bar is 58px plus 14px to the edge,
   and without the rest the last row can never scroll clear of it. */
.shell { min-height: 100vh; min-height: 100dvh; }

.view { --view-pad: var(--pad); padding-inline: var(--view-pad); padding-bottom: 120px; }
.view > *:first-child { padding-top: 26px; }

.hero {
  height: 200px;
  background-color: #1C1E1B;
  background-size: cover;
  background-position: center;
}

/* The "replace me" hatch from the marketing system, for a hero with no
   photograph behind it yet. It is a signal, not a brand texture. */
.hatch {
  background-image: repeating-linear-gradient(
    45deg,
    rgba(239, 234, 224, .09) 0 1px,
    transparent 1px 8px
  );
}

/* --- Fanebar (.tabs), floating -------------------------------------------
   Surface --card, one step lighter than --ink, which is what lifts it: rule
   4 forbids the shadow a floating bar usually gets. The frame is --hair
   rather than --line-ctrl, because what you operate is the tab, not the
   container it sits in.

   Radius 15px is the single stated exception to rule 3, and it applies to
   this bar and to nothing else.

   One element, two shapes. Above 1024px the same four items become the left
   rail further down this file rather than a second navigation component
   with the same links in it. */
.tabs {
  position: fixed;
  z-index: 15;
  left: 50%;
  transform: translateX(-50%);
  width: calc(100% - 32px);
  max-width: 340px;
  bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  background: var(--card);
  border: 1px solid var(--hair);
  border-radius: 15px;
  padding: 5px 4px;
}

.tabs a {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 48px;
  padding: 7px 0 6px;
  text-decoration: none;
  color: var(--bone-dim);
  font-family: var(--util);
  font-size: var(--fs-label);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .04em;   /* .16em does not fit MOODBOARD in a 72px column */
  line-height: 1.4;
  white-space: nowrap;     /* "Jeres dag" carries a space and would break */
  position: relative;
  transition: color var(--dur-micro) var(--slow);
}

/* Active is colour plus a 16px rule above the icon. Never colour alone, and
   never a fill: a fill gave the active tab three times the weight of the
   other three. */
.tabs a.on { color: var(--gold); }
.tabs a.on::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 16px;
  height: 1px;
  background: var(--gold);
}

.tabs svg { display: block; width: 21px; height: 21px; }

/* The wordmark belongs to the rail. On a phone the four columns are already
   at their limit and there is nowhere to put it. */
.tabs .mark { display: none; }

@media (hover: none) { .tabs a:active { opacity: .65; } }

/* --- Sideskinne, from 1024px ---------------------------------------------
   The same four items in the same order with the same labels and icons. A
   floating bar at the bottom of a wide screen is a phone habit on the wrong
   device, and the thumb it was drawn for is not there. */
@media (min-width: 1024px) {
  .shell { display: flex; }

  .tabs {
    position: sticky;
    top: 0;
    left: auto;
    bottom: auto;
    transform: none;
    align-self: flex-start;
    display: flex;
    flex-direction: column;
    gap: 2px;
    width: 210px;
    max-width: none;
    height: 100vh;
    height: 100dvh;
    flex: none;
    background: transparent;
    border: 0;
    border-right: 1px solid var(--hair);
    border-radius: 0;
    padding: 34px 0;
  }

  .tabs .mark {
    display: block;
    font-family: var(--display);
    font-style: italic;
    font-size: 21px;
    color: var(--gold);
    padding: 0 26px 30px;
  }

  .tabs a {
    flex-direction: row;
    justify-content: flex-start;
    gap: 14px;
    padding: 0 26px;
    letter-spacing: var(--track-label);
    border-left: 2px solid transparent;
  }

  /* The rail marks the active item on its left edge, so the dock's rule
     above the icon has no work to do here. */
  .tabs a.on { border-left-color: var(--gold); }
  .tabs a.on::before { content: none; }

  .tabs svg { width: 18px; height: 18px; flex: none; }

  .shell > .main { flex: 1; min-width: 0; display: flex; }
  .main > .column { flex: 1; min-width: 0; }

  /* A tall column beside the content, not a band across the top. Every
     gallery cover is 4:5, and a wide short crop ruins a standing
     photograph. It stays put while the column beside it scrolls. */
  .hero {
    width: 38%;
    max-width: 460px;
    flex: none;
    height: 100vh;
    height: 100dvh;
    position: sticky;
    top: 0;
  }

  .view { --view-pad: 56px; padding: 44px 56px 80px; max-width: 640px; }
  .view > *:first-child { padding-top: 0; }
}

/* --- Underskærm-header (.sub-head) ---------------------------------------
   Hjem pushes to sub-screens rather than unfolding. A back arrow alone is
   not a clear target, so the label sits beside it above 380px. */
.sub-head {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 6px var(--pad) 0;
}

.back {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 44px;
  padding-right: 6px;
  color: var(--gold);
  text-decoration: none;
  font-family: var(--util);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: .2em;
  text-transform: uppercase;
}
.back svg { width: 15px; height: 15px; flex: none; }

@media (max-width: 379px) {
  .back span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .back { padding-inline: 6px; min-width: 44px; }
}

/* --- Listerække (.row) ---------------------------------------------------
   Rows sit on the ground, not in cards. Cards inside cards read as admin
   software, and that single mistake is what made the first draft of the hub
   look cheap. */
.rows > * { border-top: 1px solid var(--hair); }
.rows > *:last-child { border-bottom: 1px solid var(--hair); }

.row {
  display: flex;
  gap: 16px;
  align-items: baseline;
  padding: 20px 0;
  min-height: 44px;
  transition: background var(--dur-micro) var(--slow);
}

/* The whole row is the target, not just the text in it. No chevrons: the
   source system has none, and an arrow on every row is an app habit. */
a.row, .row > a.row-link {
  text-decoration: none;
  color: inherit;
  display: flex;
  width: 100%;
  gap: 16px;
  align-items: baseline;
}

.row .t {
  font-family: var(--util);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: .16em;
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--gold);
  flex: none;
  width: 52px;
  line-height: 1.4;
}

/* Blocks, not inline. These are spans rather than paragraphs because the
   whole row is often wrapped in a link, and a stack of stacked lines is what
   the row is: a title with what is under it. */
.row .rt { display: block; font-family: var(--body); font-weight: 500; font-size: 17px; line-height: 1.35; margin: 0; }
.row .rs { display: block; color: var(--bone-dim); font-size: 15px; line-height: 1.6; margin: 4px 0 0; }
.row .rn { display: block; color: var(--bone-dim); font-size: 15px; line-height: 1.6; margin: 8px 0 0; }
.row .rb { display: block; flex: 1; min-width: 0; }
.row .cnt { margin-left: auto; flex: none; }

.row-actions { display: flex; gap: 4px; align-items: flex-start; flex: none; }

/* A row that both opens to edit and carries a link out of the product: an
   address to Maps, a handle to Instagram. Spec 2.3 and 2.4 both want that
   link one tap away on the row itself.

   The link cannot sit inside the <summary>, because tapping it would follow
   the link and toggle the disclosure at the same time. So it sits outside
   and is placed over the row, which keeps it a real link that works with
   JavaScript off and gets its own place in the tab order. */
.row-holder { position: relative; }
.row-holder .row { padding-right: 48px; }
.row-holder.has-note .row { padding-right: 92px; }
.row-aside {
  position: absolute;
  top: 12px;
  right: 0;
}

/* The note, folded away behind its own mark at the right of the row. A gate
   code matters enormously on the morning and not at all while a couple reads
   down the day, and a note on every row pushed the addresses apart. */
.note-fold > summary { list-style: none; cursor: pointer; }
.note-fold > summary::-webkit-details-marker { display: none; }
.note-fold > summary::marker { content: ""; }

.note-fold > summary { position: absolute; top: 12px; right: 44px; }
.note-fold[open] > summary .icon-btn { color: var(--gold); }
.note-fold .rn { padding: 0 48px 18px 68px; margin: -6px 0 0; }

/* An amount against a label, on the agreement. */
.row.money { justify-content: space-between; }
.row.total { border-top: 1px solid var(--line-ctrl); }

@media (hover: none) {
  .row:active { background: rgba(219, 174, 91, .05); }
}

/* --- Button (.btn) -------------------------------------------------------
   The wipe fill is the marketing system's, and it is behind a hover query
   as rule 6 requires. A phone gets the tint on :active instead. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 48px;
  padding: 15px 30px;
  border: 1px solid var(--gold-deep);
  border-radius: var(--r);
  background: transparent;
  font-family: var(--util);
  font-size: var(--fs-label-lg);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .2em;
  color: var(--gold);
  text-decoration: none;
  position: relative;
  overflow: hidden;
  cursor: pointer;
}

.btn > * { position: relative; z-index: 1; }
.btn svg { width: 15px; height: 15px; flex: none; }

.btn::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--gold);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .55s var(--slow);
}

.btn.wide { display: flex; width: 100%; }
.btn.sm { min-height: 44px; padding: 12px 20px; font-size: var(--fs-label); letter-spacing: .16em; }
.btn.plain { border-color: var(--line-ctrl); color: var(--bone); }
.btn.quiet { border-color: var(--hair); color: var(--bone-dim); }

@media (hover: hover) and (pointer: fine) {
  .btn:hover { color: var(--ink); border-color: var(--gold); }
  .btn:hover::before { transform: scaleX(1); }
  .btn.quiet:hover { color: var(--ink); }
}

@media (hover: none) {
  .btn:active { background: rgba(219, 174, 91, .16); }
}

/* A control that is only an icon still owes 44px. */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  flex: none;
  border: 0;
  background: transparent;
  color: var(--gold);
  border-radius: var(--r);
  cursor: pointer;
}
.icon-btn svg { width: 19px; height: 19px; }
.icon-btn.quiet { color: var(--bone-dim); }

/* An inline text link that reads as a control: Kort, Hent, Slet. */
.inline-link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 44px;
  font-family: var(--util);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--gold);
  text-decoration: none;
  background: none;
  border: 0;
  padding: 0;
}
.inline-link svg { width: 13px; height: 13px; flex: none; }
.inline-link.quiet { color: var(--bone-dim); }

/* --- Fremdriftslinje (.prog) ---------------------------------------------
   The line names what is unfinished, because a bare count says the couple
   is not finished without saying what to do. Gone entirely once all three
   are filled in. */
.prog { height: 2px; background: var(--hair); border-radius: var(--r); overflow: hidden; }
.prog i { display: block; height: 100%; background: var(--gold); border-radius: var(--r); }

.prog-block { margin-top: 32px; }
.prog-block .prog { margin-top: 11px; }
.prog-chips {
  margin-top: 16px;
  display: flex;
  align-items: center;
  gap: 11px;
  flex-wrap: wrap;
}

/* --- Tilstands-chip (.chip) ----------------------------------------------
   Shown only on a section that is missing something. What you press is a
   pill, what you look at is nearly square: this one is pressed, so it keeps
   the 4px ceiling rather than a full round.

   34px is the drawn height, from the reference sheet, and rule 10 wants 44px
   on anything you can press. Both, then: the pill stays 34px and the target
   is extended above and below it by five pixels a side. Growing the pill to
   44px instead would have made three chips in a row read as three buttons,
   which is the opposite of quiet. */
.chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 34px;
  padding: 6px 13px;
  border: 1px solid var(--line-ctrl);
  border-radius: var(--r-max);
  background: rgba(219, 174, 91, .16);
  color: var(--gold);
  font-family: var(--util);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  line-height: 1.4;
  text-decoration: none;
}

a.chip::after {
  content: "";
  position: absolute;
  inset: -5px 0;
  min-height: 44px;
}

/* A suggestion chip on a form is a shortcut for typing, not a fixed set. */
.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.chips button {
  min-height: 44px;
  padding: 8px 14px;
  border: 1px solid var(--line-ctrl);
  border-radius: var(--r-max);
  background: transparent;
  color: var(--bone);
  font-family: var(--util);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: .12em;
  text-transform: uppercase;
}
@media (hover: hover) and (pointer: fine) {
  .chips button:hover { color: var(--gold); border-color: var(--gold); }
}

/* --- Upload-gitter (.grid) -----------------------------------------------
   Three columns on a phone, 2px between, square crops. The image is the
   whole target. Deleting lives on the detail view, not as a cross on the
   thumbnail where a thumb hits it by accident. */
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
  margin-top: 20px;
}
@media (min-width: 760px) { .grid { grid-template-columns: repeat(4, 1fr); } }

.grid a {
  display: block;
  position: relative;
  aspect-ratio: 1;
  border-radius: var(--r);
  overflow: hidden;
  background: var(--card);
  color: inherit;
  text-decoration: none;
}
.grid img { width: 100%; height: 100%; object-fit: cover; }

/* A file with no thumbnail: a HEIC the server cannot draw yet, or a PDF.
   It shows as a card with its name, the way a document does, rather than as
   a broken image. */
.grid .file {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  height: 100%;
  padding: 10px;
  border: 1px solid var(--hair);
  border-radius: var(--r);
}
.grid .file svg { width: 20px; height: 20px; color: var(--gold-deep); }
.grid .file .name {
  font-family: var(--util);
  font-size: var(--fs-label);
  line-height: 1.35;
  color: var(--bone-dim);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  word-break: break-word;
}

.grid .caption {
  position: absolute;
  inset: auto 0 0 0;
  padding: 18px 8px 7px;
  font-size: 13px;
  line-height: 1.35;
  color: var(--bone);
  background: linear-gradient(to top, rgba(14, 17, 13, .88), transparent);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
}

/* The full-size view of one moodboard file, where the caption and the
   delete live. */
.plate {
  margin-top: 20px;
  background: var(--card);
  border: 1px solid var(--hair);
  border-radius: var(--r);
  overflow: hidden;
}
.plate img { width: 100%; height: auto; }
.plate .body { padding: 18px 20px; }

/* --- Beskedtråd (.thread) ------------------------------------------------
   Newest first. Brana and the couple are told apart by alignment and a
   sender label, not by two colours: a colour-coded chat makes the dark
   untidy. */
.thread { margin-top: 24px; display: flex; flex-direction: column; gap: 14px; }

.thread .msg {
  background: var(--card);
  border: 1px solid var(--hair);
  border-radius: var(--r);
  padding: 16px 18px;
  max-width: 90%;
}
.thread .msg.mine { align-self: flex-end; }
.thread .msg .who { display: flex; gap: 10px; align-items: baseline; margin-bottom: 8px; }
.thread .msg .body { white-space: pre-wrap; }

/* Fixed at the bottom, above the dock. Sticky rather than fixed, so a
   thread with two messages in it does not have a control floating over
   empty space below them. The ground behind it is opaque, because messages
   scroll underneath. */
.compose {
  position: sticky;
  bottom: calc(86px + env(safe-area-inset-bottom, 0px));
  margin-top: 28px;
  padding: 16px 0 4px;
  background: var(--ink);
  border-top: 1px solid var(--hair);
}
@media (min-width: 1024px) {
  .compose { bottom: 0; padding-bottom: 20px; }
}

/* --- Input (.field) ------------------------------------------------------
   16px minimum, or iOS zooms the page on focus. Rule 11. */
.field { margin-bottom: 20px; }
.field:last-child { margin-bottom: 0; }
.field label { margin-bottom: 8px; }

input[type="text"], input[type="email"], input[type="password"],
input[type="date"], input[type="time"], input[type="tel"], input[type="url"],
input[type="number"], input[type="search"], select, textarea {
  width: 100%;
  min-height: 44px;
  padding: 13px 14px;
  font-family: var(--body);
  font-weight: 300;
  font-size: 16px;
  line-height: 1.5;
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--bone);
  background: rgba(14, 17, 13, .4);
  border: 1px solid var(--line-ctrl);
  border-radius: var(--r);
  transition: border-color var(--dur-micro) var(--slow), background-color var(--dur-micro) var(--slow);
}

textarea { min-height: 96px; resize: vertical; }

input:focus, select:focus, textarea:focus {
  border-color: var(--gold);
  background: rgba(14, 17, 13, .7);
  outline: none;
}
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
}

input[type="file"] {
  width: 100%;
  min-height: 44px;
  padding: 12px 14px;
  font-family: var(--util);
  font-size: 14px;
  color: var(--bone-dim);
  background: rgba(14, 17, 13, .4);
  border: 1px dashed var(--line-ctrl);
  border-radius: var(--r);
}
input[type="file"]::file-selector-button {
  margin-right: 14px;
  min-height: 36px;
  padding: 8px 14px;
  font-family: var(--util);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--gold);
  background: transparent;
  border: 1px solid var(--gold-deep);
  border-radius: var(--r);
}

/* The error state: the field's own border turns, and the message sits under
   it with an icon, because colour on its own is not a message. */
input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"],
input[aria-invalid="true"]:focus, select[aria-invalid="true"]:focus, textarea[aria-invalid="true"]:focus {
  border-color: var(--alarm);
}

.field-err, .field-help {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 8px;
  font-size: 15px;
  line-height: 1.5;
}
.field-err { color: var(--alarm); }
.field-help { color: var(--bone-dim); }
.field-err svg { width: 18px; height: 18px; flex: none; margin-top: 4px; }

.field-pair { display: flex; gap: 12px; }
.field-pair > * { flex: 1; }

/* --- Disclosure ----------------------------------------------------------
   Editing happens in place. On a phone a modal fights the keyboard, so a
   row opens where it stands. <details> does this with no JavaScript at all,
   which also means an unsaved edit cannot be lost to a script that failed
   to load. */
.disclose > summary {
  list-style: none;
  cursor: pointer;
}
.disclose > summary::-webkit-details-marker { display: none; }
.disclose > summary::marker { content: ""; }

.form-inset {
  padding: 20px 0 4px;
  border-top: 1px solid var(--hair);
  margin-top: 4px;
}

.form-actions {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 20px;
}
.form-actions form { display: contents; }

/* --- An alternating section ----------------------------------------------
   DESIGN.md gives --band to every other section, and this is the hub's use
   of it: the ground changes under Jeres leverandører so it reads as a
   different part of the page rather than more of the programme.

   Full bleed, cancelling the view's own inline padding, because a band that
   stops short of the edges is a card, and rule 9 says a card uses --card. */
.band-section {
  background: var(--band);
  margin-inline: calc(var(--view-pad) * -1);
  padding: 36px var(--view-pad) 40px;
  border-top: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
}

/* --- One day on Programmet -----------------------------------------------
   The date and the day's name as a header, then that day's addresses under
   it. A whole wedding reads in one scroll rather than one day at a time. */
.day { margin-top: 40px; }
.day-head-line { display: flex; align-items: flex-start; gap: 12px; }
.day-head-line .eyebrow { flex: 1; min-width: 0; }
.day-head h3 { margin-top: 8px; }

/* --- Sections on the couple's page -------------------------------------- */
.block { margin-top: 40px; }
.block > h2 { margin-bottom: 0; }
.block .rows { margin-top: 18px; }
.block .empty { margin-top: 18px; }

.footer-row {
  margin-top: 56px;
  display: flex;
  flex-direction: column;
}

/* A hairline between each block, so the photographer's details and the
   language toggle are plainly two different things. */
.footer-block {
  padding: 22px 0;
  border-top: 1px solid var(--hair);
}

.footer-block .label { display: flex; align-items: center; gap: 8px; }
.footer-block .label svg { width: 14px; height: 14px; flex: none; color: var(--gold-deep); }

/* Mail, phone and WhatsApp, each with its mark and each a 44px target. */
.contact { list-style: none; margin-top: 4px; }
.contact a {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  text-decoration: none;
}
.contact svg { width: 17px; height: 17px; flex: none; color: var(--gold-deep); }
@media (hover: hover) and (pointer: fine) {
  .contact a:hover { color: var(--gold-deep); }
  .contact a:hover svg { color: var(--gold); }
}

.footer-row .lang-choice { display: flex; gap: 10px; align-items: center; margin-top: 2px; }
.footer-row .lang-choice form { display: contents; }
.footer-row .lang-choice button {
  min-height: 44px;
  padding: 10px 14px;
  background: none;
  border: 0;
  font-family: var(--util);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--bone-dim);
}
.footer-row .lang-choice button[aria-current="true"] { color: var(--gold); }
.footer-row .lang-choice .sep { color: var(--hair); }

/* A line the couple should notice once and not be nagged by: the copied
   credit list, a saved confirmation. It leaves on its own after five
   seconds, through honey like everything else. */
.flash {
  margin-top: 20px;
  padding: 14px 16px;
  background: var(--band);
  border-left: 2px solid var(--gold-deep);
  border-radius: var(--r);
  transition: opacity .6s var(--slow);
}
.flash.going { opacity: 0; }

/* Reduced motion gets the same outcome without the fade: rule 12 is
   respected without exception, and the point is that it goes. */
@media (prefers-reduced-motion: reduce) {
  .flash.going { display: none; }
}

.readout {
  margin-top: 22px;
  padding: 14px;
  border: 1px solid var(--hair);
  border-radius: var(--r);
}
.readout pre {
  margin: 9px 0 0;
  font-family: var(--body);
  font-size: 14px;
  line-height: 1.75;
  color: var(--bone-dim);
  white-space: pre-wrap;
}
