/* Postmarked: one stylesheet, placeholder styling.
   Restyle by changing the tokens below, or by editing the two
   layout sections (home list, card page) independently. */

/* ---------- 1. tokens ---------- */
:root {
  /* one ink for text and links, one muted tone for secondary text */
  --color-bg: #fafaf7;
  --color-ink: #1a1a1a;
  --color-muted: #595959;
  --color-rule: #d6d6d0;
  --color-focus: #b34700;

  --font-text: Georgia, "Times New Roman", serif;
  --font-sans: ui-sans-serif, system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  --size-base: 1rem;
  --size-small: 0.875rem;
  /* mono beside the serif body: the sign-off matches its x-height (Georgia
     0.481em, Menlo 0.547em, so 16px * 0.481 / 0.547 is about 14px); the
     postmark is smaller, about 81% of the body */
  --size-mono: 0.875rem;
  --size-postmark: 0.8125rem;
  --line-height: 1.55;

  /* the only spacing values: 1, 2, 3, 5 and 8 units of 0.5rem */
  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
  --space-5: 2.5rem;
  --space-8: 4rem;

  /* header rule, header, postmark, address, text, sign-off and footer link
     all share this one width, centred, inside a gutter on narrow screens */
  --content-width: 32rem;
  --gutter: var(--space-2);

  /* the card faces, from design/drawer/: 680 x 453 (3:2), a 6px radius, the resting
     shadow of the drawer */
  --card-width: 680px;
  --card-height: 453px;
  --card-radius: 6px;
  --card-surface: #fff;
  --card-border: #dcdcdc;
  --card-shadow: 0 3px 6px rgba(0, 0, 0, 0.1);

  /* strength of the shared grain layer over a card front, 0 to 1. The owner chose
     medium (0.22, the mockup's value) for the front; the back has no grain. The
     strengths can be compared on the grain preview page (npm start, /preview/grain/) */
  --grain-front: 0.22;
}

/* ---------- 2. base ---------- */
*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  padding-inline: var(--gutter);
  background: var(--color-bg);
  color: var(--color-ink);
  font-family: var(--font-text);
  font-size: var(--size-base);
  line-height: var(--line-height);
}

a { color: var(--color-ink); text-decoration: underline; }
a:hover { text-decoration-thickness: 2px; }

:focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 3px;
}

.skip-link {
  position: absolute;
  left: var(--gutter);
  top: calc(var(--space-8) * -1);
  padding: var(--space-1) var(--space-2);
  background: var(--color-bg);
}
.skip-link:focus { top: var(--space-1); }

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

/* Only generated text is lowercased by CSS (postmark, wordmark, sign-off). The owner's own text (body, address, caption) shows as typed. */

/* Nothing here moves, but anything that is added later must stand still when asked. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
    scroll-behavior: auto !important;
  }
}

/* ---------- 3. site frame (header, page width) ---------- */
.site-header,
.site-main {
  max-width: var(--content-width);
  margin-inline: auto;
}

/* The header, as in design/drawer/: the wordmark (a placeholder sans) with the tagline
   under it on the left, "list" (or "drawer") and "about" on the right. */
.site-header {
  display: flex;
  flex-wrap: nowrap;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-1) var(--space-3);
  padding-block: var(--space-3) var(--space-2);
}
.site-name {
  display: block;
  font-family: var(--font-sans);
  font-size: 28px;
  font-weight: 500;
  line-height: 30px;
  letter-spacing: -0.02em;
  color: var(--color-ink);
  text-decoration: none;
}
.site-name:hover { text-decoration: underline; }
.site-tagline {
  margin: 6px 0 0;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--color-muted);
}
.site-brand { min-width: 0; }
.site-nav { display: flex; flex: none; gap: var(--space-2); font-family: var(--font-mono); font-size: 12px; }
.site-nav a { display: inline-block; padding: 10px 4px; color: var(--color-muted); }
.site-nav a:hover { color: var(--color-ink); }

