/* ============================================================================
   THE DESTINATION — site.css
   The design system, derived from the Canva comps. Nothing here is taste:
   every token traces to a pixel measured in design/*.jpg or design/*.svg.

   Comps (ground truth):
     design/front-page.jpg   1366x768   HOME       + front-page.svg (vector)
     design/branding-page.jpg 1366x2735 BRANDING
     design/photography.jpg  1366x1616  PHOTOGRAPHY
     design/contact-form.svg 1366x768   CONTACT    (the .jpg is corrupt — ignore it)

   DESIGN WIDTH IS 1366. Every geometry token below is a percentage of 1366
   (x, widths) or of the stage/band height (y, heights), so the layout is
   pixel-exact at 1366 and proportional above/below it.

   ── HOW SIZING WORKS ──────────────────────────────────────────────────────
   `--k` is the design-pixel. Inside a `.td-stage` it equals
   (stage width / 1366) px, so `calc(18 * var(--k))` is 18px at a 1366 stage
   and scales with the stage everywhere else. Font sizes are NOT written as
   px: they are written as (measured cap height ÷ the font's cap/em ratio),
   so retuning one ratio variable corrects every size at once.

   ── FONT STATUS — READ THIS ───────────────────────────────────────────────
   All three families are self-hosted in assets/fonts. ZERO external requests:
   the client is Danish and a Google Fonts request is a GDPR exposure.
   SERIF  ✅ Cormorant Garamond, VARIABLE wght 300..700, set to 400. Proven to
             be the comp's serif (white-band paragraph x/cap 0.60, asc/cap 1.20
             against the file's 0.618 / 1.160) and now at the comp's WEIGHT:
             background-referenced ink mass of that paragraph is 1.012x the
             comp's, against a same-page sans control spanning 0.94..1.09.
   SANS   ✅ Outfit, VARIABLE wght 100..900, set to 350. A SUBSTITUTION — the
             comp is Canva Sans, which cannot be licensed. Best of 50 families
             on the comp's own set widths. See --font-sans.
   SCRIPT ✅ Ephesis. Also a substitution, and a weaker one: no open script
             reproduces the comp's swash capitals. See --font-script.
   ⚠ Two of the three are substitutions, so a residual width error is expected
   and is documented per token below. Do not read it as a tuning bug.
   ========================================================================= */


/* ==========================================================================
   1. FONTS — self-hosted only. Zero external requests (Danish client; a
      Google Fonts request would be a GDPR exposure). All src are relative
      same-origin paths.
   ========================================================================== */

/* --- SERIF: Cormorant Garamond — PRESENT --------------------------------
   ★ The two roman files are VARIABLE (fvar wght 300..700, read from the
   files), so the range below is not a claim, it is the axis. Declaring a
   single `300` clamped the instance to Light and no font-weight could reach
   the comp's colour. The italics ARE static 300 and stay declared as 300. */
