/* Selia by James Hotel — launch landing page
   Brand tokens: Selia Brand Guidelines p.17. Typography follows p.20's principle that
   the system works through contrast: an expressive display face against a quiet
   functional one. Client direction 2026-09-01: use Bahnschrift for everything, so
   --display currently points at --sans. When Berniche Display is licensed, repoint
   --display at it and the contrast returns with no other change. */

/* Bahnschrift — the supporting face named in Brand Guidelines p.20.
   Supplied by the client as a web kit. See bahnschrift-LICENSE.txt and the licensing
   note in the spec before this goes to production. */
@font-face{
  font-family:'Bahnschrift';
  src:url('../fonts/bahnschrift.woff2') format('woff2');
  font-weight:400;font-style:normal;font-display:swap;
}

/* Cormorant Garamond, weight 300, instanced from the variable font and subset to
   latin + the punctuation this page uses (11.9 KB). SIL Open Font Licence 1.1 —
   see cormorant-garamond-OFL.txt. Stands in for Berniche Display: it restores the
   display/functional contrast the guidelines call for on p.20 without waiting on a
   licence. Swap --display to Berniche when Leontios supplies the WOFF2. */
@font-face{
  font-family:'Cormorant Garamond';
  src:url('../fonts/cormorant-garamond-300.woff2') format('woff2');
  font-weight:300;font-style:normal;font-display:swap;
}
:root{
  --limestone:#DDD0BF;
  --sand:#CDB297;
  --clay:#985B3E;
  --blue:#707F86;

  /* Burnt Clay ground. Contrast measured against #985B3E:
       white           5.38:1  -> the ONLY colour that clears body text
       Warm Limestone  3.55:1  -> large display only (>=24px)
       Mineral Sand    2.67:1  -> fails even large text; rules and decoration only
       Mineral Blue    1.30:1  -> unusable here
     White drops below 4.5:1 at 86% opacity, so 90% is the floor for secondary text. */
  --ground:#985B3E;
  --ink:#FFFFFF;                  /* 5.38:1 — all body copy, caps, form */
  --muted:rgba(255,255,255,.90);  /* ~4.7:1 — statement and consent */
  --display-col:var(--limestone); /* 3.55:1 — large display only */

  --sans:'Bahnschrift','DIN Alternate','Helvetica Neue',Arial,sans-serif;
  /* Display face is a HIGH-CONTRAST SERIF against the grotesque, per p.20. Bahnschrift
     alone flattens the system: it is a DIN-lineage signage face and carries no display
     voice at 76px. Bahnschrift keeps every functional role — caps, body, form. */
  --display:'Cormorant Garamond',Georgia,'Times New Roman',serif;

  --gutter:clamp(1.5rem,6vw,4rem);
  --tracked:.36em;                /* micro-caps tracking */

  /* TYPE SCALE — four levels, built WITHOUT weight.
     Both shipped faces are static single weights (Bahnschrift 400, Cormorant 300); no
     variable axis, no second file. Measured 2026-09-01: the same string renders at
     946.19px in Bahnschrift and 843.47px in Cormorant at EVERY weight from 200 to 800,
     so every font-weight declaration below 600 was inert and has been removed. Do not
     add them back without adding the font files first.

     Opacity is not a lever either: white drops under 4.5:1 at 86% on this ground, so
     every body-size element has a hard floor of 90%. Hierarchy is therefore carried by
     SIZE, TRACKING and CASE alone:

       display    52px  .012em  sentence  Limestone
       statement  1.02  .004em  sentence  100%
       contact     .82  .020em  sentence   90%
       labels      .64  .360em  CAPS       90%
       legal       .68  .060em  sentence   90%  */
}