.site-main { padding-block: var(--space-5); }

/* The home page, the list and the card page share the table's ground and ink. Only these
   pages change the colours; the plain pages keep the paper tones above. */
.card-page,
.table-page {
  --color-bg: #ececec;
  --color-ink: #1c1c1c;
  --color-muted: #5b5b5b;
}
.card-page .site-header,
.card-page .site-main { max-width: var(--card-width); }
.card-page .site-main { padding-block: 0 var(--space-5); }

/* ---------- 4. home: the drawer and the list ---------- */
/* The drawer: cards lying loose on the table, picture side up (a card with no front shows
   its address side). The stage is 1100 x 760 in the design and is scaled down in steps
   on narrower windows; a phone has its own smaller stage and a tighter drop. Every
   card is a link to its own page, so none of this needs a script. Where each card lies
   (--x, --y, --r, and --px, --py, --pr for a phone) is computed at build time from the
   seeded drop (lib/drawer.js, lib/drop.js). Nothing here moves yet. */
.table-page { --drawer-scale: 1; }
@media (max-width: 1131px) { .table-page { --drawer-scale: 0.9; } }
@media (max-width: 1021px) { .table-page { --drawer-scale: 0.8; } }
@media (max-width: 911px) { .table-page { --drawer-scale: 0.7; } }
@media (max-width: 801px) { .table-page { --drawer-scale: 0.6; } }

.drawer-page .site-header,
.drawer-page .site-main { max-width: calc(1100px * var(--drawer-scale)); }
.drawer-page .site-main { padding-block: 0 var(--space-5); }
.list-page .site-header { max-width: 1100px; }

/* The stage is drawn at its design size (1100 x 760) and scaled down with a transform on narrower
   windows (a transform, not the CSS zoom property: browsers disagree about zoom, and a card's
   position under it is read differently). The transform does not shrink the box, so the extra
   height is taken back with a negative margin; the width is already the scaled width, because
   the page's main is that wide. */
.drawer {
  --stage-h: 760px;
  position: relative;
  width: 1100px;
  height: var(--stage-h);
  margin: var(--space-2) 0 calc(var(--stage-h) * (var(--drawer-scale) - 1));
  padding: 0;
  list-style: none;
  transform: scale(var(--drawer-scale));
  transform-origin: 0 0;
  /* the cards' z-indexes (a dragged card is raised past 1000) stay inside the table, so a
     card lifted into its layer is always above every card on it */
  isolation: isolate;
}
.pile {
  position: absolute;
  left: calc(var(--x) * 1px - 340px);
  top: calc(var(--y) * 1px - 226.5px);
  width: 680px;
  height: 453px;
  transform: rotate(var(--r)) scale(0.46);
}
.pile__link { display: block; width: 100%; height: 100%; border-radius: var(--card-radius); color: inherit; text-decoration: none; }
/* the focus ring is drawn before the card is scaled down, so it is drawn large */
.pile__link:focus-visible { outline: 7px solid var(--color-focus); outline-offset: 20px; }
.pile__card { width: 680px; height: 453px; }
.pile .face,
.pile .card-front { width: 680px; height: 453px; min-height: 453px; }
/* Hover and keyboard focus (no script needed): the card rises a little, comes to the top,
   and a small tag under it shows its date and addressee, tilted with the card. The tag sits
   inside the scaled card, so its sizes are drawn large (25px and 30px become about 11 and
   14 at the resting size). A phone has no hover. */
