:root{
  --cream:#FAF7F0; --card:#FFFEF9; --panel:#F0E7D2;
  --kraft:#C4A26A; --kraft-light:#E8DCC8; --kraft-dark:#8B6914;
  --terra:#C1432A; --terra-dark:#A03520; --green:#2D5016;
  --ink:#2C1810; --ink-mid:#6B5240; --ink-light:#B09060; --ink-faint:#D4B888;
}
*{box-sizing:border-box;}
html,body{margin:0;padding:0;overflow-x:hidden;}
body{background:var(--cream);color:var(--ink);
  font-family:'Work Sans',ui-sans-serif,system-ui,-apple-system,sans-serif;
  -webkit-font-smoothing:antialiased;}
img{max-width:100%;}
button{font-family:inherit;cursor:pointer;}
:where(button,input,textarea):focus-visible{outline:2px solid var(--terra);outline-offset:2px;}
.hidden{display:none !important;}

/* language toggle - present on every page once i18n.js runs, deliberately
   understated so it reads as a utility, not a competing action */
.i18n-toggle{position:fixed;top:16px;right:16px;z-index:10;background:none;
  border:1px solid var(--ink-faint);border-radius:999px;padding:5px 12px;
  font-family:'IBM Plex Mono','Courier Prime','Courier New',monospace;
  font-size:11px;letter-spacing:.04em;color:var(--ink-light);
  transition:color .15s,border-color .15s;}
.i18n-toggle:hover{color:var(--ink-mid);border-color:var(--ink-mid);}

/* ---------- page: hero + panel ---------- */
.page{min-height:100vh;}
.hero{background:var(--panel);overflow:hidden;}
.hero-inner{display:flex;flex-direction:column;align-items:center;justify-content:center;
  padding:18px;min-height:100%;}
.hero-collage{width:100%;max-width:600px;display:flex;flex-direction:column;}
.hero-stack{position:relative;width:100%;aspect-ratio:4 / 3.3;}
.hero-stack-a{width:100%;align-self:flex-start;}
.hero-stack-b{width:88%;align-self:flex-end;margin-top:-9%;}
.hero-fig{margin:0;position:absolute;width:76%;}
.hero-img{display:block;width:100%;min-width:0;border-radius:3px;
  box-shadow:0 20px 44px -18px rgba(44,24,16,.45);}
.hero-fig-verso{top:2%;left:0;width:70%;transform:rotate(8deg);z-index:1;}
.hero-fig-recto{bottom:2%;right:0;width:78%;transform:rotate(-6deg);z-index:2;}
.hero-fig-recto-b{top:0;left:2%;width:68%;transform:rotate(-9deg);z-index:1;}
.hero-fig-verso-b{bottom:0;right:0;width:74%;transform:rotate(5deg);z-index:2;}
.hero-cap{margin:20px 0 0;font-family:'Lora',Georgia,serif;font-style:italic;
  font-size:12.5px;color:var(--ink-light);text-align:center;}

.panel{background:var(--cream);padding:34px 20px;}
.wrap{max-width:520px;margin:0 auto;}

@media (min-width:900px){
  .page{display:grid;grid-template-columns:1fr 1fr;}
  .page > *{min-width:0;}
  .hero-inner{position:sticky;top:0;height:100vh;min-height:0;padding:32px;}
  .hero-collage{width:min(84%,520px,calc((100vh - 170px) * 0.68));}
  .panel{padding:56px 40px;}
}

/* ---------- brand ---------- */
.brand{margin-bottom:26px;}
.wordmark{font-family:'Lora',Georgia,serif;font-weight:500;font-size:25px;line-height:1.2;
  letter-spacing:.01em;margin:0 0 10px;}
.tagline{font-size:14px;line-height:1.62;color:var(--ink-mid);margin:0;max-width:46ch;}

/* ---------- steps / form ---------- */
.step{background:var(--card);border:1px solid var(--kraft-light);border-radius:12px;
  padding:16px 16px 16px;margin-bottom:14px;box-shadow:0 8px 22px -16px rgba(44,24,16,.4);}
.step h2{font-size:11px;font-weight:600;letter-spacing:.09em;text-transform:uppercase;
  color:var(--ink-light);margin:0 0 12px;display:flex;align-items:center;}