*,*::before,*::after{box-sizing:border-box}
body{
  margin:0;font-family:var(--sans);color:var(--ink);
  /* The Burnt Clay field spans the WHOLE document, not just the hero. Kept on .hero it
     reached 22% black at the hero's lower edge and met a flat footer, drawing a seam
     straight across the page. Same gradient, one continuous ground. */
  background:
    radial-gradient(135% 62% at 50% 26%,
      rgba(0,0,0,0) 0%,
      rgba(0,0,0,0) 46%,
      rgba(0,0,0,.10) 72%,
      rgba(0,0,0,.22) 100%),
    var(--ground);
  background-repeat:no-repeat;
  /* Page is a column: the hero takes the slack, the footer sits under it. Previously
     .hero carried min-height:100svh, so the footer could only ever begin one whole
     viewport down — no amount of spacing work could pull it above the fold. */
  min-height:100svh;display:flex;flex-direction:column;
  /* light text on a mid-tone ground gains apparent weight from subpixel AA */
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
}

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

/* ---------- hero: centred ceremonial stack ---------- */

.hero-wrap{container-type:inline-size;flex:1 0 auto;display:flex;flex-direction:column}

.hero{
  position:relative;
  flex:1 0 auto;
  display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;
  padding:clamp(3rem,11svh,7rem) var(--gutter) clamp(3rem,9svh,5.5rem);
  overflow:hidden;
  /* Field is on body — see the note there. The bottle render is shot on flat #985B3E,
     so the shading must never reach the middle of the stage or the render's feathered
     edge shows as a patch. */
}
/* Grain sits above the field but below content, at a whisper.
   z-index:-1 (not 0) so it cannot cover the text, and so that .hero's children need
   no z-index of their own — any positioned z-index on .stage would create a stacking
   context and silently break the bottle's mix-blend-mode. */
.hero::before{
  content:'';position:fixed;inset:0;z-index:-1;pointer-events:none;
  background:url('../img/grain.svg') repeat;
  opacity:.05;mix-blend-mode:multiply;   /* mid-tone ground: multiply again */
}
.hero > *{position:relative}

/* ---------- type ---------- */