.pile__card { transition: scale 0.3s ease; }
.pile__tag {
  position: absolute;
  left: 50%;
  top: 486px;
  display: flex;
  align-items: baseline;
  gap: 26px;
  padding: 10px 22px;
  white-space: nowrap;
  background: rgba(236, 236, 236, 0.94);
  border-radius: 8px;
  transform: translateX(-50%);
  opacity: 0;
  transition: opacity 0.2s ease;
  pointer-events: none;
}
/* a card that is in the air leaves nothing behind on the table (its grain layer would show through) */
.pile.is-lifted .pile__card,
.pile.is-lifted .pile__tag { display: none; }
.pile__tag-when { font-family: var(--font-mono); font-size: 25px; letter-spacing: 0.03em; color: var(--color-ink); }
.pile__tag-to { font-family: var(--font-text); font-style: italic; font-size: 30px; color: #3c3c3c; }
.pile:has(.pile__link:focus-visible),
.pile.is-dragging { z-index: 100000 !important; }
.pile__link:focus-visible .pile__card { scale: 1.025; }
.pile__link:focus-visible ~ .pile__tag,
.pile.is-dragging .pile__tag { opacity: 1; }
/* Only where a pointer really hovers: on a touch screen :hover sticks to the last card tapped, and it
   would stay raised on top of the pile. */
@media (hover: hover) {
  .pile:hover { z-index: 100000 !important; }
  .pile__link:hover .pile__card { scale: 1.025; }
  .pile:hover .pile__tag { opacity: 1; }
}
@media (hover: none) { .pile__tag { display: none; } }
@media (prefers-reduced-motion: reduce) {
  .pile__card, .pile__tag { transition: none; }
}

/* Dragging (added by assets/drawer.js for a mouse or other fine pointer): a held card
   lifts off the table with a deeper shadow. */
.drawer--drag { user-select: none; -webkit-user-select: none; }
/* The hand: a link shows the pointing hand by default, which hides the grab cursor Draggable sets on
   the card, so the link takes it too (the card can be pushed around as well as opened). It closes
   into a fist while a card is held. A lifted card is clicked to turn it, so it keeps the pointing hand. */
.drawer--drag .pile__link { cursor: grab; }
.drawer--drag .pile.is-dragging,
.drawer--drag .pile.is-dragging .pile__link { cursor: grabbing; }
.drawer--drag .pile__link { -webkit-user-drag: none; }
.pile .face,
.pile .card-front { transition: box-shadow 0.3s ease; }
@media (hover: hover) {
  .pile:hover .face,
  .pile:hover .card-front { box-shadow: 0 8px 18px rgba(0, 0, 0, 0.16); }
}
.pile.is-dragging .face,
.pile.is-dragging .card-front { box-shadow: 0 22px 40px rgba(0, 0, 0, 0.2); }

/* The table's first-visit hint (added by assets/drawer.js), gone after the first interaction. */
.drawer__hint {
  margin: -20px 0 var(--space-3);
  text-align: center;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--color-muted);
  pointer-events: none;
}

/* A card's own page, with script, becomes the drawer with that card lifted (assets/drawer.js).
   While that happens the stacked sides are hidden (only when script is enabled and the window
   is not a phone), and they come back after 1.5s whatever happens, or at once if the script
   decides to leave the page as it is (body.card-plain). */
/* (!important, because the reduced-motion rule near the top of this file switches every animation
   off with !important, and this one only flips visibility after a delay: it is not motion. Without
   it a visitor with reduced motion would never see the card if the script failed to run.) */
@media (scripting: enabled) {
  .card-page .card { visibility: hidden; animation: card-reveal 0s linear 1.5s forwards !important; }
  .card-page.card-plain .card { visibility: visible; animation: none !important; }
}
@keyframes card-reveal { to { visibility: visible; } }

/* The lifted card's layer (assets/drawer.js): the table dimmed, the card in the middle at its
   natural size (never scaled up; fitted down in a small window), turning on its vertical axis.
   Nothing above the card has a filter, a clip-path or an opacity under 1 (the scrim fades on its
   own), and the faces have no clip-path: those flatten a turn or square off the shadows. */
.lift-open { overflow: hidden; }
/* touch-action: pinch-zoom: a finger cannot pan the page behind the card, but two fingers can still zoom
   (a reader may want to enlarge the message). */