@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("fonts/cormorant-garamond-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("fonts/cormorant-garamond-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
    U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Italic is self-hosted but NOTHING IN THE COMPS IS ITALIC. Declared so the
   file is not silently dropped; do not apply font-style:italic anywhere. */
@font-face {
  font-family: "Cormorant Garamond";
  font-style: italic;
  font-weight: 300;
  font-display: swap;
  src: url("fonts/cormorant-garamond-italic-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Cormorant Garamond";
  font-style: italic;
  font-weight: 300;
  font-display: swap;
  src: url("fonts/cormorant-garamond-italic-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
    U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --- SANS: Outfit (OFL) — PRESENT, VARIABLE 100..900 ---------------------
   The comp's sans is Canva Sans (proprietary, cannot be licensed), so this is
   a substitution and the only honest test is whether it sets the COMP'S OWN
   STRINGS at the comp's widths. Signature read off the outlined glyphs in
   design/contact-form.svg and design/front-page.svg, magnified 32x by
   rewriting the viewBox (vector, not a 12px JPEG):
       cap 7.406 · x-height 5.000 ('u', flat)      -> x/cap  0.675
       'd','h','b' top == 'V','P','L' top to 1/32px -> asc/cap 1.000
       'o' 5.125 x 5.125                            -> o w/h  1.003
       vertical stem 0.844..0.875 / cap             -> 0.116
       single-storey a and g; '1' is a bare stem, ink 0.232 of cap
       digit '0' ink w/h 0.997 over six samples     -> a true CIRCLE
   Ranked 50 openly-licensed families by set width against 28 comp strings in
   five independent blocks (home nav, contact labels, contact list, branding
   price band, photography caption), each block fitted with ONE size:
       Outfit  mean error 1.13%   <- shipped
       Jost    mean error 2.07%   <- what this file used to ship
   and Outfit is also closer on every shape ratio that matters:
       x/cap    0.683 vs Jost 0.657   (comp 0.675)
       asc/cap  1.029 vs Jost 1.114   (comp 1.004 — Jost's ascenders overshoot
                                       the caps by 11%, which the comp's do not)
       '0' w/h  0.790 vs Jost 0.722   (comp 0.997)
   ⚠ NOTHING open reproduces the comp's digits. The comp's zero is a circle and
   its '1' is a bare stem (advance 0.40 of the zero's); across all 50 families
   the roundest zero was 0.83 and only Poiret One — a hairline display face with
   x/cap 0.600 — got near, so the price VALUES stay ~3% narrow by construction.
   That is a property of the substitution, not a tuning error: do not chase it
   by widening --cap-price, which would then break the LABELS in the same rows.
   Rejected with numbers, so nobody re-tests them: Rubik and Rethink Sans fit the
   widths but have a double-storey 'a'; Josefin Sans fits the widths with
   x/cap 0.572; Poppins x/cap 0.786; Futura LT (on this box) asc/cap 1.094 and
   tabular figures. */
@font-face {
  font-family: "Outfit";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/outfit-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Outfit";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/outfit-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
    U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* Fallback that IS present, so nothing 404s the page into a system font. */
@font-face {
  font-family: "Lato";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("fonts/lato-300-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Lato";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("fonts/lato-300-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
    U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --- SCRIPT: Ephesis (OFL) — PRESENT ------------------------------------
   Used for exactly two strings in the whole site: "Prices" (branding olive
   band) and "Contact Form" (contact page).
   ⚠ THE COMP'S CAPITALS CARRY SWASHES AND NO OPEN FACE REPRODUCES THEM. The
   comp's 'P' has a looped bowl, and its stem swings LEFT at the baseline into
   a long under-swash. Ranked 52 openly-licensed script families by blurred
   stroke-field correlation against design/contact-form.svg's own outlined
   "Contact Form": the whole field spans 0.26–0.43 with no separation, i.e.
   none of them is the face. (Plain IoU is the wrong metric on a hairline
   script — a one-pixel offset destroys the overlap — which is why that number
   is a correlation, not an overlap.)
   So the choice is made on the one thing that IS measurable: the ink box of
   "Prices" at the size that puts the P at the comp's 29.142 above the baseline.
   Comp = 89.335 x 34.576 design px.
       Ephesis        90.0 x 34.0   (w +0.7%, h -1.7%, aspect 2.647 vs 2.584)
       Pinyon Script  96.0 x 29.0   (w +7.5%, h -16.1%, aspect 3.310)
   Pinyon has no swash on the 'P' at all, so it can only reach the comp's WIDTH
   by -0.031em of tracking while staying 16% short of its HEIGHT — the tracking
   was papering over a missing glyph feature.
   ⚠ The trade is real and is a judgement, not a measurement: Pinyon is the
   better match for the HAND (true copperplate thick-thin contrast); Ephesis is
   more monoline. It is chosen because the swash and the ink box are what the
   comp actually shows at this size. Second choice if the hand matters more than
   the swash: Pinyon Script. Reject Great Vibes (aspect right, strokes too
   thick), Alex Brush and Italianno (brush-monoline, aspect 3.4/3.1), and
   Corinthia/My Soul (aspect close, hand far too informal). */
@font-face {
  font-family: "Ephesis";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/ephesis-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Ephesis";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/ephesis-400-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
    U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
    U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}


/* ==========================================================================
   2. TOKENS
   ========================================================================== */

:root {
  /* --- design pixel -------------------------------------------------------
     Redefined inside .td-stage as (stage width / 1366)px. The 1px default is
     what applies on mobile, where the layout reflows instead of scaling. */
  --k: 1px;
  --design-w: 1366;

  /* --- COLOUR — every value sampled, never chosen -------------------------
     Method: modal RGB over a rectangle well inside the region (flat areas
     returned a 100% modal share over 60k-120k px samples, which is the proof
     that no band carries a gradient, texture or overlay). */

  /* backgrounds */
  --c-paper:   #ffffff; /* branding white band; photography page (5 samples) */
  --c-taupe:   #8e8077; /* branding band 3 (142,128,119) */
  --c-olive:   #533c1a; /* branding band 4 AND the whole contact page (83,60,26) */
  --c-stone:   #d4cbc2; /* branding footer band 5 (212,203,194) */
  --c-canvas:  #8e8076; /* SVG rect literal behind the home photo. Use as the
                           body background so first paint is brand-coloured.
                           1 blue-step off --c-taupe; they are different
                           literals in different sources, kept apart. */
  --c-field:   #725f43; /* contact textarea (114,95,67) = white @ 18% over
                           --c-olive; the SVG carries the literal
                           fill-opacity="0.18" that produces it. */

  /* ink */
  --c-ink-light:  #ffffff; /* nav, shield stroke, monogram, all copy on the
                              photo / taupe / olive. The home and contact SVGs
                              both carry #FFFFFF literals; the warm off-whites
                              you can sample off the JPGs (#FFFDF1 etc.) are
                              chroma-subsampling artefacts, not the design. */
  --c-ink-paper:  #3d3a32; /* branding white-band paragraph + its monogram */
  --c-ink-photo:  #3d392d; /* photography caption */
  --c-ink-mark:   #3e382a; /* photography monogram */
  --c-ink-stone:  #423625; /* branding footer shield + "Contact" */
  --c-ink-muted:  #545147; /* "Discover more" on white */
  --c-placeholder: var(--c-olive); /* contact textarea placeholder: measured
                              core #543D1B, i.e. the page colour showing
                              through the lightened field */

  /* --- TYPEFACES ---------------------------------------------------------- */
  --font-sans:   "Outfit", "Lato", sans-serif;
  --font-serif:  "Cormorant Garamond", serif;
  --font-script: "Ephesis", "Cormorant Garamond", cursive;

  --w-sans: 350;   /* ✅ MEASURED, and this is why Outfit is shipped as the
                      VARIABLE file rather than the static 400 instance. The
                      comp's vertical stem / cap is 0.116 (contact-form.svg
                      outlines: 'l' 0.844..0.875 over cap 7.422). Outfit's own
                      stem/cap runs 0.1025 at wght 300, 0.1163 at 350, 0.1300
                      at 400 — so 350 lands the stroke weight to 0.3%, where
                      400 is 12% heavy. Set widths barely move (the advance of
                      "Social media templates" changes 0.25% from 350 to 400),
                      so this does NOT invalidate --cap-sans below.
                      Lato 300 is 0.064 — half as heavy — which is why the
                      rejected build read as spindly. */
  --w-serif: 400;  /* ✅ MEASURED, was 300. The comp carries more ink than
                      Cormorant Light does at the same set width: contact's
                      "Back" ink area is 77.6 design-px² in the vector against
                      72.2 at wght 300 (−7.0%) and 80.1 at 400 (+3.1%), and
                      the branding paragraph is ~11% light. Zero-error is
                      ~370; 400 is the reviewed value and halves the error.
                      ⚠ Advance width is nearly weight-invariant here, so this
                      does NOT reflow: the branding paragraph still sets in 4
                      lines at the same baselines (line ink 271.4/284.5/268.9/
                      192.6 → 277.4/290.5/270.9/195.6). If those widths matter
                      more than colour, retune --tracking-serif, not this. */
  --w-script: 400;

  /* --- CAP-HEIGHT RATIOS (cap height ÷ em) -------------------------------
     Font sizes are derived, not typed: size = measured-cap ÷ ratio.
     Retune ONE of these and every size in that family follows. */
  --cap-sans:   0.7049; /* ✅ MEASURED for Outfit. NOT Outfit's own cap ratio
                           (0.694) — it is the ratio that makes the SET WIDTHS
                           match the comp, which is the only thing a substituted
                           family can match. Solved as the single value that
                           minimises relative width error over the 25 comp
                           strings on the three graded pages, in their four
                           blocks and with the tokens below:
                             price band (13) rms 3.21%  mean -0.63%
                             caption     (3) rms 1.88%  mean -1.72%
                             nav         (2) rms 1.36%  mean +1.33%
                             taupe body  (7) rms 2.29%  mean +1.27%
                             ALL             rms 2.72%
                           Jost on the same 25 strings could not do better than
                           rms 3.61%, and its price band split +3.6% on the
                           LABELS against -3.7% on the VALUES — one size that
                           fits neither, which is the defect this replaces.
                           Was 0.6691 (Jost). */
  --cap-serif:  0.625;  /* ✅ MEASURED from cormorant-garamond-latin.woff2
                           ('H' bbox ymax 625 / upm 1000) */
  --cap-script: 0.672;  /* ✅ MEASURED for Ephesis by render: canvas
                           actualBoundingBoxAscent of "Prices" at font-size
                           1000px = 672, i.e. the swash 'P' rises 0.672em above
                           the baseline. Was 0.68652 (Pinyon). Note this is the
                           ascent of the WORD, which for a swash face is the 'P'
                           — measure it the same way if the face changes. */

  /* --- INK OFFSET --------------------------------------------------------
     A comp measures where the INK starts; CSS positions the LINE BOX. The
     first cap top sits below the line-box top by
         lead/2 + fontSize * ((hheaAscent - hheaDescent)/2 - capRatio)
     The bracketed term is a pure font constant; the classes below subtract
     the whole expression as a negative margin-top, so every positioned text
     element's `top` IS the measured ink top, on one line or six. */
  --hlf-sans:   -0.3340; /* ✅ MEASURED BY RENDER for Outfit, not from its
                            tables: at font-size 1000px / line-height 1000px
                            Chromium puts the cap top of 'H' 166px below the
                            line-box top, so the constant is 0.166 - 0.5. The
                            same probe reproduces the previous Jost value to
                            0.005 (-0.357 measured vs -0.3523 shipped), which
                            is how the probe was checked before it was trusted.
                            ⚠ Weight-invariant here: Outfit 350 and 400 both
                            give 166. Was -0.3523 (Jost). */
  --hlf-serif:  -0.3065; /* ✅ MEASURED Cormorant Garamond Light: hhea
                            ascent 924, descent -287, cap 625, upm 1000
                            -> (0.924-0.287)/2 - 0.625 */
  --hlf-script: -0.4220; /* ✅ VERIFIED BY RENDER, not derived. ⚠ The hhea
                            formula above does NOT predict a script face —
                            Chromium is not laying these out on hhea, and the
                            Pinyon value this replaces (-0.44702) was likewise
                            fitted, not computed. Ephesis at 1000px/1000px puts
                            the ink top of "Prices" 78px below the line-box top
                            -> 0.078 - 0.5.
                            ⛔ THIS CONSTANT IS PER-STRING ON A SWASH FACE, not
                            per-font: the same probe gives -0.469 for "Contact
                            Form", because its 'F' loop rises higher than the
                            'P'. One shared value cannot place both, so the md
                            size carries its own constant below. Do not collapse
                            them back into one. */
  --hlf-script-md: -0.4690; /* ✅ MEASURED BY RENDER, same probe: Ephesis puts
                            the ink top of "Contact Form" 31px below the
                            line-box top at 1000px -> 0.031 - 0.5.
                            ⚠ CURRENTLY UNUSED — see --cap-script-md. */

  /* --- MEASURED CAP HEIGHTS (design px at 1366) --------------------------- */
  --cap-nav:        13.333; /* front-page.svg outlined 'H': 10 SVG units x 1.3333 */
  --cap-footer:     13;     /* branding "Contact" C: y2588 -> baseline y2601 */
  --cap-caption:    13.044; /* ✅ RE-MEASURED, was 12.95 (and 12.5 before that).
                               Sub-pixel 0.5-coverage height of the 'A' in
                               "A selection", isolated by its own column run so
                               no neighbouring ascender is included: y389.459 ->
                               y402.504 = 13.044. The rendered page agrees —
                               swept 12.95 / 13.00 / 13.044 / 13.09 / 13.14 the
                               photography diff is .1681 / .1682 / .1506 / .1574
                               / .1577 %, so the measurement and the gate pick
                               the same value independently.
                               ⚠ The 12.95 it replaces was swept against JOST's
                               advances; a cap token that is fitted rather than
                               measured has to be re-fitted whenever the family
                               changes, which is why this one is now the glyph.
                               Consumed by --fs-caption -> .td-t-caption, which
                               the site uses exactly ONCE (the photography
                               caption), so the blast radius is that element. */
  --cap-price:      11.50;  /* ✅ RE-MEASURED, was 11 read off whole JPEG pixels.
                               The old 11 set the whole price band ~4.5% narrow
                               by itself, independently of which family was in
                               it — the largest single error on this page.
                               Sub-pixel 0.5-coverage extents of the flat-edged
                               glyphs in the olive band: V 11.558, W 11.630,
                               L 11.680, P 11.692, and the four '1' stems
                               11.327 / 11.494 / 11.570 / 11.685. Mean 11.58,
                               spread 3% — a 12px cap in a JPEG cannot do
                               better. The whole-band pixel diff, which
                               integrates 13 strings instead of one glyph edge,
                               has a broad optimum at 11.49..11.53 (0.698%
                               against 0.714% at 11.60 and 0.717% at 11.65), so
                               the band's own agreement breaks the tie INSIDE
                               the single-glyph spread. ⚠ Not a needle:
                               11.47/11.49/11.51/11.53 give .7035/.6982/.6983/
                               .6991, which is what makes it a measurement
                               rather than a rounding artefact. */
  --cap-body:       9.5;    /* branding taupe body / Instagram line 'V' y1424->1433.5 */
  --cap-microlink:  8;      /* branding "Discover more" D: y1045 -> baseline y1053 */
  --cap-label:      9;      /* contact "Company name" C: y192 -> baseline y201 */
  --cap-listitem:   8;      /* contact bullet list "Website" W: y370 -> baseline y378 */
  --cap-para:       10;     /* branding white paragraph 'W': y963 -> baseline y973 */
  --cap-link:       14;     /* "View photography" V: y1272 -> baseline y1286;
                               "Contact us now" C: y2328 -> baseline y2342 */
  --cap-script-lg:  29;     /* branding "Prices" P: y1959 -> baseline y1988 */
  --cap-script-md:  22.90;  /* ⚠ NO CONSUMER TODAY. `.td-t-script--md` is used by
                               no page: contact/index.html ships "Contact Form"
                               as an inlined SVG PATH lifted from the comp's own
                               contact-form.svg, with the words in a .td-sr span
                               for assistive tech — which is strictly better than
                               any substituted face, and is why that heading is
                               exact. "Prices" cannot be done the same way:
                               branding-page has no vector, only a JPEG, so it is
                               the one string on the site still set in the script
                               FONT. Kept (with the value re-derived) so the
                               second size is ready if a page ever needs it:
                               the comp's vector ink block is 175.417 x 28.250,
                               Ephesis sets "Contact Form" 0.829em tall, so the
                               size reproducing 28.250 is 34.08px and this token
                               is 34.08 x --cap-script (0.672) = 22.90.
                               Was 24, scaled off the Prices box. */

  /* --- TYPE SIZES (derived; multiply by --k at the point of use) ---------- */
  --fs-nav:       calc(var(--cap-nav)       / var(--cap-sans));   /* ~18.3 */
  --fs-footer:    calc(var(--cap-footer)    / var(--cap-sans));   /* ~17.8 */
  --fs-caption:   calc(var(--cap-caption)   / var(--cap-sans));   /* ~17.1 */
  --fs-price:     calc(var(--cap-price)     / var(--cap-sans));   /* ~15.1 */
  --fs-body:      calc(var(--cap-body)      / var(--cap-sans));   /* ~13.0 */
  --fs-microlink: calc(var(--cap-microlink) / var(--cap-sans));   /* ~11.0 */
  --fs-label:     calc(var(--cap-label)     / var(--cap-sans));   /* ~12.3 */
  --fs-listitem:  calc(var(--cap-listitem)  / var(--cap-sans));   /* ~11.0 */
  --fs-para:      calc(var(--cap-para)      / var(--cap-serif));  /* 16.0 exact */
  --fs-link:      calc(var(--cap-link)      / var(--cap-serif));  /* 22.4 exact */
  --fs-script-lg: calc(var(--cap-script-lg) / var(--cap-script)); /* ~43.9 */
  --fs-script-md: calc(var(--cap-script-md) / var(--cap-script)); /* ~36.4 */

  /* --- LEADING — measured baseline-to-baseline, expressed in design px ---- */
  --lead-para:     17.8;  /* white band: baselines 972 / 990 / 1007.5 / 1025.5 */
  --lead-body:     15.2;  /* taupe body: ink tops 1318..1394, 6 lines */
  --lead-caption:  20;    /* photography: baselines 403 / 423 / 443 */
  --lead-pricerow: 32.7;  /* olive rows: tops 2053..2249, deltas 33/33/32/33/32/33 */
  --lead-listitem: 21;    /* contact bullets: tops 328..454, all deltas exactly 21 */

  /* --- TRACKING — THE TWO FAMILIES DIFFER. Do not unify them. ------------
     SANS = 0, and that is PROVEN, not assumed: in the outlined nav the
     side-bearings are symmetric ('i' in "List": lsb 0.8281 vs rsb 0.8249 SVG
     units) and tracking is added to the right side only, so symmetry puts it
     at 0 within 0.005px.
     SERIF = -0.040em, and that is MEASURED — it is the single number that
     reconciles Cormorant's natural set width with the comp's. Solved from
     five strings across two independent source files, at font-size = cap/0.625:
        "Back"            (contact SVG, rendered 4x — no JPEG blur)  -0.0393
        white para line 1 (branding.jpg)                             -0.0400
        white para line 2                                            -0.0401
        white para line 4                                            -0.0404
        "Contact us now"  (olive band)                               -0.0318
     ⚠⚠ THOSE FIVE SOLVES WERE RUN AT --w-serif 300, AND THE WEIGHT IS NOW
     400. Cormorant's wght axis moves the ADVANCE WIDTHS (+2.2% from 300 to
     400), so -0.040em stopped reproducing the comp the moment the weight was
     corrected: measured at 400 it set the white paragraph 277.4 / 290.5 /
     195.6 against the comp's 274 / 287 / 192 — 3.4px wide on every line.
     RE-SOLVED AT 400: -0.044em gives 274.1 / 287.0 / 193.4 and keeps the
     comp's four-line break. Whole-page diff vs branding-page.jpg, with the
     build round-tripped through the comp's own encoder (JPEG q80, 4:2:0 —
     its luma quantisation table matches PIL q80 with ZERO error):
        -0.040em  meanAbs 1.4922  1.0468% of px >16
        -0.044em  meanAbs 1.4742  1.0295%
     ★ Weight and tracking are NOT independent. Change one and re-solve the
     other, or the two errors cancel and the page looks settled while both
     are wrong — which is exactly the state this file was in.
     ⚠ WITHOUT any negative tracking the paragraph sets 12% wide and breaks
     into FIVE lines instead of the comp's four — structural, not a nuance. */
  --tracking-sans:   0;
  --word-sans:       0em;
  --tracking-serif:  -0.044em;
  --tracking-script: -0.003em; /* ✅ RE-SOLVED for Ephesis: at the size that
                                  puts the swash 'P' at the comp's 29.142 above
                                  the baseline (43.37px), "Prices" already sets
                                  90.0px against the comp's 89.335, so the
                                  correction is 0.665px over 5 gaps.
                                  ⚠ It matters that this is now ~0: the -0.024em
                                  it replaces was not tracking, it was Pinyon
                                  being squeezed to the comp's WIDTH while its
                                  swash-less 'P' left it 16% short of the comp's
                                  HEIGHT. A tracking value doing a glyph's job
                                  is the tell that the face is wrong. */

  /* --- SPACING — every value is a measured gap, in design px -------------- */
  --sp-gutter-pct:  6.8814%; /* taupe band left margin 94/1366 — the only real
                                left margin in the comps; reused as the mobile
                                gutter so even the reflow traces to a comp */
  --gap-chev-link:      12; /* "View photography" ink end 236 -> chevron 249 */
  --gap-chev-contact:   18; /* taupe "Contact us now" 1109 -> 1128 */
  --gap-chev-centred:   24; /* olive "Contact us now" 731 -> 755 */
  --gap-chev-micro:      5; /* "Discover more" 711 -> 716 (an ordinary word space) */
  --gap-chev-back:      17; /* contact "‹ Back": chevron ends 86, text at 103 */

  /* --- CHEVRONS ----------------------------------------------------------
     TWO DIFFERENT MARKS. Do not unify them.
       heavy  7 x 11 px, thick — the three "› " links
       light  4 x  7 px, hairline — "Discover more" only
       back  10 x 16 px, thick, pointing LEFT — contact page only
     Drawn as masks so they inherit `color` and never depend on a font's
     glyph coverage. */
  --chev-w: 7;
  --chev-h: 11;

  /* --- MARKS -------------------------------------------------------------
     assets/monogram.svg       outlined shield + interlocking TD ligature
     assets/monogram-mark.svg  the bare ligature, no shield
     Both carry `currentColor` and zero hex literals, so they tint from `color`
     when used as a mask. */
  /* ⛔ INLINED AS DATA URIs ON PURPOSE — do not "tidy" these back into
     url("monogram.svg"). A mask-image whose request fails resolves to
     `none`, and `none` does not hide the element: it UNMASKS it, so
     .td-mark's `background-color: currentColor` paints the whole box. On the
     home and branding heroes that is a 76x100 SOLID WHITE RECTANGLE over the
     photograph (measured: 7,600 px, 27.9% of the mark's box, with the mask
     neutralised). @supports cannot help — the property is supported, the
     FETCH is what fails — and an <img> fallback cannot inherit `color`,
     which the footer mark needs (#423625 on stone, white on the photo).
     Inlining removes the failure mode entirely and costs 3,216 bytes gzipped
     for both marks, against two fewer HTTP requests.
     Source of truth stays assets/monogram.svg / monogram-mark.svg — these are
     those files, comment-stripped and percent-encoded, nothing redrawn.
     Regenerate with: python3 -c "import re,urllib.parse;s=re.sub(r'\\s+',' ',
     re.sub(r'<!--.*?-->','',open(F).read(),flags=re.S)).strip();
     print(urllib.parse.quote(s,safe=\"~!@$*()_-+=:;,./?[]{}| '\")
     .replace(chr(34),chr(39)).replace('%20',' '))" */
  --mark-shield: url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220.4375 0.6875 57.125 75.125%22 fill=%22currentColor%22 role=%22img%22 aria-label=%22The Destination%22%3E%3Cdefs%3E%3CclipPath id=%22m-frame%22%3E%3Crect x=%220%22 y=%220%22 width=%2258%22 height=%2276%22/%3E%3C/clipPath%3E %3CclipPath id=%22m-shield%22%3E%3Cpath d=%22M 0.425781 0.6875 L 57.554688 0.6875 L 57.554688 75.78125 L 0.425781 75.78125 Z M 0.425781 0.6875 %22 clip-rule=%22nonzero%22/%3E%3C/clipPath%3E %3CclipPath id=%22m-t%22%3E%3Crect x=%220%22 y=%220%22 width=%2228%22 height=%2252%22/%3E%3C/clipPath%3E %3CclipPath id=%22m-d%22%3E%3Crect x=%220%22 y=%220%22 width=%2231%22 height=%2253%22/%3E%3C/clipPath%3E%3C/defs%3E%3Cg clip-path=%22url(%23m-frame)%22%3E%3Cg clip-path=%22url(%23m-shield)%22%3E%3Cpath fill=%22currentColor%22 d=%22M 57.421875 20.320312 C 57.390625 17.835938 57.292969 15.785156 57.195312 14.382812 C 57.179688 14.027344 56.871094 13.738281 56.519531 13.738281 C 54.664062 13.691406 52.96875 12.949219 51.679688 11.769531 C 50.386719 10.578125 49.5 8.945312 49.242188 7.109375 L 49.226562 7.042969 C 49.144531 6.703125 49.097656 6.332031 49.082031 5.945312 C 49.046875 5.574219 48.742188 5.285156 48.355469 5.300781 C 48.015625 5.316406 47.660156 5.332031 47.339844 5.332031 C 46.582031 5.332031 45.484375 5.074219 44.191406 4.640625 C 42.257812 4.011719 39.886719 3.027344 37.289062 2.203125 C 34.691406 1.363281 31.867188 0.6875 28.996094 0.6875 C 25.175781 0.6875 21.414062 1.882812 18.1875 3.042969 C 16.574219 3.625 15.09375 4.203125 13.800781 4.625 C 12.511719 5.042969 11.414062 5.316406 10.65625 5.316406 C 10.316406 5.316406 9.976562 5.300781 9.640625 5.285156 C 9.269531 5.269531 8.945312 5.558594 8.914062 5.929688 C 8.882812 6.316406 8.847656 6.6875 8.769531 7.027344 L 8.753906 7.09375 C 8.496094 8.929688 7.605469 10.578125 6.316406 11.753906 C 5.027344 12.949219 3.332031 13.671875 1.476562 13.722656 C 1.121094 13.738281 0.832031 14.011719 0.800781 14.367188 C 0.703125 15.769531 0.605469 17.820312 0.574219 20.304688 C 0.523438 23.644531 0.445312 27.046875 0.445312 30.433594 C 0.445312 37.824219 0.816406 45.128906 2.816406 51.582031 C 3.816406 54.808594 5.21875 57.808594 7.203125 60.488281 C 9.171875 63.164062 11.71875 65.503906 14.964844 67.375 C 20.253906 70.441406 23.675781 72.519531 25.753906 73.84375 C 26.804688 74.503906 27.511719 74.972656 27.964844 75.28125 C 28.191406 75.425781 28.351562 75.539062 28.449219 75.601562 C 28.496094 75.632812 28.53125 75.667969 28.5625 75.683594 L 28.59375 75.699219 C 28.835938 75.875 29.175781 75.875 29.417969 75.699219 C 29.578125 75.585938 32.773438 73.3125 43.03125 67.375 C 46.273438 65.503906 48.824219 63.164062 50.792969 60.488281 C 53.757812 56.46875 55.4375 51.710938 56.386719 46.582031 C 57.339844 41.453125 57.550781 35.949219 57.550781 30.417969 C 57.550781 27.046875 57.46875 23.644531 57.421875 20.320312 Z M 53.855469 51.164062 C 52.886719 54.277344 51.550781 57.132812 49.679688 59.664062 C 47.808594 62.195312 45.417969 64.390625 42.339844 66.164062 C 33.804688 71.101562 30.125 73.519531 28.996094 74.28125 C 27.867188 73.503906 24.191406 71.085938 15.65625 66.164062 C 12.574219 64.375 10.1875 62.179688 8.316406 59.664062 C 5.511719 55.875 3.898438 51.324219 2.976562 46.339844 C 2.058594 41.355469 1.847656 35.933594 1.847656 30.433594 C 1.847656 27.078125 1.929688 23.675781 1.976562 20.335938 C 2.007812 18.207031 2.089844 16.414062 2.171875 15.078125 C 4.105469 14.882812 5.863281 14.042969 7.253906 12.785156 C 8.769531 11.382812 9.816406 9.480469 10.125 7.300781 C 10.171875 7.109375 10.203125 6.898438 10.238281 6.6875 C 10.382812 6.6875 10.511719 6.6875 10.65625 6.6875 C 11.703125 6.6875 12.898438 6.367188 14.238281 5.929688 C 16.253906 5.269531 18.609375 4.300781 21.125 3.496094 C 23.65625 2.6875 26.351562 2.058594 28.996094 2.058594 C 32.53125 2.058594 36.144531 3.171875 39.335938 4.332031 C 40.933594 4.914062 42.417969 5.480469 43.773438 5.929688 C 45.113281 6.367188 46.304688 6.6875 47.355469 6.6875 C 47.5 6.6875 47.644531 6.6875 47.773438 6.6875 C 47.808594 6.898438 47.839844 7.09375 47.886719 7.300781 C 48.195312 9.464844 49.242188 11.382812 50.757812 12.785156 C 52.128906 14.042969 53.90625 14.882812 55.855469 15.078125 C 55.9375 16.414062 56.019531 18.207031 56.050781 20.335938 C 56.097656 23.675781 56.179688 27.0625 56.179688 30.433594 C 56.164062 37.757812 55.777344 44.953125 53.855469 51.164062 Z M 53.855469 51.164062 %22 fill-opacity=%221%22 fill-rule=%22nonzero%22/%3E%3C/g%3E%3Cg transform=%22matrix(1, 0, 0, 1, 14, 9)%22%3E%3Cg clip-path=%22url(%23m-t)%22%3E%3Cg fill=%22currentColor%22 fill-opacity=%221%22%3E%3Cg transform=%22translate(0.956525, 39.136026)%22%3E%3Cg%3E%3Cpath d=%22M 7.953125 -25.546875 C 6.179688 -25.546875 4.851562 -25.09375 3.96875 -24.1875 C 3.09375 -23.289062 2.457031 -21.765625 2.0625 -19.609375 C 2.0625 -19.554688 1.984375 -19.53125 1.828125 -19.53125 C 1.671875 -19.53125 1.59375 -19.554688 1.59375 -19.609375 C 1.707031 -20.617188 1.832031 -21.9375 1.96875 -23.5625 C 2.113281 -25.1875 2.1875 -26.40625 2.1875 -27.21875 C 2.1875 -27.363281 2.265625 -27.4375 2.421875 -27.4375 C 2.578125 -27.4375 2.65625 -27.363281 2.65625 -27.21875 C 2.65625 -26.601562 3.410156 -26.296875 4.921875 -26.296875 C 7.304688 -26.210938 10.125 -26.171875 13.375 -26.171875 C 15.03125 -26.171875 16.785156 -26.195312 18.640625 -26.25 L 21.5 -26.296875 C 22.507812 -26.296875 23.257812 -26.363281 23.75 -26.5 C 24.238281 -26.644531 24.554688 -26.925781 24.703125 -27.34375 C 24.722656 -27.457031 24.800781 -27.515625 24.9375 -27.515625 C 25.082031 -27.515625 25.15625 -27.457031 25.15625 -27.34375 C 25.070312 -26.53125 24.988281 -25.296875 24.90625 -23.640625 C 24.820312 -21.992188 24.78125 -20.648438 24.78125 -19.609375 C 24.78125 -19.554688 24.695312 -19.53125 24.53125 -19.53125 C 24.363281 -19.53125 24.28125 -19.554688 24.28125 -19.609375 C 24.195312 -21.796875 23.75 -23.332031 22.9375 -24.21875 C 22.125 -25.101562 20.773438 -25.546875 18.890625 -25.546875 C 17.628906 -25.546875 16.734375 -25.488281 16.203125 -25.375 C 15.671875 -25.257812 15.304688 -25.019531 15.109375 -24.65625 C 14.910156 -24.289062 14.8125 -23.675781 14.8125 -22.8125 L 14.8125 -3.40625 C 14.8125 -2.539062 14.910156 -1.910156 15.109375 -1.515625 C 15.304688 -1.117188 15.679688 -0.847656 16.234375 -0.703125 C 16.796875 -0.566406 17.679688 -0.5 18.890625 -0.5 C 18.972656 -0.5 19.015625 -0.414062 19.015625 -0.25 C 19.015625 -0.0820312 18.972656 0 18.890625 0 C 17.960938 0 17.21875 -0.015625 16.65625 -0.046875 L 13.375 -0.078125 L 10.21875 -0.046875 C 9.664062 -0.015625 8.894531 0 7.90625 0 C 7.851562 0 7.828125 -0.0820312 7.828125 -0.25 C 7.828125 -0.414062 7.851562 -0.5 7.90625 -0.5 C 9.113281 -0.5 10 -0.566406 10.5625 -0.703125 C 11.125 -0.847656 11.507812 -1.125 11.71875 -1.53125 C 11.925781 -1.9375 12.03125 -2.5625 12.03125 -3.40625 L 12.03125 -22.890625 C 12.03125 -23.753906 11.929688 -24.363281 11.734375 -24.71875 C 11.535156 -25.070312 11.171875 -25.296875 10.640625 -25.390625 C 10.109375 -25.492188 9.210938 -25.546875 7.953125 -25.546875 Z M 7.953125 -25.546875 %22/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/g%3E%3Cg transform=%22matrix(1, 0, 0, 1, 14, 15)%22%3E%3Cg clip-path=%22url(%23m-d)%22%3E%3Cg fill=%22currentColor%22 fill-opacity=%221%22%3E%3Cg transform=%22translate(1.094973, 39.742337)%22%3E%3Cg%3E%3Cpath d=%22M 12.75 0.171875 C 11.9375 0.171875 11 0.128906 9.9375 0.046875 C 9.539062 0.015625 9.039062 -0.0078125 8.4375 -0.03125 C 7.832031 -0.0625 7.164062 -0.078125 6.4375 -0.078125 L 3.578125 -0.046875 C 3.066406 -0.015625 2.363281 0 1.46875 0 C 1.414062 0 1.390625 -0.0820312 1.390625 -0.25 C 1.390625 -0.414062 1.414062 -0.5 1.46875 -0.5 C 2.539062 -0.5 3.320312 -0.566406 3.8125 -0.703125 C 4.300781 -0.847656 4.632812 -1.125 4.8125 -1.53125 C 5 -1.9375 5.09375 -2.5625 5.09375 -3.40625 L 5.09375 -22.890625 C 5.09375 -23.734375 5.007812 -24.347656 4.84375 -24.734375 C 4.675781 -25.128906 4.34375 -25.40625 3.84375 -25.5625 C 3.351562 -25.71875 2.578125 -25.796875 1.515625 -25.796875 C 1.460938 -25.796875 1.4375 -25.878906 1.4375 -26.046875 C 1.4375 -26.210938 1.460938 -26.296875 1.515625 -26.296875 L 3.625 -26.25 C 4.800781 -26.195312 5.738281 -26.171875 6.4375 -26.171875 C 7.5 -26.171875 8.550781 -26.210938 9.59375 -26.296875 C 10.820312 -26.359375 11.675781 -26.390625 12.15625 -26.390625 C 15.070312 -26.390625 17.660156 -25.828125 19.921875 -24.703125 C 22.179688 -23.578125 23.925781 -22.046875 25.15625 -20.109375 C 26.394531 -18.171875 27.015625 -16 27.015625 -13.59375 C 27.015625 -10.875 26.375 -8.46875 25.09375 -6.375 C 23.820312 -4.28125 22.09375 -2.664062 19.90625 -1.53125 C 17.71875 -0.394531 15.332031 0.171875 12.75 0.171875 Z M 12.328125 -0.46875 C 14.566406 -0.46875 16.554688 -0.9375 18.296875 -1.875 C 20.035156 -2.8125 21.390625 -4.195312 22.359375 -6.03125 C 23.328125 -7.875 23.8125 -10.085938 23.8125 -12.671875 C 23.8125 -15.109375 23.347656 -17.320312 22.421875 -19.3125 C 21.492188 -21.300781 20.175781 -22.867188 18.46875 -24.015625 C 16.757812 -25.171875 14.785156 -25.75 12.546875 -25.75 C 11.335938 -25.75 10.398438 -25.675781 9.734375 -25.53125 C 9.078125 -25.394531 8.59375 -25.113281 8.28125 -24.6875 C 7.976562 -24.269531 7.828125 -23.644531 7.828125 -22.8125 L 7.828125 -3.875 C 7.828125 -3.03125 7.945312 -2.367188 8.1875 -1.890625 C 8.425781 -1.410156 8.875 -1.050781 9.53125 -0.8125 C 10.1875 -0.582031 11.117188 -0.46875 12.328125 -0.46875 Z M 12.328125 -0.46875 %22/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
  --mark-bare:   url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%2216.4375 20.5625 25.6875 34.375%22 fill=%22currentColor%22 role=%22img%22 aria-label=%22The Destination%22%3E%3Cdefs%3E%3CclipPath id=%22m-frame%22%3E%3Crect x=%220%22 y=%220%22 width=%2258%22 height=%2276%22/%3E%3C/clipPath%3E %3CclipPath id=%22m-shield%22%3E%3Cpath d=%22M 0.425781 0.6875 L 57.554688 0.6875 L 57.554688 75.78125 L 0.425781 75.78125 Z M 0.425781 0.6875 %22 clip-rule=%22nonzero%22/%3E%3C/clipPath%3E %3CclipPath id=%22m-t%22%3E%3Crect x=%220%22 y=%220%22 width=%2228%22 height=%2252%22/%3E%3C/clipPath%3E %3CclipPath id=%22m-d%22%3E%3Crect x=%220%22 y=%220%22 width=%2231%22 height=%2253%22/%3E%3C/clipPath%3E%3C/defs%3E%3Cg transform=%22matrix(1, 0, 0, 1, 14, 9)%22%3E%3Cg clip-path=%22url(%23m-t)%22%3E%3Cg fill=%22currentColor%22 fill-opacity=%221%22%3E%3Cg transform=%22translate(0.956525, 39.136026)%22%3E%3Cg%3E%3Cpath d=%22M 7.953125 -25.546875 C 6.179688 -25.546875 4.851562 -25.09375 3.96875 -24.1875 C 3.09375 -23.289062 2.457031 -21.765625 2.0625 -19.609375 C 2.0625 -19.554688 1.984375 -19.53125 1.828125 -19.53125 C 1.671875 -19.53125 1.59375 -19.554688 1.59375 -19.609375 C 1.707031 -20.617188 1.832031 -21.9375 1.96875 -23.5625 C 2.113281 -25.1875 2.1875 -26.40625 2.1875 -27.21875 C 2.1875 -27.363281 2.265625 -27.4375 2.421875 -27.4375 C 2.578125 -27.4375 2.65625 -27.363281 2.65625 -27.21875 C 2.65625 -26.601562 3.410156 -26.296875 4.921875 -26.296875 C 7.304688 -26.210938 10.125 -26.171875 13.375 -26.171875 C 15.03125 -26.171875 16.785156 -26.195312 18.640625 -26.25 L 21.5 -26.296875 C 22.507812 -26.296875 23.257812 -26.363281 23.75 -26.5 C 24.238281 -26.644531 24.554688 -26.925781 24.703125 -27.34375 C 24.722656 -27.457031 24.800781 -27.515625 24.9375 -27.515625 C 25.082031 -27.515625 25.15625 -27.457031 25.15625 -27.34375 C 25.070312 -26.53125 24.988281 -25.296875 24.90625 -23.640625 C 24.820312 -21.992188 24.78125 -20.648438 24.78125 -19.609375 C 24.78125 -19.554688 24.695312 -19.53125 24.53125 -19.53125 C 24.363281 -19.53125 24.28125 -19.554688 24.28125 -19.609375 C 24.195312 -21.796875 23.75 -23.332031 22.9375 -24.21875 C 22.125 -25.101562 20.773438 -25.546875 18.890625 -25.546875 C 17.628906 -25.546875 16.734375 -25.488281 16.203125 -25.375 C 15.671875 -25.257812 15.304688 -25.019531 15.109375 -24.65625 C 14.910156 -24.289062 14.8125 -23.675781 14.8125 -22.8125 L 14.8125 -3.40625 C 14.8125 -2.539062 14.910156 -1.910156 15.109375 -1.515625 C 15.304688 -1.117188 15.679688 -0.847656 16.234375 -0.703125 C 16.796875 -0.566406 17.679688 -0.5 18.890625 -0.5 C 18.972656 -0.5 19.015625 -0.414062 19.015625 -0.25 C 19.015625 -0.0820312 18.972656 0 18.890625 0 C 17.960938 0 17.21875 -0.015625 16.65625 -0.046875 L 13.375 -0.078125 L 10.21875 -0.046875 C 9.664062 -0.015625 8.894531 0 7.90625 0 C 7.851562 0 7.828125 -0.0820312 7.828125 -0.25 C 7.828125 -0.414062 7.851562 -0.5 7.90625 -0.5 C 9.113281 -0.5 10 -0.566406 10.5625 -0.703125 C 11.125 -0.847656 11.507812 -1.125 11.71875 -1.53125 C 11.925781 -1.9375 12.03125 -2.5625 12.03125 -3.40625 L 12.03125 -22.890625 C 12.03125 -23.753906 11.929688 -24.363281 11.734375 -24.71875 C 11.535156 -25.070312 11.171875 -25.296875 10.640625 -25.390625 C 10.109375 -25.492188 9.210938 -25.546875 7.953125 -25.546875 Z M 7.953125 -25.546875 %22/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/g%3E%3Cg transform=%22matrix(1, 0, 0, 1, 14, 15)%22%3E%3Cg clip-path=%22url(%23m-d)%22%3E%3Cg fill=%22currentColor%22 fill-opacity=%221%22%3E%3Cg transform=%22translate(1.094973, 39.742337)%22%3E%3Cg%3E%3Cpath d=%22M 12.75 0.171875 C 11.9375 0.171875 11 0.128906 9.9375 0.046875 C 9.539062 0.015625 9.039062 -0.0078125 8.4375 -0.03125 C 7.832031 -0.0625 7.164062 -0.078125 6.4375 -0.078125 L 3.578125 -0.046875 C 3.066406 -0.015625 2.363281 0 1.46875 0 C 1.414062 0 1.390625 -0.0820312 1.390625 -0.25 C 1.390625 -0.414062 1.414062 -0.5 1.46875 -0.5 C 2.539062 -0.5 3.320312 -0.566406 3.8125 -0.703125 C 4.300781 -0.847656 4.632812 -1.125 4.8125 -1.53125 C 5 -1.9375 5.09375 -2.5625 5.09375 -3.40625 L 5.09375 -22.890625 C 5.09375 -23.734375 5.007812 -24.347656 4.84375 -24.734375 C 4.675781 -25.128906 4.34375 -25.40625 3.84375 -25.5625 C 3.351562 -25.71875 2.578125 -25.796875 1.515625 -25.796875 C 1.460938 -25.796875 1.4375 -25.878906 1.4375 -26.046875 C 1.4375 -26.210938 1.460938 -26.296875 1.515625 -26.296875 L 3.625 -26.25 C 4.800781 -26.195312 5.738281 -26.171875 6.4375 -26.171875 C 7.5 -26.171875 8.550781 -26.210938 9.59375 -26.296875 C 10.820312 -26.359375 11.675781 -26.390625 12.15625 -26.390625 C 15.070312 -26.390625 17.660156 -25.828125 19.921875 -24.703125 C 22.179688 -23.578125 23.925781 -22.046875 25.15625 -20.109375 C 26.394531 -18.171875 27.015625 -16 27.015625 -13.59375 C 27.015625 -10.875 26.375 -8.46875 25.09375 -6.375 C 23.820312 -4.28125 22.09375 -2.664062 19.90625 -1.53125 C 17.71875 -0.394531 15.332031 0.171875 12.75 0.171875 Z M 12.328125 -0.46875 C 14.566406 -0.46875 16.554688 -0.9375 18.296875 -1.875 C 20.035156 -2.8125 21.390625 -4.195312 22.359375 -6.03125 C 23.328125 -7.875 23.8125 -10.085938 23.8125 -12.671875 C 23.8125 -15.109375 23.347656 -17.320312 22.421875 -19.3125 C 21.492188 -21.300781 20.175781 -22.867188 18.46875 -24.015625 C 16.757812 -25.171875 14.785156 -25.75 12.546875 -25.75 C 11.335938 -25.75 10.398438 -25.675781 9.734375 -25.53125 C 9.078125 -25.394531 8.59375 -25.113281 8.28125 -24.6875 C 7.976562 -24.269531 7.828125 -23.644531 7.828125 -22.8125 L 7.828125 -3.875 C 7.828125 -3.03125 7.945312 -2.367188 8.1875 -1.890625 C 8.425781 -1.410156 8.875 -1.050781 9.53125 -0.8125 C 10.1875 -0.582031 11.117188 -0.46875 12.328125 -0.46875 Z M 12.328125 -0.46875 %22/%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E");

  /* --- MOTION ------------------------------------------------------------
     The comps are one static state. Nothing animates on load. The only motion
     is a hover fade on links, at a length short enough to read as a material
     response rather than an effect. */
  --hover-ease: 220ms cubic-bezier(0.4, 0, 0.2, 1);
  --hover-dim: 0.62;
}


/* ==========================================================================
   3. RESET + PAGE
   ========================================================================== */

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

html {
  /* The pixel gate runs at a 1366 viewport. A rendered scrollbar steals ~15px
     of layout width and shrinks --k proportionally, so the QA harness must
     launch Chromium with --hide-scrollbars (or size the viewport to
     1366 + scrollbarWidth). Nothing in CSS can fix that from inside. */
  -webkit-text-size-adjust: 100%;
}

body, figure, blockquote, dl, dd, h1, h2, h3, h4, p, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg, picture, video { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button, input, textarea, select { font: inherit; color: inherit; }

.td-page {
  background: var(--c-canvas);
  color: var(--c-ink-paper);
  font-family: var(--font-sans);
  font-weight: var(--w-sans);
  letter-spacing: var(--tracking-sans);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}
.td-page--paper { background: var(--c-paper); }
.td-page--olive { background: var(--c-olive); }

/* Screen-reader-only, for the page title each comp deliberately omits. */
.td-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}


/* ==========================================================================
   4. STAGE — the design frame
      A .td-stage is one comp region drawn at its measured aspect ratio.
      It is the container that --k is measured against, so EVERY absolutely
      positioned child must live inside one.
      Set the aspect with a modifier, or with `style="--stage-h: <px>"`.
   ========================================================================== */

.td-stage {
  position: relative;
  width: 100%;
  container-type: inline-size;
  --k: calc(100cqw / 1366);
  --stage-h: 768;
  aspect-ratio: var(--design-w) / var(--stage-h);
  overflow: hidden;
}
/* If container query units are unavailable, fall back to a fixed 1366 render
   rather than silently collapsing every calc() to zero. */
@supports not (container-type: inline-size) {
  .td-stage { --k: 1px; }
}

.td-stage--home        { --stage-h: 768;  } /* front-page.jpg 1366x768 */
.td-stage--photography { --stage-h: 1616; } /* photography.jpg 1366x1616 */
.td-stage--contact     { --stage-h: 768;  } /* contact-form.svg 1366x768 */

/* Branding bands. Sum check: 768+408+636+660+263 = 2735 = comp height. */
.td-band { position: relative; width: 100%; container-type: inline-size;
           --k: calc(100cqw / 1366);
           aspect-ratio: var(--design-w) / var(--stage-h); overflow: hidden; }
@supports not (container-type: inline-size) { .td-band { --k: 1px; } }
.td-band--hero   { --stage-h: 768; }                              /* photo */
.td-band--paper  { --stage-h: 408; background: var(--c-paper); color: var(--c-ink-paper); }
.td-band--taupe  { --stage-h: 636; background: var(--c-taupe); color: var(--c-ink-light); }
.td-band--olive  { --stage-h: 660; background: var(--c-olive); color: var(--c-ink-light); }
.td-band--footer { --stage-h: 263; background: var(--c-stone); color: var(--c-ink-stone); }

/* Absolutely positioned child. Give it left/top (and width where the comp
   sets a measured text column) from the geometry classes below. */
.td-abs { position: absolute; }


/* ==========================================================================
   5. TYPE PRIMITIVES
      Each sets family, size, leading and the ink offset, so a positioned
      element's `top` is the top of the INK — the thing the comps measure.
   ========================================================================== */

/* Every text class sets three numbers — --fs (design px), --lead (design px,
   defaults to single-line) and --hlf (the font constant) — and the base class
   turns them into size, leading and the ink correction. Add `.td-t` alongside
   any `.td-t-*`. */
.td-t {
  display: block;
  letter-spacing: var(--tracking);
  word-spacing: var(--word);
  white-space: nowrap;
  --tracking: var(--tracking-sans);
  --word: var(--word-sans);
  --lead: var(--fs);
  --hlf: var(--hlf-sans);
  font-size: calc(var(--fs) * var(--k));
  line-height: calc(var(--lead) * var(--k));
  margin-top: calc(-1 * (var(--lead) / 2 + var(--fs) * var(--hlf)) * var(--k));
}
.td-t--wrap { white-space: normal; }

/* sans */
.td-t-nav       { font-family: var(--font-sans); font-weight: var(--w-sans);
                  --fs: var(--fs-nav); }
.td-t-footer    { font-family: var(--font-sans); font-weight: var(--w-sans);
                  --fs: var(--fs-footer); }
.td-t-caption   { font-family: var(--font-sans); font-weight: var(--w-sans);
                  --fs: var(--fs-caption); --lead: var(--lead-caption);
                  white-space: normal; }
.td-t-price     { font-family: var(--font-sans); font-weight: var(--w-sans);
                  --fs: var(--fs-price); }
.td-t-body      { font-family: var(--font-sans); font-weight: var(--w-sans);
                  --fs: var(--fs-body); --lead: var(--lead-body);
                  white-space: normal; }
.td-t-microlink { font-family: var(--font-sans); font-weight: var(--w-sans);
                  --fs: var(--fs-microlink); }
.td-t-label     { font-family: var(--font-sans); font-weight: var(--w-sans);
                  --fs: var(--fs-label); }
.td-t-listitem  { font-family: var(--font-sans); font-weight: var(--w-sans);
                  --fs: var(--fs-listitem); }

/* serif — upright, weight 300. NOTHING in the comps is italic. */
.td-t-para      { font-family: var(--font-serif); font-weight: var(--w-serif);
                  font-style: normal; --hlf: var(--hlf-serif);
                  --tracking: var(--tracking-serif); --word: 0em;
                  --fs: var(--fs-para); --lead: var(--lead-para);
                  white-space: normal; }
.td-t-link      { font-family: var(--font-serif); font-weight: var(--w-serif);
                  font-style: normal; --hlf: var(--hlf-serif);
                  --tracking: var(--tracking-serif); --word: 0em;
                  --fs: var(--fs-link); }

/* script — two strings on the whole site */
.td-t-script     { font-family: var(--font-script); font-weight: var(--w-script);
                   --hlf: var(--hlf-script); --fs: var(--fs-script-lg);
                   --tracking: var(--tracking-script); --word: 0em; }
/* ⛔ The ink offset is per-STRING on a swash face, not per-font — the 'F' loop
   in "Contact Form" rises higher than the 'P' in "Prices". See --hlf-script. */
.td-t-script--md { --fs: var(--fs-script-md); --hlf: var(--hlf-script-md); }


/* ==========================================================================
   6. CHEVRONS + LINKS
      No buttons exist anywhere in the comps. Every call to action is plain
      text plus a chevron. Do not add a border, fill, radius or shadow.
   ========================================================================== */

.td-chev {
  display: inline-block;
  vertical-align: baseline;
  width:  calc(var(--chev-w) * var(--k));
  height: calc(var(--chev-h) * var(--k));
  background-color: currentColor;
  -webkit-mask: var(--chev-src) no-repeat center / contain;
          mask: var(--chev-src) no-repeat center / contain;
  --chev-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 7 11'%3E%3Cpath d='M1.3 1.2 L5.5 5.5 L1.3 9.8' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* hairline variant — "Discover more" only, measured 4 x 7 px */
.td-chev--light {
  --chev-w: 4; --chev-h: 7;
  --chev-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 7'%3E%3Cpath d='M0.8 0.8 L3.2 3.5 L0.8 6.2' fill='none' stroke='%23000' stroke-width='1.1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* left-pointing, contact page "‹ Back" only, measured 10 x 16 px */
.td-chev--back {
  --chev-w: 10; --chev-h: 16;
  --chev-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 16'%3E%3Cpath d='M7.6 1.7 L2.4 8 L7.6 14.3' fill='none' stroke='%23000' stroke-width='2.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* A link is a text run + a chevron separated by a MEASURED gap. Set the gap
   with --chev-gap (design px); the defaults per class below are the comp's. */
.td-link { display: inline-flex; align-items: center; white-space: nowrap;
           --chev-gap: var(--gap-chev-link);
           transition: opacity var(--hover-ease); }
.td-link .td-chev { margin-left: calc(var(--chev-gap) * var(--k)); flex: none; }
.td-link:hover, .td-link:focus-visible { opacity: var(--hover-dim); }
.td-link:focus-visible { outline: 1px solid currentColor; outline-offset: 4px; }

.td-link--contact  { --chev-gap: var(--gap-chev-contact); }
.td-link--centred  { --chev-gap: var(--gap-chev-centred); }
.td-link--micro    { --chev-gap: var(--gap-chev-micro); }
.td-link--back     { flex-direction: row-reverse; --chev-gap: 0; }
.td-link--back .td-chev { margin-left: 0;
                          margin-right: calc(var(--gap-chev-back) * var(--k)); }


/* ==========================================================================
   7. MARKS — the shield and the bare ligature
      Masked, so they take their colour from `color` and stay crisp at any
      scale. Aspect ratios are the measured ink boxes.
   ========================================================================== */

.td-mark {
  display: block;
  background-color: currentColor;
  -webkit-mask: var(--mark-shield) no-repeat center / contain;
          mask: var(--mark-shield) no-repeat center / contain;
}
.td-mark--bare {
  -webkit-mask-image: var(--mark-bare);
          mask-image: var(--mark-bare);
}


/* ==========================================================================
   8. HOME — front-page.jpg / front-page.svg
      One full-bleed photograph. Two nav items. One shield. Nothing else
      is DRAWN: no scrim, no bar, no burger, no scroll.
      The headline and the footer exist but are not drawn — the comp omits
      both, so each ships as .td-sr (§3) to keep the h1 and the contentinfo
      landmark. They are out of flow and clipped, so they paint no pixels.
   ========================================================================== */

.td-hero { position: absolute; inset: 0; z-index: 0; }
.td-hero__img { width: 100%; height: 100%; object-fit: cover;
                object-position: 50% 50%; }
/* The photo is opaque and overhangs all four canvas edges. There is NO tint,
   gradient, mask, filter or mix-blend-mode over it — proven from the SVG,
   where the photo's drawn extent is x -144.6..1610.6, y -925.4..1268.5 and
   the #8E8076 rect beneath it is never visible. Do not add one. */

/* Nav. THE PAIR IS NOT CENTRED — combined span 524.60..826.53, centre 675.56,
   7.4px LEFT of the canvas centre 683. Each item is positioned absolutely at
   its own measured pen-start; a centred flex row lands 7.4px right. */
.td-nav { position: absolute; inset: 0; z-index: 1; color: var(--c-ink-light); }
.td-nav__item { position: absolute; top: 7.4193%; }  /* cap top y57 of 768 */
.td-nav__item--hotels   { left: 38.4040%; }          /* pen start x524.599 */
.td-nav__item--branding { left: 54.9291%; }          /* pen start x750.331 */

/* The nav links are links, and were the only ones on the site with no designed
   states: no hover change at all, and Chrome's default `outline: auto 1px
   rgb(16,16,16)` ringing white type on a photograph in system black. Same fade
   and same ring as .td-link, so both families behave identically. The comp is
   one static state, so this is invisible until you hover or tab. */
.td-nav__item { transition: opacity var(--hover-ease); }
.td-nav__item:hover,
.td-nav__item:focus-visible { opacity: var(--hover-dim); }
.td-nav__item:focus-visible { outline: 1px solid currentColor; outline-offset: 4px; }

/* Current-page state, BRANDING PAGE ONLY — never on home. The rule is 1px at
   y71 spanning x751..832, i.e. 7px WIDER than the glyphs, because the
   underlined string carries a trailing space. Reproduce with the padding,
   not by widening a border on the text. */
.td-nav__item--current {
  padding-right: calc(7 * var(--k));
  border-bottom: 1px solid currentColor;
  padding-bottom: calc(2 * var(--k));  /* rule sits 2px below the baseline */
}

/* Shield lockup — upper third, horizontally centred (ink centre x682.65 vs
   canvas centre 683.0). All the empty space is below it. */
.td-hero__mark {
  position: absolute; z-index: 1; color: var(--c-ink-light);
  left: 47.1867%; top: 20.9531%;
  width: 5.5754%;      /* 76.16 / 1366 */
  aspect-ratio: 76.16 / 100.13;
}

/* --- 8b. HOME AT ANY WINDOW ----------------------------------------------
   The comp is a 1366x768 artboard; a browser window is not. The stage is the
   WINDOW, the photograph covers it, and the lockup is anchored to the
   PHOTOGRAPH rather than to the stage's top-left.

   --k stops being "stage width / 1366" and becomes the COVER SCALE,
   max(w/1366, h/768) — how many screen pixels one design pixel of the
   photograph now occupies. Type and the shield are sized from it, so they
   stay the same size *relative to the room* at every window ratio. Width-only
   --k breaks as soon as the window is taller than 16:9: at 1024x768 the photo
   is drawn at 1.00 but width-only --k is 0.75, so the lockup shrank 25%
   against a room that had not shrunk, and slid 40 design px right of the
   plaster it belongs on.

   HORIZONTAL is anchored to the frame CENTRE. Under object-position 50% 50%
   the centre is the one point cover never moves, so each comp x is carried as
   a signed offset from the design centre 683. At 1366x768 the scale is 1 and
   683-158.401 = 524.599 — the same pixel the percentage above gives, to
   0.01px. A percentage left cannot do this: it tracks the stage, and at
   1024x768 it puts "Hotel List" 40px right of where the room wants it.

   VERTICAL stays on the percentages above (7.4193% and 20.9531%) and must.
   Anchoring the lockup to the photograph vertically as well reads correctly
   at ratios taller than 16:9 — but a window WIDER than 16:9 crops the top of
   the photograph away, so the comp's y57 goes with it: measured, the nav
   landed at -78px at 2560x1080 and -387px at 3840x1080. It was gone from the
   screen and nothing in the layout complained. The percentages keep the
   lockup at the comp's proportion of the frame the viewer can actually see.

   Cropping is symmetric and deliberate. The sofa (x400..1070) survives any
   window at least as wide as 1:1.357, and the olive urn (x60..355) any window
   wider than 1.62:1; what the edges give up first is the mirror sliver on the
   left and the door mullion on the right. Do not shift object-position to
   save them — off-centre cover moves the frame centre, and the shield's
   centring is the thing the comp is most insistent about. */
/* <picture> is display:inline and has no height, so height:100% on the img
   never resolves and the photo falls back to its intrinsic ratio. */
.td-stage--home .td-hero picture { display: block; position: absolute; inset: 0; }

@media (min-width: 1024px) {
  .td-stage--home {
    aspect-ratio: auto;
    height: 100vh;
    --k: max(100cqw / 1366, 100vh / 768);
  }
  @supports (height: 100svh) {
    .td-stage--home {
      height: 100svh;
      --k: max(100cqw / 1366, 100svh / 768);
    }
  }

  .td-stage--home .td-nav__item--hotels   { left: calc(50% + (524.599 - 683) * var(--k)); }
  .td-stage--home .td-nav__item--branding { left: calc(50% + (750.331 - 683) * var(--k)); }

  .td-stage--home .td-hero__mark {
    left:  calc(50% + (644.58 - 683) * var(--k));
    width: calc(76.16 * var(--k));
  }
}


/* ==========================================================================
   9. BRANDING — branding-page.jpg
      Five full-bleed bands, in this order. The hero is the SAME 1366x768
      photograph as home (mean abs diff 1.48/255; the only real delta is the
      Branding underline), so reuse the identical asset.
   ========================================================================== */

/* --- band 2: white intro ------------------------------------------------- */
.td-intro__mark {                     /* BARE ligature, no shield */
  position: absolute; color: var(--c-ink-paper);
  left: 49.2679%; top: 30.6373%;      /* x673, band-rel y125 */
  width: 1.4641%;                     /* 20 / 1366 */
  aspect-ratio: 20 / 26;
}
.td-intro__text {                     /* centred, 4 lines as set */
  position: absolute; text-align: center;
  left: 39.5315%; top: 47.3039%;      /* ink x540, band-rel y193 */
  width: 21.3031%;                    /* 291 / 1366. The comp's INK block is
                                         286 wide (x540..826); the box must be
                                         a few px wider than the ink or the
                                         browser wraps line 2 early. This width
                                         is what produces the comp's 4 lines —
                                         verified, see the acceptance test. */
  color: var(--c-ink-paper);
}
.td-intro__link {
  position: absolute; color: var(--c-ink-muted);
  left: 47.0717%; top: 67.8922%;      /* x643, band-rel y277 */
}

/* --- band 3: taupe ------------------------------------------------------- */
.td-craft__link {                     /* "View photography ›" — LEFT, serif */
  position: absolute;
  left: 6.8814%; top: 14.7799%;       /* ink x94, band-rel y94 */
}
.td-craft__sketches {                 /* flattened scan composite */
  position: absolute;
  left: 32.6501%; top: 12.8931%;      /* x446, band-rel y82 */
  width: 30.3807%;                    /* 415 / 1366 */
  aspect-ratio: 415 / 472;
}
.td-craft__sketches img { width: 100%; height: 100%; object-fit: fill; }
.td-craft__body {                     /* 6 lines, left-aligned, ragged right */
  position: absolute; text-align: left;
  left: 71.8887%; top: 22.3270%;      /* x982, band-rel y142 */
  width: 16.4714%;                    /* 225 / 1366 */
  /* ⚠ 225, NOT the 218 this used to be. 218 was the comp's LONGEST INK LINE,
     which is a LOWER BOUND on the designer's box, not a measurement of it —
     the upper bound is the first word that must NOT fit, which puts the comp's
     own box anywhere in [218, ~263]. Sitting on the lower bound left 0.2px of
     slack, so any substituted face that set one line 1% wide reflowed the
     paragraph: line 4 lost "find" to line 5 and the block stopped matching the
     comp's six-line shape at all. 225 restores the comp's break on every line
     and cannot pull a word UP — the next candidate is 25px away. Verified by
     render: line ink 216/198/187/219/178/69 vs the comp's 218/195/185/212/171/66. */
}
.td-craft__social {                   /* the Instagram line — ends in a stop */
  position: absolute;
  left: 71.8887%; top: 38.9937%;      /* x982, band-rel y248 */
}
.td-craft__contact {                  /* "Contact us now  ›" — serif */
  position: absolute;
  left: 71.8887%; top: 47.9560%;      /* x982, band-rel y305 */
}

/* --- band 4: olive prices ------------------------------------------------ */
.td-prices__title {                   /* script */
  position: absolute;
  left: 34.5513%; top: 22.2727%;      /* box x471.97, band-rel y147 */
  /* ⚠ The BOX is not the comp's x478 label column and must not be "corrected"
     back to it. The comp's "Prices" INK starts at x473.258 because the P's
     swash hangs left of the column; a substituted face hangs by a different
     amount, so aligning the BOX at 478 puts the INK wherever that face happens
     to put it — with the previous script it landed at 477.5 (no swash at all,
     nothing hanging) and with this one at 479.3, i.e. 4-6px right of the comp
     either way. This value is set so the rendered INK LEFT lands on the comp's
     473.26, which is the same rule §5 already applies vertically (`top` IS the
     measured ink top). Re-derive it if the script face changes: it is
     473.258 - (rendered ink left - box left). */
}
.td-prices__list {
  position: absolute;
  left: 34.9927%; top: 36.6667%;      /* label col x478, first cap band-rel y242 */
  width: 30%;                         /* only needs to reach the value column */
}
.td-prices__row {
  display: block; position: relative;
  height: calc(var(--lead-pricerow) * var(--k));   /* 32.7px row pitch */
}
.td-prices__label { position: absolute; left: 0; top: 0; }
.td-prices__value { position: absolute; top: 0;
                    left: calc(266 * var(--k)); } /* value col x744 - x478 */
.td-prices__contact {                 /* the ONLY centred link on the page */
  position: absolute;
  left: 44.2167%; top: 78.1818%;      /* x604, band-rel y515 */
}

/* --- band 5: footer ------------------------------------------------------
   Exactly two elements. Margins are NOT symmetric (117 left vs 136 right) —
   reproduce the measured values, do not centre a container. */
.td-footer__mark {
  position: absolute; color: var(--c-ink-stone);
  left: 8.5652%; top: 36.5019%;       /* x117, band-rel y96 */
  width: 3.4407%;                     /* 47 / 1366 */
  aspect-ratio: 47 / 62;
}
.td-footer__link {
  position: absolute; color: var(--c-ink-stone);
  left: 85.2855%; top: 44.1065%;      /* x1165, band-rel y116 */
}
/* The footer "Contact" is a link and was the last one on the site with no
   designed states — measured opacity 1 on hover (no change at all) and
   Chrome's default `outline: auto 1px rgb(16,16,16)` on focus, i.e. system
   black on the stone band. .td-link and .td-nav__item already carry the fade
   and the 1px currentColor ring; this makes the third and last link family
   behave the same. Invisible in the comp, which is one static state. */
.td-footer__link { transition: opacity var(--hover-ease); }
.td-footer__link:hover,
.td-footer__link:focus-visible { opacity: var(--hover-dim); }
.td-footer__link:focus-visible { outline: 1px solid currentColor;
                                 outline-offset: 4px; }


/* ==========================================================================
   10. PHOTOGRAPHY — photography.jpg
       White page. FIVE photographs (not six), asymmetric and overlapping.
       Nothing is drawn but the lockup and the photographs: no nav, no visible
       footer, no headline, no borders, no radius, no shadow.
       Only photo-4 is above its neighbours.
   ========================================================================== */

.td-collage { background: var(--c-paper); color: var(--c-ink-photo); }

/* The page's three landmark wrappers exist for semantics only, as on contact
   (§11) — but contact's are static blocks, and that will not work here: below
   1024px .td-collage IS the grid and its children carry grid-area, so a static
   wrapper would swallow the tracks and stack all five photographs in slot one.
   `display: contents` generates no box at any width, so the photographs stay
   grid items and the comp geometry is untouched.
   ⛔ The stage must stay a <div>: <header>/<footer> map to banner/contentinfo
   only when no <section> or <article> sits between them and <body>. */
.td-collage__banner,
.td-collage__main,
.td-collage__colophon { display: contents; }

/* The comp gives this page no exit: it is entered from /branding/ and, as
   drawn, links nowhere at all. The return sits in the footer with the
   skip-link treatment — clipped to 1px so nothing paints in the static
   design, full size the moment a keyboard reaches it. It is the page's only
   tab stop. It deliberately does NOT take a .td-t class: those carry type
   metrics fitted to comp ink, and this string is not in the comp. */
.td-collage__return {
  position: absolute;
  left: 9.0776%; top: 92%;              /* under the caption's column, x124 */
  width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  font-family: var(--font-serif); font-weight: var(--w-serif);
  font-size: calc(var(--fs-link) * var(--k));
  letter-spacing: var(--tracking-serif);
  color: var(--c-ink-photo);
}
.td-collage__return:focus-visible {
  width: auto; height: auto; margin: 0;
  padding: calc(10 * var(--k)) calc(14 * var(--k));
  overflow: visible; clip: auto; clip-path: none;
  background: var(--c-paper);
  outline: 1px solid currentColor; outline-offset: 4px;
}

.td-collage__mark {
  position: absolute; color: var(--c-ink-mark);
  left: 14.9341%; top: 18.3168%;      /* x204, y296 */
  width: 1.9766%;                     /* 27 / 1366 */
  aspect-ratio: 27 / 33;
}
.td-collage__caption {                /* centred on the same axis as the mark:
                                         mark centre x217.0, caption centres
                                         x216.5 — one shared vertical axis */
  position: absolute; text-align: center;
  left: 9.0776%; top: 24.0099%;       /* ink x124, y388 */
  width: 13.6164%;                    /* 186 / 1366 */
}
/* ⛔ THE ASSET FILENAMES DO NOT MATCH THESE SLOT NUMBERS. The manifest numbers
   the crops in one order and the comp's z-stack in another. Put:
     item--1 -> collage/collage-1-dining-407     (403x526)
     item--2 -> collage/collage-2-bowls-389      (383x526)
     item--3 -> collage/collage-3-marble-959     (959x420)
     item--4 -> collage/collage-5-ceiling-423    (423x481)  <- ceiling, not 4
     item--5 -> collage/collage-4-chairs-424     (424x521)  <- chairs, not 5
   Getting 4 and 5 the "obvious" way round scores mean abs 100/255 on those two
   boxes — measured, because that is exactly the mistake this note came from. */
.td-collage__item { position: absolute; z-index: 1; }
.td-collage__item img { width: 100%; height: 100%; object-fit: cover; }
.td-collage__item--1 { left: 30.1611%; top:  3.0322%; width: 29.5022%; height: 32.5495%; }
.td-collage__item--2 { left: 61.1274%; top:  3.0322%; width: 28.0381%; height: 32.5495%; }
.td-collage__item--3 { left: 22.6208%; top: 36.6955%; width: 70.2050%; height: 25.9901%; }
.td-collage__item--5 { left: 30.1611%; top: 64.7277%; width: 31.0395%; height: 32.2401%; }
.td-collage__item--4 { left: 58.1259%; top: 57.2401%; width: 30.9663%; height: 29.7649%;
                       z-index: 2; }  /* the ceiling shot; its hard edges cut
                                         across both 3 and 5 */


/* ==========================================================================
   11. CONTACT — contact-form.svg
       Olive page. Back link, script title, four labels, a seven-item bullet
       list, a prompt line, one lightened textarea, shield bottom-right.
   ========================================================================== */

/* The page's three landmark wrappers exist for semantics only. They are
   static, zero-height boxes: every child is .td-abs, so the containing block
   stays .td-stage and the comp geometry below is untouched.
   ⛔ Do not give them position, padding, border or margin. */
.td-contact__banner, .td-contact__main, .td-contact__colophon { display: block; }

.td-contact { background: var(--c-olive); color: var(--c-ink-light);
  /* ⚠ THE ONE DELIBERATE DIVERGENCE FROM THE COMP, at every width. The comp
     draws the placeholder in --c-olive on the lightened field: 1.69:1, which
     a picture can be and an input cannot. Nothing darker passes — the field
     is rgb(114,95,67) and even black reaches only 3.43:1 — so the accessible
     direction is lighter. Built the way the field is (white @18% over the
     olive; this is white @82% over the field): 4.74:1. One line to revert. */
  --c-placeholder: rgb(230, 226, 221);
}

/* SANS SIZES ARE FITTED TO THE COMP'S INK WIDTHS, not its cap heights: the
   comp's face cannot be licensed and the substitute matches only one of the
   two, so the cap solves --fs-label / --fs-listitem (§2) are deliberately
   overridden here.
   ⚠ RE-SOLVED 2026-09-09 FOR OUTFIT. The previous 12.35 / 10.875 were width
   solves for JOST and were left behind when --font-sans migrated to Outfit,
   which sets wider at the same cap — so every label rendered long. Measured
   on a 4x render, aggregate built-ink / comp-ink was 1.0361 over the five
   label-size strings and 1.0151 over the seven list items; dividing the old
   sizes through gives the values below, which return 1.0123 and 1.0006.
   ⛔ Re-solve these whenever --font-sans or --w-sans changes. */
.td-contact .td-t-label    { --fs: 11.92; --hlf: -0.3825; }
.td-contact .td-t-listitem { --fs: 10.70; --hlf: -0.3825; }

.td-contact__back    { position: absolute; left: 5.6369%; top: 8.8542%;
                       --fs: 16.6; --gap-chev-back: 15.8; }
/* Chevron ink is y68.25..83.25, i.e. 3.16px below where align-items:center
   puts its 10x16 box. */
.td-contact__back .td-chev--back { align-self: flex-start;
                                   margin-top: calc(3.16 * var(--k)); }

/* Title. The comp's roundhand cannot be licensed, so it is drawn from the
   comp's OWN outlines (11 paths lifted verbatim from contact-form.svg),
   inline so no request can fail. The viewBox is the ink box, so left/top are
   the ink corner. */
.td-contact__title   { position: absolute; left: 16.9107%; top: 16.7969%;
                       width: calc(175.5 * var(--k)); height: auto; }
.td-contact__title > svg { display: block; width: 100%; height: auto; }

/* Ink tops read at 4x: y192.75 / 228.25 / 269.25 / 304.25 / 328.50 / 495.25.
   Whole-px rounding is most of a scanline at 8-12px type, so these carry 4dp.
   The label rhythm is deliberately uneven (36 / 41 / 35 px) and there are NO
   field rules or boxes between the labels — an exhaustive sweep of the band
   found zero ink there. Reproduce the comp; do not regularise it. */
.td-contact__label   { position: absolute; left: 16.8375%; }
.td-contact__label--1 { top: 25.0977%; }   /* y192.75 */
.td-contact__label--2 { top: 29.7201%; }   /* y228.25 */
.td-contact__label--3 { top: 35.0586%; }   /* y269.25 */
.td-contact__label--4 { top: 39.6159%; }   /* y304.25 */

.td-contact__list {
  position: absolute; left: 18.0820%; top: 42.7734%; /* dot x247, item y328.5 */
}
/* .td-t's negative margin-top must move the INK, not the box: on a list item
   in flow it drags the next item up and the 21px pitch collapses to 19.38. An
   inner block, plus flow-root to stop the margin collapsing through the item,
   holds the measured 21.00. */
.td-contact__item {
  position: relative; display: flow-root;
  height: calc(var(--lead-listitem) * var(--k));      /* 21px pitch, exact */
  padding-left: calc(11 * var(--k));                  /* dot x247 -> text x258 */
}
.td-contact__item > span { display: block; }
/* Item 7 moves as a whole, disc included — an indent, not a leading space. */
.td-contact__item--indent { margin-left: calc(4.5 * var(--k)); }
/* The bullets are discs 3px across at x247.75 / y332.75, not 4x4 squares. */
.td-contact__item::before {
  content: ""; position: absolute;
  left: calc(0.75 * var(--k)); top: calc(4.25 * var(--k));
  width: calc(3 * var(--k)); height: calc(3 * var(--k));
  border-radius: 50%; background: currentColor;
}
.td-contact__prompt  { position: absolute; left: 16.8375%; top: 64.4857%; }

/* Panel and placeholder re-measured on a 4x render 2026-09-09. The panel ink
   box is y530.25..667.25, not the y531 x 136 previously shipped. The
   placeholder is set near label size but solves separately: at 12.35 it set
   57.75 against the comp's 54.00 (1.0694) and sat 1.25px low; 11.85 returns
   53.75 (0.9954) and padding-top 14.75 puts the ink top on y547.00. */
.td-contact__field {
  position: absolute; left: 16.8375%; top: 69.0755%;  /* y530.5 */
  width: 45.4612%;                    /* 621 / 1366 */
  height: 17.7734%;                   /* 136.5 / 768 */
  background: var(--c-field);         /* = white @ 18% over --c-olive */
  border: 0; border-radius: 0; resize: none; outline: 0;
  color: var(--c-ink-light);
  padding: calc(14.75 * var(--k)) calc(16 * var(--k)) calc(16 * var(--k));
  font-family: var(--font-sans); font-weight: var(--w-sans);
  font-size: calc(11.85 * var(--k));
  line-height: 1;
}
.td-contact__field::placeholder { color: var(--c-placeholder); opacity: 1; }
.td-contact__field:focus { outline: 1px solid var(--c-ink-light);
                           outline-offset: 2px; }
.td-contact__mark {
  position: absolute; color: var(--c-ink-light);
  left: 90.2635%; top: 81.5104%;      /* x1233, y626 */
  width: 4.0996%;                     /* 56 / 1366 */
  aspect-ratio: 56 / 73;
}
/* preserveAspectRatio="xMidYMid meet" reproduces the mask's `contain`. */
.td-contact__mark > svg { display: block; width: 100%; height: 100%; }


/* ==========================================================================
   12. MOBILE — below 1024px the comp layout is switched off entirely.
       There is no mobile comp, so nothing here can be "matched"; the rule is
       to invent as little as possible. The gutter is the taupe band's own
       left margin (6.8814%), the type sizes are the comp's own px values, and
       the stacking order is DOM order. No new sections, copy or controls.
   ========================================================================== */

@media (max-width: 1023.98px) {
  .td-stage:not(.td-stage--home):not(.td-band--hero),
  .td-band:not(.td-band--hero) {
    aspect-ratio: auto;
    height: auto;
    container-type: normal;
    --k: 1px;
    padding: 56px var(--sp-gutter-pct);
  }

  /* Every comp-positioned element returns to flow. */
  .td-stage:not(.td-stage--home) .td-abs,
  .td-band:not(.td-band--hero) [class*="__"],
  .td-nav__item {
    position: static;
    left: auto; top: auto; width: auto; height: auto;
  }

  /* The hero keeps its photograph and its lockup; it is the one region whose
     composition survives a narrow viewport. */
  .td-stage--home, .td-band--hero { aspect-ratio: 3 / 4; }
  .td-nav { display: flex; justify-content: center; gap: 40px;
            inset: auto 0 auto 0; top: 7.4193%; }
  .td-nav__item { font-size: 16px; }
  .td-hero__mark { left: 50%; transform: translateX(-50%); width: 76px; }

  .td-intro__text, .td-collage__caption { width: auto; max-width: 34ch;
                                          margin-inline: auto; }
  .td-craft__body { width: auto; max-width: 40ch; }

  /* Stacked flow spacing, from the comp's own vertical gaps. */
  .td-intro__mark   { margin: 0 auto 40px; }
  .td-intro__link   { margin-top: 20px; text-align: center; }
  .td-craft__sketches { width: min(415px, 100%); margin: 32px auto; }
  .td-craft__body   { margin-top: 32px; }
  .td-craft__social { margin-top: 20px; }
  .td-craft__contact, .td-prices__contact { margin-top: 32px; }
  .td-prices__list  { margin-top: 40px; }

  /* Price rows become two-column flex instead of an absolute value column. */
  .td-prices__row   { display: flex; justify-content: space-between;
                      gap: 24px; height: auto; margin-bottom: 14px; }
  .td-prices__label, .td-prices__value { position: static; left: auto; }

  .td-footer__mark  { width: 47px; }
  .td-footer__link  { margin-top: 24px; }

  /* Collage: the overlaps depend on absolute geometry, so on mobile it
     becomes a plain single column at each photo's own aspect ratio. */
  .td-collage__item { position: static; width: 100%; height: auto;
                      margin-bottom: 18px; }
  .td-collage__item--1 { aspect-ratio: 403 / 526; }
  .td-collage__item--2 { aspect-ratio: 383 / 526; }
  .td-collage__item--3 { aspect-ratio: 959 / 420; }
  .td-collage__item--4 { aspect-ratio: 423 / 481; }
  .td-collage__item--5 { aspect-ratio: 424 / 521; }

  .td-contact__field { width: 100%; height: 160px; padding: 16px; }
  .td-contact__mark  { margin: 40px 0 0 auto; width: 56px; }
  .td-contact__label, .td-contact__list, .td-contact__prompt,
  .td-contact__title, .td-contact__back { margin-bottom: 20px; }
  .td-contact__item { height: auto; margin-bottom: 8px; }

  /* --- CONTACT, refined ---------------------------------------------------
     No mobile comp exists, so nothing here is matched; the rule is to invent
     as little as possible. ONE lever: --k. The bare 1px default above would
     set the list at 10.70px and the field at 11.85px — under the 12px
     legibility floor and under the 16px at which iOS zooms a focused
     textarea. Pinning --k to 16 / (field size) scales the whole comp from one
     number: the field lands on exactly 16.000px and the smallest type on
     14.45px. Vertical gaps are the comp's own ink-top intervals, restated as
     margins through inkGap = (lead - inkOffset) + margin-bottom, because
     .td-t carries a negative margin-top. One horizontal axis, the gutter.
     ⛔ inkGap = 0.8825 * --fs for a single-leaded line (and 0.967 * --fs for
     the prompt, whose --lead is 1.169). Both fall out of --hlf -0.3825, so
     RE-DERIVE every constant below whenever a --fs above changes.
     These selectors are long because the generic mobile rules are (0,3,0). */
  .td-page.td-page--olive .td-stage.td-contact {
    --k: 1.350211px;                                   /* 16 / 11.85 */
    padding: calc(66.5 * var(--k)) var(--sp-gutter-pct) calc(69 * var(--k));
  }

  /* Block-level so its margins are block margins, shrink-wrapped so the focus
     ring outlines the words. ⛔ (0,4,0) is required: at (0,2,0) the generic
     `.td-abs { width: auto }` won, the link went full-width and row-reverse
     packed it against the RIGHT edge. Padded to a measured 70.6x48.6 tap
     target, the 9.2 folded back into margin-top so nothing moves; +1.28 is
     what align-items:center contributed on desktop, re-added explicitly after
     pinning to flex-start. */
  .td-stage.td-contact .td-abs.td-contact__back {
    display: flex; width: max-content; align-items: flex-start;
    padding-block: calc(9.2 * var(--k));
    margin-top: calc(-1 * (9.2 + 3.2121 + 1.28) * var(--k));
    margin-bottom: calc((62.50 - (19.16 - 3.2121) - 9.2 - 1.28) * var(--k));
  }
  /* The title's box is its ink box, so its gap is a plain 63.75 - 28.25. */
  .td-stage.td-contact .td-abs.td-contact__title {
    width: calc(175.5 * var(--k)); height: auto; margin-left: 0;
    margin-bottom: calc((63.75 - 28.25) * var(--k));
  }
  /* inkGap 10.519 = 0.8825 * 11.92 */
  .td-contact .td-contact__label--1 { margin-bottom: calc((35.50 - 10.519) * var(--k)); }
  .td-contact .td-contact__label--2 { margin-bottom: calc((41.00 - 10.519) * var(--k)); }
  .td-contact .td-contact__label--3 { margin-bottom: calc((35.00 - 10.519) * var(--k)); }
  .td-contact .td-contact__label--4 { margin-bottom: calc((24.25 - 10.519) * var(--k)); }

  /* 17px = the comp's own x247 - x230. inkGap 9.443 = 0.8825 * 10.70. */
  .td-contact .td-contact__list {
    margin-left: calc(17 * var(--k));
    margin-bottom: calc((40.75 - 9.443) * var(--k));
  }
  .td-contact .td-contact__item { margin-bottom: calc((21 - 9.443) * var(--k)); }
  .td-contact .td-contact__item:last-child { margin-bottom: 0; }

  /* The prompt is the one string that has to wrap; 1.169 is the comps' own
     body/caption leading ratio (15.2/13.0 and 20/17.1). inkGap 11.527. */
  .td-contact .td-contact__prompt {
    --lead: calc(var(--fs) * 1.169);
    margin-bottom: calc((35.75 - 11.527) * var(--k));
    max-width: 34ch;
  }
  /* A guard: everything fits one line at 320 (widest 212px in a 276px
     column), but .td-t defaults to nowrap and a translation would overflow. */
  .td-contact .td-contact__label,
  .td-contact .td-contact__item > span { white-space: normal; }

  /* Block-level (a textarea is inline-block, which leaves a descender gap);
     width is the column, the rest is the comp's geometry through --k. */
  .td-stage.td-contact .td-abs.td-contact__field {
    display: block; width: 100%; height: calc(136.5 * var(--k));
    padding: calc(14.75 * var(--k)) calc(16 * var(--k)) calc(16 * var(--k));
    margin-bottom: calc(69 * var(--k));
  }
  .td-stage.td-contact .td-abs.td-contact__mark {
    width: calc(56 * var(--k)); height: auto;
    margin-left: auto; margin-right: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .td-link, .td-nav__item { transition: none; }
}



/* ==========================================================================
   13. HOTEL LIST — /hotel-list/ and /hotel-list/<place>/
       ⚠⚠ THERE IS NO COMP FOR EITHER PAGE. Everything below is either lifted
       from a comp measurement (marked ✅ with its source) or derived from one
       (marked ★ with the derivation). Anything that is neither is marked
       ⚠ EXTENSION and is listed for the designer to sign off — a new UI
       convention with no anchor in the comps is the failure mode this file
       exists to prevent.

       The two pages reuse, unchanged: .td-band--hero (photo + nav + shield),
       .td-band--paper / --taupe / --olive / --footer, .td-mark, .td-link,
       .td-chev, and every .td-t-* primitive. No new type family, no new
       colour, no button, no border, no radius, no shadow.
   ========================================================================== */

/* --- 13a. A BAND THAT SIZES TO ITS CONTENT -------------------------------
   ⚠ EXTENSION (structural, unavoidable). Every band in §4 fixes its height
   with `aspect-ratio: 1366 / --stage-h`, because every band in the comps is a
   measured rectangle. These pages carry client copy of unknown length (the
   place standfirsts run 420–942 characters and the hotel descriptions
   208–556), so a fixed band height would clip or strand. .td-band--flow keeps
   everything else about a band — the container, --k, the ground colour — and
   lets the height come from the content.
   ★ The block padding is the branding paper band's own inset: its content ink
   starts 125 design px below the band top and ends 121 above the bottom
   (band 408 tall, monogram y125, link baseline ~y287). Mean 123. */
.td-band--flow {
  aspect-ratio: auto;
  height: auto;
  padding-block: calc(var(--band-pad) * var(--k));
}
.td-page { --band-pad: 123; }

/* --- 13b. SPACING — five measured vertical gaps ---------------------------
   ✅ Every one is a gap between two ink boxes in a comp, not a chosen number.
     17  photography.jpg: marble plate -> chairs tile, 17/959 of the plate
         (the pair's own horizontal gap is 16 — the same interval)
     20  branding-page.jpg, TWICE and independently: white paragraph last
         baseline y257.5 -> "Discover more" ink top y277 = 19.5; taupe body
         block bottom y227.5 -> Instagram line ink top y248 = 20.5
     33  photography.jpg: plate bottom -> chairs top, 33/959
     42  branding-page.jpg white band: monogram bottom y151 -> paragraph ink
         top y193
     59  photography.jpg: monogram bottom y329 -> caption ink top y388     */
.td-page {
  --gap-photo:        17;   /* photograph to photograph */
  --gap-block-link:   20;   /* a text block to the link that follows it */
  --gap-group:        33;   /* one photographic group to the next */
  --gap-mark-text:    42;   /* the bare monogram to the text under it */
  --gap-mark-caption: 59;   /* the bare monogram to a caption under it */
}

/* --- 13c. TWO DISPLAY SIZES IN THE SERIF ---------------------------------
   ⚠ EXTENSION. The comps' largest serif is --cap-link 14 ("View photography",
   "Contact us now"), which is a LINK size, not a title size. These pages need
   a page title (the place name) and a sub-title (the hotel name), and the
   comps contain neither.
   ★ Derived rather than chosen, from the only display type the comps DO have —
   the script band title "Prices" at --cap-script-lg 29, and its documented
   second step --cap-script-md 22.90. Those two caps are the comps' own display
   interval (22.90 / 29 = 0.7897). Set in the serif at the same cap heights:
       place name  cap 29    -> 29    / 0.625 = 46.4 px
       hotel name  cap 22.90 -> 22.90 / 0.625 = 36.64 px
   ★ CORROBORATION, not the derivation: the client's own Wix pages set the
   place name at 50px and the hotel name at 46px Cormorant Garamond. 46.4
   lands within 1% of the old HOTEL size and 7% under the old PLACE size, so
   this scale is quieter than the site it replaces — which is the brief. */
  .td-page { --cap-place: var(--cap-script-lg); --cap-hotel: var(--cap-script-md); }
  .td-page {
    --fs-place: calc(var(--cap-place) / var(--cap-serif));   /* 46.40 */
    --fs-hotel: calc(var(--cap-hotel) / var(--cap-serif));   /* 36.64 */
  }
/* Leading: the comps give no multi-line display line-height. ★ 1.169 is the
   comps' OWN body/caption leading ratio (15.2/13.0 = 1.169 and 20/17.1 =
   1.169, two independent blocks agreeing to 3dp) and is already used for the
   contact prompt in §12. Reused here so a two-word place name that wraps on a
   phone sets on the site's own rhythm. */
.td-t-place { font-family: var(--font-serif); font-weight: var(--w-serif);
              font-style: normal; --hlf: var(--hlf-serif);
              --tracking: var(--tracking-serif); --word: 0em;
              --fs: var(--fs-place); --lead: calc(var(--fs) * 1.169);
              white-space: normal; }
.td-t-hotel { font-family: var(--font-serif); font-weight: var(--w-serif);
              font-style: normal; --hlf: var(--hlf-serif);
              --tracking: var(--tracking-serif); --word: 0em;
              --fs: var(--fs-hotel); --lead: calc(var(--fs) * 1.169);
              white-space: normal; }

/* --- 13d. MEASURE --------------------------------------------------------
   ⚠ EXTENSION. The comps' two text columns are 291 design px (white band,
   4 lines of 16px serif) and 225 (taupe band, 6 lines of 13px sans). Neither
   can hold this content: Greece's standfirst is 942 characters, which sets in
   ~31 lines at 291.
   ★ 403 is not invented — it is the site's own tile module: the width of
   collage item 1 in photography.jpg (403x526) and the width every destination
   tile and hotel photograph is cut to (assets/destinations/ASSETS.json,
   "tile": "403:526"). At 403 the 16px serif sets ~66 characters and the 13px
   sans ~62, both inside the 45–75 convention. Using the photographic module as
   the text measure is what keeps the copy columns locked to the image grid. */
.td-page { --measure-para: 403; }

/* --- 13e. NAV, CURRENT PAGE ----------------------------------------------
   ✅ The comp's rule is 1px at y71 spanning x751..832 — 7px WIDER than the
   "Branding" glyphs, because the underlined string carries a trailing space.
   §8 reproduces that with border-bottom + padding; the branding page found
   that a border sits on the text box, not on the comp's y71, and replaced it
   with a positioned ::after. That mechanism is promoted here so the Hotel List
   item gets the identical treatment without page-local CSS — a reviewer must
   never see two different current-page marks one click apart.
   Widths: "Branding" ink 73.52 + 7.48 = 81 (the comp's own span). "Hotel List"
   ink is 76.48 on the same measurement (§14), so its rule is 76.5 + 7 = 83.5. */
.td-nav__item--current { border-bottom: 0; padding-bottom: 0;
                         --nav-rule-w: 81; }
.td-nav__item--hotels.td-nav__item--current { --nav-rule-w: 83.5; }
.td-nav__item--current::after {
  content: ""; position: absolute; left: 0;
  top: calc((14 + var(--fs-nav) * (0.5 + var(--hlf-sans))) * var(--k));
  width: calc(var(--nav-rule-w) * var(--k));
  height: calc(1 * var(--k));
  background: currentColor;
}

/* --- 13f. HERO SCRIM -----------------------------------------------------
   ⚠ EXTENSION, and REQUIRED on some heroes. The comps put white nav type and a
   white shield on ONE photograph, whose top band is dark enough to carry them.
   These pages put the same white lockup on six different photographs, and the
   measured mean luma of the sourced heroes runs 0.238 (asia portrait) to 0.874
   (greece) — ASSETS.json. White type on L 0.874 is unreadable.
   ★ Not a new invention: this is byte-for-byte the scrim the built branding
   page already ships at <1024px (its page-local block), promoted to site.css
   and made available at every width. Apply it only where the hero needs it. */
/* ⛔ THE HOME HERO'S SCRIM. /, /hotel-list/ and /hotel-list/sweden/ all serve the
   SAME photograph (assets/hero/hero-*). Before 2026-09-13 the home page scrimmed it
   with rgba(31,25,18) at 0.46/0.38/0.14 over 30%, while the other two fell through
   to .td-hero--scrim below — rgba(46,33,14) at 0.62/0.60/0.30 over 46%. One
   photograph, two treatments, one click apart: exactly what §13e forbids.
   These are the home page's own measured values; its page-local rule is unchanged
   (it is a comped page and must stay byte-identical), so this carries them to the
   other two rather than restyling the comp.
   ⚠ It is declared ONLY inside the mobile query, like the home rule it mirrors.
   That is what lets the two pages drop their `@media (min-width:1024px){...
   display:none}` override — a class named --scrim that rendered no scrim meant
   "has the class" and "is scrimmed" had come apart, and the decision lived in two
   places. */
@media (max-width: 1023.98px) {
  .td-hero--scrim-home::after {
    content: ""; position: absolute; left: 0; right: 0; top: 0; height: 30%;
    background: linear-gradient(to bottom,
                rgba(31, 25, 18, 0.46)   0%,
                rgba(31, 25, 18, 0.38)  40%,
                rgba(31, 25, 18, 0.14)  75%,
                rgba(31, 25, 18, 0)    100%);
    pointer-events: none;
  }
}

.td-hero--scrim::after {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 46%;
  background: linear-gradient(to bottom,
              rgba(46, 33, 14, 0.62)   0%,
              rgba(46, 33, 14, 0.60)  40%,
              rgba(46, 33, 14, 0.30)  62%,
              rgba(46, 33, 14, 0)    100%);
  pointer-events: none;
}


/* ==========================================================================
   13g. THE INDEX — /hotel-list/
        Band spine, in the branding page's own order:
          .td-band--hero          photograph + nav (Hotel List current) + shield
          .td-band--flow --paper  monogram, then the six places
          .td-band--flow --taupe  "Contact us now ›"   (the page's exit)
          .td-band--footer        reused verbatim
   ========================================================================== */

/* The bare monogram opens the paper band exactly as it opens the branding
   white band: ✅ 20 design px wide, ink box 20x26, --c-ink-paper, centred. */
.td-places__mark {
  color: var(--c-ink-paper);
  width: calc(20 * var(--k));
  aspect-ratio: 20 / 26;
  margin: 0 auto calc(var(--gap-mark-text) * var(--k));
}

/* An optional one-paragraph introduction, if the client writes one. The live
   /destinations index carries NO editorial copy at all, so nothing is invented
   here: omit the element and the monogram sits straight above the places.
   ✅ Treatment is the branding white band's paragraph — centred serif at
   --fs-para / --lead-para — at the 403 measure of 13d. */
.td-places__standfirst {
  text-align: center;
  width: calc(var(--measure-para) * var(--k));
  max-width: 100%;
  margin: 0 auto calc(var(--gap-group) * var(--k));
  color: var(--c-ink-paper);
}

/* --- the six places ------------------------------------------------------
   ★ A 96-track grid. Not a new mechanism: the built photography page lays its
   collage out on `repeat(96, 1fr)` below 1024px, so 96ths are already this
   site's sub-grid. The six slots use FIVE of the comp's own vertical edges,
   rounded to the nearest 96th (worst error 4.5px at a 1366 stage, 0.33%):
       photography.jpg plate      x 22.6208% .. 92.8258%  -> lines 23 .. 90
       photography.jpg tile 1     x 30.1611% .. 59.6633%  -> lines 30 .. 57
       photography.jpg tile 2     x 61.1274% .. 89.1655%  -> lines 60 .. 87
   Two slot widths only, and they differ by 37% so the run reads as a collage
   and not as a grid:
       BIG   span 37 = 526.5 design px
       SMALL span 27 = 384.2 design px
   ⛔ THOSE TWO NUMBERS ARE AN ASSET CONSTRAINT, NOT A PREFERENCE. No image may
   be displayed above 1.0x its intrinsic width. The index tiles ship at
   403 / 566 / 806 (ASSETS.json), so a BIG slot needs a 566 file and a SMALL
   slot a 403 file. Available widths per place: asia 566, denmark 806,
   greece 403, mallorca 806, france 403, sweden NONE. The default assignment
   below is the only one that keeps the client's own index order AND every tile
   at or under 1.0x — do not reorder the slots without re-checking that table.
   Rows are declared explicitly; auto-placement would reflow the whole run if
   one item were removed. */
.td-places__list {
  display: grid;
  grid-template-columns: repeat(96, 1fr);
  row-gap: calc(var(--gap-group) * var(--k));
  align-items: start;
}
.td-places__item { position: relative; display: block; }

.td-places__item--1 { grid-row: 1; grid-column: 23 / 60; }  /* BIG   */
.td-places__item--2 { grid-row: 1; grid-column: 63 / 90; }  /* small */
.td-places__item--3 { grid-row: 2; grid-column: 23 / 50; }  /* small */
.td-places__item--4 { grid-row: 2; grid-column: 53 / 90; }  /* BIG   */
.td-places__item--5 { grid-row: 3; grid-column: 30 / 57; }  /* small */
.td-places__item--6 { grid-row: 3; grid-column: 60 / 87; }  /* small */

/* The stagger. ★ ONE offset, the comp's own 33, applied to the slot that is
   NOT the row's big one, so it alternates sides down the page (right, left,
   right) without a second value. This is what stops three rows of two reading
   as a table. */
.td-places__item--2,
.td-places__item--3,
.td-places__item--6 { margin-top: calc(var(--gap-group) * var(--k)); }

/* ✅ 403:526 — the photography comp's tile 1, and the crop every index tile
   was cut to. object-fit is `cover` only as a guard; the files already carry
   this exact ratio, so it should never crop anything. */
.td-places__figure { display: block; width: 100%; aspect-ratio: 403 / 526;
                     margin: 0 0 calc(var(--gap-block-link) * var(--k)); }
.td-places__figure picture { display: block; width: 100%; height: 100%; }
.td-places__figure img { width: 100%; height: 100%; object-fit: cover; }

/* The place name IS the comps' link idiom, at the comps' link size: serif
   --cap-link 14 (22.4px) plus the heavy 7x11 chevron at the measured 12px gap.
   ✅ Identical to "View photography ›". Left-aligned to the photograph's own
   left edge, which is how every text block in the comps aligns to the element
   above it. */
.td-places__name { color: var(--c-ink-paper); }

/* Hover/focus: ✅ the comps' only motion — the same 220ms fade to 0.62 that
   .td-link and .td-nav__item already carry — and it moves the LABEL only.
   ⛔ Do not fade or scale the photograph: no comp shows an effect over a
   photograph, and a dimming image reads as a state change the design does not
   have. The whole tile is the hit area; the ring outlines the whole tile. */
.td-places__link { display: block; color: inherit;
                   transition: opacity var(--hover-ease); }
.td-places__link:hover .td-places__name,
.td-places__link:focus-visible .td-places__name { opacity: var(--hover-dim); }
.td-places__link:focus-visible { outline: 1px solid currentColor;
                                 outline-offset: 4px; }

/* --- a place with no page yet -------------------------------------------
   ⚠ EXTENSION, and the evidence says it applies to SWEDEN, not Greece.
   ⛔ THE BRIEF'S PREMISE IS WRONG AND TWO SOURCES SAY SO: assets/destinations/
   ASSETS.json records that on the live index the "Coming soon" label sits above
   SWEDEN; and Sweden is the page that is actually unfinished (it is a duplicate
   of Mallorca — same hero id, same nine photographs, Mallorcan hotels under a
   Swedish heading, two descriptions replaced with Bali copy) while Greece is
   complete (3 hotels, 2,416 characters, 9 photographs, 12 links). Sweden is
   also the one place with NO photography sourced, because none exists.
   The treatment is the absence itself: no photograph, no link, and the name
   set at the BASELINE its row-mate's name occupies (align-self:end), so the
   empty slot above it reads as space the design left, not as a broken tile.
   "Coming soon" is set in the comps' own smallest sans — --fs-microlink 11,
   the "Discover more" size — in --c-ink-muted, the "Discover more" colour.
   No badge, no pill, no border: the comps draw none. */
.td-places__item--pending {
  align-self: end;                     /* its name sits on its row-mate's name */
  display: flex; align-items: baseline;
  color: var(--c-ink-muted);
}
.td-places__item--pending .td-places__name { color: var(--c-ink-muted); }
/* ⛔ Inline, on the name's own baseline — NOT stacked under it. Stacked, the
   whole two-line block bottom-aligns and the NAME then sits a note's height
   below its row-mate's name, which is the one thing this slot has to get
   right. The gap is the comp's measured 18px (taupe "Contact us now" ink end
   1109 -> chevron 1128). */
.td-places__note { color: var(--c-ink-muted); white-space: nowrap;
                   margin-left: calc(var(--gap-chev-contact) * var(--k)); }

/* The index's exit. ✅ The branding taupe band's own closing element, kept at
   its own class so the two pages cannot drift apart. Centred, because the one
   centred link in the comps (.td-prices__contact) is a band's closing line. */
/* ⛔ Centre the LINK, never the band. Making .td-band--flow.td-band--taupe a
   flex row centres its children — which is right for a band holding one link
   and catastrophic for a hotel band, where it turned .td-hotel__head,
   __gallery and __copy into three side-by-side flex items and blew the
   photographs 28% over their intrinsic width. Measured, not guessed:
   asia-haadson-*-403 rendered at 392.2px against a 306px slot. */
.td-places__contact { display: flex; width: fit-content; margin-inline: auto; }


/* ==========================================================================
   13h. THE PLACE PAGE — /hotel-list/<place>/
        One template, six instances. Band spine:
          .td-band--hero                the place's own photograph + nav + shield
          .td-band--flow --paper        monogram, place name, standfirst
          .td-band--flow --paper|--taupe  one band per hotel, alternating ground
          .td-band--flow --olive        "Contact us now ›" centred
          .td-band--footer              reused verbatim
        ★ Alternating paper/taupe per hotel is the branding page's own device —
        that page changes ground at every band — and it is what keeps three or
        four structurally identical hotel movements from reading as a template.
        The olive close is .td-prices__contact's band, reused with only its
        closing line in it.
   ========================================================================== */

.td-place__mark {                      /* identical to .td-places__mark */
  color: var(--c-ink-paper);
  width: calc(20 * var(--k)); aspect-ratio: 20 / 26;
  margin: 0 auto calc(var(--gap-mark-text) * var(--k));
}
.td-place__name {                      /* h1, serif display 46.4 — see 13c */
  text-align: center; color: var(--c-ink-paper);
  margin: 0 auto calc(var(--gap-block-link) * var(--k));
}
.td-place__standfirst {                /* ✅ the branding white band's paragraph */
  text-align: center; color: var(--c-ink-paper);
  width: calc(var(--measure-para) * var(--k));
  max-width: 100%; margin: 0 auto;
}

/* --- one hotel ----------------------------------------------------------
   Reading order is fixed by the source and confirmed by two independent
   extractions and a live capture: location -> name -> photographs ->
   description -> booking links. (The old site's desktop y-coordinates
   contradict each other on name-vs-photographs because Wix positions every
   block absolutely; the live innerText and the single-column mobile DOM both
   give this order, and it is the one reproduced here.)
   ⛔ The old site sets every hotel name ROTATED 90° in the left margin. That
   device appears in NO comp and nowhere in this file. It is deliberately not
   carried over; it is the one piece of the old visual language being replaced.

   Geometry uses the branding taupe band's own three horizontal anchors,
   stacked rather than set side by side (a 958px photograph run cannot sit
   between two text columns at 1366):
     head       x94    = --sp-gutter-pct, the taupe band's left margin and the
                         only real left margin in the comps
     gallery    x309..x1268 = 22.6208% .. 92.8258%, the photography comp's
                         marble plate — its widest element and, per the build
                         notes, the page's own measuring stick
     copy       403 wide, flush RIGHT to the plate's right edge, so the text
                         column hangs off the photographs rather than sitting
                         under their centre. That asymmetry is the taupe band's
                         (link left, image centre, body right), turned vertical. */
.td-hotel__head {
  margin-left: var(--sp-gutter-pct);
  margin-bottom: calc(var(--gap-group) * var(--k));
}
/* ✅ --cap-link 14 serif, and the colour is not a new token: the old site's
   location line is #8E8076, which is --c-canvas exactly and one blue step from
   --c-taupe #8e8077. On a taupe ground it inverts to the light ink. */
.td-hotel__location { color: var(--c-canvas); white-space: normal;
                      margin-bottom: calc(var(--gap-block-link) * var(--k)); }
/* ⛔ On the taupe ground the location line takes the LIGHT INK, not --c-stone and
   not white at a reduced opacity.
   The original rule still holds and is why an opacity was never used: the comps
   carry exactly one opacity value (--hover-dim 0.62, the link fade), and a second
   one invented to mute a colour is a new convention.
   ⚠ CHANGED 2026-09-13 — --c-stone (#d4cbc2) was chosen as a measured neutral
   already in the palette, which was sound on palette grounds and never checked on
   CONTRAST. On the taupe ground it gives **2.38:1**, which fails AA at every width
   AND fails the relaxed 3.0 large-text bar at 1920 where the line renders 31.5px.
   That made it the worst text on the site — and it is on /hotel-list/*, pages that
   have NO comp, so it was OUR value, worse than anything the designer drew.
   --c-ink-light gives **3.82:1**, the CEILING on this band (white is the lightest
   there is), passes the large-text bar at 1920, and equals the comp's own body copy
   on the same band. Still an existing token, so no new convention.
   ⛔ 3.82 does NOT pass AA for normal-size text, and nothing on this band can:
   reaching 4.5:1 needs a darker taupe or dark text on it — a palette decision that
   belongs to the designer, not to a build. Muting stays carried by size and family,
   not by colour. Measured by .build-tooling/contrast-check.mjs. */
.td-band--taupe .td-hotel__location { color: var(--c-ink-light); }
.td-hotel__name     { color: var(--c-ink-paper); }
.td-band--taupe .td-hotel__name,
.td-band--taupe .td-hotel__copy { color: var(--c-ink-light); }

/* THREE PHOTOGRAPHS ACROSS. ★ The tile width is solved, not chosen: three
   tiles plus two of the comp's own 20px pair gaps across the 959px plate is
       (959 - 2*20) / 3 = 306.33
   and it is rounded DOWN to 306 for a reason — denmark-gilleleje-badehotel-2
   is 306 px wide at its crop, so 306 puts that file at exactly 1.000x instead
   of 1.001x.
   ⛔ FOUR HOTEL IMAGES ARE NARROWER THAN THIS SLOT and cannot fill it without
   breaking the 1.0x rule (ASSETS.json "below_slot_width"):
       mallorca-concepcio-3  245   mallorca-concepcio-1  368
       mallorca-cal-reiet-2  386   denmark-gilleleje-2   306 (exactly fits)
   So Concepció and Cal Reiet must either set --hotel-tile-w on their own
   movement (245 and 386 respectively) or run with two photographs instead of
   three. That is a client/designer decision, not a CSS one — see the notes.
   The gallery is a flex row rather than a grid so a two-photograph movement
   centres on the same axis with no second rule. */
.td-page { --hotel-tile-w: 306; }
.td-hotel__gallery {
  display: flex; justify-content: center;
  gap: calc(var(--gap-block-link) * var(--k));   /* 20 — the comp's pair gap */
  margin-left: 22.6208%;
  width: 70.2050%;
  margin-bottom: calc(var(--gap-group) * var(--k));
}
.td-hotel__shot {
  flex: 0 0 calc(var(--hotel-tile-w) * var(--k));
  aspect-ratio: 403 / 526;             /* the site's tile module */
  margin: 0;
}
.td-hotel__shot picture { display: block; width: 100%; height: 100%; }
.td-hotel__shot img { width: 100%; height: 100%; object-fit: cover; }

/* The copy column: 403 wide (13d), right edge flush with the plate at
   92.8258%, so it hangs off the photographs' right edge. */
.td-hotel__copy {
  width: calc(var(--measure-para) * var(--k));
  max-width: 100%;
  margin-left: calc(92.8258% - var(--measure-para) * var(--k));
}
.td-hotel__desc { margin: 0 0 calc(var(--gap-block-link) * var(--k)); }

/* BOOKING LINKS. ✅ A vertical list at the contact page's own list pitch
   (--lead-listitem 21, measured exactly on that comp), each item set in the
   comps' smallest sans (--fs-microlink 11) with the HAIRLINE 4x7 chevron and
   its measured 5px gap — i.e. the "Discover more ›" idiom, which is the comps'
   own micro call-to-action.
   ⛔ The old site letter-spaced these at 1.1px. The comps' sans tracking is
   PROVEN zero (§2, symmetric side-bearings in the outlined nav), and §14
   forbids moving it. They are set untracked. If the designer wants tracked
   micro-caps, that is a change to the design system, not to this page. */
.td-hotel__links { display: block; }
.td-hotel__link  { display: block; height: calc(var(--lead-listitem) * var(--k)); }
.td-hotel__link > .td-link { display: inline-flex; }

/* The place page's exit, on the olive band. ✅ .td-prices__contact's own
   treatment: the only centred link in the comps. */
.td-place__contact { display: flex; width: fit-content; margin-inline: auto; }


/* ==========================================================================
   13i. MOBILE — below 1024px
        ⛔ These rules must come AFTER §12, which turns every band into a
        1px-scaled padded block. They restore --k and the flow geometry for
        the two new page types only.
        There is no mobile comp for anything on this site, so the rule §12
        states applies here too: invent as little as possible. One horizontal
        axis (the comp's own gutter), one vertical scale, DOM order.
   ========================================================================== */

@media (max-width: 1023.98px) {
  /* ★ ONE LEVER, the same one the contact page uses in §12: pin --k so the
     type lands on a phone-legible scale, and every measured gap in this
     section follows from it automatically.
     Contact pinned --k so its smallest type reached 16px. Here the size that
     carries the reading is the body (the hotel descriptions), so
        --k = 16 / --fs-body = 16 / (--cap-body / --cap-sans)
            = 16 / (9.5 / 0.7049) = 16 / 13.4771 = 1.187203
     ⚠ --fs-body is 13.4771, NOT 13.0. 13.0 is the rounded comment beside
     --fs-body in §2, and using it put every size on this page 3.6% large.
     Measured on the rendered page after the correction:
        micro-links      11.35 -> 13.47  (over the 12px legibility floor)
        body             13.48 -> 16.00
        serif paragraph  16.00 -> 19.00
        serif link       22.40 -> 26.59
        hotel name       36.64 -> 43.50
        place name       46.40 -> 55.09
     ⛔ Do not pin it to 16/11 instead: that reaches iOS's no-zoom threshold on
     the micro links but takes the place name to 67.5px, which is a headline
     this design does not have. */
  .td-page .td-band--flow {
    --k: 1.187203px;
    aspect-ratio: auto; height: auto; container-type: normal;
    padding-block: calc(var(--band-pad) * var(--k));
    padding-inline: var(--sp-gutter-pct);
  }

  /* §12 sets `position: static; width: auto; height: auto` on every
     [class*="__"] inside a band. For these pages that is what we want for the
     text blocks; these rules re-establish the few boxes that must keep a size. */

  /* The collage becomes a single column. ⛔ The stagger is dropped, not
     scaled: a vertical offset inside one column is not a stagger, it is an
     uneven margin, and the composition it belongs to no longer exists. */
  .td-page .td-places__list {
    display: block;
  }
  .td-page .td-places__item {
    display: block; width: auto;
    margin: 0 0 calc(var(--gap-group) * var(--k));
  }
  .td-page .td-places__item--pending { margin-bottom: 0; align-self: auto;
                                       display: flex; align-items: baseline; }
  .td-page .td-band--flow .td-places__figure {
    width: 100%; height: auto; aspect-ratio: 403 / 526;
    margin-bottom: calc(var(--gap-block-link) * var(--k));
  }
  /* ⛔ A full-width tile on a 390px phone is 390 CSS px against a 403px file —
     0.97x, inside the rule. It goes over 1.0x above 403px of column width, so
     the tile is capped at its own file width and left-aligned to the gutter
     rather than stretched. */
  .td-page .td-band--flow .td-places__figure,
  .td-page .td-band--flow .td-place__standfirst,
  .td-page .td-band--flow .td-places__standfirst { max-width: 403px; }
  .td-page .td-band--flow .td-places__mark,
  .td-page .td-band--flow .td-place__mark {
    width: calc(20 * var(--k)); height: auto;
    margin: 0 auto calc(var(--gap-mark-text) * var(--k));
  }
  .td-page .td-places__name,
  .td-page .td-places__note { margin-top: 0; }

  /* Tap targets. ✅ The comps have no buttons and this adds none: the box is
     grown with padding and the growth folded back out with a negative margin,
     exactly as §12 does for the contact page's Back link, so nothing moves. */
  .td-page .td-places__link,
  .td-page .td-hotel__link > .td-link {
    min-height: 44px; display: flex; align-items: center;
  }
  .td-page .td-places__link { display: block; min-height: 0; }
  .td-page .td-places__name { min-height: 44px; display: inline-flex;
                              align-items: center; }

  /* The place page. The three-anchor geometry collapses to one axis. */
  .td-page .td-place__name,
  .td-page .td-place__standfirst { margin-inline: auto; text-align: center; }
  .td-page .td-place__name { margin-bottom: calc(var(--gap-block-link) * var(--k)); }

  .td-page .td-hotel__head {
    margin-left: 0; margin-bottom: calc(var(--gap-block-link) * var(--k));
  }
  .td-page .td-hotel__location { margin-bottom: calc(var(--gap-photo) * var(--k)); }

  /* ★ The triptych becomes a single column of full-width photographs at the
     same 403:526 module, with the comp's own 17px photo-to-photo gap. A
     three-across row at 390px would put each tile at 116px — smaller than the
     263px the site it replaces already shipped. */
  .td-page .td-hotel__gallery {
    display: block; width: auto; margin-left: 0;
    margin-bottom: calc(var(--gap-group) * var(--k));
  }
  .td-page .td-band--flow .td-hotel__shot {
    width: 100%; max-width: 403px; height: auto;
    aspect-ratio: 403 / 526;
    margin: 0 0 calc(var(--gap-photo) * var(--k));
  }
  .td-page .td-band--flow .td-hotel__shot:last-child { margin-bottom: 0; }

  .td-page .td-band--flow .td-hotel__copy {
    width: auto; max-width: 403px; margin-left: 0;
  }
  .td-page .td-hotel__desc { margin-bottom: calc(var(--gap-block-link) * var(--k)); }
  .td-page .td-hotel__link { height: auto; }

  /* ✅ The 1px rule is positioned against the nav item, and §12 makes that item
     static — so below 1024px it would hang off the hero band instead. Replaced
     by the underline the built branding page already ships at this width, which
     is why this is a promotion rather than a new treatment. */
  .td-page .td-nav__item--current::after { display: none; }
  .td-page .td-nav__item--current {
    text-decoration: underline; text-decoration-thickness: 1px;
    text-underline-offset: 5px;
  }

  /* ⚠ A SHARED DEFAULT THE SYSTEM WAS MISSING. §12 leaves .td-nav__item at
     its comp metrics on a phone, so its box is 5.4px tall — not a tap target.
     Home and branding each fixed that in PAGE-LOCAL CSS, and they fixed it
     DIFFERENTLY (home: 18.3px, gap clamp(34px,11vw,150px), padding 0 6px;
     branding: clamp(16px,4.1vw,20px), gap clamp(28px,8vw,56px), padding-inline
     8px) — two pages disagreeing about one component, which is the divergence
     this file exists to stop. The branding values are promoted here as the
     default because these pages copy that page's spine.
     ✅ This changes NOTHING already built: both page-local blocks are also
     (0,2,0) and their <style> follows this <link>, so each keeps its own
     numbers byte-for-byte. Verified by render. */
  /* ⛔ A SECOND SHARED DEFAULT THE SYSTEM WAS MISSING, and this one is a bug.
     §12's `.td-band:not(.td-band--hero) [class*="__"] { width: auto }` is
     (0,3,0) and therefore BEATS §12's own `.td-footer__mark { width: 47px }`
     at (0,1,0) — so on a page with no page-local CSS the footer shield loses
     its width and the masked box grows to the full column. Measured on the
     first render of /hotel-list/ at 390: a 390px shield. Every built page hid
     this because each carries its own footer rule.
     ✅ Values are the built branding page's, promoted; its own page-local
     block is also (0,3,0) and follows this file, so it keeps them byte-for-byte
     (verified: 0 differing pixels of 950,040 at 390). */
  .td-page .td-band--footer {
    display: flex; align-items: center; justify-content: space-between;
    gap: 24px; padding-block: clamp(44px, 12vw, 80px);
  }
  .td-page .td-band--footer .td-footer__mark {
    width: clamp(38px, 11vw, 62px); height: auto; aspect-ratio: 47 / 62;
    margin: 0; flex: none;
  }
  .td-page .td-band--footer .td-footer__link { margin: 0; }

  /* Tap targets for the two closing links and the footer link, the same way:
     grow the box, do not restyle the text. */
  .td-page .td-places__contact,
  .td-page .td-place__contact,
  .td-page .td-band--footer .td-footer__link {
    min-height: 44px; display: inline-flex; align-items: center; margin-top: 0;
  }
  /* ⛔ (0,3,0) is required: §12's `[class*="__"] { width: auto }` is (0,3,0)
     and beats a (0,2,0) `width: fit-content`, which left the closing link
     full-width and therefore left-aligned instead of centred. Caught by
     looking at the render, not by the box numbers. */
  .td-page .td-band--flow .td-places__contact,
  .td-page .td-band--flow .td-place__contact {
    display: flex; width: fit-content; margin-inline: auto;
  }

  .td-page .td-nav { gap: clamp(28px, 8vw, 56px); }
  .td-page .td-nav__item {
    margin-top: 0;
    min-height: 44px; min-width: 44px;
    display: inline-flex; align-items: center; justify-content: center;
    padding-inline: 8px;
    font-size: clamp(16px, 4.1vw, 20px);
    line-height: 1;
  }
}


/* ==========================================================================
   14. ACCEPTANCE TESTS — the numbers the pixel gate is allowed to argue with
       All at a 1366 viewport, Chromium launched with --hide-scrollbars.
       Mask the four Canva selection handles and column x=1365 before diffing.

   ALREADY VERIFIED with this file (font-independent geometry, measured, not
   asserted — Playwright box measurements + pixelmatch against the comps):
     home stage            1366x768 exact
     hero shield           x644.56 y160.91 w76.16 h100.13  (comp 644.57/160.92/76.16/100.13)
     home page diff        mean abs 2.37/255, 0.70% of px >16   (nav + handles masked)
     shield region diff    mean abs 3.96, 3.03% >16  — the comp's OWN vector
                           re-rendered scores 10.8% on the same box, so this is
                           under the JPEG noise floor
     branding bands        768 / 408 / 636 / 660 / 263, total 2735; boundaries
                           at y768 / 1176 / 1812 / 2472 — all exact
     band backgrounds      #FFFFFF #8E8077 #533C1A #D4CBC2 — exact RGB match
     sketch collage        x446 y1258 415x472; region diff mean 1.67, 2.76% >16
     photography page      mean abs 1.88, 1.83% >16 (caption + mark masked);
                           all five photo boxes and the z-order exact
     contact textarea      x230 y531 621x136, fill rgb(114,95,67)
     price row pitch       32.69px (comp 32.7); contact list pitch 21px exact
     white paragraph       4 lines, pitch 17.8px, line advances
                           271.4 / 284.5 / 268.9 / 192.6 vs comp ink
                           274 / 287 / — / 192
     network               ZERO external requests

   SANS ACCEPTANCE — measured on the rendered page against the comps.
   Numbers are comp / build, design px at 1366:
     "Hotel List"   ink width 76.04 / 76.48    cap top y56.19 / y56.16
     "Branding"     ink width 73.53 / 73.52    cap height 13.54 / 13.50
     caption lines  176.2 / 171.3, 185.1 / 185.9, 153.1 / 153.0
     taupe body     sets in SIX lines, ink 218/195/185/212/171/66 against
                    216/198/187/219/178/69 — same words on every line
     price labels   35.3/34.6, 100.7/102.0, 55.0/58.4, 165.7/170.5,
                    145.4/145.6, 32.1/32.8
     price values   84.1/79.6 .. 98.8/93.6 — these stay ~5% NARROW and cannot
                    be fixed by sizing, see --font-sans on the comp's digits
     Adjust --cap-sans (size) and --w-sans (weight) to hit these.
     ⛔ NEVER adjust letter-spacing to hit them — the sans tracking is
     PROVEN zero and moving it is how a build stops being the comp. Word
     spacing was measured separately and also came out at 0; see --word-sans.

   HOTEL LIST ACCEPTANCE (§13) — there is NO comp, so these are REGRESSION and
   RULE numbers, not comp-match numbers. Measured on built /hotel-list/ and
   /hotel-list/asia/ served locally, Chromium --hide-scrollbars.
     branding regression   0 differing px of 3,736,010 at 1366 AND 0 of 950,040
                           at 390, against the same page rendered with this
                           file as it was before §13 was added.
                           ⚠ THE HARNESS WAS PROVED SENSITIVE FIRST, and the
                           first control was VACUOUS: a 1-step change to
                           --c-taupe also scored 0 (under pixelmatch's 0.05
                           threshold). A 16-step change gives 663,724 px at
                           1366, and --c-stone gives 358,124 / 72,653 — the
                           second control was needed because branding overrides
                           --c-taupe below 1024px, so the taupe control is a
                           TRUE negative at 390 and proves nothing there.
     external requests     ZERO on all four renders
     1.0x image rule       no image over its intrinsic width on any render.
                           ⛔ CHECK AGAINST THE FILE ON DISK, NOT naturalWidth:
                           with `w` descriptors + `sizes` the HTML spec
                           normalises naturalWidth by the selected candidate's
                           density, so a 403px file inside a 384px slot reports
                           383 and every compliant image reads as a violation.
                           That instrument produced 5 false positives before it
                           was corrected.
     index slots @1366     i1 x313.03 w526.48 · i2 x882.20 w384.19 (drop 33)
                           i3 x313.03 w384.19 (drop 33) · i4 x739.91 w526.48
                           i5 x412.64 w384.19 · i6 x839.52 w384.19 (pending)
                           France name and Sweden name share y2999.59 exactly
     hotel geometry @1366  gallery x309 w959 (= the comp's marble plate)
                           each shot 306 wide, two 20px gaps -> 958 of 959
                           copy x865 w403, right edge 1268 = 92.8258%
                           head x93.98 = the comp's 94px left margin
     mobile scale @390     place 55.09 · hotel 43.50 · para 19.00 · link 26.59
                           · body 16.00 · micro 13.47   (--k 1.187203)
     no page-local CSS     both pages render from site.css alone — nothing in
                           §13 needs a <style> block in the HTML.

   SCRIPT ACCEPTANCE — the comp's swash capitals are not reproducible by any
   open face, so judge the INK BOX, not the glyph:
     "Prices"       comp x473.26 w89.34 y1958.86 h34.58 (aspect 2.584)
                    build x473.25 w87.49 y1959.55 h32.57 (aspect 2.686)
     It is the ONLY string on the site set in the script font. "Contact Form"
     ships as an SVG path lifted from the comp's own vector — see
     --cap-script-md — so it is exact and is not a test of the face.
   ========================================================================== */