.hero__logo{width:clamp(150px,18cqw,230px);height:auto;display:block}
.rule{
  display:block;width:clamp(40px,5cqw,64px);height:1px;
  background:var(--sand);opacity:.9;
  margin-block:clamp(1.5rem,3.4svh,2.4rem);
}
.caps{
  margin:0;font-size:clamp(.6875rem,1cqw,.84375rem);
  letter-spacing:var(--tracked);text-transform:uppercase;
  color:var(--muted);line-height:1.6;
}
.caps--wide{letter-spacing:.42em}
.caps a{
  color:inherit;text-decoration:none;
  border-bottom:1px solid rgba(255,255,255,.35);padding-bottom:2px;
  transition:border-color .3s ease,color .3s ease;
}
.caps a:hover{color:var(--ink);border-bottom-color:var(--ink)}
.caps a:focus-visible{outline:2px solid #FFFFFF;outline-offset:3px;border-radius:1px}

.hero__tagline{
  margin:0;
  font-family:var(--display);font-weight:300;
  /* Cormorant runs small on the body: it needs ~1.36x Bahnschrift's size to hold the
     same optical presence. 56px grotesque -> 76px serif. */
  font-size:clamp(2.6rem,6.65cqw,4.75rem);
  line-height:1.06;letter-spacing:.012em;
  color:var(--display-col);
  /* The string has only two break points, so two lines can only ever be 483px/334px —
     a 1.45:1 wedge; text-wrap:balance cannot improve on it. Capping the measure just
     under the width of "measured" forces the three-line stack instead:
       Time,
       measured
       differently.
     137 / 335 / 334 — the lower two are within 1px of each other, the column narrows
     to roughly the bottle's own width, and the break now falls where the sentence
     pauses. em, not ch: ch is unreliable across a serif and a grotesque. */
  max-width:4.9em;
}

/* The paragraph people actually read, so it is the one element at full white and the
   only one with near-zero tracking — the two things that separate it from every label
   around it now that weight cannot. */
/* Bahnschrift has no Light, so "lighter" here is built from size and air rather than
   stroke: smaller type with open tracking reads lighter without being lighter. It stays
   at full white because it is the paragraph people actually read. */
.hero__statement{
  margin:0;max-width:60ch;
  font-size:clamp(1rem,1.05cqw,1.0625rem);line-height:1.95;letter-spacing:.03em;
  color:var(--ink);
}

/* deliberate, uneven vertical rhythm */
.s-caps{margin-bottom:clamp(1.4rem,3svh,2.2rem)}
.s-stage   {margin-bottom:clamp(2.6rem,6svh,4.2rem)}
.s-tagline {margin-bottom:clamp(1rem,2.2svh,1.5rem)}
.s-opening {margin-bottom:clamp(2.2rem,5svh,3.4rem)}
.s-statement{margin-bottom:clamp(2.2rem,5svh,3.2rem)}

/* ---------- signup: underline fields, no filled button ---------- */

.signup{
  display:flex;align-items:flex-end;gap:clamp(1rem,3cqw,2rem);
  width:100%;max-width:30rem;
}
/* The field recedes, the button acts. A data-entry field and the primary action must
   not read as the same object — that was the flaw in the filled-Sand pair, not just the
   contrast. Border is Limestone, not Sand: a field boundary is a non-text UI component
   and needs 3:1. Sand is 2.67:1 on this ground and fails; Limestone is 3.55:1. */
.signup input{
  flex:1 1 auto;min-width:0;
  background:transparent;
  border:1px solid #DDD0BF;border-radius:2px;
  padding:.95rem 1.15rem;text-align:center;      /* 44px min touch target */
  /* 16px is a hard floor, not a preference: iOS Safari zooms the whole viewport when a
     field under 16px takes focus, and does not zoom back. */
  font:inherit;font-size:1rem;letter-spacing:.14em;text-transform:uppercase;
  color:#FFFFFF;                                 /* 5.38:1 on Clay */
  transition:border-color .3s ease,background-color .3s ease;
}
/* 90% is the floor: white drops below 4.5:1 at 86% opacity on this ground. */
.signup input::placeholder{color:var(--muted);opacity:1;letter-spacing:.16em}
.signup input:hover{border-color:#FFFFFF;background:rgba(255,255,255,.04)}
.signup input:focus{outline:0;border-color:#FFFFFF;background:rgba(255,255,255,.06)}
.signup input:focus-visible{outline:2px solid #FFFFFF;outline-offset:3px}

/* The brightest element on the page, which is correct: it is the page's only conversion
   goal. Ink on Limestone is 9.90:1. font-weight is omitted deliberately — see the note
   at the top of the type scale. */
.signup button{
  flex:none;
  background:#DDD0BF;
  border:1px solid #DDD0BF;border-radius:2px;cursor:pointer;
  padding:.95rem 1.6rem;                         /* matches the field's height exactly */
  font:inherit;font-size:.8125rem;
  letter-spacing:.24em;text-transform:uppercase;
  color:#2A2622;                                 /* 9.90:1 — pending Leontios, #000 is the fallback */
  transition:filter .3s ease;
}
/* Darkens the approved colour rather than introducing a new one (p.16). */
.signup button:hover{filter:brightness(.94)}
.signup button:active{filter:brightness(.90)}
.signup button:focus-visible{outline:2px solid #FFFFFF;outline-offset:3px}

/* The consent paragraph was removed from index.html (client direction, 2026-09-01:
   redundant, and Privacy Policy was linked twice on one page). These rules stay because
   subscribe.php's no-JavaScript fallback page still uses .consent. */
.consent{
  margin:clamp(1rem,2.4svh,1.5rem) 0 0;max-width:34ch;
  font-size:.72rem;line-height:1.8;letter-spacing:.01em;color:var(--muted);
}
.consent a{color:var(--muted);text-underline-offset:3px}
.consent a:hover{color:var(--sand)}
.signup-done{
  margin:0;font-family:var(--display);font-weight:300;
  font-size:1.25rem;letter-spacing:.02em;color:var(--ink);
}

/* ---------- stage ---------- */

.stage{display:flex;flex-direction:column;align-items:center;position:relative}
.stage::before{
  content:'';position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:120%;aspect-ratio:1/1.35;
  background:radial-gradient(closest-side,rgba(255,255,255,.07),rgba(255,255,255,0) 72%);
  pointer-events:none;
}
/* The render's own baked contact shadow is neutralised in the asset now (see
   build_bottle_variant.py GROUND_Y), so this is the ONLY shadow and it can be tuned.
   Two layers, because one blurred ellipse always reads as a sticker drop-shadow: a tight
   dark core where the glass meets the ground, and a wide soft cast around it. Warm, not
   neutral black — a neutral shadow on a Clay ground goes grey and looks pasted on.
   Sits at bottom:0.6% because the asset is cropped to ~0.8% of Clay below the base. */
.stage::after{
  content:'';position:absolute;left:50%;bottom:.6%;transform:translateX(-50%);
  width:58%;height:3%;
  background:
    radial-gradient(closest-side,rgba(48,22,10,.42),rgba(48,22,10,0) 100%);
  filter:blur(6px);pointer-events:none;
}
.stage::before{
  /* the wide cast — replaces the old white bloom, which lifted the whole stage */
  content:'';position:absolute;left:50%;bottom:-1%;transform:translateX(-50%);
  width:86%;height:7%;
  background:radial-gradient(closest-side,rgba(48,22,10,.20),rgba(48,22,10,0) 100%);
  filter:blur(16px);pointer-events:none;
  aspect-ratio:auto;top:auto;
}
.stage > *{position:relative}

/* ---------- hero grid ---------- */
/* Stacked by default. Above 1200px the bottle moves beside the copy instead of above it:
   at 1920 the page was living in a 500px centre column with 1400px of width unused, and
   the vertical budget was the only thing capping the bottle. Side by side it reaches
   ~880px tall — 2.6x the stacked layout — and the page still lands inside 1080. */
.hero__grid{display:flex;flex-direction:column;align-items:center;width:100%}
.hero__copy{display:flex;flex-direction:column;align-items:center;width:100%}

/* ===== BOTTLE VARIANT: A ===== do not edit by hand; use scripts/use_bottle.sh =====
   Every number that changes when the bottle render changes lives here and nowhere else.
   A = the plain rectangular-label bottle. B = the ornate scalloped-label bottle.
   Swap with:  ./scripts/use_bottle.sh A   |   ./scripts/use_bottle.sh B                */
:root{
  --bottle-aspect:.5217;        /* crop width / crop height */
  --label-left:14.766%;
  --label-top:30.282%;
  --label-width:70.029%;
  --label-height:54.920%;
  --label-lw:.7003;              /* print window width as a fraction of the photo width */
  --label-radius:.046;          /* corner radius as a fraction of --lw */
  --print-inset:3% 3.5% 4%;     /* paper margin inside the label window */
}
/* ===== END BOTTLE VARIANT ===== */

/* ---------- the bottle ---------- */

.vessel-photo{
  --pw:min(74cqw,298px);
  --lw:calc(var(--pw) * var(--label-lw));
  --pr:calc(var(--lw) * .030);          /* print corner radius */
  --lr:calc(var(--lw) * var(--label-radius));
  width:var(--pw);position:relative;display:block;
  /* no filter here: filter would isolate the blend group and break multiply */
}
/* Dark theme uses a real RGBA cutout, so no blend mode and no stacking-context traps. */
.vessel-photo img.bottle{width:100%;height:auto;display:block}

/* label window measured from the white-normalised render */
.labelwin{position:absolute;left:var(--label-left);top:var(--label-top);
  width:var(--label-width);height:var(--label-height)}

/* The printed panel. Margins are a real label's, not a uniform inset: sides and top
   at 9-10%, the foot deeper at 11% because a label optically centres above its middle.
   The old 4.5% ring left the image nearly full-bleed, which is what made it read as a
   screen set into the glass rather than artwork printed on paper. */
.print{
  position:absolute;inset:var(--print-inset);
  border-radius:var(--pr);overflow:hidden;
  /* No hard keyline. Printed ink has no 1px border — it has a soft edge where the
     press meets the stock, plus the faintest lift of the paper around it. */
  box-shadow:
    0 1px 2px rgba(42,38,34,.16),
    0 0 0 1px rgba(42,38,34,.07),
    inset 0 0 calc(var(--lw) * .012) rgba(42,38,34,.22),
    inset 0 1px 0 rgba(255,255,255,.16);
}
.print video,.print img.poster{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;display:block;
  /* Pulled toward the palette so it reads as printed ink, not a lit display.
     Saturation stops at .66: the teaser is the page's first job (spec s1) and the
     carved wordmark reveal has to survive. The reveal is luminance, not hue, so
     desaturating does not weaken it — it is the sunset that loses its screen glow. */
  filter:saturate(.66) contrast(.96) brightness(1.01) sepia(.10);
}
.print img.poster{display:none}

/* Paper grain laid over the image. This is the detail that sells "printed": ink and
   stock must share one surface texture. Without it the image floats above the paper
   however well the colour is matched. */
.print::after{
  content:'';position:absolute;inset:0;pointer-events:none;
  background:url('../img/grain.svg') repeat;
  background-size:88px 88px;
  opacity:.16;mix-blend-mode:multiply;
}

.print-ink{
  position:absolute;inset:0;pointer-events:none;border-radius:inherit;
  background:
    linear-gradient(90deg,
      rgba(42,38,34,.20) 0%,rgba(42,38,34,.04) 12%,
      rgba(255,255,255,.05) 46%,
      rgba(42,38,34,.05) 84%,rgba(42,38,34,.17) 100%),
    radial-gradient(115% 90% at 50% 45%,rgba(205,178,151,0) 55%,rgba(152,91,62,.16) 100%);
  box-shadow:inset 0 0 calc(var(--lw)*.05) rgba(42,38,34,.20);
}

.labelgloss{
  position:absolute;inset:0;pointer-events:none;
  border-radius:var(--lr);overflow:hidden;   /* must match the paper, or it shows a square corner */
  background:
    linear-gradient(101deg,rgba(255,255,255,.34) 0 5%,rgba(255,255,255,.10) 5% 13%,rgba(255,255,255,0) 30%),
    linear-gradient(259deg,rgba(255,255,255,.20) 0 4%,rgba(255,255,255,0) 19%),
    linear-gradient(180deg,rgba(255,255,255,.14) 0 5%,rgba(255,255,255,0) 18%);
}



/* Honeypot. Off-screen, not display:none and not type="hidden" — a bot that parses CSS
   skips anything hidden the obvious way, so the field has to look real to a parser and
   be unreachable for a person. aria-hidden and tabindex="-1" keep it away from screen
   readers and the tab order. */
.hp{position:absolute;left:-9999px;top:auto;width:1px;height:1px;overflow:hidden}

.signup-error{
  margin:clamp(.9rem,2svh,1.3rem) auto 0;max-width:34ch;
  font-size:.78rem;line-height:1.7;letter-spacing:.01em;
  color:var(--ink);                     /* white 5.38:1 — an error must never be the
                                           palest thing on the page */
}
.signup input[aria-invalid="true"]{border-bottom-color:rgba(255,255,255,.85)}

/* ---------- page tail ---------- */
/* There is no standalone <footer> any more (client direction 2026-09-01). The social
   marks moved up under the form and the legal line moved into the copy column, where the
   two-column layout can settle it on the bottle's baseline. The contact icon row is gone:
   "Lardos" in the caps line now carries the Maps link, and a pin pointing at the same URL
   was a duplicate. The phone and email moved into the legal line so the page does not
   lose its visible name/address/phone — Google reconciles a local business partly on it. */

.hero__legal{
  margin:0;
  font-size:.75rem;line-height:1.9;letter-spacing:.05em;
  color:var(--muted);                    /* 4.73:1 — 90% is the floor, cannot recede more */
  text-align:center;
}
/* A phone number broken across two lines is unreadable and unclickable as a unit. */
.hero__legal a[href^="tel:"],.hero__legal a[href^="mailto:"]{white-space:nowrap}
.hero__legal a{color:inherit;text-underline-offset:3px}
.hero__legal a:hover{color:var(--ink)}

/* Centred on the FORM's width, not the column's, so the marks sit under the middle of the
   signup rather than adrift under a 704px column. */
.iconlink{position:relative;display:inline-flex}
.iconlink > a{
  display:inline-flex;align-items:center;justify-content:center;
  width:2.75rem;height:2.75rem;border-radius:50%;   /* 44px touch target */
  color:var(--muted);text-decoration:none;
  transition:color .3s ease,background-color .3s ease;
}
.iconlink > a:hover{color:var(--ink);background:rgba(255,255,255,.07)}
.iconlink > a:focus-visible{outline:2px solid #FFFFFF;outline-offset:2px}

/* Collapsed, not removed: display:none would take the name out of the accessibility tree,
   and max-width:0 would still be measured. Absolute, so revealing it cannot shift the row. */
.iconlink .lbl{
  position:absolute;top:100%;left:50%;
  transform:translateX(-50%) translateY(-.25rem);
  white-space:nowrap;pointer-events:none;
  font-size:.7rem;letter-spacing:.04em;color:var(--ink);
  opacity:0;transition:opacity .3s ease,transform .3s ease;
}
.iconlink > a:hover .lbl,
.iconlink > a:focus-visible .lbl{opacity:1;transform:translateX(-50%) translateY(0)}

.hero__social{
  margin:clamp(1rem,2.2svh,1.6rem) 0 0;
  padding-bottom:1.2rem;                 /* reserves the strip the label drops into */
  width:100%;max-width:34rem;
  display:flex;justify-content:center;align-items:center;gap:clamp(.6rem,1.4cqw,1rem);
}

/* ---------- icons ---------- */
/* Hairline, not UI weight. 1.1 across a 24px box, scaled up rather than thickened —
   thickening is what makes an icon read as an interface control instead of an engraved
   mark. They sit on the baseline, not centred on the line box, or a large glyph beside
   small tracked type looks like it is floating. */
.ico{
  width:1.45em;height:1.45em;flex:none;          /* was 1.05em */
  fill:none;stroke:currentColor;stroke-width:1.1;
  stroke-linecap:round;stroke-linejoin:round;
  vertical-align:-.32em;margin-right:0;
  opacity:.72;                                   /* the mark supports the text, never leads it */
  transition:opacity .3s ease;
}
a:hover .ico{opacity:1}
/* Social marks are the page's last element and carry no text, so they take the size the
   contact icons cannot: 1.75em, 30% over the previous 1.35em. */
/* 25% larger than the contact marks, per client direction 2026-09-01. */
.ico--social{
  width:1.8125em;height:1.8125em;                /* 1.45em x 1.25 */
  margin-right:0;vertical-align:middle;opacity:.7;
}

.foot__social{margin-top:clamp(.15rem,.5svh,.4rem)}

/* ---------- prose page (privacy) ---------- */

.prose-wrap{background:var(--ground);min-height:100svh;container-type:inline-size}
.prose{
  max-width:64ch;margin:0 auto;
  padding:clamp(3rem,10svh,6rem) var(--gutter) clamp(4rem,10svh,7rem);
  color:var(--muted);
  font-size:.92rem;line-height:1.9;letter-spacing:.012em;
}
.prose h1{
  font-family:var(--display);font-weight:300;
  font-size:clamp(2rem,5cqw,3rem);line-height:1.1;letter-spacing:.012em;
  color:var(--display-col);margin:0 0 clamp(2rem,5svh,3rem);
}
.prose h2{
  font-size:.72rem;letter-spacing:var(--tracked);text-transform:uppercase;
  color:var(--ink);margin:clamp(2.4rem,6svh,3.4rem) 0 .9rem;
}
.prose p{margin:0 0 1.2rem}
.prose a{color:var(--ink);text-underline-offset:3px}
.prose a:hover{color:var(--limestone)}
.prose__updated{font-size:.72rem;letter-spacing:.06em;opacity:.8;margin-top:clamp(2.5rem,6svh,3.5rem)}
.prose__back{
  display:inline-block;margin-top:clamp(2rem,5svh,3rem);
  font-size:.68rem;letter-spacing:var(--tracked);text-transform:uppercase;
  color:var(--ink);text-decoration:none;border-bottom:1px solid var(--sand);padding-bottom:2px;
}

[hidden]{display:none!important}

/* ---------- replay ---------- */

.replay{
  margin-top:clamp(1.1rem,2.4svh,1.6rem);
  background:none;border:0;cursor:pointer;font:inherit;
  font-size:.62rem;letter-spacing:var(--tracked);text-transform:uppercase;
  color:var(--muted);padding:.4rem;transition:color .3s ease;
}
.replay:hover{color:var(--sand)}
.replay:focus-visible{outline:2px solid var(--clay);outline-offset:3px}

/* ---------- desktop ---------- */

/* narrow screens: stack the signup so the field has room */
@container (max-width:479px){
  /* Stacked, and the button matches the field's width. A centred, narrower button under a
     full-width field reads as a secondary action; on a page with exactly one thing to do,
     the primary action should be the full width of the column and a large tap target. */
  .signup{flex-direction:column;align-items:stretch;gap:.9rem}
  .signup button{align-self:stretch;padding-inline:.6rem}
}

/* .foot declares container-type itself, and an @container query resolves against the
   nearest ANCESTOR container — never the element that declares one. So .foot's own
   padding cannot be set from the block below; it stayed on the base 96px/64px and ate
   114px of the fold while its children correctly took the tighter values. Media query
   for the element, container query for its children. */
@media (min-width:1024px){
}

/* Desktop, re-cut 2026-09-01 so the whole page — hero, signup and footer — lands
   inside 1920x1080 without scrolling. Every gap here is smaller than the base scale.
   Stated plainly: this trades away some of the negative space the brand guidelines ask
   for (p.15, "use less, give it more room"). The client asked for no scrolling on a
   landing page and that is the cost; the base scale below 1024px keeps the roomier
   rhythm. All of it is svh-based, so a taller screen automatically gets the air back. */
@container (min-width:1024px){
  .hero{padding:clamp(1.4rem,3svh,2.4rem) var(--gutter) clamp(1.2rem,2.4svh,1.9rem)}

  /* Bottle is the single biggest block on the page, so it takes the biggest cut:
     74svh -> 31svh. At 1080 that is a 335px-tall render, still the hero's anchor. */
  .vessel-photo{--pw:calc(clamp(260px,31svh,400px) * .5317)}

  .s-caps     {margin-bottom:clamp(2rem,4.3svh,3.2rem)}
  .s-stage    {margin-bottom:clamp(.85rem,1.8svh,1.4rem)}
  .s-tagline  {margin-bottom:clamp(1.2rem,2.6svh,2rem)}
  .s-opening  {margin-bottom:clamp(.85rem,1.8svh,1.4rem)}
  .s-statement{margin-bottom:clamp(2.4rem,5svh,3.8rem)}

  /* Two lines, not three: at this width the measure can afford it, and the third line
     cost 80px of vertical the page no longer has. */
  .hero__tagline{font-size:clamp(2rem,2.7cqw,3.3rem);max-width:7.2em}
  .hero__statement{max-width:54ch}
  /* wide enough for two lines: at 60ch it broke to three and orphaned "Policy." */
  .consent{max-width:82ch;margin-top:clamp(.7rem,1.5svh,1.1rem)}

}

/* Must sit AFTER the 1024px block: both are container queries on .hero-wrap with equal
   specificity, so the later one wins. Placed earlier, every override here lost. */
@container (min-width:1200px){
  .hero__grid{
    flex-direction:row;align-items:stretch;justify-content:center;
    gap:clamp(2.5rem,5cqw,6rem);
  }
  /* stretch, not centre: the grid's height is the bottle's height (it is the taller of
     the two), so the copy column's bottom edge IS the bottle's bottom edge. */
  .hero__copy{align-items:flex-start;text-align:left;width:auto;max-width:44rem}
  .hero__main{
    display:flex;flex-direction:column;align-items:flex-start;width:100%;
    /* Top, not centred: the block starts level with the top of the bottle and the legal
       line still falls to the base. margin-block:auto split the slack evenly, which put
       113px above the copy and 113px below it. */
    margin-bottom:auto;
  }
  .hero__legal{text-align:left;margin-top:0}
  /* The copy has to hold its own beside a 650px bottle, or the column reads as a
     caption pinned to a photograph rather than the other half of the composition. */
  .caps{font-size:clamp(.78rem,.8cqw,.84375rem)}
  .hero__grid .s-stage{margin-bottom:0}
  .hero__tagline{max-width:none;font-size:clamp(2.6rem,3.95cqw,4.75rem)}
  .hero__statement{max-width:68ch}   /* two lines, not three */
  /* the form fills the copy column rather than sitting in a 30rem island */
  .signup{max-width:34rem}
  .vessel-photo{--pw:calc(clamp(380px,60svh,760px) * .5317)}
}

/* ---------- phones ---------- */
/* Measured 2026-09-01: the stacked layout was the desktop composition scaled down, and it
   buried the signup form 210-334px BELOW the fold on every phone. The bottle alone took
   63% of an iPhone 14 and 78% of an SE. The desktop earns a large render because the copy
   sits beside it; stacked, the same render just buries the only call to action on the
   page. Everything below is scoped to phones — the desktop composition is untouched. */
@container (max-width:767px){
  .hero{padding:clamp(.9rem,2.4svh,1.5rem) var(--gutter) clamp(.9rem,2.2svh,1.4rem)}

  /* Sized from the viewport HEIGHT, not its width: on a phone the constraint is vertical.
     Everything else is measured first and the bottle takes what is left. */
  .vessel-photo{--pw:calc(clamp(150px,38svh,360px) * var(--bottle-aspect))}

  .s-stage    {margin-bottom:clamp(.7rem,1.8svh,1.1rem)}
  .s-caps     {margin-bottom:clamp(.5rem,1.4svh,.85rem)}
  .s-tagline  {margin-bottom:clamp(.45rem,1.2svh,.75rem)}
  .s-statement{margin-bottom:clamp(.8rem,2svh,1.25rem)}

  /* One line on phones (client direction 2026-09-01). Measured against the real font:
     the string needs 1079px of width per 100px of size, so the largest one-line size is
     8.05cqw. Set at 7.7cqw for a ~5% margin. Deliberately NOT white-space:nowrap — if the
     webfont ever fails and the wider Georgia fallback loads, wrapping to two lines is
     recoverable and a horizontal overflow is not. */
  .hero__tagline{font-size:clamp(1.5rem,7.7cqw,2.8rem);line-height:1.15;max-width:none}
  .hero__statement{font-size:1rem;line-height:1.65;letter-spacing:.02em}

  /* Deliberate two-line break; the separator only makes sense inline. Tracking comes
     down from .42em to .24em here or line one wraps on its own and the "designed" break
     becomes three lines — 34 tracked caps need 388px at .42em and a phone gives 342px. */
  .caps--wide{letter-spacing:.24em}
  .caps{font-size:clamp(.64rem,3cqw,.72rem)}
  .caps__sep{display:none}
  .caps__second{display:block}

  .hero__social{margin-top:clamp(.5rem,1.4svh,.85rem);padding-bottom:0}
  .hero__legal{margin-top:clamp(.6rem,1.6svh,1rem);line-height:1.75}
}

/* ---------- entrance ---------- */

@media (prefers-reduced-motion:no-preference){
  .reveal{opacity:0;transform:translateY(14px);animation:rise .9s cubic-bezier(.22,.61,.36,1) forwards}
  .reveal:nth-child(1){animation-delay:.05s}
  .reveal:nth-child(2){animation-delay:.15s}
  .reveal:nth-child(3){animation-delay:.25s}
  .reveal:nth-child(4){animation-delay:.35s}
  .reveal:nth-child(5){animation-delay:.55s}
  .reveal:nth-child(6){animation-delay:.65s}
  .reveal:nth-child(7){animation-delay:.75s}
  .reveal:nth-child(8){animation-delay:.85s}
  @keyframes rise{to{opacity:1;transform:none}}
}

@media (prefers-reduced-motion:reduce){
  .print video,.arch-body video{display:none}
  .print img.poster,.arch-body img.poster{display:block}
}