/* No tap highlight: on a phone the lifted card is a zero-size box holding the faces, and iOS drew its grey
   highlight on that box, a small dot in the middle of the screen at every tap. */
.lift { --s: 1; position: fixed; inset: 0; z-index: 700; touch-action: pinch-zoom; overscroll-behavior: none; -webkit-tap-highlight-color: transparent; }
.lift__scrim {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: rgba(236, 236, 236, 0.38);
  cursor: default;
}
.lift__turn:focus-visible { outline: none; }
.lift--keys:has(.lift__turn:focus-visible) .lift__card { outline: 3px solid var(--color-focus); outline-offset: 10px; border-radius: var(--card-radius); }
.lift__stage { position: absolute; left: 50%; top: calc(50% - 8px); width: 0; height: 0; }
.lift__card {
  position: absolute;
  left: calc(var(--card-width) / -2);
  top: calc(var(--card-height) / -2);
  width: var(--card-width);
  height: var(--card-height);
  cursor: pointer;
  will-change: transform; /* its own layer for the whole time it is in the air */
}
/* The perspective sits on this wrapper, so the card's own travel (a flat transform) and its turn
   (a 3D one) are separate, and the shadows below are not part of the 3D scene. */
.lift__persp { position: absolute; inset: 0; perspective: 2400px; }
.lift__rot { position: absolute; inset: 0; transform-style: preserve-3d; will-change: transform; }
/* Each side of the card (the picture side and the back) is a flat plate that turns, with its
   shadow as images behind its face: the resting shadow fades out and the lifted one fades in as the
   card rises (opacity is cheap). They are pre-drawn PNGs (npm run shadows) of the design's own
   box-shadows, and turn with the card, so the shadow looks the same before, during and after a
   turn and is never redrawn. (A blurred box-shadow on a face that is turning was repainted on every
   frame, which stuttered in Safari.) */