.step h2 .n{display:inline-flex;width:18px;height:18px;border-radius:50%;background:var(--terra);
  color:#fff;align-items:center;justify-content:center;font-size:10px;margin-right:8px;}
.step h2 .opt{margin-left:6px;text-transform:none;letter-spacing:0;font-weight:400;color:var(--ink-faint);}

.drop{border:1.5px dashed var(--kraft);border-radius:10px;background:var(--panel);
  padding:28px 14px;text-align:center;color:var(--ink-mid);font-size:13px;transition:background .15s;}
.drop.drag{background:var(--kraft-light);}
.drop.has-photo{padding:0;border-style:solid;overflow:hidden;}
.drop.has-photo img{display:block;width:100%;max-height:280px;object-fit:cover;}
.drop small{display:block;margin-top:6px;color:var(--ink-light);font-size:11px;}
.drop .cap{padding:8px;font-size:11px;color:var(--ink-light);background:var(--panel);}

.field{margin:0;}
.field + .field{margin-top:8px;}
.field input,.field textarea{width:100%;border:1.5px solid var(--kraft-light);border-radius:8px;
  padding:9px 10px;font-size:13px;color:var(--ink);background:var(--cream);font-family:inherit;}
.field textarea{min-height:148px;resize:vertical;line-height:1.7;
  font-family:'IBM Plex Mono','Courier Prime','Courier New',monospace;color:#38332c;}
.counter{font-size:10.5px;color:var(--ink-light);text-align:right;margin-top:3px;}
.counter.warn{color:var(--terra);}
.note{font-size:11.5px;line-height:1.5;color:var(--ink-light);margin:10px 0 0;}

.turnstile-wrap{display:flex;justify-content:center;margin-top:10px;}

.cta{width:100%;background:var(--terra);color:#fff;border:none;border-radius:10px;
  padding:14px;font-weight:600;font-size:14px;transition:background .15s;margin-top:4px;}
.cta:hover{background:var(--terra-dark);}
.cta:disabled{background:var(--ink-faint);cursor:not-allowed;}
.cta.ghost{background:none;color:var(--ink-mid);text-decoration:underline;text-underline-offset:3px;
  padding:10px;font-weight:500;margin-top:6px;}
.reassure{font-size:12px;line-height:1.6;color:var(--ink-mid);margin:12px 2px 0;}
.tries{font-size:11.5px;color:var(--ink-light);margin:6px 2px 0;}
.err{background:#fbeae6;border:1px solid var(--terra);color:var(--terra-dark);
  border-radius:8px;padding:10px 12px;font-size:12.5px;margin-top:10px;}

/* ---------- loading ---------- */
.loading{text-align:center;padding:40px 12px;}
.loading p{font-family:'Lora',Georgia,serif;font-style:italic;color:var(--ink-mid);font-size:15px;
  margin:0;transition:opacity .3s ease;min-height:1.4em;}
.loading p.loading-reassure{font-family:'Work Sans',ui-sans-serif,sans-serif;font-style:normal;
  font-size:11.5px;color:var(--ink-light);margin:14px auto 0;max-width:34ch;line-height:1.55;min-height:0;}
.loading-art{position:relative;width:96px;height:96px;margin:0 auto 20px;}
.loading-wash{position:absolute;inset:-14px;border-radius:50%;filter:blur(18px);
  opacity:.5;background:radial-gradient(circle at 38% 34%,var(--wash-a,#e7ded0),var(--wash-b,#f7f3ea) 72%);
  transition:background 1s ease;}
.loading-icon{position:relative;width:100%;height:100%;color:var(--ink-mid);
  opacity:.85;filter:blur(.3px);}
.loading-icon path{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;
  stroke-dasharray:230;stroke-dashoffset:230;animation:sketchIn 1.1s ease forwards;}
.loading-icon .sketch-under{stroke-width:0.7;opacity:.3;animation-duration:.85s;}
.loading-icon .sketch-main{stroke-width:1;opacity:.62;animation-delay:.06s;}
@keyframes sketchIn{to{stroke-dashoffset:0;}}

/* ---------- result ---------- */
.result-title{font-family:'Lora',Georgia,serif;font-style:italic;font-size:16px;color:var(--ink-mid);
  text-align:center;margin:0 0 14px;}
.pc-shot{position:relative;border-radius:6px;overflow:hidden;background:var(--card);
  box-shadow:0 16px 38px -14px rgba(44,24,16,.3);}
.pc-img{display:block;width:100%;}

/* ---------- front / back flip ---------- */
.pc-flipwrap{width:100%;}
.pc-flip{position:relative;width:100%;perspective:2200px;cursor:pointer;}
.pc-flip-inner{position:relative;width:100%;transition:transform .7s cubic-bezier(.4,.05,.2,1);
  transform-style:preserve-3d;aspect-ratio:3 / 2;}
.pc-flip.flipped .pc-flip-inner{transform:rotateY(180deg);}
.pc-face{position:absolute;inset:0;backface-visibility:hidden;border-radius:6px;overflow:hidden;
  background:var(--card);box-shadow:0 16px 38px -14px rgba(44,24,16,.3);}
.pc-face-back{transform:rotateY(180deg);}
.pc-face .pc-img{width:100%;height:100%;object-fit:cover;}

/* secondary: the flip hint - a real action (there's real content on the
   other side), so it gets an outline in the accent colour, one step down
   from the solid primary button. */
.pc-btn-secondary{display:flex;align-items:center;gap:7px;margin:18px auto 0;
  padding:8px 16px;border:1.5px solid var(--terra);border-radius:999px;background:none;
  font-family:'IBM Plex Mono','Courier Prime','Courier New',monospace;font-size:12px;
  letter-spacing:.04em;color:var(--terra);cursor:pointer;transition:background .15s,color .15s;}
.pc-btn-secondary:hover{background:var(--terra);color:#fff;}
.pc-flip-ic{font-size:14px;line-height:1;transition:transform .5s ease;}
.pc-btn-secondary[aria-pressed="true"] .pc-flip-ic{transform:rotate(180deg);}
@media (prefers-reduced-motion:reduce){.pc-flip-inner,.pc-flip-ic{transition:none;}}

/* tertiary: plain text, grouped in one row so two low-weight options read
   as "other things you can do", not as two more buttons */
.pc-tertiary-row{display:flex;justify-content:center;align-items:center;
  gap:18px;margin:16px auto 0;flex-wrap:wrap;}
.pc-btn-tertiary{display:inline-flex;align-items:center;gap:6px;background:none;border:none;
  padding:2px;font-family:'IBM Plex Mono','Courier Prime','Courier New',monospace;
  font-size:11px;letter-spacing:.02em;color:var(--ink-light);text-decoration:underline;
  text-underline-offset:3px;cursor:pointer;}
.pc-btn-tertiary:hover{color:var(--ink-mid);}
/* a clearly-visible outlined chevron, leading the label rather than
   trailing it - the same rotate-on-open language as the flip hint's ↻ -
   so once .revealed drops the underline below, the button still reads
   as something that opens, not plain text */
.pc-tertiary-ic{width:16px;height:16px;flex-shrink:0;transition:transform .5s ease;}
.pc-btn-tertiary[aria-pressed="true"] .pc-tertiary-ic{transform:rotate(180deg);}
@media (prefers-reduced-motion:reduce){.pc-tertiary-ic{transition:none;}}

/* the sender's original photo - tucked away until asked for, a little
   surprise rather than a wall of content */
.pc-original{max-width:320px;margin:22px auto 0;text-align:center;
  animation:fadeUp .5s ease both;}
.pc-original-img{display:block;width:100%;border-radius:6px;
  box-shadow:0 16px 34px -18px rgba(44,24,16,.4);}
.pc-original-cap{margin:10px 0 0;font-family:'Lora',Georgia,serif;font-style:italic;
  font-size:12.5px;color:var(--ink-light);}

/* space the share / recompose actions well clear of the hint */
#result #share{margin-top:26px;}

/* ---------- recipient ---------- */
.recipient{display:flex;flex-direction:column;align-items:center;justify-content:center;
  min-height:100vh;padding:48px 16px;text-align:center;}
/* the envelope is one illustrated image - the greeting, the "appuyez pour
   ouvrir" hint and the airmail styling are all baked into it */
/* env-wrap's own height is pinned inline (see fadeOutEnvelope in
   recipient.js) right before env-collapse goes absolute below - without
   an explicit height, env-wrap has no content of its own once its only
   child leaves the flow, so it would collapse to 0 height instantly and
   snap env-expiry (sitting right after it) up into that space. That snap
   was the real "giật layout", not the fade itself. */
.env-wrap{position:relative;width:100%;max-width:460px;}
.env-collapse{margin-top:44px;transition-property:opacity;transition-timing-function:ease;}
/* a pure dissolve - no shrinking, no height change. An earlier version
   animated max-height/margin to zero alongside opacity to avoid the jump
   above, but collapsing a rectangular illustration's own box reads as the
   envelope folding or closing shut, which is exactly the wrong metaphor.
   transition-duration is set inline in JS, computed from how much clip
   is left to play at the current rate, so the dissolve always finishes
   exactly as the clip reaches its natural end - never a fixed constant
   that can drift out of sync with the actual playback speed. */
.env-collapse.fading{position:absolute;top:44px;left:0;right:0;margin-top:0;
  opacity:0;pointer-events:none;}
.env-stage{opacity:0;animation:fadeUp .5s ease forwards;width:100%;max-width:460px;
  display:block;padding:0;border:none;background:none;cursor:pointer;
  border-radius:8px;overflow:hidden;box-shadow:0 18px 36px -10px rgba(44,24,16,.32);}
/* nested inside env-stage now, not swapped in for a separate <img> - one
   element for both the resting poster frame and the playing clip, so
   there's never a frame mismatch at the moment it starts moving. A
   matching paper tone (not black) shows through on the rare frame where
   the browser is still decoding after play() is called. Fixed dimensions
   so the box never resizes once metadata loads - that resize was the
   real cause of the "jump" at press, not the video content itself. */
.env-video{display:block;width:100%;height:auto;background:var(--kraft-light);}
/* a second, plain-spoken nudge for anyone who doesn't read "appuyez pour
   ouvrir" off the illustration itself - kept light and hand-drawn so it
   reads as a friendly aside, not a UI instruction competing with the art.
   A real click target too (same action as the envelope), so someone who
   only notices the note can still open the card from it. Sits centered
   above the envelope, over its flap - a real envelope's flap is always
   on top, so that's where the opening motion starts. Absolutely
   positioned (not stacked in flow above env-stage) so hiding it on click
   never shifts env-stage/env-video up underneath it. */
.env-hint{position:absolute;top:0;left:50%;transform:translateX(-50%);
  display:block;border:none;background:none;padding:0;margin:0;
  color:var(--ink-faint);opacity:0;animation:fadeUp .6s ease .35s forwards;}
.env-hint-text{font-family:'Caveat',cursive;font-size:23px;line-height:1;
  white-space:nowrap;}
/* deliberately the quietest text on the page - a footnote under the
   envelope, never competing with the "appuyez pour ouvrir" baked into it */
.env-expiry{font-family:'IBM Plex Mono','Courier Prime','Courier New',monospace;
  font-size:10.5px;letter-spacing:.02em;color:var(--ink-light);opacity:0;
  margin:12px 0 0;animation:fadeUp .6s ease .1s forwards;}
/* a bit larger than the base postcard size elsewhere - this screen has
   nothing else competing for width, so the card can afford to fill more
   of it. Still fluid (width:100% under the cap), so it scales down like
   always on narrower viewports instead of forcing a fixed size. */
.revealed{width:100%;max-width:700px;margin:0 auto;}
.revealed .pc-shot,.revealed .pc-flipwrap{animation:cardRise 1.3s cubic-bezier(.42,0,.58,1) both;
  will-change:opacity,filter,transform;}
/* the postcard is the one thing this screen is about - no caption above
   it (its own tertiary button already says what it is) and no expiry
   note below it (the envelope screen already said that), generous air
   before the flip hint. The flip hint itself goes the other way: this
   page has no other primary action competing for a solid button, and a
   recipient who doesn't already know there's a verso needs the flip
   button to read as unmistakably clickable, not as a quiet aside - so it
   gets the same solid treatment as the composer's own primary .cta,
   instead of the ghost/outline it uses everywhere else. Scoped to
   .revealed (this page) rather than the base .pc-btn-secondary rule. */
.revealed .pc-btn-secondary{margin-top:38px;background:var(--terra);color:#fff;}
.revealed .pc-btn-secondary:hover{background:var(--terra-dark);}
.revealed .pc-tertiary-row{margin-top:22px;}
.revealed .pc-btn-tertiary{text-decoration:none;}
/* pulled further from the card than before, so the "create your own"
   pitch reads as a separate afterthought, not part of the moment itself */
.cta-back{margin-top:64px;text-align:center;opacity:0;animation:fadeUp .6s ease .3s forwards;}
.cta-back-text{font-family:'Lora',Georgia,serif;font-style:italic;font-size:13px;
  color:var(--ink-light);margin:0 0 12px;}
/* the one thing this page asks for - but a recipient came here to receive
   something, not to be sold to, so it stays an outline in the accent
   colour rather than the composer's own solid, urgent red */
.cta-back-link{display:inline-block;background:none;border:1.5px solid var(--terra);
  color:var(--terra);border-radius:10px;padding:13px 28px;font-weight:600;font-size:13.5px;
  text-decoration:none;transition:background .15s,color .15s;}
.cta-back-link:hover{background:var(--terra);color:#fff;}
.expired{max-width:360px;margin:16vh auto 0;text-align:center;padding:0 16px;}
.expired h2{font-family:'Lora',Georgia,serif;font-style:italic;font-size:21px;margin:0 0 6px;}
.expired p{font-size:13.5px;color:var(--ink-mid);}

/* on its side, a phone gives width instead of height - let the card use it.
   The OS already rotates the page; this just makes sure the postcard is
   what grows to fill the extra room, not the chrome around it. */
@media (orientation:landscape) and (max-height:560px){
  .recipient{min-height:auto;padding:20px 16px;}
  .revealed{max-width:560px;}
  .pc-btn-secondary{margin-top:12px;padding:6px 14px;}
  .pc-tertiary-row{margin-top:10px;gap:14px;}
  .cta-back{margin-top:22px;}
  .cta-back-text{font-size:12px;margin-bottom:8px;}
  .cta-back-link{padding:11px 24px;}
}

@keyframes fadeUp{from{opacity:0;transform:translateY(16px);}to{opacity:1;transform:translateY(0);}}
@keyframes fadeOut{to{opacity:0;transform:translateY(-8px);}}
@keyframes cardReveal{from{opacity:0;transform:translateY(30px) scale(.96);}to{opacity:1;transform:translateY(0) scale(1);}}
/* the card settling into focus - soft and faint at first, sharpening and
   fading in at an unhurried, even pace. Kept to a single from/to pair:
   a timing-function is reapplied fresh at every keyframe segment, so
   extra stops here would each get their own fast start and make the
   motion feel like it snaps into place rather than gliding. */
@keyframes cardRise{
  from{opacity:0;transform:translateY(14px);filter:blur(6px);}
  to{opacity:1;transform:translateY(0);filter:blur(0);}
}

/* ---------- modal ---------- */
.modal{position:fixed;inset:0;background:rgba(44,24,16,.45);display:flex;align-items:center;
  justify-content:center;z-index:100;padding:20px;}
.modal-card{background:var(--card);border-radius:12px;padding:24px;max-width:420px;width:100%;
  box-shadow:0 24px 48px rgba(0,0,0,.25);position:relative;}
.modal-card h3{font-family:'Lora',Georgia,serif;font-style:italic;font-size:19px;margin:0 0 6px;}
.modal-card p{font-size:13px;line-height:1.6;color:var(--ink-mid);margin:0 0 14px;}
.modal-close{position:absolute;top:10px;right:12px;background:none;border:none;font-size:18px;color:var(--ink-mid);}
.share-caption{background:var(--panel);border-radius:8px;padding:12px 14px;margin-bottom:14px;}
.share-caption-label{font-size:11px;color:var(--ink-light);margin:0 0 8px;line-height:1.4;}
.share-caption-text{font-family:'Lora',Georgia,serif;font-style:italic;font-size:13.5px;
  color:var(--ink);line-height:1.55;margin:0 0 10px;}
.link-copy{background:none;border:1.5px solid var(--kraft-light);border-radius:7px;
  padding:6px 12px;font-size:11.5px;font-weight:600;color:var(--ink-mid);}
.link-copy.done{border-color:var(--green);color:var(--green);}
.link-row{display:flex;gap:8px;margin-bottom:12px;}
.link-row input{flex:1;min-width:0;border:1.5px solid var(--kraft-light);border-radius:7px;
  padding:9px;font-size:12px;background:var(--panel);color:var(--ink-mid);}
.link-row button{background:var(--ink);color:#fff;border:none;border-radius:7px;
  padding:0 14px;font-size:12.5px;font-weight:600;white-space:nowrap;}
.link-row button.done{background:var(--green);}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms !important;transition-duration:.001ms !important;}
}