.lift__side {
  position: absolute;
  inset: 0;
  will-change: transform; /* both sides are drawn as layers from the start, so the back is not drawn for the first time half way through a turn */
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
.lift__side--back { transform: rotateY(180deg); }
/* The images are drawn for a 680 x 453 card. They are laid out as nine pieces (border-image), so the
   soft edges keep their true width on a card of another size (a phone's portrait back), and at
   680 x 453 they are exactly the image. The slice is the padding plus the width of the blur that
   reaches in over the card. */
.lift__shadow {
  position: absolute;
  box-sizing: border-box;
  border-style: solid;
  border-color: transparent;
  pointer-events: none;
  transition: opacity 0.35s ease;
}
.lift__shadow--rest {
  inset: -20px;
  border-width: 40px;
  border-image: url("/assets/shadows/card-resting.png") 40 fill / 40px stretch;
}
.lift__shadow--up {
  inset: -80px;
  border-width: 140px;
  border-image: url("/assets/shadows/card-lifted.png") 140 fill / 140px stretch;
  opacity: 0;
}
.lift.is-up .lift__shadow--rest { opacity: 0; }
.lift.is-up .lift__shadow--up { opacity: 1; }
.lift .face,
.lift .card-front {
  position: absolute;
  inset: 0;
  width: var(--card-width);
  height: var(--card-height);
  min-height: var(--card-height);
  margin: 0;
  box-shadow: none; /* the shadow is the images behind the face (.lift__shadow) */
}
/* A card picked up on a phone (assets/drawer.js adds .lift--phone). The first side (the picture, or a
   postal card's address side) is the same 680 x 453 plate as on the table, scaled down to the width of
   the screen (--k), so the card does not change shape when it is tapped; the second side is the
   portrait page for reading, at its natural size (--pw wide). The plates are centred on the same
   point, so as the card turns the wide plate rotates away and the tall plate rotates in: the change of
   shape happens while it is edge-on, and it reads as the page standing up. */
.lift--phone .lift__card { left: 0; top: 0; width: 0; height: 0; }
.lift--phone .lift__side {
  inset: auto;
  left: 50%;
  top: 50%;
  width: var(--pw);
  transform: translate(-50%, -50%);
}
.lift--phone .lift__side--back { transform: translate(-50%, -50%) rotateY(180deg); }
.lift--phone .lift__side--plate {
  width: var(--card-width);
  height: var(--card-height);
  transform: translate(-50%, -50%) scale(var(--k));
}
.lift.lift--phone .lift__face {
  position: relative;
  inset: auto;
  width: 100%;
  height: auto;
  min-height: 24rem; /* the tall page is as tall as its message needs, so the card does not grow far past the plate (about 15rem) when it turns; it used to fill the screen (up to 40rem) */
}
.lift.lift--phone .lift__side--plate .lift__face {
  position: absolute;
  inset: 0;
  width: var(--card-width);
  height: var(--card-height);
  min-height: var(--card-height);
}
/* under the picture: its date and addressee, then the two hints (the first time) */
.lift-open .drawer__more,
.lift-open .drawer__hint { visibility: hidden; } /* the table's own words do not show through the dimming */
.lift__meta { display: none; }
.lift--phone .lift__meta {
  display: block;
  position: absolute;
  left: 50%;
  top: calc(50% - 8px + var(--fh) / 2 + var(--space-2));
  width: var(--pw);
  transform: translateX(-50%);
  transition: opacity 0.2s ease 0.5s; /* back in only once the tall page has turned away, or it would show over it */
}
.lift--phone.lift--on-back .lift__meta { opacity: 0; pointer-events: none; transition-delay: 0s; }
.lift__meta-row { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-2); }
.lift__meta-when { font-family: var(--font-mono); font-size: 13px; letter-spacing: 0.03em; color: var(--color-ink); }
.lift__meta-to { font-family: var(--font-text); font-style: italic; font-size: 16px; color: #3c3c3c; text-align: right; }
.lift__meta-hints { margin: var(--space-3) 0 0; text-align: center; font-family: var(--font-mono); font-size: 12px; line-height: 20px; letter-spacing: 0.04em; color: var(--color-muted); }
.lift--phone .lift__hint, .lift--phone .lift__turn-hint { display: none; }

.lift__turn-hint {
  position: absolute;
  right: 14px;
  top: 426px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--color-muted);
  pointer-events: none;
}
.lift__hint {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(50% - 8px + var(--card-height) / 2 * var(--s) + 28px);
  margin: 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--color-muted);
  pointer-events: none;
}

.drawer__more {
  margin: 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--color-muted);
}
.drawer__more--phone { display: none; }

/* A phone (under 700px): the stage is 358 x 480 with the newest six, about 250 wide. */
@media (max-width: 43.75rem) {
  .table-page { --drawer-scale: 0.92; }
  .drawer-page .site-header,
  .drawer-page .site-main { max-width: calc(358px * var(--drawer-scale)); }
  .drawer { --stage-h: 480px; width: 358px; }
  .pile {
    left: calc(var(--px) * 1px - 340px);
    top: calc(var(--py) * 1px - 226.5px);
    transform: rotate(var(--pr)) scale(0.368);
  }
  .pile:nth-child(n + 7) { display: none; }
  .drawer__more--desktop { display: none; }
  .drawer__more--phone { display: block; }
}
@media (max-width: 23.75rem) { .table-page { --drawer-scale: 0.8; } }

/* The list: every card, plain, a postmark on the left and the addressee on the right. */
.list-main { max-width: 592px; padding-block: var(--space-5) var(--space-8); }
.list {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid #d0d0d0;
}
.list-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 0 var(--space-2);
  min-height: 44px;
  padding: 12px 4px;
  border-bottom: 1px solid #d0d0d0;
  color: inherit;
  text-decoration: none;
}
.list-row:hover { background: rgba(0, 0, 0, 0.04); }
.list-row:focus-visible { outline-offset: -3px; }
.list-row time { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.03em; white-space: nowrap; }
.list-row__to { font-family: var(--font-text); font-style: italic; font-size: 15px; color: #3a3a3a; }
.list-row .list-row__flag { flex-basis: 100%; margin: 0; font-size: 11px; line-height: 1.2; }

/* ---------- 5. card faces ---------- */
/* Every face is 680px wide and at least 453px high (3:2): the same footprint for the
   picture side and for both sides of the back. White, a 1px border, a 6px radius. The
   decoration is placed in the 680 x 453 frame the design was drawn in (numbers from
   design/drawer/); the message grows the card rather than being cut, and there is
   no overflow: hidden anywhere, because the stamp's rough edge is drawn outside its box. */
.card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
}
.face {
  position: relative;
  box-sizing: border-box;
  width: min(100%, var(--card-width));
  min-height: var(--card-height);
  background: var(--card-surface);
  border: 1px solid var(--card-border);
  border-radius: var(--card-radius);
  box-shadow: var(--card-shadow);
  font-family: var(--font-mono);
  color: var(--color-ink);
  overflow-wrap: break-word;
}

/* Printed words and rules (decoration). */
.postcard__title {
  position: absolute;
  inset: 20px 0 auto;
  margin: 0;
  padding-left: 0.6em;
  text-align: center;
  font-size: 10px;
  line-height: 14px;
  letter-spacing: 0.6em;
}
.postcard__rule {
  position: absolute;
  left: 396px;
  top: 46px;
  bottom: 22px;
  width: 1px;
  background: var(--color-ink);
}
.postcard__imprint {
  position: absolute;
  left: 401px;
  bottom: 24px;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-size: 8px;
  letter-spacing: 0.2em;
  color: #8a8a8a;
}

/* The postmark, as two lines: date and time, then the zone name and coordinates. */
.postcard__postmark,
.postcard__when { margin: 0; font-size: 12px; font-weight: normal; line-height: 18px; letter-spacing: 0.03em; }
.pm-main { display: block; white-space: nowrap; }
.pm-sub { display: block; font-size: 10px; line-height: 16px; color: var(--color-muted); }
.pm-place { white-space: nowrap; }

/* The message side (picture-card back): in flow, so a long message grows the card. */
.postcard--back { display: flex; flex-direction: column; padding: 46px 0 22px; }
.postcard__message {
  display: flex;
  flex: 1;
  flex-direction: column;
  width: 344px;
  margin-left: 28px;
}
.postcard--back .postcard__postmark { margin-top: 2px; }
.postcard__text {
  margin-top: 16px;
  font-family: var(--font-text);
  font-size: 15px;
  line-height: 22.5px;
}
.postcard__text p { margin: 0 0 22.5px; }
.postcard__text p:last-child { margin-bottom: 0; }
.postcard__signoff {
  margin: auto 0 0;
  padding-top: var(--space-3);
  font-size: 12px;
  line-height: 18px;
  letter-spacing: 0.04em;
  text-transform: lowercase;
}

/* The stamp's dashed box (518,32; 116 x 178) and the stamp in it, 108 x 168, placed by
   its seeded --sl, --st and tilt (set inline per card). --k scales both for a phone. */
.postcard__zone {
  --k: 1;
  position: absolute;
  left: 518px;
  top: 32px;
  box-sizing: border-box;
  width: calc(116px * var(--k));
  height: calc(178px * var(--k));
  /* The dashes are drawn as an image with fixed dash lengths, not a CSS border: while a card
     turns, the browser draws it at a different resolution and lays a CSS border's dashes out
     again, so they seemed to move. This keeps every dash where it is. (#a5a5a5, 3px dashes
     and gaps, a 1px line; a phone has its own at 0.74.) */
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='116' height='178' viewBox='0 0 116 178'%3E%3Crect x='.5' y='.5' width='115' height='177' fill='none' stroke='%23a5a5a5' stroke-dasharray='3 3'/%3E%3C/svg%3E") 0 0 / 100% 100% no-repeat;
}
.card__stamp {
  position: absolute;
  left: calc((var(--sl) - 518) * var(--k) * 1px);
  top: calc((var(--st) - 32) * var(--k) * 1px);
  width: calc(108px * var(--k));
  line-height: 0;
}
.card__stamp svg { display: block; width: 100%; height: auto; }

/* The addressee on three ruled lines. The address is the owner's own text and shows as
   typed. The rules are one ruled background on the addressee itself (a rule every 40px, 34px
   down each line) and the text's line height is the same 40px, so a long addressee wraps onto
   the second and third rules, one line to a rule, as on a real postcard. The first rule is
   where it always was; the block starts 6px higher to make room for the line above it. */
.postcard__address { position: absolute; left: 420px; top: 238px; width: 232px; }
.postcard__to {
  min-height: 120px;
  margin: 0;
  background: repeating-linear-gradient(to bottom, transparent 0 34px, var(--color-ink) 34px 35px, transparent 35px 40px);
  font-family: var(--font-text);
  font-style: italic;
  font-size: 19px;
  line-height: 40px;
}
.postcard__lines { display: none; }

/* A postal card (no front). Address side: the postmark top left, the addressee lower
   right, a printed line bottom left. Message side: the text alone, the full width. */
.postcard--postal-address { height: var(--card-height); }
.postcard--postal-address .postcard__title { top: 22px; }
.postcard--postal-address .postcard__postmark { position: absolute; left: 28px; top: 53px; }
.postcard--postal-address .postcard__address { left: 300px; top: 257px; width: 352px; }
.postcard--postal-address .postcard__to {
  min-height: 132px;
  background: repeating-linear-gradient(to bottom, transparent 0 39px, var(--color-ink) 39px 40px, transparent 40px 44px);
  font-size: 22px;
  line-height: 44px;
}
.postcard__print {
  position: absolute;
  left: 28px;
  bottom: 24px;
  margin: 0;
  font-size: 8px;
  line-height: 10px;
  letter-spacing: 0.22em;
  color: #8a8a8a;
}
.postcard--postal-message { display: flex; flex-direction: column; padding: 28px 32px 26px; }
.postcard--postal-message .postcard__text { margin-top: 24px; font-size: 16px; line-height: 25px; }
.postcard--postal-message .postcard__text p { margin-bottom: 25px; }
.postcard--postal-message .postcard__text p:last-child { margin-bottom: 0; }
.postcard--postal-message .postcard__imprint {
  inset: auto 32px 27px auto;
  writing-mode: horizontal-tb;
  transform: none;
  letter-spacing: 0.22em;
}

/* A phone (under 700px): the back stands up as a portrait card, as in
   design/drawer/08-phone.png. The postmark (three lines) sits top left, the stamp
   top right over its dashed box (scaled to 0.74), the addressee on a ruled line, a
   full-width rule, then the message at 16 on 25 and the sign-off at the bottom. The
   same applies to a postal card's address side. This is the interim reading layout
   for a narrow screen, and the same layout is the second side of a card picked up on a phone. */
@media (max-width: 43.75rem) {
  :is(.card, .lift__side--portrait) .postcard--back,
  :is(.card, .lift__side--portrait) .postcard--postal-address { display: flex; flex-direction: column; height: auto; min-height: 30rem; padding: 44px 20px 24px; }
  :is(.card, .lift__side--portrait) .postcard__message { display: contents; }
  :is(.card, .lift__side--portrait) .postcard__postmark { order: 1; margin: 0 6.5rem 0 0; }
  :is(.card, .lift__side--portrait) .postcard--back .postcard__postmark,
  :is(.card, .lift__side--portrait) .postcard--postal-address .postcard__postmark { position: static; margin-top: 0; }
  :is(.card, .lift__side--portrait) .pm-sub { display: flex; flex-direction: column; }
  :is(.card, .lift__side--portrait) .pm-dot--sub { display: none; }
  :is(.card, .lift__side--portrait) .postcard__zone {
    --k: 0.74; left: auto; right: 14px; top: 40px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='86' height='132' viewBox='0 0 86 132'%3E%3Crect x='.5' y='.5' width='85' height='131' fill='none' stroke='%23a5a5a5' stroke-dasharray='3 3'/%3E%3C/svg%3E");
  }
  :is(.card, .lift__side--portrait) .postcard__address { position: static; order: 2; width: auto; margin: 33px 6.5rem 0 0; }
  :is(.card, .lift__side--portrait) .postcard--postal-address .postcard__address { margin-top: calc(2.5rem - 3px); }
  :is(.card, .lift__side--portrait) .postcard__to,
  :is(.card, .lift__side--portrait) .postcard--postal-address .postcard__to {
    /* a rule under every line of the addressee, as on the wide card: one ruled background, 36px apart, with the same line height.
       The first rule stays where the single rule was (the block starts 3px higher). */
    min-height: 36px;
    background: repeating-linear-gradient(to bottom, transparent 0 31px, var(--color-ink) 31px 32px, transparent 32px 36px);
    font-size: 19px;
    line-height: 36px;
  }
  :is(.card, .lift__side--portrait) .postcard__rule { position: static; order: 3; width: auto; height: 1px; margin-top: 32px; }
  :is(.card, .lift__side--portrait) .postcard__text { order: 4; margin-top: 24px; font-size: 16px; line-height: 25px; }
  :is(.card, .lift__side--portrait) .postcard__text p { margin-bottom: 25px; }
  :is(.card, .lift__side--portrait) .postcard__text p:last-child { margin-bottom: 0; }
  :is(.card, .lift__side--portrait) .postcard__signoff { order: 5; }
  :is(.card, .lift__side--portrait) .postcard__imprint,
  :is(.card, .lift__side--portrait) .postcard--postal-message .postcard__imprint {
    inset: auto 20px 25px auto;
    writing-mode: horizontal-tb;
    transform: none;
    text-transform: uppercase;
    letter-spacing: 0.22em;
  }
  :is(.card, .lift__side--portrait) .postcard--postal-message { padding: 26px 20px 26px; }
  :is(.card, .lift__side--portrait) .postcard__print { left: 20px; }
  :is(.card, .lift__side--portrait) .postcard--postal-address .postcard__address { position: static; width: auto; margin: calc(3rem - 3px) 6.5rem 0 0; }
}

/* ---------- 5b. card front ---------- */
/* The picture side, as in docs/FRONTS.md: the same footprint as the back (680 wide, 3:2,
   the same border, radius and shadow), 12px of white round the picture, the picture
   filling the rest with object-fit: cover, and a 36px mono caption row (the caption on
   the left; the "no. N" was retired on 31 oct 2026). */
.card-front {
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  width: min(100%, var(--card-width));
  aspect-ratio: 680 / 453;
  margin: 0;
  padding: 12px 12px 0;
  background: var(--card-surface);
  border: 1px solid var(--card-border);
  border-radius: var(--card-radius);
  box-shadow: var(--card-shadow);
}
.card-front__picture { position: relative; flex: 1; min-height: 0; }
.card-front__picture img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 2px;
}
.card-front__caption {
  display: flex;
  align-items: center;
  height: 36px;
  padding-left: 2px;
  font-family: var(--font-mono);
  font-size: 12px;
  letter-spacing: 0.02em;
  line-height: normal;
  color: var(--color-muted);
}

/* The shared grain layer (lib/grain.js): fills its positioned parent, never catches clicks. */
.grain { position: absolute; inset: 0; pointer-events: none; }

/* ---------- 6. plain pages (about, 404) ---------- */
.page h1 { margin: 0 0 var(--space-3); font-size: 1.5rem; font-weight: normal; }
.page p { margin: 0 0 var(--space-2); }
.page p:last-child { margin-bottom: 0; }
