/* ==========================================================================
   Castiglioncello — the design system

   Derived from public/mockup/e.html, the direction the owner approved, with the
   one change he asked for on approval: BLUE IS THE PREDOMINANT COLOUR. Amber
   survives in exactly two places (docs/DESIGN.md §2) and a third amber element
   is a bug.

   What makes this look like the mockup and not like the three rejected ones:
     1. bands  — every section has its own ground, consecutive sections never
                 share one, and one edge is cut on an angle
     2. left   — nothing is centred, anywhere
     3. numbers— every section header carries "01 — Selezione"
     4. motion — listed in §14, and nothing beyond it
     5. furniture — badges, chips, counters, tabs, sticky panels: the page has to
                 read as software, not as a brochure

   How to work with this file:
     - COLOURS COME FROM ROLE TOKENS, never from a literal. `--ground`, `--text`,
       `--accent`, `--fill`, `--hair` are reassigned by the band classes (§3). A
       component that uses the role tokens is correct on white, on paper, on pale
       blue and on night without one extra selector. A component that hard-codes
       #fff is broken on four grounds out of nine.

       There is no longer a dark-mode block: §1-bis records why it was removed and
       what a proposal to bring it back would have to answer. The role tokens are
       what made that removal a deletion of twenty lines rather than a rewrite, and
       they are what would make bringing it back one too.
     - no !important outside the reduced-motion block, no id selectors, three
       levels of nesting at most.
     - page-specific rules go in public/assets/css/parts/<page>.css and may only
       use tokens declared here.
   ========================================================================== */

/* ---------------------------------------------------------------- 1. tokens */

:root {
    /* Palette (docs/DESIGN.md §2). Blue carries everything. */
    --night:   #08161d;   /* darkest ground: hero, footer, sec--dark      */
    --night-2: #0e2230;   /* card ground on a dark band                   */
    --deep:    #0f3a4d;   /* the deep blue band, the one with the cut edge */

    --blue:    #1c7ba1;   /* THE colour: fills, buttons, ticker            */
    --blue-2:  #2a9dc4;   /* lighter fill, gradients                       */
    --blue-3:  #155f7d;   /* blue as TEXT on a light ground: 6.2:1 minimum */
    --blue-4:  #7fd0ea;   /* blue as TEXT on a dark ground: 7.0:1 minimum  */
    --blue-l:  #e6f3f8;   /* the pale band                                 */

    --w:       #fff;
    --paper:   #f7f9fa;
    --paper-2: #eef3f6;

    --ink:     #0a1a22;
    --ink-2:   #4c626d;
    /* The mockup's third ink was #8398a3. It measures 2.85:1 on the paper band,
       and it is the colour of every mono label on the site — the eyebrows, the
       filter labels, the units under the figures. Darkened until it passes 4.5:1
       on all four light grounds (measured: 5.59 white, 5.29 paper, 5.00 paper-2,
       4.94 pale blue). This is the one palette value that deviates from the
       mockup, and it deviates for a reason that is not taste. */
    --ink-3:   #556b77;

    --line:    #dde5e9;   /* decorative hairline: dividers, card rules      */
    --line-2:  #7f939f;   /* a border you must be able to SEE: form fields,
                             3.2:1 on white — an input outlined at 1.28:1 is
                             not a visible control (WCAG 1.4.11)            */

    --amber:   #d99b4a;   /* two uses only: .pill dot, .badge--amber        */

    /* ---- role tokens: the band classes and the dark-mode block reassign
            these, and every component below reads them instead of a literal. */
    --ground:    var(--w);
    --ground-2:  var(--paper);       /* chips, small inset surfaces          */
    --text:      var(--ink);
    --text-2:    var(--ink-2);
    --text-3:    var(--ink-3);
    --heading:   var(--ink);
    --accent:    var(--blue-3);      /* eyebrows, links, underlines           */
    --price:     var(--ink);         /* the price is the strongest text there is */
    --fill:      var(--blue);        /* solid blue: buttons, arrow badges    */
    --fill-2:    var(--blue-3);      /* its hover                            */
    --fill-text: #fff;               /* the fill is always blue, so this is
                                        never var(--w): in dark mode that would
                                        put dark text on a blue button */
    --hair:      var(--line);
    --focus:     var(--blue-3);
    --card:      var(--w);           /* card ground                          */
    --card-line: transparent;        /* card border on dark bands only       */
    --glass:     rgba(255, 255, 255, .92);   /* header, viewer controls       */

    /* Placeholder field: two stops and a thin mark, no fake photograph. */
    --ph-a:    var(--paper-2);
    --ph-b:    #dfe8ed;
    --ph-mark: rgba(10, 26, 34, .14);

    /* ---- elevation: THREE shadows now, and the third is why the surfaces
            stopped reading as coloured rectangles.

       The old pair was one soft blob each — `0 2px 6px` over `0 10px 24px` at
       .06 and .07. A single wide blur at a flat alpha is what a browser default
       looks like: it darkens the ground under a card without ever describing an
       edge, so a white card on a white band has no boundary at all and reads as
       a region of the page rather than as an object standing on it. That is the
       single biggest reason this site was described as "too plain".

       What replaces them is the ordinary three-layer recipe, and each layer has
       a job that the other two cannot do:

         1. a 1px contact shadow, almost under the element — the layer that says
            the thing is SITTING on the page and not printed into it;
         2. a short mid blur with a negative spread, which carries the form;
         3. a long, wide, very low-alpha blur pushed downwards, which carries the
            weight. The negative spread is what keeps it from washing sideways
            into the neighbouring card in a three-up grid.

       The ink is `8 22 29` — `--night`, never a neutral black. A grey shadow
       over a blue-grey ground goes muddy; the palette's own darkest value reads
       as the same light falling on everything.

       `--sh-0` is new and is the resting state of anything small and
       interactive: a chip, a pager cell, a thumbnail, a filled control's ghost
       twin. Those carried nothing at all before, which is exactly why they
       looked drawn onto the page rather than placed on it. */
    --sh-0: 0 1px 2px rgba(8, 22, 29, .05);
    --sh:   0 1px 2px rgba(8, 22, 29, .05),
            0 4px 10px -4px rgba(8, 22, 29, .07),
            0 16px 32px -18px rgba(8, 22, 29, .17);
    --sh-2: 0 1px 2px rgba(8, 22, 29, .06),
            0 10px 22px -10px rgba(8, 22, 29, .14),
            0 34px 60px -28px rgba(8, 22, 29, .28);

    /* ---- the edge: a hairline INSIDE a surface.

       Painted as an inset ring in `box-shadow` rather than as a `border`, for
       two reasons that are not style: it costs no layout, so it can be added to
       a component whose padding is already measured; and it composites in the
       same declaration as the drop shadow, so hover can move both at once.

       It is a role token. On a dark band the line that describes an edge is
       LIGHTER than the surface, not darker, so the dark block below reassigns it
       — which is the whole point of §10 and the reason one declaration is
       correct on all nine grounds. */
    --edge:   rgba(10, 26, 34, .07);
    --edge-2: rgba(10, 26, 34, .13);

    /* ---- filled controls.

       `--fill-top` is the top stop of the button gradient: `--blue-2` and not a
       lightened `--blue`, so the button is made of two colours the palette
       already owns. The sheen is the 1px of light along the top inside edge that
       every physical control has, and the glow is the shadow a coloured button
       casts — tinted with a darkened version of its own colour rather than with
       grey, which is the difference between a button that is lit and a button
       that has been cut out and pasted down. */
    --fill-top:   var(--blue-2);
    --fill-sheen: inset 0 1px 0 rgba(255, 255, 255, .22);
    --fill-glow:  0 1px 2px rgba(12, 68, 91, .30),
                  0 10px 22px -10px rgba(12, 68, 91, .50);
    --fill-glow-2: 0 2px 4px rgba(12, 68, 91, .32),
                   0 18px 34px -14px rgba(12, 68, 91, .55);

    /* Typefaces. fonts.css declares the faces; these are the stacks.
       Fraunces and Inter are variable (opsz 9–144 / wght 100–900 and
       wght 100–900 respectively), IBM Plex Mono ships as 400 and 500. */
    --d: 'Fraunces', 'Iowan Old Style', Georgia, 'Times New Roman', serif;
    --t: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --m: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

    --e: cubic-bezier(.16, .84, .28, 1);

    /* The radius ramp, widened by two pixels at each step and given a fourth.
       12/18 is the radius of a form control; the surfaces of this site are
       photographs and panels, and at that size a 12px corner reads as a default
       rather than as a decision. Nothing here is round: the ramp stops well
       short of the pill, which stays at 99px and is the only fully round thing
       on the page. */
    --radius:    14px;
    --radius-l:  20px;
    --radius-s:  9px;
    --radius-xl: 26px;

    /* Height of the header in its tall state, and it has to MATCH: 40px of
       control plus the padding below. The fixed header overlays the page, so
       every page without a full-bleed opening offsets its body by this much and
       anchors scroll to below it. */
    --top-h: 68px;      /* 40 + 2 x 14 */

    /* Height of the language notice, measured by site.js while it is on screen
       and 0 the rest of the time. The header and the content offset by it, so a
       fixed notice never sits on top of the brand. */
    --notice-h: 0px;
}

/* See 1-bis: this stops the browser from darkening the parts of the page it owns
   — fields, scrollbars, autofill — while the page itself stays light. */
:root { color-scheme: light; }

@media (min-width: 51.25rem) {   /* 820px, the mockup's one structural break */
    :root { --top-h: 76px; }     /* 40 + 2 x 18 */
}

/* ---------------------------------------------------- 1-bis. one theme only

   There WAS a dark mode here, keyed on `prefers-color-scheme`. It has been
   removed on the owner's instruction, and the reasoning is worth keeping because
   somebody will eventually propose putting it back.

   A dark mode that follows the browser is not a feature the agency controls. It
   means half the visitors see a site nobody at the agency has ever looked at,
   and it means the owner cannot answer the only question that matters about a
   photograph of a house — "does it look right?" — because there are two answers.
   On a property site the photographs are the product: the ground behind them is
   part of how a room reads, and choosing it is an editorial decision, not a
   system preference.

   `color-scheme: light` is declared on purpose and is not a leftover. Without it
   a browser in dark mode still darkens what IT owns — the form fields, the
   scrollbars, the autofill wash, the date picker — so the page would stay light
   while the enquiry form turned charcoal. That mismatch was visible before this
   line existed.

   The dark BANDS are untouched. `--night` and `--deep` are part of the design
   (docs/DESIGN.md §3): the page alternates light and dark grounds by intention,
   and that alternation is exactly what a browser-driven dark mode flattened. */

/* ------------------------------------------------------------------ 2. base */

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

html {
    scroll-behavior: smooth;
    /* An anchor must not land under the fixed header. */
    scroll-padding-top: calc(var(--top-h) + var(--notice-h) + 12px);
    background: var(--ground);
}

body {
    margin: 0;
    background: var(--ground);
    color: var(--text);
    font-family: var(--t);
    /* 16.5px: the mockup's body size. Half a pixel over 16 is enough to stop
       Inter looking like a form label and small enough to keep the measure. */
    font-size: 16.5px;
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    overflow-x: hidden;
}

/* clip rather than hidden where it exists: overflow-x:hidden turns the body
   into a scroll container and that is what breaks position:sticky on the price
   panel in Safari. */
@supports (overflow: clip) {
    body { overflow-x: clip; }
}

::selection { background: var(--blue); color: #fff; }

img, svg, video { display: block; max-width: 100%; }
img { height: auto; }

a { color: inherit; }

h1, h2, h3, h4 {
    margin: 0;
    font-family: var(--d);
    font-weight: 400;
    line-height: 1.1;
    letter-spacing: -.025em;
    color: var(--heading);
    /* The opsz axis is what makes Fraunces look drawn for this size rather than
       scaled up. 120 is the display end of the axis (range 9–144). */
    font-variation-settings: 'opsz' 120;
    text-wrap: balance;
}

h1 { font-size: clamp(2.1rem, 5.4vw, 4rem); }
h2 { font-size: clamp(1.9rem, 4.2vw, 3.3rem); }
h3 { font-size: clamp(1.2rem, 1.8vw, 1.5rem); }
h4 { font-size: 1.1rem; }

p { margin: 0 0 1.1em; }
p:last-child { margin-bottom: 0; }

ul, ol { margin: 0; padding: 0; list-style: none; }

button, input, select, textarea { font: inherit; color: inherit; }

hr { border: 0; border-top: 1px solid var(--hair); margin: 0; }

:focus-visible {
    outline: 3px solid var(--focus);
    outline-offset: 2px;
    border-radius: 2px;
}

/* Figures line up in columns: prices, areas, room counts, references. */
.mono, .card__p, .pbox__p, .q b, .sp dd, .rescount b, .stat b, .bar__p {
    font-variant-numeric: tabular-nums;
}

.skip-link {
    position: absolute;
    left: -9999px;
    z-index: 300;
}

.skip-link:focus {
    left: 20px;
    top: 20px;
    padding: 12px 18px;
    background: var(--night);
    color: #fff;
    font-family: var(--m);
    font-size: 12px;
    letter-spacing: .12em;
    text-transform: uppercase;
    text-decoration: none;
    border-radius: var(--radius-s);
}

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

/* -------------------------------------------------------------- 3. the bands

   .sec is the unit of the page. Its ground comes from a modifier and two
   consecutive sections must never carry the same one — that alternation is the
   whole reason this direction was approved and the previous three were not.

   Order that works, and the one the home follows:
   dark → ticker (blue) → white → deep+cut → paper → white → pale → deep → foot. */

.wrap {
    max-width: 1400px;
    margin: 0 auto;
    padding: 0 20px;
}

/* `.container` is the name the page templates use, and there are 22 of them.
   Aliasing beats renaming: the templates carry the test hooks, and a rename for
   the sake of tidiness would risk a red suite for no visible gain. */
.container {
    width: 100%;
    max-width: 1400px;
    margin: 0 auto;
    padding: 0 20px;
}


@media (min-width: 51.25rem) {
    .wrap,
    .container { padding: 0 44px; }
}

/* Running text never gets wider than this, whatever the band. */
.measure { max-width: 46rem; }

/* The line under a heading, before a form or a list. */
.lead {
    max-width: 46ch;
    margin: 14px 0 32px;
    font-size: 18px;
    color: var(--text-2);
}
.stack > * + * { margin-top: 1rem; }

.sec {
    position: relative;
    padding: 96px 0;
    background: var(--ground);
    color: var(--text);
}

@media (min-width: 51.25rem) {
    .sec { padding: 130px 0; }
}

.sec--tight { padding: 64px 0; }

.sec--paper { --ground: var(--paper); }
.sec--blue  { --ground: var(--blue-l); }

/* The two dark grounds. Everything a component needs to be correct on a dark
   band is in these two rule sets, and nothing else in the file says
   ".sec--dark .something". */
.sec--dark,
.sec--deep,
.hero,
.foot {
    --ground:    var(--night);
    --ground-2:  rgba(255, 255, 255, .07);
    --text:      rgba(255, 255, 255, .78);
    --text-2:    rgba(255, 255, 255, .62);
    --text-3:    rgba(255, 255, 255, .55);
    --heading:   #fff;
    --accent:    var(--blue-4);
    --focus:     var(--blue-4);
    --hair:      rgba(255, 255, 255, .11);
    --card:      var(--night-2);
    --card-line: rgba(255, 255, 255, .09);
    --ph-a:      #12262f;
    --ph-b:      #0b1a22;
    --ph-mark:   rgba(255, 255, 255, .13);
    --line-2:    rgba(255, 255, 255, .28);
    --price:     var(--blue-4);
    --pill:      rgba(255, 255, 255, .1);
    /* On a dark ground an edge is drawn with light, not with shade — see the
       token's own note in §1. Without this reassignment every inset ring added
       by §24 would be a darker line on an already dark surface: invisible on
       `--night`, and a smudge on `--night-2`. */
    --edge:      rgba(255, 255, 255, .09);
    --edge-2:    rgba(255, 255, 255, .17);
    --sh-0:      none;
    --sh:        none;
    --sh-2:      0 2px 6px rgba(0, 0, 0, .30), 0 26px 60px -20px rgba(0, 0, 0, .60);
}

.sec--deep { --ground: var(--deep); }

/* An angled edge, so one band visibly ends and the next begins. 3.2vw of cut
   pulled up by the same amount: the band above keeps its full height. */
.sec--cut {
    clip-path: polygon(0 0, 100% 3.2vw, 100% 100%, 0 100%);
    margin-top: -3.2vw;
    padding-top: calc(96px + 3.2vw);
}

@media (min-width: 51.25rem) {
    .sec--cut { padding-top: calc(130px + 3.2vw); }
}

/* Section header: the number, the heading, one line of prose, and a link on the
   right that ends up under it on a phone. Left aligned, like everything. */
.shead {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 20px;
    margin-bottom: 44px;
}

.shead > div { max-width: 62ch; }

.shead p {
    color: var(--text-2);
    max-width: 44ch;
    margin: 12px 0 0;
}

.snum {
    display: block;
    margin-bottom: 12px;
    font-family: var(--m);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: .2em;
    text-transform: uppercase;
    color: var(--accent);
}

.arrow {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    font-family: var(--m);
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: .14em;
    text-transform: uppercase;
    text-decoration: none;
    color: var(--accent);
}

.arrow svg {
    width: 18px;
    height: 18px;
    stroke: currentColor;
    stroke-width: 1.8;
    fill: none;
    transition: transform .3s var(--e);
}

.arrow:hover svg { transform: translateX(6px); }

/* NO backdrop-filter here, and that is a performance decision with numbers
   behind it. A `backdrop-filter` is only cached while its backdrop holds still.
   Both places this pill is used — the home hero and the property cover — sit on
   a photograph that drifts continuously (`hero-drift`, `pcover-drift`), so the
   backdrop changed on every frame and the compositor re-rendered the hero's
   whole backdrop into an offscreen texture and re-blurred it, forever, at the
   display's refresh rate. Measured on an M4 over CDP at 1440x900 dpr2: one
   single blurred pill over the drifting photograph tripled the GPU process's
   CPU per frame (0.45 -> 1.42 ms); dropping all of them took the property page
   from 3.65 to 2.09 ms/frame and the home from 4.94 to 3.72.

   The blur was doing real work, though — it flattened the leaves behind 11px
   white mono text — so it is replaced, not deleted. A dark floor under the same
   translucent white veil buys the same calm for nothing:

     * over a photograph the floor sets a contrast bottom the picture cannot
       raise. Measured luminance ripple inside the pill fell from 26.6 (no blur,
       photograph legible through it) to 14.6 — the blurred original was 15.4;
     * on a flat ground (`.sec--dark`, where page.html.twig puts a `.pill`) a
       #08161d floor over #08161d is invisible, so the veil alone still paints
       it and the pill looks exactly as it always did.

   Order matters: background-image paints over background-color. */
.pill {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    padding: 8px 15px;
    border-radius: 99px;
    background-color: rgba(8, 22, 29, .48);
    background-image: linear-gradient(rgba(255, 255, 255, .1), rgba(255, 255, 255, .1));
    border: 1px solid rgba(255, 255, 255, .2);
    font-family: var(--m);
    font-size: 11px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: #fff;
}

/*
    THE PULSING AMBER DOT IS GONE. Its only markup was `<p class="pill"><i></i>` in the
    home hero, and that pill went with the counting figures. The two other `.pill`
    users — the thank-you page and a property cover — never had an `<i>`.

    Two things follow, both worth writing down.

    docs/DESIGN.md §2 spends amber on exactly two things, and this was one of them. The
    remaining one is `badge--amber` on a featured listing. If a future screen wants
    amber, that is the budget it is drawing from.

    And it was not free. `pulse` was one of the four continuous animations measured on
    the home, and a 7px dot cost +2.73 ms of CPU per frame — more than the full-screen
    2880x1472 photograph behind it. The cost of a compositor animation has nothing to
    do with how big the thing is.
*/

/* The locality ticker. Blue ground — §2 gives the ticker to blue.
   The list has to be printed TWICE in the markup: the animation ends at -50%,
   which is seamless only if the second half repeats the first. */
.ticker {
    background: var(--blue);
    color: #fff;
    padding: 13px 0;
    overflow: hidden;
    white-space: nowrap;
}

.ticker__in {
    display: inline-flex;
    gap: 44px;
    font-family: var(--m);
    font-size: 12px;
    letter-spacing: .18em;
    text-transform: uppercase;
    animation: slide 26s linear infinite;
}

@keyframes slide { to { transform: translateX(-50%); } }

/*
    THE THREE COUNTING FIGURES ARE GONE — `.stats`, `.stat`, and with them the pill's
    pulsing dot below.

    They sat in the hero: how many listings, how many sections, how many languages. The
    owner's verdict was that they served nobody, and the record should say he was
    right. The reasoning that put them there was «nothing here can go stale», which is
    an argument about maintenance and not a reason for a visitor to read something.
    «2 sezioni» and «3 lingue» are facts about the website, in the largest type on the
    first screen a buyer sees.

    What replaced them is in parts/home.css: the two entrances, Vendita and Affitti,
    each carrying its own count — which is where a count means something. «Vendita · 7
    immobili» tells you whether the tap is worth it.

    A note for whoever reads the git history: the comment deleted here recorded a
    careful measurement of how to keep those tiles legible over a drifting photograph
    without a backdrop-filter. That work was right and is not wasted — the same
    technique is what `.pill` still uses, documented above it.
*/

/* Grids. One column, then two, then three with --3. minmax(0,1fr) everywhere:
   the automatic minimum of a track is its content, and one unbreakable mono
   reference widens the whole page on a phone. */
/* A single-column grid still needs its track declared: the automatic minimum
   of an `auto` track is the content's min-content width, and one unbreakable
   mono reference (or a 2.6rem price) then makes the whole document wider than
   the phone. minmax(0, 1fr) is the only safe track, at every width. */
.grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 24px; }

@media (min-width: 40rem) {
    .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 66.25rem) {
    .grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ----------------------------------------------------- 4. the placeholder

   What the agency looks at for weeks: a calm gradient field with one thin mark
   in the middle. Not a fake photograph, not a grey box with a camera icon.
   Light by default, --d for the dark variant; the ratio modifiers keep the grid
   from jumping while the real photographs are still being uploaded. */

.ph {
    position: relative;
    overflow: hidden;
    background: linear-gradient(150deg, var(--ph-a), var(--ph-b));
}

.ph::after {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    width: 34px;
    height: 26px;
    transform: translate(-50%, -50%);
    border: 1.6px solid var(--ph-mark);
    border-radius: 4px;
}

.ph--d {
    --ph-a: #12262f;
    --ph-b: #0b1a22;
    --ph-mark: rgba(255, 255, 255, .13);
}

.ph--wide { aspect-ratio: 3 / 2; }
.ph--pano { aspect-ratio: 16 / 9; }
.ph--sq   { aspect-ratio: 1 / 1; }
.ph--tall { aspect-ratio: 3 / 4; }

/* Photographs. The partial always emits width and height, so the box is
   reserved before the file arrives; this only has to keep it fluid. */
.image { width: 100%; height: auto; }

/* site.js adds the fade class, so with JavaScript off the photograph is simply
   visible — the right way for this to fail. */
.image--fade { opacity: 0; transition: opacity 500ms ease; }
.image--fade.is-loaded { opacity: 1; }

/* The placeholder branch of partials/image.html.twig. It is a .ph as well, so
   it inherits the field and the mark; this only makes the span behave like the
   image it stands in for. */
.image--missing {
    display: block;
    width: 100%;
}

/* --------------------------------------------------------- 5. scroll progress */

.prog {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 150;
    width: 0;
    height: 3px;
    background: linear-gradient(90deg, var(--blue-3), var(--blue-2));
    transition: width .1s linear;
}

/* --------------------------------------------------------------- 6. header

   One line at 390, 768, 1024 and 1440. That is the constraint that broke the
   two previous attempts, and it is met by three things:
     - the name never wraps and truncates rather than pushing anything down;
     - the navigation row appears at 1100 with four entries and grows to six at
       1400, no more;
     - everything that is not promoted lives in the Menu panel, which is the
       complete index. The panel HIDES the entries the row is already showing
       (§6-bis), so nothing is ever listed twice at the same width.

   Default state is SOLID, not transparent: a page that forgets to declare a
   full-bleed opening is then merely less cinematic, instead of showing white
   text on a white band. Only a page that carries the `hero-page` body class
   gets the transparent header over its photograph. */

body {
    /* the fixed header overlays the page; every page but a hero one offsets */
    padding-top: calc(var(--top-h) + var(--notice-h));
}

body.hero-page { padding-top: 0; }

.top {
    position: fixed;
    top: var(--notice-h);
    left: 0;
    right: 0;
    z-index: 100;
    padding: 14px 0;
    background: var(--glass);
    backdrop-filter: blur(18px);
    box-shadow: 0 1px 0 var(--hair);
    transition: padding .35s var(--e), background .35s var(--e), box-shadow .35s var(--e), top .2s var(--e);
}

/* site.js adds .sm past 70px of scroll. */
.top.sm { padding: 8px 0; }

@media (min-width: 51.25rem) {
    .top { padding: 18px 0; }
    .top.sm { padding: 8px 0; }
}

.top__in {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
}

/* Over the hero photograph: transparent, white, and its own token set so the
   pill group, the underline and the focus ring stay legible on a photograph. */
.hero-page .top:not(.sm) {
    --text: rgba(255, 255, 255, .86);
    --text-2: rgba(255, 255, 255, .72);
    --heading: #fff;
    --accent: var(--blue-4);
    --focus: var(--blue-4);
    --pill: rgba(255, 255, 255, .14);
    --line-2: rgba(255, 255, 255, .32);
    background: transparent;
    backdrop-filter: none;
    box-shadow: none;
    padding: 20px 0;
    color: var(--text);
}

.brand {
    display: flex;
    align-items: center;
    gap: 11px;
    flex: 0 1 auto;
    min-width: 0;          /* under 1100, truncate rather than wrap */
    text-decoration: none;
    color: var(--heading);
}

.brand__m {
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    border-radius: 10px;
    background: var(--fill);
    color: #fff;
    display: grid;
    place-items: center;
    font-family: var(--d);
    font-size: 17px;
    line-height: 1;
    transition: width .35s var(--e), height .35s var(--e), font-size .35s var(--e);
}

.top.sm .brand__m { width: 31px; height: 31px; font-size: 15px; }

.brand__n {
    font-family: var(--d);
    /* 'opsz' 20 and not 120: at 15px the display axis has to sit at the text
       end of its range or the name looks like a shrunk poster. */
    font-variation-settings: 'opsz' 20;
    font-size: clamp(13.5px, 1vw + 9px, 16px);
    letter-spacing: -.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The row of promoted entries.

   It is a wrapping flex line one line tall with the overflow clipped: entries
   that do not fit move to a second line that is never painted. That is what
   makes the header survive real data — a category called "Case coloniche" and a
   page called "Esperienze sul territorio", in three languages, edited by the
   agency — without measuring anything in JavaScript. The brand never shrinks and
   the tools never shrink; the row gives up entries instead, and the Menu panel
   beside it is the complete index, so nothing becomes unreachable. */
.nav {
    display: none;
    flex: 1 1 auto;
    min-width: 0;
    flex-wrap: wrap;
    align-content: flex-start;
    gap: 2px;
    height: 2.4rem;
    overflow: hidden;
}

.nav__link {
    position: relative;
    display: block;
    padding: 9px 12px;
    border-radius: 9px;
    font-size: 14.5px;
    font-weight: 450;
    text-decoration: none;
    color: var(--text-2);
    white-space: nowrap;
    transition: color .22s;
}

.nav__link:hover { color: var(--heading); }

.nav__link::after {
    content: '';
    position: absolute;
    left: 12px;
    right: 12px;
    bottom: 4px;
    height: 2px;
    background: var(--accent);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform .3s var(--e);
}

.nav__link:hover::after,
.nav__link--active::after { transform: scaleX(1); }

.nav__link--active { color: var(--heading); }

/* Inset, because the row clips its own overflow: a ring drawn outside the box
   would be cut in half. */
.nav__link:focus-visible { outline-offset: -3px; }

.tools {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 0 0 auto;
}

/*
    THE LANGUAGE SWITCHER'S COMPONENT IS GONE, with the switcher itself.

    It was taken out of the header row first and survived in the menu panel; the owner
    then found it still there on a phone and it is out of both. The one copy left is
    the footer's, which has its own `.foot__lang-*` rules — so `.lang`, `.lang__link`
    and `.lang__current` had no markup left anywhere and are deleted rather than kept
    "in case".

    Why the site can afford not to have one in the chrome: the bare root redirects by
    Accept-Language, and whoever lands on the wrong language gets the notice band at
    the top of the page — one tap, remembered in a cookie, never shown again. Google
    serves the right version by itself, because each language is its own address with
    its own hreflang.
*/

/* .cta below sets display:inline-flex on the same weight and later in the
   file, so the header's own copy has to say where it is. */
.top .top__cta { display: none; }

/* ------ the Menu button and its panel ------ */

.menu { position: relative; flex: 0 0 auto; }

.menu__button {
    display: flex;
    align-items: center;
    gap: 9px;
    height: 40px;
    padding: 0 13px;
    border: 1px solid var(--line-2);
    border-radius: 10px;
    background: none;
    color: var(--text);
    font-family: var(--m);
    font-size: 11px;
    letter-spacing: .14em;
    text-transform: uppercase;
    cursor: pointer;
    list-style: none;
}

.menu__button::-webkit-details-marker { display: none; }
.menu__button::marker { content: ''; }
.menu__button:hover { color: var(--heading); border-color: var(--accent); }

.menu__bars {
    display: block;
    width: 15px;
    height: 1.5px;
    background: currentColor;
    box-shadow: 0 5px 0 currentColor, 0 -5px 0 currentColor;
}

.menu__label { display: none; }

.menu__panel {
    position: absolute;
    top: calc(100% + 12px);
    right: 0;
    z-index: 10;
    width: min(20rem, calc(100vw - 40px));
    padding: 14px;
    border-radius: var(--radius-l);
    background: var(--card);
    color: var(--ink);
    box-shadow: var(--sh-2);
    border: 1px solid var(--card-line);
}

/* Inside the panel the ground is always the card ground, whatever band the
   header happens to be floating over. */
.menu__panel {
    --text: var(--ink);
    --text-2: var(--ink-2);
    --heading: var(--ink);
    --accent: var(--blue-3);
    --focus: var(--blue-3);
    --pill: var(--paper-2);
}

.menu__link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 11px 12px;
    border-radius: var(--radius-s);
    font-size: 15px;
    text-decoration: none;
    color: var(--text);
}

.menu__link:hover { background: var(--paper); color: var(--accent); }

.menu__link--active {
    color: var(--accent);
    font-weight: 550;
}

.menu__foot {
    margin-top: 10px;
    padding-top: 12px;
    border-top: 1px solid var(--line);
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 10px;
    justify-items: start;
}

.menu__foot .lang { display: flex; }   /* the panel copy, live under 640 */

.menu__phone {
    font-family: var(--m);
    font-size: 12.5px;
    letter-spacing: .04em;
    text-decoration: none;
    color: var(--accent);
}

/* ------ 6-bis. promotion of the navigation entries ------

   One step. Under 1100 there is no row at all and the Menu panel is the whole
   navigation; from 1100 the row appears and shows as many of the first six
   entries as fit on one line. */

@media (min-width: 68.75rem) {          /* 1100 — the row appears */
    .nav { display: flex; }

    /* From here the row is the flexible one: it gives up an entry to the panel
       rather than let the agency's name lose its last letters. */
    .brand { flex-shrink: 0; }

    /* Never more than six, whatever menu() returns: past six the row stops
       being navigation and becomes a wall. Which of the six actually fit is
       decided by the clip above, and the rest are in the panel. */
    .nav__link:nth-child(n+7) { display: none; }
}

/* ONE MENU AT ANY WIDTH.

   A burger sitting next to a full navigation row reads as two menus, and it was
   the first thing the owner noticed. Removing the language pill freed the space
   the seventh and eighth entries needed, so from 1200 the row carries the whole
   navigation and the button goes away entirely. Below 1200 the button is the
   navigation and the row is hidden. There is never a width where both are on
   screen. */
@media (min-width: 75rem) {
    .nav__link:nth-child(n+7) { display: inline-flex; }
    .top .menu { display: none; }
}

/* From 640 the pill group and the call to action are in the header itself, so
   the copies inside the panel go away. */
@media (min-width: 40rem) {
    .top .top__cta { display: inline-flex; }
}

@media (min-width: 51.25rem) {
    .menu__label { display: block; }
}

/* ------------------------------------------------------- 7. breadcrumbs */

.breadcrumbs {
    padding: 18px 0 0;
    background: var(--ground);
}

.breadcrumbs__list {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    font-family: var(--m);
    font-size: 10.5px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--text-3);
}

.breadcrumbs__item + .breadcrumbs__item::before {
    content: '/';
    margin-right: 8px;
    color: var(--text-3);
}

.breadcrumbs__item a { color: var(--text-2); text-decoration: none; }
.breadcrumbs__item a:hover { color: var(--accent); }

/* -------------------------------------------------- 8. language notice

   Fixed to the very top, above the header, because the page comes out of a file
   cache and the notice is decided in the browser: it cannot be part of the
   document flow without shifting a layout that is already painted. site.js
   measures it into --notice-h, and the header and the body offset by that. */

.language-notice {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 130;
    background: var(--night);
    color: rgba(255, 255, 255, .8);
    font-size: 14px;
}

.language-notice__inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 18px;
    /* padding-block, never `padding: 10px 0`: this element also carries .wrap
       and the shorthand would wipe out the gutter. Same reason in .consent__inner
       and .bar__in. */
    padding-block: 10px;
}

.language-notice__text { margin: 0; flex: 1 1 14rem; }

.language-notice__action {
    font-family: var(--m);
    font-size: 11px;
    letter-spacing: .12em;
    text-transform: uppercase;
    text-decoration: none;
    color: var(--blue-4);
}

.language-notice__action:hover { text-decoration: underline; }

.language-notice__dismiss {
    border: 0;
    background: none;
    padding: 0;
    cursor: pointer;
    font-family: var(--m);
    font-size: 11px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .5);
}

.language-notice__dismiss:hover { color: #fff; }

/* ----------------------------------------------------------------- 9. hero

   Full viewport, photograph under a scrim, content on the bottom left, figures
   in a four-up strip. The photograph is atmosphere: no name, no price, no link
   (docs/DESIGN.md §8). */

.hero {
    position: relative;
    /* max() and not 100svh alone: on a 500px-tall laptop window the strip of
       figures would otherwise be cut in half. */
    min-height: max(100svh, 34rem);
    display: grid;
    align-items: end;
    background: var(--ground);
    color: var(--text);
    overflow: hidden;
}

.hero__bg { position: absolute; inset: 0; }

/* No centred mark on a full-screen field: at 100svh it lands in the middle of
   the headline and reads as a stray box. */
.hero__bg .ph::after { display: none; }

/* Ken Burns, 26s, alternating: slow enough that you notice it only if you stop
   to look. The frame is oversized by 6% on every side so the scale never shows
   an edge. */
.hero__bg > * {
    position: absolute;
    inset: -6%;
    width: 112%;
    height: 112%;
    object-fit: cover;
    aspect-ratio: auto;
    animation: kb 26s ease-in-out infinite alternate;
}

@keyframes kb {
    from { transform: scale(1) translate(0, 0); }
    to   { transform: scale(1.12) translate(-2%, -2%); }
}

.hero__bg::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(to top,
        rgba(8, 22, 29, .95) 0%,
        rgba(8, 22, 29, .55) 42%,
        rgba(8, 22, 29, .35) 100%);
}

.hero__in {
    position: relative;
    padding-top: calc(var(--top-h) + 48px);
    padding-bottom: 44px;
    color: #fff;
}

.hero h1 {
    color: #fff;
    font-size: clamp(2.4rem, 6.6vw, 5.6rem);
    max-width: 17ch;
    margin: 22px 0 0;
    text-wrap: initial;   /* the word-by-word split does the breaking */
}

/* One word at a time. The spans only exist once site.js has split the heading:
   with the script off the heading is plain text and perfectly readable. */
.hero h1 .w {
    display: inline-block;
    opacity: 0;
    transform: translateY(28px);
    animation: up .9s var(--e) forwards;
}

@keyframes up { to { opacity: 1; transform: none; } }

.hero__sub {
    color: rgba(255, 255, 255, .74);
    max-width: 42ch;
    margin-top: 20px;
    font-size: 18px;
}

/* Bottom right, not centred: rule 2 of the direction is that nothing is
   centred, and a hint under the middle of the hero is the one thing in the
   mockup that would have contradicted it. */
.scrollhint {
    display: none;         /* no room beside the figures on a phone */
    position: absolute;
    right: 20px;
    bottom: 18px;
    flex-direction: column;
    align-items: flex-end;
    gap: 8px;
    font-family: var(--m);
    font-size: 9.5px;
    letter-spacing: .24em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .5);
}

.scrollhint::after {
    content: '';
    width: 1px;
    height: 26px;
    background: linear-gradient(rgba(255, 255, 255, .5), transparent);
    transform-origin: top;
    animation: drop 1.8s infinite;
}

/* The line grows from the top, then collapses towards the bottom — unchanged.
   What changed is that it no longer moves `transform-origin` between keyframes.
   `transform-origin` is not an accelerated property, so a keyframe that touches
   it drops the whole animation onto the main thread and repaints this line every
   frame; and because the hint lives inside `.hero`, that repaint dirtied the
   hero. Measured cost of that one keyframe on the home: 4.94 -> 4.34 ms of CPU
   per frame, for a rule that describes a 1x26px tick mark.

   A fixed origin at the top gives the identical motion, because collapsing to
   the bottom edge is the same picture as collapsing to the top edge and then
   being pushed down by the element's own height. `transform` alone composites. */
@keyframes drop {
    0%   { transform: translateY(0) scaleY(0); }
    50%  { transform: translateY(0) scaleY(1); }
    100% { transform: translateY(26px) scaleY(0); }
}

@media (min-width: 40rem) {
    .scrollhint { display: flex; }
}

@media (min-width: 51.25rem) {
    .scrollhint { right: 44px; }
}

/* ---------------------------------------------------------------- 10. cards */

.card {
    position: relative;
    display: block;
    text-decoration: none;
    color: var(--text);
    background: var(--card);
    border: 1px solid var(--card-line);
    border-radius: 16px;
    overflow: hidden;
    box-shadow: var(--sh);
    transition: transform .4s var(--e), box-shadow .4s var(--e);
}

.card:hover { transform: translateY(-8px); box-shadow: var(--sh-2); }

.card__ph { position: relative; overflow: hidden; }

.card__ph > .ph,
.card__ph > .image { transition: transform .8s var(--e); }

.card:hover .card__ph > .ph,
.card:hover .card__ph > .image { transform: scale(1.07); }

.badge {
    position: absolute;
    top: 14px;
    left: 14px;
    background: var(--night);
    color: #fff;
    font-family: var(--m);
    font-size: 10px;
    letter-spacing: .12em;
    text-transform: uppercase;
    padding: 6px 11px;
    border-radius: var(--radius-s);
}

/* Amber, use 2 of 2: a listing the agency has chosen to feature. */
.badge--amber { background: var(--amber); color: var(--night); }

.badge--blue { background: var(--fill); color: #fff; }

/* The favourite button. It is real: site.js keeps the list in localStorage and
   reveals the button, so with the script off it is not rendered at all rather
   than rendered dead. */
.fav {
    position: absolute;
    top: 12px;
    right: 12px;
    width: 36px;
    height: 36px;
    border: 0;
    border-radius: 10px;
    background: rgba(255, 255, 255, .9);
    backdrop-filter: blur(8px);
    display: grid;
    place-items: center;
    cursor: pointer;
    transition: background .2s, transform .2s var(--e);
}

.fav:hover { background: #fff; transform: scale(1.08); }

.fav svg { width: 17px; height: 17px; stroke: var(--night); stroke-width: 1.6; fill: none; }

.fav[aria-pressed='true'] svg { stroke: var(--blue); fill: var(--blue); }

.card__go {
    position: absolute;
    bottom: 14px;
    right: 14px;
    width: 42px;
    height: 42px;
    border-radius: var(--radius);
    background: var(--fill);
    display: grid;
    place-items: center;
    opacity: 0;
    transform: translateY(10px);
    transition: opacity .35s var(--e), transform .35s var(--e);
}

.card:hover .card__go { opacity: 1; transform: none; }

.card__go svg { width: 19px; height: 19px; stroke: #fff; stroke-width: 2; fill: none; }

/* display:block is load-bearing, not tidiness.

   `.card__b` is a <span>, because the whole card is one <a> and an <a> may not
   contain a <div>. An inline box does not establish a containing block for
   block-level children: `.card__loc` is a flex container and the title and
   summary are blocks, so they broke out of the inline box and the padding was
   simply dropped — the text ended up flush against the card's edges. Every card
   on the site is built this way, so the fix belongs here and not in one page's
   stylesheet. */
.card__b {
    display: block;
    padding: 20px 22px 22px;
}

/* Same reason: a <span> used as a line of text needs a box of its own before a
   margin or a padding on it means anything. */
.card__b > span,
.card__b > p { display: block; }

.card__loc {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 0;
    font-family: var(--m);
    font-size: 10.5px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--text-3);
}

.card__loc svg { width: 13px; height: 13px; stroke: currentColor; stroke-width: 1.6; fill: none; flex: 0 0 auto; }

.card h3 { font-size: 1.2rem; margin: 9px 0 0; line-height: 1.3; }

/* The shared property card caps its title at two lines WHEREVER it is used.

   The cap was declared in `parts/catalog.css`, so the very same partial ran to
   three or four lines on the two pages that do not load that file — the
   experience sheet and the property sheet's territory band — and one long title
   made a cell taller than the cards beside it. Measured with a 130-character
   title: two lines on the catalogue, four on the experience page. A component
   used by three pages carries its own limits (docs/DESIGN.md §10). The whole
   title stays in the DOM for whoever listens to the page: it is clipped, not
   cut. Same declaration as catalog.css, so neither file fights the other. */
.card .property-card__title {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
    overflow-wrap: anywhere;
}

.card__p {
    font-family: var(--d);
    font-size: 1.6rem;
    margin: 14px 0 0;
    color: var(--price);
}

/* "Price on request" is not a figure and must not be typeset as one. */
.card__p--request { font-size: 1.15rem; color: var(--text-2); }

.card__d {
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--hair);
}

.dc {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--m);
    font-size: 11px;
    color: var(--text-2);
    background: var(--ground-2);
    padding: 5px 10px;
    border-radius: 7px;
}

.dc svg { width: 13px; height: 13px; stroke: currentColor; stroke-width: 1.6; fill: none; }

/* --------------------------------------------- 11. the section blocks

   The home's real content: one photographic block per active category with its
   live count. Bigger than the mockup's, because §8 gives them the room the card
   grid used to have. */

.blocks { display: grid; grid-template-columns: minmax(0, 1fr); gap: 18px; }

@media (min-width: 56.25rem) {
    .blocks { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.block {
    position: relative;
    display: grid;
    align-items: end;
    min-height: clamp(18rem, 34vw, 26rem);
    border-radius: var(--radius-l);
    overflow: hidden;
    isolation: isolate;
    text-decoration: none;
    color: #fff;
}

.block > .ph,
.block > .image {
    position: absolute;
    inset: 0;
    z-index: -2;
    width: 100%;
    height: 100%;
    object-fit: cover;
    aspect-ratio: auto;
    transition: transform .8s var(--e);
}

.block::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(to top, rgba(8, 22, 29, .9), rgba(8, 22, 29, .25));
    transition: background .5s;
}

.block:hover > .ph,
.block:hover > .image { transform: scale(1.08); }

/* The gradient turns blue on hover. This is the one motion that says "this is a
   door", and it is the reason the home does not need cards. */
.block:hover::after {
    background: linear-gradient(to top, rgba(28, 123, 161, .92), rgba(8, 22, 29, .35));
}

.block__b { position: relative; padding: 26px; }

.block h3 { font-size: clamp(1.5rem, 2.6vw, 1.9rem); color: #fff; }

.block__meta {
    display: block;
    margin-top: 9px;
    font-family: var(--m);
    font-size: 11px;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .7);
}

.block__n {
    position: absolute;
    top: 22px;
    right: 24px;
    font-family: var(--m);
    font-size: 11px;
    color: rgba(255, 255, 255, .45);
}

/* ------------------------------------------- 12. catalogue furniture */

.fbar {
    position: relative;
    z-index: 5;
    background: var(--card);
    border: 1px solid var(--card-line);
    border-radius: var(--radius-l);
    box-shadow: var(--sh-2);
    padding: 12px;
}

@media (min-width: 62.5rem) {
    .fbar {
        display: grid;
        grid-template-columns: minmax(0, 1.5fr) auto minmax(0, 1fr) auto minmax(0, 1fr) auto minmax(0, 1fr) auto;
        gap: 8px;
        align-items: stretch;
    }
}

.fc {
    padding: 11px 15px;
    border-radius: var(--radius);
    transition: background .2s;
}

.fc:hover { background: var(--ground-2); }

.fc label,
.fc .fc__label {
    display: block;
    margin-bottom: 4px;
    font-family: var(--m);
    font-size: 9.5px;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--text-3);
}

/*
    THE CONTROL FILLS THE CELL, and this is a bug fix rather than tidiness.

    Reported by the owner as "non mi fa scrivere sul campo" — it will not let me
    type in the field. It was true, and it was not the field's fault: `.fc` carries
    11px/15px of padding and draws the hover and focus states, so the visible
    "field" is the whole cell — but the input inside it had `padding: 0`, so the
    only rectangle that actually took a click was a thin strip of text. Clicking
    anywhere else in something that looks and highlights exactly like a field did
    nothing at all.

    The fix pushes the cell's padding onto the control: negative margins take the
    input out to the cell edges and matching padding puts the text back where it
    was, so the box grows and nothing moves. The whole cell under the label is now
    the control. `width: calc(100% + 30px)` is the horizontal half of the same
    trick — 15px of padding on each side.

    A select gets it too: clicking the padding beside a chevron and having nothing
    open is the same defect wearing different clothes.
*/
.fc input,
.fc select {
    width: calc(100% + 30px);
    margin: 0 -15px -11px;
    padding: 0 15px 11px;
    border: 0;
    background: none;
    font-family: var(--t);
    font-size: 15.5px;
    font-weight: 500;
    color: var(--text);
}

.fc select { appearance: none; cursor: pointer; }

/* The control is focused, not the box around it: the ring goes on the .fc so it
   frames label and value together. */
.fc:focus-within { background: var(--ground-2); outline: 3px solid var(--focus); outline-offset: 0; }
.fc input:focus, .fc select:focus { outline: none; }

.fdiv { display: none; width: 1px; background: var(--line); margin: 8px 0; }

.fbar .cta { width: 100%; }

@media (min-width: 62.5rem) {
    .fdiv { display: block; }
    .fbar .cta { width: auto; }
}

.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 22px; }

.chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: var(--night);
    color: #fff;
    font-size: 13px;
    font-weight: 500;
    padding: 8px 14px;
    border-radius: 99px;
    text-decoration: none;
    border: 0;
}

.chip__x {
    border: 0;
    background: none;
    padding: 0;
    color: rgba(255, 255, 255, .65);
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
    text-decoration: none;
}

.chip__x:hover { color: #fff; }

.chip--off {
    background: var(--card);
    color: var(--text-2);
    border: 1px solid var(--line-2);
    cursor: pointer;
}

.chip--off:hover { border-color: var(--accent); color: var(--accent); }

.rescount {
    display: flex;
    align-items: baseline;
    gap: 12px;
    padding: 34px 0 24px;
}

.rescount b { font-family: var(--d); font-size: 2.4rem; font-weight: 400; line-height: 1; }

.rescount span {
    font-family: var(--m);
    font-size: 11px;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--text-3);
}

.pager { display: flex; flex-wrap: wrap; gap: 7px; padding-top: 48px; }

.pager a,
.pager span {
    min-width: 44px;
    height: 44px;
    display: grid;
    place-items: center;
    padding: 0 12px;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--card);
    font-family: var(--m);
    font-size: 13px;
    text-decoration: none;
    color: var(--text-2);
    transition: transform .2s var(--e), border-color .2s;
}

.pager a:hover { border-color: var(--accent); color: var(--accent); transform: translateY(-2px); }

.pager span { color: var(--text-3); border-style: dashed; }

.pager a[aria-current] {
    background: var(--night);
    border-color: var(--night);
    color: #fff;
}

/* ------------------------------------------------ 13. the property sheet */

.pgrid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 34px; }

@media (min-width: 65rem) {
    .pgrid {
        grid-template-columns: minmax(0, 1fr) 400px;
        gap: 48px;
        align-items: start;
    }
}

.viewer {
    position: relative;
    border-radius: var(--radius-l);
    overflow: hidden;
    box-shadow: var(--sh);
    background: var(--ph-a);
}

.viewer > .ph,
.viewer > .image { aspect-ratio: 16 / 10; width: 100%; object-fit: cover; }

.vnav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 48px;
    height: 48px;
    border: 0;
    border-radius: 50%;
    background: var(--glass);
    backdrop-filter: blur(8px);
    display: grid;
    place-items: center;
    cursor: pointer;
    box-shadow: var(--sh);
    transition: transform .24s var(--e), background .24s;
}

.vnav:hover { background: #fff; transform: translateY(-50%) scale(1.09); }
.vnav svg { width: 20px; height: 20px; stroke: var(--night); stroke-width: 2; fill: none; }
.vnav--l { left: 16px; }
.vnav--r { right: 16px; }

.vcount {
    position: absolute;
    bottom: 16px;
    right: 16px;
    background: rgba(8, 22, 29, .8);
    backdrop-filter: blur(8px);
    color: #fff;
    font-family: var(--m);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    padding: 8px 15px;
    border-radius: 99px;
}

.vfull {
    position: absolute;
    bottom: 16px;
    left: 16px;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: rgba(255, 255, 255, .94);
    backdrop-filter: blur(8px);
    color: var(--night);
    font-size: 13px;
    font-weight: 500;
    padding: 9px 15px;
    border-radius: 99px;
    text-decoration: none;
}

.vfull svg { width: 15px; height: 15px; stroke: currentColor; stroke-width: 2; fill: none; }
.vfull:hover { background: var(--fill); color: #fff; }

.thumbs {
    display: grid;
    grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: 9px;
    margin-top: 9px;
}

.thumbs a {
    position: relative;
    border-radius: 10px;
    overflow: hidden;
    transition: transform .24s var(--e);
}

.thumbs a:hover { transform: translateY(-3px); }

.thumbs .ph,
.thumbs .image { aspect-ratio: 1; width: 100%; object-fit: cover; }

.thumbs a[aria-current]::after {
    content: '';
    position: absolute;
    inset: 0;
    border: 2.5px solid var(--fill);
    border-radius: 10px;
}

.thumbs__more {
    display: grid;
    place-items: center;
    aspect-ratio: 1;
    background: var(--night);
    color: #fff;
    font-family: var(--m);
    font-size: 11px;
    border-radius: 10px;
    text-decoration: none;
}

/* ------ tabs ------

   The buttons are useless without JavaScript, so the markup ships them hidden
   and every panel open; site.js marks the container live, which is what turns
   the panels into one-at-a-time. Nothing is hidden before the script runs. */

.tabs {
    display: flex;
    gap: 4px;
    border-bottom: 1px solid var(--hair);
    margin: 46px 0 26px;
    overflow-x: auto;
}

.tab {
    position: relative;
    border: 0;
    background: none;
    font-family: var(--t);
    font-size: 15px;
    font-weight: 550;
    color: var(--text-3);
    padding: 13px 18px;
    cursor: pointer;
    white-space: nowrap;
}

.tab::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 2.5px;
    background: var(--accent);
    transform: scaleX(0);
    transition: transform .3s var(--e);
}

.tab[aria-selected='true'] { color: var(--text); }
.tab[aria-selected='true']::after { transform: scaleX(1); }

[data-tabs].is-live .panel { display: none; }
[data-tabs].is-live .panel.on { display: block; animation: fin .45s var(--e); }

@keyframes fin {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
}

/* Two panels in a row with the script off need air between them. */
[data-tabs] .panel + .panel { margin-top: 34px; }

.prose {
    max-width: 66ch;
    font-size: 17.5px;
    line-height: 1.78;
    color: var(--text-2);
}

.prose p { margin: 0 0 1.35em; }

/* The opening paragraph carries the description: slightly larger and at full
   strength, so the sheet reads as a dossier and not as a wall. */
.prose p:first-child { color: var(--text); font-size: 19.5px; }

.specs {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1px;
    background: var(--hair);
    border-radius: 14px;
    overflow: hidden;
    margin: 0;
}

@media (min-width: 37.5rem) {
    .specs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.sp {
    background: var(--card);
    padding: 18px 22px;
    display: flex;
    align-items: center;
    gap: 14px;
}

.sp svg { width: 20px; height: 20px; stroke: var(--accent); stroke-width: 1.6; fill: none; flex: 0 0 auto; }

.sp dt {
    font-family: var(--m);
    font-size: 9.5px;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--text-3);
}

.sp dd { margin: 3px 0 0; font-size: 16px; font-weight: 600; font-variant-numeric: tabular-nums; }

/* Test hook: tests/Functional/PropertyTest asserts this class on the data sheet.
   Keep it on the value element whatever the surrounding markup. */
.dossier__value--mono {
    font-family: var(--m);
    font-variant-numeric: tabular-nums;
    letter-spacing: -.01em;
}

/* Energy class as a bar of seven segments, blue where lit. */
.ebar { display: flex; gap: 3px; margin-top: 8px; }
.ebar i { height: 7px; flex: 1; border-radius: 2px; background: var(--hair); }
.ebar i.on { background: var(--fill); }

.side { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }

@media (min-width: 65rem) {
    .side { position: sticky; top: calc(var(--top-h) + var(--notice-h) + 12px); }
}

.pbox {
    background: var(--card);
    border-radius: var(--radius-l);
    box-shadow: var(--sh-2);
    overflow: hidden;
}

.pbox__t {
    padding: 24px;
    background: var(--night);
    color: rgba(255, 255, 255, .8);
}

.pbox__p {
    margin: 0;
    font-family: var(--d);
    font-size: 2.6rem;
    line-height: 1;
    letter-spacing: -.03em;
    color: #fff;
}

.pbox__r {
    margin: 11px 0 0;
    font-family: var(--m);
    font-size: 11px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .6);
}

.pbox__q {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1px;
    background: var(--hair);
    margin: 0;
}

.q { background: var(--card); padding: 15px 10px; }
.q b { display: block; font-size: 17px; font-weight: 650; }

.q span {
    display: block;
    margin-top: 4px;
    font-family: var(--m);
    font-size: 9px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--text-3);
}

.pbox__a { padding: 20px 22px 22px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 9px; }

.agent {
    display: flex;
    align-items: center;
    gap: 14px;
    background: var(--card);
    border-radius: var(--radius-l);
    box-shadow: var(--sh);
    padding: 18px;
}

.agent__ph { width: 56px; height: 56px; border-radius: 50%; flex: 0 0 auto; }
.agent b { display: block; font-size: 15.5px; }

.agent span {
    font-family: var(--m);
    font-size: 10.5px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--text-3);
}

/* ---------------------------------------------------- 14. calls to action */

.cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    background: var(--fill);
    color: var(--fill-text);
    font-family: var(--t);
    font-size: 14.5px;
    font-weight: 600;
    padding: 12px 20px;
    border: 0;
    border-radius: 10px;
    text-decoration: none;
    cursor: pointer;
    transition: background .24s var(--e), transform .24s var(--e), box-shadow .24s var(--e);
}

.cta:hover { background: var(--fill-2); transform: translateY(-2px); box-shadow: var(--sh-2); }

.cta svg { width: 17px; height: 17px; stroke: currentColor; stroke-width: 1.8; fill: none; }

.cta--sm { padding: 9px 15px; font-size: 13.5px; }
.cta--lg { padding: 15px 30px; font-size: 15.5px; }
.cta--full { width: 100%; padding: 15px; font-size: 15.5px; }

.cta--ghost {
    background: transparent;
    color: var(--text);
    border: 1.5px solid var(--line-2);
}

.cta--ghost:hover {
    background: transparent;
    border-color: var(--fill);
    color: var(--fill);
    box-shadow: none;
}

.cta[disabled], .cta[aria-disabled='true'] {
    opacity: .55;
    pointer-events: none;
}

/* A status line, not an alarm: a closed deal is information. */
.notice {
    padding: 16px 20px;
    border-left: 3px solid var(--fill);
    background: var(--ground-2);
    color: var(--text-2);
    border-radius: 0 var(--radius-s) var(--radius-s) 0;
}

.notice--quiet { border-left-color: var(--line-2); }

.tag {
    display: inline-block;
    padding: 5px 10px;
    border: 1px solid var(--line-2);
    border-radius: 7px;
    font-family: var(--m);
    font-size: 10px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--text-2);
}

.tag--strong { border-color: var(--fill); color: var(--accent); }

/* ------------------------------------------------------------- 15. forms */

.form { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }

@media (min-width: 43.75rem) {
    .form { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .form__wide { grid-column: 1 / -1; }
}

.form__label {
    display: block;
    margin-bottom: 7px;
    font-size: 13.5px;
    font-weight: 600;
}

.form__required { color: var(--accent); }

.form__field {
    width: 100%;
    padding: 13px 15px;
    border: 1.5px solid var(--line-2);
    border-radius: var(--radius);
    background: var(--card);
    color: var(--text);
    font-family: var(--t);
    font-size: 16px;   /* 16px exactly: less than that and iOS zooms on focus */
    transition: border-color .2s, box-shadow .2s;
}

.form__field:focus {
    outline: none;
    border-color: var(--fill);
    box-shadow: 0 0 0 4px rgba(28, 123, 161, .18);
}

.form__field:focus-visible { outline: 3px solid var(--focus); outline-offset: 1px; }

textarea.form__field { min-height: 110px; resize: vertical; }
select.form__field { appearance: none; cursor: pointer; }
.form__field--data, .form__field--mono { font-family: var(--m); }

.form__field--invalid,
.form__field[aria-invalid='true'] { border-color: #b3402f; }

.form__hint { margin: 6px 0 0; font-size: 12.5px; color: var(--text-3); }
.form__error { margin: 6px 0 0; font-size: 13px; color: #b3402f; font-weight: 500; }

.form__errors {
    margin-bottom: 24px;
    padding: 16px 20px;
    border-left: 3px solid #b3402f;
    background: var(--ground-2);
    border-radius: 0 var(--radius-s) var(--radius-s) 0;
    font-size: 14.5px;
}

.form__errors-title { font-weight: 600; margin-bottom: 6px; }

.form__check,
.form__consent {
    display: flex;
    gap: 11px;
    font-size: 13.5px;
    color: var(--text-2);
    line-height: 1.5;
}

.form__check input,
.form__consent input {
    width: auto;
    margin-top: 3px;
    accent-color: var(--blue);
    flex: 0 0 auto;
}

.form__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 14px;
}

/* Honeypot. Off-screen rather than display:none: some bots skip hidden fields
   and fill positioned ones, and a screen reader gets the label either way. */
.form__trap {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* "Reference RIF-101 already filled in": says the form knows where you came
   from, which is the whole reason the sheet's form converts. */
.prefill {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    margin-bottom: 22px;
    padding: 8px 15px;
    border-radius: 99px;
    background: var(--night);
    color: #fff;
    font-family: var(--m);
    font-size: 11.5px;
    letter-spacing: .04em;
}

/* ------------------------------------------------- 16. cookie consent

   Refusing is exactly as easy as accepting: same size, same padding, same
   position, one click each. The panel is a card at the bottom of the viewport,
   above everything, and consent.js decides whether it is shown at all. */

.consent {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 200;
    background: var(--card);
    box-shadow: 0 -6px 26px rgba(8, 22, 29, .16);
    border-top: 1px solid var(--hair);
    font-size: 14.5px;
}

.consent__inner { padding-block: 20px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }

@media (min-width: 56.25rem) {
    .consent__inner {
        grid-template-columns: minmax(0, 1fr) auto;
        align-items: center;
        gap: 28px;
    }
    .consent__panel { grid-column: 1 / -1; }
}

.consent__title {
    margin: 0 0 4px;
    font-family: var(--m);
    font-size: 10px;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--accent);
}

.consent__text { margin: 0; color: var(--text-2); max-width: 66ch; }
.consent__text a { color: var(--accent); }

.consent__actions { display: flex; flex-wrap: wrap; gap: 9px; }

.consent__panel {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 14px;
    padding-top: 18px;
    border-top: 1px solid var(--hair);
}

.consent__group-title { display: block; font-weight: 600; font-size: 13.5px; color: var(--text); }
.consent__group-note { display: block; font-size: 12.5px; color: var(--text-3); }

/* ------------------------------------------------------------ 17. footer */

.foot {
    background: var(--ground);
    color: var(--text);
    padding: 78px 0 30px;
    font-size: 15px;
}

.foot__in { display: grid; grid-template-columns: minmax(0, 1fr); gap: 38px; }

@media (min-width: 51.25rem) {
    .foot__in { grid-template-columns: minmax(0, 2fr) repeat(3, minmax(0, 1fr)); }
}

.foot h4 {
    margin-bottom: 16px;
    font-family: var(--m);
    font-size: 10px;
    font-weight: 500;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--accent);
}

.foot__n {
    margin: 0 0 12px;
    font-family: var(--d);
    font-size: 1.5rem;
    color: var(--heading);
}

.foot__line { margin: 0 0 6px; color: var(--text-2); }
.foot__list li { margin-bottom: 9px; }
.foot a { text-decoration: none; color: var(--text); }
.foot a:hover { color: #fff; text-decoration: underline; }

.foot__button {
    border: 0;
    background: none;
    padding: 0;
    font: inherit;
    color: var(--text);
    cursor: pointer;
    text-align: left;
}

.foot__button:hover { color: #fff; text-decoration: underline; }

.foot__e {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    justify-content: space-between;
    border-top: 1px solid var(--hair);
    margin-top: 52px;
    padding-top: 24px;
    font-family: var(--m);
    font-size: 10.5px;
    letter-spacing: .1em;
    color: var(--text-3);
}

/* ----------------------------------------------- 18. sticky bar (phone) */

.bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 90;
    background: var(--glass);
    backdrop-filter: blur(16px);
    box-shadow: 0 -6px 26px rgba(8, 22, 29, .11);
    transform: translateY(110%);
    transition: transform .38s var(--e);
}

.bar.on { transform: none; }

@media (min-width: 65rem) {
    .bar { display: none; }
}

.bar__in {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding-block: 12px;
}

.bar__p { margin: 0; font-family: var(--d); font-size: 1.4rem; line-height: 1; }

.bar__r {
    margin: 0 0 2px;
    font-family: var(--m);
    font-size: 10px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--text-3);
}

/* -------------------------------------------------------------- 19. motion

   Staggered reveal. .rv alone does nothing: site.js ARMS the elements that are
   still below the fold and reveals them as they arrive. Elements already on
   screen when the script runs are never armed, so there is no flash, and with
   the script off nothing is hidden at all. */

.rv { transition: opacity .7s var(--e), transform .7s var(--e); }
.rv--armed:not(.in) { opacity: 0; transform: translateY(26px); }

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

    html { scroll-behavior: auto; }

    /* Every state that a motion was going to arrive at has to be the state. The
       mockup forgot the split headline here and it stayed invisible. */
    .rv--armed:not(.in) { opacity: 1; transform: none; }
    .hero h1 .w { opacity: 1; transform: none; }
    .card__go { opacity: 1; transform: none; }
}

@media print {
    .top, .prog, .foot, .consent, .language-notice, .bar, .skip-link,
    .fav, .card__go, .scrollhint, .vnav { display: none; }

    body { padding-top: 0; color: #000; background: #fff; }
    .sec { padding: 24px 0; }
    .sec--cut { clip-path: none; margin-top: 0; }
    [data-tabs].is-live .panel { display: block; }
    a { color: #000; text-decoration: underline; }
}

/* A second and third heading inside a footer column need air above them. */
.foot h4 + .foot__list + h4,
.foot__hours-title,
.foot__social-title { margin-top: 26px; }

/* The experience card carries a line of summary where a listing carries a
   price; the dossier row grows into the space beside its icon. */
.card__t { margin: 11px 0 0; font-size: 15px; color: var(--text-2); }
.sp > div { flex: 1; min-width: 0; }

/* --------------------------------------------- 20. the hidden attribute

   The last word in the file, and it has to be: the controls that ship hidden and
   are revealed by site.js (.tabs, .fav, .consent__panel) all declare a display
   of their own, and a class selector and a bare attribute selector carry the
   same weight — so only source order can decide. Before this rule was here, the
   tab strip was visible with JavaScript off, which is exactly the failure the
   `hidden` attribute exists to prevent. */
[hidden] { display: none; }

/* The language switcher, in the footer where it belongs (see the note in
   partials/header.html.twig for why it left the header). Full language names, not
   two-letter codes: down here there is room, and «Deutsch» is unambiguous where
   «DE» has to be decoded. */
.foot__lang {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 1rem;
}

.foot__lang-link,
.foot__lang-current {
    font-family: var(--m);
    font-size: 0.6875rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
}

.foot__lang-link {
    color: rgba(255, 255, 255, 0.58);
    text-decoration: none;
    border-bottom: 1px solid transparent;
    /* NOT `var(--transition)`. Only `admin.css` declares that token, so on the
       public site this whole declaration was invalid at computed-value time and
       was dropped: the footer's three language links snapped between states
       while every other link on the page eased. Written out. */
    transition: color .2s var(--e), border-color .2s var(--e);
}

.foot__lang-link:hover { color: #fff; border-color: currentColor; }
.foot__lang-current { color: var(--blue-4); }

/* ==========================================================================
   §14. TOUCH TARGETS AND FIELD SIZES — measured, not guessed

   Measured over CDP at 320, 360, 390, 430, 768 and 1024 with
   Emulation.setDeviceMetricsOverride, which is the only honest way: headless Chrome
   clamps a WINDOW to about 500px and then reports the clamped width as if it were
   what you asked for.

   The good news first, because it is the expensive thing to get right and it was
   already right: NO HORIZONTAL OVERFLOW at any of those six widths, on the home, the
   catalogue or a property page. `scrollWidth == clientWidth` everywhere.

   What was wrong was smaller and duller, and this section fixes it.

   TWO THRESHOLDS, and they are not the same rule. WCAG 2.2 AA (2.5.8) asks for
   24x24 CSS px and is a floor nothing may sit under. Apple and Android both ask for
   44 and 48, and that is a quality target rather than a legal one — so 44 is applied
   where a control is a CONTROL, and the 24 floor everywhere else.

   WHAT IS DELIBERATELY LEFT ALONE, so nobody "fixes" it later: a link inside a
   sentence — «Leggi l'informativa» in the consent line, measured 122x17 — cannot be
   given a 44px box without tearing the paragraph apart, and WCAG 2.5.8 exempts
   inline links in a block of text for exactly that reason. The skip link measures
   140x27 and is only ever reached by a keyboard, which does not tap.
   ========================================================================== */

/*
    The filter fields were 15.5px — the body size, inherited on purpose. On a form
    field it is a bug and not a style: below 16px iOS ZOOMS THE PAGE on focus, and on
    the catalogue that throws the grid out from under the reader's thumb. Half a pixel
    is invisible; the zoom is not. This is the third time this defect has appeared in
    this project, which is why the number is spelled out here.
*/
.fc input,
.fc select { font-size: 16px; }

/*
    The footer's link list measured 19px a row — under the AA floor, and it is the
    densest cluster of links on the site. The padding buys the height; the negative
    inline margin keeps the text where it was so the column does not shift.
*/
.foot__list li { margin-bottom: 2px; }

.foot__list a,
.foot__lang-link {
    display: inline-flex;
    align-items: center;
    min-height: 28px;
    padding-block: 4px;
}

/*
    `.arrow` is the link beside a section heading — «Vendita →», «Tutte le esperienze
    →». It measured 19px tall, under the AA floor, and on the home it is the ONLY way
    into a catalogue from that band. It is a component in this file rather than in
    parts/home.css, which is why it is fixed here.
*/
.arrow {
    display: inline-flex;
    align-items: center;
    min-height: 28px;
}

/*
    The language-notice band. Its two controls measured 18px tall, and this band is
    shown to EXACTLY the visitor who has to tap one: somebody whose browser is in
    German who has landed on the Italian page. The one moment the site speaks to a
    person in a language they did not choose is not the moment to give them an 18px
    target.
*/
.language-notice__action,
.language-notice__dismiss {
    display: inline-flex;
    align-items: center;
    min-height: 30px;
}

/* Breadcrumbs measured 14px tall. They are navigation, not prose. */
.breadcrumbs__item a {
    display: inline-flex;
    align-items: center;
    min-height: 26px;
}

/*
    ON TOUCH ONLY, the primary controls go to 44. `hover: none` is the right query
    here rather than a width: a small laptop window is still a mouse, and a mouse
    does not need a 44px burger — while a 1024px tablet does.
*/
@media (hover: none) {
    .menu__button,
    .cta,
    .brand,
    .lang__link {
        min-height: 44px;
    }

    .menu__button,
    .lang__link {
        /* Width too: the burger measured 43px across, and a square control that is
           one pixel short of the guideline on its narrow axis is the one people miss
           with a thumb. */
        min-width: 44px;
        justify-content: center;
    }

    .foot__list a,
    .foot__lang-link,
    .breadcrumbs__item a,
    .arrow,
    .language-notice__action,
    .language-notice__dismiss { min-height: 44px; }
}

/* ==========================================================================
   §21. THE CARD'S EYEBROW AND ITS DATA ROW ARE FLEX AGAIN — a specificity bug

   (The numbers in this file are not unique: §14 appears twice, once as "calls to
   action" and once as the appended touch-target block. Cite these by name.)

   `.card__loc` declares `display: flex; align-items: center; gap: 7px` in §10 and
   `.card__d` declares `display: flex; flex-wrap: wrap; gap: 7px`. Neither of them
   was flex on any card on the site, because both are <span> elements — the whole
   card is one <a>, and an <a> may not contain a <div> — and §10's own

       .card__b > span, .card__b > p { display: block }

   is (0,1,1) against their (0,1,0). One class loses to one class plus one type
   whatever the source order, so the declaration that follows it in the file never
   arrived.

   WHAT IT LOOKED LIKE, measured over CDP at 320, 390, 768 and 1400 on
   /it/vendita/: `img, svg, video { display: block }` in §2 makes the locality pin a
   block, so with the flex row gone the 13px pin fell onto a line of its own ABOVE
   the place name and the eyebrow box measured 29.5px tall instead of 13 — on every
   card, at every width, in all three languages. A map pin orphaned over
   «QUERCIANELLA, VALLE DEL CHIOMA» is the second thing the eye meets in a cell.

   `.card__d` was quietly a block too: the chips are `inline-flex`, so they flowed
   as inline boxes and their spacing came from the whitespace between the tags in
   the markup rather than from the 6px gap the stylesheet asks for.

   Restored here rather than in parts/catalog.css because the card is shared: the
   catalogue, the experience page and the property page's territory band all print
   partials/property-card.html.twig, and only two of them load catalog.css.

   Neither of these elements is ever shipped `hidden`, so this block being below
   `[hidden] { display: none }` breaks nothing (the invariant is documented at §20).
   ========================================================================== */

.card__b > .card__loc,
.card__b > .card__d { display: flex; }

/* ==========================================================================
   §22. THE ANGLED EDGE, WITH A FLOOR — the band that vanished on a phone

   (Numbered 22 and not 16: the numbers in this file are not unique — §14 appears
   twice and the appended block above is §21 — and 16 and 17 are taken by the cookie
   banner and the footer. Cite these by name, as §21 asks.)

   docs/DESIGN.md §1 lists the cut edge as one of the four things that make this
   direction the approved one and the three before it rejected. §3 declares it as
   3.2vw of rise, and 3.2vw is a constant ANGLE — 1.83° at every width. What is not
   constant is whether a person can see it.

   Measured rise, all four widths of this pass:

       320    10.2px   1.83°   reads as a horizontal line
       390    12.5px   1.83°   reads as a horizontal line
       768    24.6px   1.83°   just visible
      1400    44.8px   1.83°   unmistakably a diagonal

   So the signature of the direction is present on the desktop and absent on a
   phone, which is the same fault as a band that collapses: the rhythm the desktop
   has is not the rhythm the phone has. A floor of 30px fixes it in the only way
   that can work — by letting the ANGLE grow as the width shrinks (4.4° at 390,
   5.4° at 320) instead of holding the angle and losing the edge.

   Above 938px the floor is never reached and nothing changes: 3.2vw passes 30px
   there, so every desktop width is exactly as it was.

   It is here, in site.css, because `.sec--cut` is a band and four pages use it —
   the home, an editorial page's closing band, the property page's experiences band
   and the experience page's properties band. Appending rather than editing §3 in
   place is the rule for this file while three lanes share it; a later rule of equal
   weight wins, which is all this needs. `--cut-rise` is declared on the same rule
   rather than on `:root` so nothing outside this component can read it by accident.

   No `display` is set here, so this block sitting below `[hidden] { display: none }`
   does not touch the invariant documented at §20.
   ========================================================================== */

.sec--cut {
    --cut-rise: max(3.2vw, 30px);
    clip-path: polygon(0 0, 100% var(--cut-rise), 100% 100%, 0 100%);
    margin-top: calc(var(--cut-rise) * -1);
    padding-top: calc(96px + var(--cut-rise));
}

@media (min-width: 51.25rem) {
    .sec--cut { padding-top: calc(130px + var(--cut-rise)); }
}

/* ==========================================================================
   §23. THE FLOATING HEADER CARRIES ITS OWN SCRIM

   §6 gives a `hero-page` header `background: transparent` over the photograph, and
   with nothing behind it the agency's name is white type on whatever the most
   recently published listing happens to look like. Nobody had looked, because until
   this pass no page declared `hero-page` at all — the home was standing on a solid
   white bar instead of floating over its own opening.

   Measured on the rendered pixels, at 320, with the language notice open (the case
   that matters, and the one a hero-side gradient cannot cover — see below):

       the agency name   1.78:1   over rgb(206,193,167), a sunlit house
       the burger        1.54:1   over rgb(209,202,182)

   THE SCRIM BELONGS TO THE HEADER AND NOT TO THE HERO, and that is the whole point
   of this section. The header is fixed at `top: var(--notice-h)`: when the language
   notice opens it slides 95px down at 390 and 173px down at 320, straight out of any
   gradient painted on the hero. The two numbers above were taken in exactly that
   state. A scrim that travels with the element it protects has no such case.

   The ramp is short — it dies at the header's own bottom edge — and it is sized from
   the arithmetic rather than by eye: white type needs a background luminance at or
   under 0.183 to reach 4.5:1, which over a blown-out white pixel needs 0.54 of
   near-black. It carries 0.60 at the line the brand sits on, and 0 by the bottom.
   Over a dark photograph none of it is visible; over a bright one it reads as the
   top of the picture being held back, which is what it is.

   `--line-2` is raised with it. At 0.32 over the wash the Menu button's border
   measured 1.5:1 — the glyph inside carried the control on its own and the box
   around it was a suggestion. WCAG 1.4.11 asks 3:1 of a control's boundary.

   `:not(.sm)` is §6's own guard and it still does the work: past 70px of scroll the
   header turns solid white and this rule stops applying, gradient and all.

   No `display` here either, so §20's invariant is untouched.
   ========================================================================== */

.hero-page .top:not(.sm) {
    --line-2: rgba(255, 255, 255, .5);
    background: linear-gradient(
        to bottom,
        rgba(4, 12, 16, .78) 0%,
        rgba(4, 12, 16, .60) 52%,
        rgba(4, 12, 16, .26) 82%,
        rgba(4, 12, 16, 0) 100%
    );
}

/* ==========================================================================
   §24. THE REFINEMENT PASS — what was missing, and what each part of it fixes

   The owner's verdict on the site as built was that it looked «troppo basic,
   troppo semplice»: correct, coherent, and flat. This section is the answer, and
   it is deliberately ONE block appended to the end rather than fifty edits
   scattered through the file — the same rule §22 and §23 followed, for the same
   reason: a later rule of equal weight wins, so nothing above has to be touched
   and the whole change can be read, reviewed or reverted in one place.

   The diagnosis, because "make it nicer" is not a brief and this section had to
   be written against something:

     1. NO EDGES. Every surface on the site is a light rectangle on a light
        ground held apart by one soft blur. A white card on a white band had no
        boundary at all — it was a region of the page, not an object on it. Fixed
        at the token level (`--edge`, §1) and applied to every surface below.
     2. NO LIGHT. The dark bands were flat slabs of one hex value, the buttons
        were flat rectangles of another. Nothing on the page had a direction the
        light came from, and that is most of the difference between "clean" and
        "cheap". Fixed with one raking gradient per dark band and the sheen /
        glow pair on every filled control.
     3. THE TYPE WAS SET, NOT TYPESET. Fraunces was pinned to one optical size
        and one tracking for headings from 1.1rem to 5.6rem. A display face at
        5.6rem needs less tracking and a different drawing than the same face at
        1.2rem, and pinning both is the single change that most makes a serif
        look bought rather than chosen.
     4. NO SECOND READING. A premium page rewards a second look with detail that
        was not needed on the first: the rule beside a section number, the veil
        under a photograph, the hairline that separates a card's data row, the
        way a control answers a cursor. None of that existed.

   What this section is NOT allowed to do, and does not:
     - introduce a colour. The palette of §2 is unchanged, amber is still spent
       on its two uses, and every value below is a token or an alpha of one.
     - hard-code a ground. Everything here reads role tokens (§10), so it is
       correct on all nine bands without a single `.sec--dark .something`.
     - add motion beyond the vocabulary of docs/DESIGN.md §5. Nothing here
       animates on its own; everything added is a response to hover or focus, and
       all of it dies inside §19's reduced-motion block.
     - cost a frame. No `backdrop-filter` is added over anything that moves — the
       measurement in §3 stands — and no new continuous animation exists.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   24.1  THE GROUNDS — one light source per dark band

   `--night` and `--deep` were painted as flat fills, and a flat fill over 900px
   of height is the one thing that reads as "a coloured div" no matter what sits
   on it. Each dark band now carries a single very wide, very low-alpha radial in
   its own blue, placed off-centre so the band has a direction: the page reads as
   lit from one side rather than as a slab.

   It is `background-image` and not `background`, on purpose: §3 sets
   `background: var(--ground)` and the shorthand owns the colour. Leaving the
   colour to the role token is what keeps these two rules correct if a band is
   ever re-grounded.

   `.hero` is deliberately absent: it has a photograph and a measured scrim, and
   a third layer over that is a change to a contrast calculation somebody made
   with numbers (parts/home.css §1). The light bands are absent too — a gradient
   on white is either invisible or dirty, and there is no third option. */

.sec--dark {
    background-image:
        radial-gradient(120% 78% at 12% 0%, rgba(28, 123, 161, .17), transparent 58%),
        radial-gradient(90% 60% at 92% 100%, rgba(15, 58, 77, .40), transparent 62%);
}

.sec--deep {
    background-image:
        radial-gradient(115% 80% at 85% 0%, rgba(42, 157, 196, .20), transparent 60%),
        radial-gradient(80% 70% at 0% 100%, rgba(8, 22, 29, .45), transparent 65%);
}

.foot {
    background-image:
        radial-gradient(90% 70% at 8% 0%, rgba(28, 123, 161, .14), transparent 60%);
}

/* The pale band was one flat #e6f3f8 too. A gradient there would be dirty, so it
   gets a hairline at the top instead: the join with the white band above it is
   what the eye was failing to register. */
.sec--blue { box-shadow: inset 0 1px 0 rgba(21, 95, 125, .10); }

/* ---------------------------------------------------------------------------
   24.2  THE TYPE — one optical size per size, and tracking that scales

   §2 pins `'opsz' 120` and `-.025em` on h1 through h4. Fraunces runs 9 to 144,
   and the axis is not decoration: it changes the stroke contrast and the serif
   drawing, so the value that makes a 5.6rem headline look drawn makes a 1.1rem
   subhead look spindly, and the tracking that closes up a headline opens a
   subhead into a wall.

   The rule is the ordinary one and it is worth stating because it is the whole
   of the change: THE BIGGER THE TYPE, THE TIGHTER THE TRACKING AND THE HIGHER
   THE OPTICAL SIZE. Four sizes, four pairs. */

h1 {
    font-variation-settings: 'opsz' 144;
    letter-spacing: -.038em;
    line-height: 1.02;
}

h2 {
    font-variation-settings: 'opsz' 132;
    letter-spacing: -.032em;
    line-height: 1.06;
}

h3 {
    font-variation-settings: 'opsz' 48;
    letter-spacing: -.018em;
    line-height: 1.22;
}

h4 {
    font-variation-settings: 'opsz' 32;
    letter-spacing: -.012em;
}

/* Running prose. `pretty` is the counterpart of the `balance` already on the
   headings: it costs nothing where it is unsupported and it removes the single
   orphaned word at the end of a paragraph, which is the most visible thing that
   separates a typeset page from a rendered one. */
p, li { text-wrap: pretty; }

/* The line under a heading. It was 18px of `--text-2` at the body's own
   line-height, which is a paragraph in a different colour rather than a lead. */
.lead {
    font-size: clamp(1.0625rem, .5vw + .95rem, 1.235rem);
    line-height: 1.62;
    letter-spacing: -.006em;
    color: var(--text-2);
    text-wrap: pretty;
}

/* A link inside running text. The default underline sits on the baseline and
   cuts every descender in the line; these two properties are the whole of the
   fix and they have been standard for years. */
.prose a,
.page__prose a,
.consent__text a,
.form__check a,
.form__consent a {
    color: var(--accent);
    text-decoration-line: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: .2em;
    text-decoration-color: color-mix(in srgb, currentColor 38%, transparent);
    transition: text-decoration-color .2s;
}

.prose a:hover,
.page__prose a:hover,
.consent__text a:hover,
.form__check a:hover,
.form__consent a:hover { text-decoration-color: currentColor; }

/* ---------------------------------------------------------------------------
   24.3  THE SECTION HEADER — the number gets a rule, the header gets a floor

   `01 — Selezione` is one of the four things docs/DESIGN.md §1 says makes this
   direction the approved one, and it was set as eleven pixels of mono floating
   in space. A short rule leading into it is the cheapest possible mark and it is
   what turns a label into a masthead.

   The hairline under the whole header is the second reading of §1's "the page
   reads as a structure rather than a scroll": it gives every band the same
   editorial floor, so the eye can find where a section starts without reading
   the heading. */

.snum {
    display: flex;
    align-items: center;
    gap: 12px;
    font-size: 10.5px;
    letter-spacing: .22em;
}

.snum::before {
    content: '';
    flex: 0 0 auto;
    width: 24px;
    height: 1px;
    background: currentColor;
    opacity: .55;
}

.shead {
    gap: 20px 32px;
    margin-bottom: 40px;
    padding-bottom: 22px;
    border-bottom: 1px solid var(--hair);
}

.shead p {
    margin-top: 14px;
    max-width: 48ch;
    text-wrap: pretty;
}

/* The link on the right of a section header. It was mono text with an arrow;
   it is now mono text with an arrow that has somewhere to go. */
.arrow {
    position: relative;
    gap: 10px;
    padding: 6px 2px;
    letter-spacing: .16em;
    transition: color .2s;
}

.arrow::after {
    content: '';
    position: absolute;
    left: 2px;
    right: 2px;
    bottom: 0;
    height: 1px;
    background: currentColor;
    opacity: .28;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform .4s var(--e), opacity .2s;
}

.arrow:hover::after { transform: scaleX(1); opacity: .55; }

/* ---------------------------------------------------------------------------
   24.4  THE SURFACES — an edge, a floor, and a photograph that is framed

   The card is the most repeated object on the site: the catalogue prints a grid
   of them, the home prints them for the territory, the property sheet prints
   them for the neighbouring experiences. Whatever a card looks like is what the
   site looks like.

   Four changes, and each is a sentence:

     - AN EDGE. `inset 0 0 0 1px var(--edge)` in the same `box-shadow` as the
       drop shadow. On white the card finally has a boundary; on a dark band the
       token is a highlight instead and the same declaration draws the same edge
       the other way up. This is the change of §24 that does the most work.
     - A SHORTER LIFT AND A LONGER SHADOW. The hover was 8px of travel; it is 6
       now, and the shadow it lands in is much wider. A big shadow under a small
       movement is the language of something heavy and well made — a big movement
       under a small shadow is the language of a tooltip.
     - THE PHOTOGRAPH IS FRAMED. A picture that runs to the corner of a card with
       nothing between it and the ground has no bottom edge, so the caption looks
       as if it slid out from under it. The veil is 40px of the card's own ground
       fading up into the picture, plus a hairline along the joint.
     - AIR. 20/22 of padding on a card carrying an eyebrow, a two-line title, a
       price and a data row is tight. 24/26, and the data row gets a real rule.

   The `overflow: hidden` on `.card` is what makes the inset ring follow the
   corner radius for free. */

.card {
    border-radius: 18px;
    box-shadow: var(--sh), inset 0 0 0 1px var(--edge);
    transition: transform .5s var(--e), box-shadow .5s var(--e);
}

.card:hover {
    transform: translateY(-6px);
    box-shadow: var(--sh-2), inset 0 0 0 1px var(--edge-2);
}

/* The joint between the picture and the caption. `--card` is the role token, so
   the veil is made of whatever ground the card itself is standing on and the
   fade is invisible as a fade — it reads as the photograph being held. */
.card__ph::after {
    content: '';
    position: absolute;
    inset: auto 0 0 0;
    height: 46px;
    pointer-events: none;
    background: linear-gradient(to top, var(--card), color-mix(in srgb, var(--card) 0%, transparent));
    opacity: .55;
}

.card__ph {
    box-shadow: inset 0 -1px 0 var(--edge);
}

.card__b { padding: 22px 24px 24px; }

.card__loc {
    font-size: 10px;
    letter-spacing: .16em;
    color: var(--text-3);
}

.card h3 {
    margin-top: 10px;
    font-size: clamp(1.16rem, .5vw + 1rem, 1.3rem);
}

.card__p {
    margin-top: 15px;
    font-size: clamp(1.5rem, 1.4vw + 1rem, 1.72rem);
    letter-spacing: -.028em;
    font-variation-settings: 'opsz' 72;
}

.card__d {
    margin-top: 18px;
    padding-top: 16px;
    gap: 8px;
    border-top: 0;
    /* A rule that fades out towards the right instead of stopping dead against
       the padding. One gradient, no extra element. */
    background-image: linear-gradient(to right, var(--hair), color-mix(in srgb, var(--hair) 0%, transparent));
    background-repeat: no-repeat;
    background-size: 100% 1px;
    background-position: 0 0;
}

/* The little data chips. A filled grey pill on a white card is one more grey
   rectangle; an outlined one with the ground showing through is a label. */
.dc {
    background: transparent;
    box-shadow: inset 0 0 0 1px var(--edge-2);
    padding: 5px 11px;
    border-radius: 8px;
    font-size: 10.5px;
    letter-spacing: .04em;
    color: var(--text-2);
}

/* The badge over the photograph. It was a solid `--night` rectangle, which on a
   dark picture disappears and on a bright one is a hole. A dark floor under a
   thin white veil is the technique §3 already uses for `.pill`, for the same
   reason and at the same cost: nothing. */
/*
    The floor is .72 and the arithmetic is the reason, not the look.

    White mono at 10px is SMALL TEXT, so it owes 4.5:1 against the worst pixel
    the photograph can put behind it — which is a blown-out sky, i.e. white. In
    sRGB the composite of `--night` at alpha a over white, under the .10 white
    veil, is `0.9(a·night + (1-a)·255) + 25.5` per channel. At .62 that lands on
    a relative luminance of 0.200 and the badge measures 4.19:1 — below AA, and
    the first version of this rule had exactly that value. At .72 it is 0.134 and
    the badge measures 5.69:1. Nothing else about the badge changes.

    Over a dark photograph none of this is visible: the floor is under the
    picture, not over it, and at 0.72 a night-blue crop still reads through.
*/
.badge {
    top: 15px;
    left: 15px;
    padding: 6px 12px;
    border-radius: 8px;
    background-color: rgba(8, 22, 29, .72);
    background-image: linear-gradient(rgba(255, 255, 255, .10), rgba(255, 255, 255, .10));
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .18);
    letter-spacing: .14em;
}

/* The two badges that carry a meaning through colour keep their fill: a veiled
   amber is not amber. They get the sheen instead. */
.badge--amber,
.badge--blue {
    background-image: linear-gradient(rgba(255, 255, 255, .14), rgba(255, 255, 255, 0));
    box-shadow: var(--fill-sheen), 0 2px 8px rgba(8, 22, 29, .28);
}

.badge--amber { background-color: var(--amber); }
.badge--blue  { background-color: var(--fill); }

/* The arrow that appears in the corner of a card on hover. */
.card__go {
    bottom: 15px;
    right: 15px;
    background-image: linear-gradient(180deg, var(--fill-top), var(--fill));
    box-shadow: var(--fill-sheen), var(--fill-glow);
}

.fav {
    border-radius: 11px;
    box-shadow: inset 0 0 0 1px rgba(8, 22, 29, .08), 0 2px 8px rgba(8, 22, 29, .18);
}

/* ---------------------------------------------------------------------------
   24.5  EVERY OTHER PHOTOGRAPH GETS THE SAME FRAME

   One declaration, one list. These are the picture boxes of the five page
   stylesheets, and they were each a photograph cropped to a radius with nothing
   between it and the page. The ring is the same one the card uses, so a
   photograph reads the same wherever it appears — which is what a system is. */

.viewer,
.block,
.thumbs a,
.pviewer,
.featured__frame,
.territory__frame,
.experience-card__ph,
.experience__frame,
.experience-gallery__item,
.pvideo__stage {
    position: relative;
}

.viewer::after,
.block::before,
.thumbs a::before,
.featured__frame::after,
.territory__frame::after,
.experience-card__ph::after,
.pvideo__stage::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
    border-radius: inherit;
    box-shadow: inset 0 0 0 1px rgba(8, 22, 29, .10);
}

/* The three that sit on a dark band draw the ring with light instead. */
.block::before,
.thumbs a::before {
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .14);
}

.viewer {
    border-radius: var(--radius-l);
    box-shadow: var(--sh);
}

/* ---------------------------------------------------------------------------
   24.6  THE SECTION BLOCKS — the home's real content

   docs/DESIGN.md §8 gives these the room the card grid used to have, so they are
   the largest thing on the home after the opening and they were the flattest: a
   photograph, one linear scrim, a heading at the bottom left.

   The scrim is now two layers rather than one — a deep foot that holds the type
   and a much lighter raking wash from the side — so the picture keeps its middle
   and the caption still sits on something. The blue-on-hover of §5 stays exactly
   as it was, because it is the motion that says "this is a door" and it is the
   reason the home does not need cards. What is added on hover is the arrow. */

.block {
    border-radius: var(--radius-l);
}

.block::after {
    background:
        linear-gradient(to top, rgba(8, 22, 29, .92) 0%, rgba(8, 22, 29, .58) 26%, rgba(8, 22, 29, .10) 62%, rgba(8, 22, 29, 0) 100%),
        linear-gradient(to right, rgba(8, 22, 29, .38), rgba(8, 22, 29, 0) 55%);
}

.block:hover::after {
    background:
        linear-gradient(to top, rgba(20, 96, 126, .94) 0%, rgba(20, 96, 126, .60) 30%, rgba(8, 22, 29, .12) 66%, rgba(8, 22, 29, 0) 100%),
        linear-gradient(to right, rgba(8, 22, 29, .34), rgba(8, 22, 29, 0) 55%);
}

.block__b { padding: 28px 28px 26px; }

.block h3 {
    letter-spacing: -.03em;
    font-variation-settings: 'opsz' 120;
    text-shadow: 0 1px 24px rgba(4, 12, 16, .45);
}

.block__meta {
    margin-top: 11px;
    letter-spacing: .18em;
    color: rgba(255, 255, 255, .78);
}

/* ---------------------------------------------------------------------------
   24.7  THE CONTROLS — a button that is lit, a field that is a field

   `.cta` was a flat blue rectangle with a 10px radius. It is the most important
   object on a site whose whole purpose is to produce an enquiry, and there was
   nothing about it that anybody had drawn.

   Three layers, and none of them is a colour that is not already in §2:
     - a gradient from `--blue-2` down to `--blue`. Two stops, eight per cent of
       lightness between them: enough that the button has a top and a bottom, not
       so much that it looks like a 2009 gel;
     - the sheen, one pixel of white at .22 along the top inside edge — the
       highlight every physical control has and no flat rectangle does;
     - a glow tinted with a DARKENED BLUE rather than with grey, so the button
       casts the shadow its own colour would cast.

   `:active` is new and matters more than it looks: a control that does not move
   under a finger is the difference between a page and an interface. */

.cta {
    padding: 13px 22px;
    border-radius: 11px;
    letter-spacing: -.005em;
    background-image: linear-gradient(180deg, var(--fill-top), var(--fill));
    box-shadow: var(--fill-sheen), var(--fill-glow);
    transition: background-color .24s var(--e), transform .18s var(--e), box-shadow .24s var(--e), filter .2s;
}

.cta:hover {
    transform: translateY(-2px);
    filter: saturate(1.06);
    box-shadow: var(--fill-sheen), var(--fill-glow-2);
}

.cta:active {
    transform: translateY(0);
    box-shadow: var(--fill-sheen), 0 1px 2px rgba(12, 68, 91, .34);
}

.cta--sm { padding: 10px 16px; border-radius: 10px; }
.cta--lg { padding: 16px 32px; border-radius: 12px; }
.cta--full { padding: 16px; border-radius: 12px; }

/* The ghost. It was a 1.5px `--line-2` outline and nothing else, so beside the
   filled button it read as the disabled state of the same control. A hairline
   ring plus the faintest wash of the ground gives it a body without giving it a
   colour, and `--line-2` stays the border colour because WCAG 1.4.11 asks 3:1 of
   a control's boundary and that token is the one that measures it (§9.3). */
.cta--ghost {
    border: 1px solid var(--line-2);
    background-image: none;
    background-color: color-mix(in srgb, var(--text) 4%, transparent);
    box-shadow: var(--sh-0);
    transition: background-color .24s var(--e), border-color .24s var(--e), color .24s var(--e), transform .18s var(--e);
}

.cta--ghost:hover {
    background-color: color-mix(in srgb, var(--fill) 9%, transparent);
    border-color: var(--fill);
    color: var(--accent);
    transform: translateY(-2px);
    box-shadow: var(--sh-0);
}

.cta--ghost:active { transform: translateY(0); }

/* ---- the fields ---------------------------------------------------------

   The enquiry form is the end of every path through this site. Its fields were
   1.5px outlines around nothing: correct, legible, and the same rectangle a
   browser draws by itself.

   What is added is one inset shadow — the shallow well that says a field is
   somewhere text GOES — and a focus state that is a ring rather than a border
   colour change. The border keeps `--line-2` at its measured 3.2:1; only the
   weight drops to 1px, and weight is not what 1.4.11 measures. */

.form__field {
    border-width: 1px;
    border-radius: 12px;
    padding: 14px 16px;
    box-shadow: inset 0 1px 2px rgba(8, 22, 29, .045);
    transition: border-color .2s, box-shadow .2s, background-color .2s;
}

.form__field:hover { border-color: var(--text-3); }

.form__field:focus {
    border-color: var(--fill);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--fill) 16%, transparent),
                inset 0 1px 2px rgba(8, 22, 29, .04);
}

.form__field::placeholder { color: var(--text-3); opacity: 1; }

/*
    A SELECT HAD NO CHEVRON, and that is a defect rather than a refinement.

    `select.form__field` carries `appearance: none`, which removes the control's
    own arrow — and nothing put one back. Every select on this site therefore
    looked exactly like a text input that would not accept typing.

    The mark is an inline SVG in a data URI. The public CSP allows it
    (`img-src 'self' data:` — App\Http\Headers::csp), and `background-image` on a
    form control is the only way to draw it without adding an element to every
    template that prints a select. The ADMIN CSP does not allow data: URIs, so
    this technique may not be copied into admin.css without changing that header.

    The stroke colour is baked into the file, so it cannot follow a role token.
    `--ink-3` is the value, and it is the same ink every other label on a light
    ground carries; a select on a dark band would need its own rule and there is
    none on the site.
*/
select.form__field {
    padding-right: 44px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23556b77' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 15px center;
    background-size: 17px 17px;
}

.form__label {
    margin-bottom: 8px;
    font-size: 13px;
    letter-spacing: .002em;
}

.form__hint { line-height: 1.45; }

/* The error summary and the status notice. Both were a 3px left rule against a
   grey wash. The rule stays — it is the fastest way to find a message on a page
   — and gains the wash of its own colour, so the block reads as one object
   rather than as a coloured stripe next to a grey one. */
.notice {
    border-left-width: 2px;
    padding: 17px 22px;
    border-radius: 0 var(--radius) var(--radius) 0;
    background: color-mix(in srgb, var(--fill) 7%, var(--ground));
    box-shadow: inset 0 0 0 1px var(--edge);
}

.notice--quiet {
    background: var(--ground-2);
    box-shadow: inset 0 0 0 1px var(--edge);
}

.form__errors {
    border-left-width: 2px;
    padding: 17px 22px;
    border-radius: 0 var(--radius) var(--radius) 0;
    background: color-mix(in srgb, #b3402f 7%, var(--ground));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, #b3402f 18%, transparent);
}

.tag {
    border-width: 1px;
    border-radius: 8px;
    padding: 5px 11px;
    letter-spacing: .14em;
    background: color-mix(in srgb, var(--text) 3%, transparent);
}

.prefill {
    background-image: linear-gradient(180deg, rgba(255, 255, 255, .10), rgba(255, 255, 255, 0));
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .14), var(--sh-0);
    letter-spacing: .1em;
    font-size: 11px;
    text-transform: uppercase;
}

/* ---------------------------------------------------------------------------
   24.8  THE CATALOGUE FURNITURE — the bar, the chips, the pager

   The filter bar is the first thing on a catalogue page and it was a white
   rectangle with grey lines in it. It is now a panel: an edge, a deeper shadow
   because it overlaps the band above it, and dividers that fade at both ends
   instead of butting into the padding. */

.fbar {
    padding: 10px;
    border-radius: var(--radius-l);
    box-shadow: var(--sh-2), inset 0 0 0 1px var(--edge);
}

.fc { border-radius: 12px; }

.fc:hover { background: color-mix(in srgb, var(--fill) 5%, transparent); }

.fc:focus-within {
    background: color-mix(in srgb, var(--fill) 7%, transparent);
    outline-width: 2px;
    outline-offset: -2px;
}

.fc label,
.fc .fc__label {
    font-size: 9px;
    letter-spacing: .18em;
    margin-bottom: 5px;
}

/* A divider that stops before it reaches either end of the cell. */
.fdiv {
    background: linear-gradient(to bottom,
        color-mix(in srgb, var(--hair) 0%, transparent),
        var(--hair) 22%,
        var(--hair) 78%,
        color-mix(in srgb, var(--hair) 0%, transparent));
    margin: 4px 0;
}

/* The active filter chips. A solid `--night` pill is the heaviest object on the
   page sitting under the lightest — the filter bar — and there are up to six of
   them. Same weight, less mass: the fill keeps its darkness and gains a sheen
   and a shadow, so it reads as a token that was placed rather than as a hole. */
.chip {
    padding: 8px 15px;
    background-image: linear-gradient(180deg, rgba(255, 255, 255, .12), rgba(255, 255, 255, 0));
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .12), 0 2px 8px rgba(8, 22, 29, .16);
    letter-spacing: -.003em;
}

.chip--off {
    background-image: none;
    background-color: color-mix(in srgb, var(--text) 3%, transparent);
    box-shadow: var(--sh-0);
    border-width: 1px;
}

.chip--off:hover {
    background-color: color-mix(in srgb, var(--fill) 8%, transparent);
    border-color: var(--fill);
}

/* The result count. It is the one figure on a catalogue page and it was set at
   the same optical size as a subhead. */
.rescount b {
    font-size: clamp(2.1rem, 2.4vw, 2.7rem);
    letter-spacing: -.035em;
    font-variation-settings: 'opsz' 96;
}

.rescount span { letter-spacing: .2em; }

.pager a,
.pager span {
    border-radius: 11px;
    border-color: var(--edge-2);
    box-shadow: var(--sh-0);
    transition: transform .2s var(--e), border-color .2s, background-color .2s, box-shadow .2s;
}

.pager a:hover {
    background: color-mix(in srgb, var(--fill) 7%, var(--card));
    border-color: var(--fill);
    box-shadow: var(--sh);
}

.pager a[aria-current] {
    background-image: linear-gradient(180deg, var(--fill-top), var(--fill));
    background-color: var(--fill);
    border-color: transparent;
    box-shadow: var(--fill-sheen), var(--fill-glow);
}

/* ---------------------------------------------------------------------------
   24.9  THE HEADER — glass that is actually glass

   `backdrop-filter: blur(18px)` over a scrolling page is the one place on this
   site where a backdrop filter is the right tool and is already paid for: the
   backdrop is the page, the page is not animating, and §3's measurement was
   about a filter over a photograph that DRIFTS. Nothing here changes that.

   What it was missing is `saturate()`. A blur alone averages the colours behind
   it towards grey, which is why unsaturated glass looks like a dirty window
   rather than like glass; the saturate puts back what the blur took out. It is
   two words and it is most of the difference.

   The hairline under the header becomes a shadow the moment the page has
   scrolled, and stays a hairline before that. A bar that gains depth when it
   starts overlapping content is telling the reader something true. */

.top {
    background: var(--glass);
    backdrop-filter: blur(20px) saturate(1.7);
    -webkit-backdrop-filter: blur(20px) saturate(1.7);
    box-shadow: inset 0 -1px 0 var(--hair);
}

.top.sm {
    box-shadow: inset 0 -1px 0 var(--hair), 0 8px 26px -14px rgba(8, 22, 29, .30);
}

/* The mark beside the name. A flat blue square with a serif C in it; now the
   same square with the button's own light on it, so the two blue objects in the
   header are made of the same material. */
.brand__m {
    background-image: linear-gradient(150deg, var(--blue-2), var(--blue-3));
    box-shadow: var(--fill-sheen), 0 2px 8px -2px rgba(12, 68, 91, .45);
    font-variation-settings: 'opsz' 24;
}

.brand__n { letter-spacing: -.015em; }

/* A navigation entry answers the cursor with a ground as well as an underline.
   The underline alone is a link; a link that lights up is a control. */
.nav__link {
    border-radius: 10px;
    transition: color .22s, background-color .22s;
}

.nav__link:hover {
    background-color: color-mix(in srgb, var(--text) 6%, transparent);
}

.nav__link::after {
    left: 14px;
    right: 14px;
    height: 1.5px;
    border-radius: 2px;
}

.menu__button {
    border-radius: 11px;
    box-shadow: var(--sh-0);
    transition: border-color .2s, background-color .2s, color .2s;
}

.menu__button:hover { background-color: color-mix(in srgb, var(--fill) 8%, transparent); }

.menu__panel {
    border-radius: var(--radius-l);
    box-shadow: var(--sh-2), inset 0 0 0 1px var(--edge);
}

.menu__link { border-radius: 10px; }

.breadcrumbs__item a { transition: color .2s; }

/* ---------------------------------------------------------------------------
   24.10  THE FOOTER — the last band, and it was the plainest

   Four columns of links on a dark ground with a rule at the bottom. Everything
   below is hierarchy rather than decoration: the agency's name gets the display
   face at a size that closes the page, each column heading gets the same leading
   rule the section numbers get, and the whole band gets a lit top edge so it
   reads as the floor of the page rather than as one more dark section. */

.foot {
    padding: 88px 0 32px;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .07);
}

.foot h4 {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 18px;
    letter-spacing: .2em;
}

.foot h4::before {
    content: '';
    flex: 0 0 auto;
    width: 16px;
    height: 1px;
    background: currentColor;
    opacity: .5;
}

.foot__n {
    font-size: clamp(1.45rem, 2vw, 1.85rem);
    letter-spacing: -.028em;
    font-variation-settings: 'opsz' 96;
    margin-bottom: 14px;
}

.foot a { transition: color .2s; }

.foot__e {
    margin-top: 60px;
    padding-top: 26px;
    letter-spacing: .12em;
}

.foot__lang-link,
.foot__lang-current { border-radius: 8px; }

/* ---------------------------------------------------------------------------
   24.11  THE PROPERTY SHEET — the page the whole site exists to reach

   The price panel, the data sheet, the tab strip and the gallery. Each of them
   was a correct arrangement of information with no surface under it. */

.pbox {
    border-radius: var(--radius-l);
    box-shadow: var(--sh), inset 0 0 0 1px var(--edge);
    overflow: hidden;
}

.pbox__p {
    letter-spacing: -.032em;
    font-variation-settings: 'opsz' 96;
}

/* The two small figures under the price. They were two boxes of the card ground
   inside a box of the card ground. */
.q {
    background: color-mix(in srgb, var(--text) 3.5%, var(--card));
    border-radius: var(--radius-s);
    box-shadow: inset 0 0 0 1px var(--edge);
    padding: 14px 12px;
}

.agent__ph { box-shadow: inset 0 0 0 1px var(--edge-2), var(--sh-0); }

/* A row of the data sheet. The icon carried the accent and the value carried the
   weight, and between them was nothing. */
.sp {
    padding-bottom: 2px;
}

.sp dt {
    letter-spacing: .16em;
    font-size: 9.5px;
}

.sp dd { font-size: 16.5px; }

.sp svg { opacity: .85; }

/* The data sheet as a whole. `.specs` is a grid whose 1px GAPS are the hairline
   — the cells paint `--card` over a `--hair` ground — which is a good trick and
   left alone. What it never had is an outside edge, so the sheet's own outer
   boundary was the only line in it that did not exist. */
.specs {
    border-radius: var(--radius);
    box-shadow: inset 0 0 0 1px var(--edge), var(--sh-0);
}

/* The narrative. 17.5px at 1.78 over 66ch is a good measure and a good rhythm;
   what it lacked is the one thing that makes long prose read as typeset — a
   first paragraph that is visibly the opening and not just a larger line. */
.prose { letter-spacing: -.002em; }

.prose p:first-child {
    font-size: 20px;
    line-height: 1.62;
    letter-spacing: -.008em;
}

/* The energy bar. Seven segments of a hairline grey with the first four filled
   flat; now the filled ones carry the button's own gradient, so the figure reads
   as a measurement rather than as four blue dashes. */
.ebar i {
    height: 6px;
    border-radius: 3px;
    background: color-mix(in srgb, var(--text) 9%, transparent);
}

.ebar i.on {
    background-image: linear-gradient(180deg, var(--fill-top), var(--fill));
    box-shadow: var(--fill-sheen);
}

/* The tab strip over the dossier. */
.tab {
    border-radius: 10px 10px 0 0;
    transition: color .2s, background-color .2s;
}

.tab:hover { background-color: color-mix(in srgb, var(--text) 5%, transparent); }

.tab::after { height: 2px; border-radius: 2px 2px 0 0; }

/* The gallery furniture rides on a photograph, so it keeps its fixed white —
   §10's one documented exception. What it gains is an edge and a real shadow, so
   a white circle over a bright picture stops dissolving into it. */
.vnav,
.vfull,
.lightbox__nav,
.lightbox__close {
    box-shadow: inset 0 0 0 1px rgba(8, 22, 29, .10), 0 3px 14px rgba(4, 12, 16, .30);
}

/* The counter keeps the .8 floor it already had — it is 12px mono over a
   photograph and the alpha IS the contrast. What it gains is the ring, and it
   loses the `backdrop-filter` it declared: it sits on `.viewer`, whose stage
   holds still, so that blur was cheap, but it was also invisible under an opaque
   .8 floor. One less filter for a picture nobody can see through. */
.vcount {
    background: rgba(8, 22, 29, .8);
    backdrop-filter: none;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .16);
}

.thumbs a {
    border-radius: 10px;
    overflow: hidden;
    transition: transform .3s var(--e), box-shadow .3s var(--e);
}

.thumbs a:hover { box-shadow: var(--sh); }

/* The sticky bar on a phone, which is the only call to action on the screen for
   most of the page's height. */
.bar {
    backdrop-filter: blur(18px) saturate(1.7);
    -webkit-backdrop-filter: blur(18px) saturate(1.7);
    box-shadow: inset 0 1px 0 var(--hair), 0 -10px 34px -12px rgba(8, 22, 29, .26);
}

.bar__p { letter-spacing: -.03em; }

/* ---------------------------------------------------------------------------
   24.12  THE COOKIE BANNER

   It is the first thing a first visit sees, over the opening photograph, and it
   was a flat panel with a hard top rule. */

.consent {
    backdrop-filter: blur(20px) saturate(1.7);
    -webkit-backdrop-filter: blur(20px) saturate(1.7);
    box-shadow: inset 0 1px 0 var(--hair), 0 -16px 44px -18px rgba(8, 22, 29, .30);
}

.consent__title { letter-spacing: -.015em; }

.consent__panel {
    border-radius: var(--radius);
    box-shadow: inset 0 0 0 1px var(--edge);
    background: color-mix(in srgb, var(--text) 3%, transparent);
}

/* ---------------------------------------------------------------------------
   24.13  THE LAST DETAILS

   The focus ring, the selection, the scrollbar. None of them is noticed when it
   is right and all three are noticed when they are wrong. */

/*
    THE FOCUS RING IS LEFT ALONE, and this note is here so the next person does
    not "fix" it the way it was nearly fixed here.

    §2 declares `:focus-visible { outline: 3px solid var(--focus); border-radius:
    2px }`, and that `border-radius` looks like a bug: it is set on the ELEMENT,
    not on the outline, and browsers have followed an element's own radius when
    drawing its outline for years. It is harmless in fact. It weighs (0,1,0) and
    sits near the top of the file, so every component that declares a radius of
    its own — every button, card, field and panel below it — wins on source
    order. What is left for it to affect is the handful of elements that have no
    radius at all, where 2px of rounding on the ring is an improvement.

    Reverting it is what would break something: `revert` rolls an author
    declaration back to the USER-AGENT value, which is 0, so the rule would go
    from doing nothing to squaring off exactly the elements it currently helps.
    The ring itself — 3px of `--focus` at offset 2 — is measured and untouched.
*/

::selection { background: var(--blue-3); color: #fff; }

/* A scrollbar the width of the browser default is a grey channel down the side
   of a page whose whole design is about calm grounds. Firefox takes the two
   properties, WebKit takes the pseudo-elements, and a browser that takes neither
   is unharmed. */
html {
    scrollbar-width: thin;
    scrollbar-color: rgba(10, 26, 34, .22) transparent;
}

::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }

::-webkit-scrollbar-thumb {
    background: rgba(10, 26, 34, .20);
    border: 3px solid transparent;
    border-radius: 99px;
    background-clip: content-box;
}

::-webkit-scrollbar-thumb:hover { background: rgba(10, 26, 34, .34); background-clip: content-box; }

/* The scroll progress bar was a flat blue line. */
.prog {
    background-image: linear-gradient(90deg, var(--blue-3), var(--blue-2));
    box-shadow: 0 0 12px rgba(42, 157, 196, .55);
}

/*
    Everything §24 adds that moves is a transition on hover or focus, so §19's
    reduced-motion block already kills all of it — it sets `transition: none` on
    `*`. This rule is here for the one thing that block cannot reach: `filter`
    and `transform` end states that a user who asked for no motion should simply
    never see applied.
*/
@media (prefers-reduced-motion: reduce) {
    .cta:hover,
    .cta--ghost:hover,
    .card:hover,
    .pager a:hover { transform: none; filter: none; }
}

/* ---------------------------------------------------------------------------
   24.14  THE LANGUAGE NOTICE

   It is fixed above everything, it is the FIRST thing a visitor from a German or
   English browser sees, and it was a flat black strip with two mono labels in
   it — one of which is a link and one of which is a button, both drawn exactly
   alike. The one moment the site speaks to somebody in a language they did not
   choose is not the moment to make them guess which word is the way out.

   The band gets the same raking light the dark sections got, the offer becomes a
   small filled control and the dismissal stays a quiet label. Nothing moves and
   nothing is added to the markup: the two controls already carry different
   classes, they simply looked the same. */

.language-notice {
    background-image:
        radial-gradient(80% 200% at 10% 50%, rgba(28, 123, 161, .22), transparent 70%);
    box-shadow: inset 0 -1px 0 rgba(255, 255, 255, .10);
}

.language-notice__action {
    padding: 7px 14px;
    border-radius: 9px;
    background-image: linear-gradient(180deg, var(--fill-top), var(--fill));
    box-shadow: var(--fill-sheen), 0 2px 8px -2px rgba(4, 12, 16, .6);
    color: #fff;
    letter-spacing: .13em;
    transition: filter .2s, box-shadow .2s;
}

.language-notice__action:hover {
    text-decoration: none;
    filter: saturate(1.08);
    box-shadow: var(--fill-sheen), 0 4px 14px -3px rgba(4, 12, 16, .7);
}

.language-notice__dismiss {
    padding: 7px 4px;
    letter-spacing: .13em;
    transition: color .2s;
}

/* ---------------------------------------------------------------------------
   24.15  WHAT §24 DELIBERATELY DID NOT DO

   Written down so the next pass does not have to rediscover it.

   - `.pill` was left alone. Its floor is `--night` at .48 under a .10 white veil,
     which measures 2.82:1 against a blown-out white pixel — under AA for the
     11px mono it carries. It is not a defect in practice: every place the pill
     is used it stands on a heavy scrim (the hero at 0.86–0.95, the property
     cover at 0.95) or on a flat dark band, never on raw photograph. Raising the
     floor would darken it on grounds where it is already correct, so the honest
     fix is a measurement of the two real placements, not a token change. Left as
     found, and recorded.
   - `[hidden] { display: none }` is still the rule §20 wants at the end of the
     file, and §24 does not move it further from the end than the footer language
     switcher already did: nothing in this section sets `display` on an element
     the scripts reveal. The three that ship `hidden` — `.fav`, `.tabs`, the
     share control's two buttons — are styled here on colour, radius and shadow
     only. Check that before appending anything below this line.
   - The angled edge, the ticker, the Ken Burns drift and the reveal are §5's
     motion vocabulary and are untouched. §24 adds no animation, only responses
     to hover and focus.
   ========================================================================== */


/* ==========================================================================
   25. IL SITO E IL PANNELLO TORNANO UNA COSA SOLA — in azzurro

   COS'ERA QUI PRIMA, e perché non c'è più. Questa sezione conteneva il passaggio
   del sito pubblico alla tinta dei mockup dell'agenzia: crema al posto del
   grigio-azzurro, verde bosco al posto dell'azzurro. Il pannello aveva fatto la
   stessa mossa nella sua §37 — e poi l'ha disfatta nella sua §48, tornando
   all'azzurro del progetto. Per un po' le due metà sono state due tinte diverse:
   il pannello azzurro, il sito verde.

   La decisione: l'AZZURRO È IL COLORE, di qua e di là. Dei mockup si tiene la
   LINGUA, che è la parte che vale — il serif ad alto contrasto, il bronzo come
   unico accento caldo, le superfici chiare tenute insieme da un filetto invece
   che da un'ombra, i riquadri delle icone (§26). Il verde era la tinta con cui
   quei disegni sono stati fatti, non il marchio di questa agenzia.

   COME FUNZIONA. Non si ricolora niente: si riassegnano i TOKEN, e le
   ventiquattro sezioni sopra cambiano da sole. È il meccanismo per cui la §10 di
   docs/DESIGN.md esiste, usato nella direzione opposta a quella per cui è stato
   scritto — e la prova che era scritto bene.

   I VALORI SONO QUELLI DEL PANNELLO, cifra per cifra, perché due file che
   dichiarano «lo stesso grigio» con due valori diversi divergono al primo
   ritocco. Dove il sito si scosta dal pannello c'è scritto perché, e i casi sono
   due: `--line-2` e le bande scure.

   I contrasti sono misurati sui cinque fondi chiari — bianco, paper, paper-2,
   sabbia, azzurro tenue — e accanto a ogni inchiostro c'è il PEGGIORE dei cinque:

     --ink      #16242b   13.72:1
     --ink-2    #4c626d    5.53:1
     --ink-3    #56676f    5.08:1     ← il colore di ogni etichetta smorzata
     --blue-3   #155f7d    6.11:1     ← l'azzurro come testo, e il pieno
     --bronze-2 #7d5f3f    5.06:1     ← il bronzo quando va letto
     bianco sul pieno                  7.08:1
   ========================================================================== */

:root {
    /* ---- i fondi: gli stessi del pannello --------------------------------
       Erano #f7f9fa e #eef3f6. La differenza con questi è di un punto e mezzo di
       chiarezza e non si vede a occhio nudo: quello che conta è che adesso sono
       LO STESSO valore scritto nei due fogli. */
    --w:       #fff;
    --paper:   #f5f7f8;
    --paper-2: #eaeff2;

    /* La sabbia. Nome ereditato dai mockup, dove era davvero sabbia; qui è il
       fondo freddo dei riquadri delle icone e dei tag della §26. Il nome resta
       perché è quello che il pannello usa per la stessa cosa. */
    --sand:    #eef4f7;

    /* ---- l'azzurro, quello del progetto ----------------------------------
       Tre ruoli e tre valori, e NON sono intercambiabili:

         --blue    è un PIENO e soltanto un pieno. Come testo su paper-2 misura
                   4.12:1, sotto la soglia: non va mai scritto.
         --blue-3  è l'azzurro come TESTO su fondo chiaro (6.11:1 il peggiore) ed
                   è anche il pieno di un controllo (7.08:1 col bianco).
         --blue-4  è l'azzurro come testo su fondo scuro: 6.99:1 sul più chiaro
                   dei tre fondi scuri. */
    --blue:    #1c7ba1;
    --blue-2:  #2a9dc4;   /* la cima del gradiente di un controllo pieno */
    --blue-3:  #155f7d;
    --blue-4:  #7fd0ea;
    --blue-l:  #e6f3f8;   /* la banda tenue: l'azzurro come SUPERFICIE */

    /* ---- gli inchiostri --------------------------------------------------- */
    --ink:     #16242b;
    --ink-2:   #4c626d;
    --ink-3:   #56676f;

    --line:    #dfe6ea;   /* il filetto decorativo: divisori, righe di card */

    /*
        PRIMO SCOSTAMENTO DAL PANNELLO, e non è una svista.

        La §48 del pannello porta `--line-2` a #9fb0b8, che sul bianco misura
        2.24:1. Questo token è il bordo dei campi del modulo e dei bottoni
        fantasma — cioè il contorno di un CONTROLLO, e la 1.4.11 delle WCAG ne
        chiede 3:1. Il valore del sito ne misura 3.2 ed è quello che la §9.3 di
        docs/DESIGN.md ha scritto apposta. Resta.

        Che il pannello abbia lo stesso difetto è una cosa da sistemare là, non
        una ragione per riprodurla qui.
    */
    --line-2:  #7f939f;

    /* ---- il bronzo: l'unico accento -------------------------------------
       È la cosa che i mockup hanno insegnato a questo progetto e che l'azzurro
       da solo non faceva: un tratto caldo su una pagina fredda. Rame e azzurro è
       un accostamento più vecchio di questo sito.

       `--bronze` è un TRATTO, mai un testo: su paper-2 misura 2.2:1. Quando il
       bronzo deve essere LETTO si usa `--bronze-2`, che passa su tutti e cinque
       i fondi chiari. Non è mai un controllo: il colore d'azione è uno, e non è
       questo.

       Eredita i due usi che la §2 dava all'ambra — il punto pulsante della
       pastiglia e il segno di un annuncio in evidenza — più il filetto delle
       sopracciglia di sezione (§25.3), che è quello che lega una pagina
       all'altra. Un quarto uso è un errore. */
    --bronze:   #ad8a6a;
    --bronze-2: #7d5f3f;
    --amber:    var(--bronze);

    /* ---- il pieno diventa profondo ---------------------------------------

       Nella §1 il bottone era `--blue` liscio: 4.78:1 col bianco, dentro la
       soglia ma appena, e chiaro abbastanza da leggersi come un pulsante di
       sistema. Nei mockup — e adesso nel pannello — il controllo pieno è una
       tinta fonda e calma. `--blue-3` porta il bianco a 7.08:1, e un gradiente
       che sale verso `--blue` gli mette addosso la luce senza schiarire il piede
       su cui il testo appoggia. */
    --fill:      var(--blue-3);
    --fill-top:  var(--blue);
    --fill-2:    var(--blue);   /* l'hover: il controllo si accende, non si spegne */

    /* ---- la luce si alleggerisce -----------------------------------------

       La ricetta a tre strati della §18.3 resta — contatto, forma, peso — con le
       alfe del pannello, che sono più basse. Su una pagina tenuta insieme dai
       filetti (§26) l'ombra non deve più fare il lavoro del bordo: deve solo dire
       che la cosa sta APPOGGIATA e non stampata. L'inchiostro è `--ink`, mai un
       nero neutro: un'ombra grigia su un fondo azzurrino va nel fango. */
    --sh-0: 0 1px 2px rgba(22, 36, 43, .05);
    --sh:   0 1px 2px rgba(22, 36, 43, .05),
            0 6px 16px -8px rgba(22, 36, 43, .10),
            0 22px 44px -24px rgba(22, 36, 43, .16);
    --sh-2: 0 1px 3px rgba(22, 36, 43, .06),
            0 12px 28px -12px rgba(22, 36, 43, .14),
            0 40px 72px -32px rgba(22, 36, 43, .26);

    --edge:   rgba(22, 36, 43, .07);
    --edge-2: rgba(22, 36, 43, .13);

    /* Il bagliore di un controllo pieno è tinto del PROPRIO colore scurito: è la
       differenza fra un bottone illuminato e un bottone ritagliato e incollato. */
    --fill-sheen:  inset 0 1px 0 rgba(255, 255, 255, .18);
    --fill-glow:   0 1px 2px rgba(11, 58, 77, .24),
                   0 8px 20px -10px rgba(11, 58, 77, .42);
    --fill-glow-2: 0 2px 4px rgba(11, 58, 77, .26),
                   0 16px 32px -14px rgba(11, 58, 77, .50);
}

/*
    SECONDO SCOSTAMENTO DAL PANNELLO: le bande scure restano quelle della §2.

    Il pannello non ne ha nessuna — è una superficie di lavoro chiara dalla prima
    all'ultima riga — quindi non c'è niente da allineare. `--night`, `--night-2` e
    `--deep` sono il disegno del sito pubblico (docs/DESIGN.md §1: le bande), e il
    bianco ci misura da 12.1 a 18.4:1, `--blue-4` da 7.0 a 10.6.
*/

/* ------------------------------------------------- 25.1 il serif ad alto contrasto

   La prima cosa che si vede nei mockup, e l'unica che non è un colore. Fraunces
   è già qui ed è variabile: `opsz` in cima alla sua scala e il peso leggero
   danno le aste sottili e lo stacco netto fra pieni e filetti di un carattere di
   impianto classico, senza un file in più da scaricare.

   `SOFT` a zero toglie l'arrotondamento che rende Fraunces amichevole; `WONK` a
   zero toglie le forme buffe della g e della y, che su un sito che vende case
   sono un'idea in più di quante ne servano. */
h1, h2, h3,
.card h3,
.shead h2,
.pcover__figure,
.pbox__p {
    font-variation-settings: 'opsz' 144, 'SOFT' 0, 'WONK' 0;
}

h1, h2 { font-weight: 400; letter-spacing: -.018em; }

/* ------------------------------------------------- 25.2 il filetto di bronzo

   Sotto ogni sopracciglio di sezione. È il segno che ricorre da una pagina
   all'altra ed è l'unico posto in cui il bronzo compare come tratto pieno.

   IL FILETTO C'ERA GIÀ — cambia colore, non si aggiunge. La §22 aveva messo un
   tratto di `currentColor` a .55 prima del sopracciglio. Un secondo tratto in
   bronzo dopo la parola ne faceva DUE di colori diversi ai due capi della stessa
   riga, che è la cosa che si nota per prima e si spiega per ultima. */
.snum { color: var(--text-3); }

.snum::before {
    width: 28px;
    height: 2px;
    background: var(--bronze);
    opacity: 1;
}

/* Sulle bande scure il bronzo pieno cade a 3.8:1 sul `--deep`. Come TRATTO è
   dentro la soglia della 1.4.11 (3:1 per un oggetto grafico) e ci resta, ma
   schiarito di un gradino sta sul fondo invece di sparirci dentro. */
.sec--dark .snum::before,
.sec--deep .snum::before,
.hero .snum::before,
.foot .snum::before { background: #c9a37e; }

/* ------------------------------------------------- 25.3 l'hover di un pieno

   `.cta:hover` scrive `background`, e la scorciatoia azzera anche
   `background-image`: il gradiente della §24.7 spariva all'hover e il bottone si
   appiattiva proprio nel momento in cui lo si sta guardando. Rimesso, un gradino
   più chiaro del riposo — il controllo si accende. */
.cta:hover,
.cta:focus-visible {
    background-image: linear-gradient(180deg, var(--blue-2), var(--fill-2));
}

.cta--ghost:hover,
.cta--ghost:focus-visible { background-image: none; }

/* ==========================================================================
   26. LA COMPONENTISTICA DEL PANNELLO, PORTATA SUL SITO

   La §25 ha allineato le TINTE. Questa allinea i MODI: le tre o quattro cose che
   il pannello sa fare e che il sito pubblico non faceva, e che sono la ragione
   per cui il cruscotto sembra disegnato e la scheda immobile sembra composta.

   Non è una passata di rifinitura sopra la §24: la §24 aveva dato ai piani un
   bordo e una luce, e quel lavoro resta intatto. Qui si aggiungono OGGETTI —
   il quadrato dell'icona, il riquadro dei recapiti — e si toglie
   un'incoerenza: lo stato attivo che sul pannello è azzurro e sul sito era nero.

   Come sempre in questo foglio: nessuna animazione nuova, solo risposte a hover
   e fuoco, che la §19 spegne per intero.
   ========================================================================== */

/* ------------------------------------------------- 26.1 il quadrato dell'icona

   È la firma dei mockup e la cosa che il pannello ripete in ogni schermata: un
   pittogramma non sta appoggiato al testo, sta dentro un quadrato tinto con
   l'angolo tondo. Costa niente e cambia la lettura di una lista di dati — senza,
   le righe della scheda sono testo con un segno grigio davanti; con, sono voci.

   Il token è di RUOLO, quindi la stessa dichiarazione è giusta su fondo chiaro e
   su fondo scuro (§10). Sulle bande scure il quadrato non può essere l'azzurro
   tenue — sparirebbe — quindi diventa un velo di luce, che è il modo in cui una
   superficie scura descrive un incasso.

   IL PADDING È PARTE DELLA SCATOLA. Un <svg> è un elemento rimpiazzato: con
   `box-sizing: border-box` il riquadro misura 38px e il disegno dentro ne misura
   20, cioè esattamente quanto misurava prima. Il pittogramma non cambia di un
   pixel; gli si costruisce intorno la stanza. */

:root {
    --icon-box:  var(--blue-l);
    --icon-mark: var(--blue-3);
}

.sec--dark, .sec--deep, .hero, .foot {
    --icon-box:  rgba(255, 255, 255, .08);
    --icon-mark: var(--blue-4);
}

.sp svg {
    box-sizing: border-box;
    width: 38px;
    height: 38px;
    padding: 9px;
    border-radius: 11px;
    background: var(--icon-box);
    box-shadow: inset 0 0 0 1px var(--edge);
    stroke: var(--icon-mark);
    stroke-linecap: round;
    stroke-linejoin: round;
    /* La §24 lo aveva smorzato a .85 perché era un segno nudo accanto a un
       valore in grassetto. Dentro il suo riquadro non compete più con niente e
       torna pieno: un pittogramma slavato dentro una cornice è un errore di
       stampa. */
    opacity: 1;
}

/* Il riquadro alza la riga da 62 a 74 pixel, e la scheda dati ha sei righe: il
   respiro verticale della cella si accorcia di quanto il quadrato aggiunge, così
   il foglio nel complesso resta della stessa altezza. */
.sp { gap: 15px; }

/* ------------------------------------------------- 26.2 lo stato attivo è azzurro

   Sul pannello ogni stato attivo è azzurro: la voce di menu su cui sei, la
   pagina del paginatore, la scheda aperta. Sul sito la pastiglia di un filtro
   acceso era `--night`, cioè l'oggetto più scuro della pagina — più scuro del
   testo — messo sotto la barra più chiara. Leggeva come un buco, non come uno
   stato, e diceva «spento» con la stessa forza con cui voleva dire «acceso».

   Stesso peso, altro significato: il pieno del sito, con la sua luce e la sua
   ombra colorata. Il bianco ci misura 7.08:1 (§25).

   `:not(.chip--off)` NON e' prudenza: e' obbligatorio. `.chip--off` e' la
   pastiglia SPENTA — il filtro che si puo' accendere — e le sue dichiarazioni
   stanno alla §24.8, cioe' sopra questa. Una regola su `.chip` scritta qui le
   scavalca tutte e tre (fondo, immagine e ombra) per solo ordine di sorgente, e
   la prima versione di questo blocco lo ha fatto: sulla pagina delle esperienze
   i cinque ambiti da scegliere sono diventati cinque pieni azzurri con sopra il
   testo grigio che era stato scelto per il bianco. Illeggibili, e tutti con
   l'aria di essere gia' selezionati. */
.chip:not(.chip--off) {
    background-color: var(--fill);
    background-image: linear-gradient(180deg, var(--fill-top), var(--fill));
    box-shadow: var(--fill-sheen), var(--fill-glow);
}

.chip:not(.chip--off):hover { box-shadow: var(--fill-sheen), var(--fill-glow-2); }

.chip__x { color: rgba(255, 255, 255, .72); }

/* ------------------------------------------------- 26.3 il filetto sotto il titolo

   Nei mockup ogni testata di pagina porta un tratto corto di bronzo sotto il
   titolo. Sul sito il tratto di bronzo c'era già ma stava PRIMA del sopracciglio
   (§25.2), e la §24.3 aveva dato alla testata un pavimento grigio da un capo
   all'altro della banda.

   Le due cose diventano una: il pavimento comincia con 56 pixel di bronzo e poi
   passa al filetto. Un solo segno per testata, all'inizio della riga come tutto
   il resto in questo progetto, e il bronzo compare due volte sulla stessa
   colonna invece che ai due capi opposti della stessa riga — che è la ragione
   per cui la §25.2 aveva rifiutato la versione precedente.

   Due gradienti e nessun elemento in più. Il bordo va tolto esplicitamente: se
   restasse, sotto i 56 pixel di bronzo ci sarebbe ancora la sua riga grigia. */
.shead {
    border-bottom: 0;
    background-image:
        linear-gradient(var(--bronze), var(--bronze)),
        linear-gradient(var(--hair), var(--hair));
    background-repeat: no-repeat, no-repeat;
    background-size: 56px 2px, 100% 1px;
    background-position: 0 100%, 0 100%;
}

/* Sulle bande scure il bronzo pieno cade a 3.8:1 e il filetto grigio sparisce.
   Il primo si schiarisce di un gradino, il secondo diventa luce. */
.sec--dark .shead,
.sec--deep .shead,
.hero .shead,
.foot .shead {
    background-image:
        linear-gradient(#c9a37e, #c9a37e),
        linear-gradient(rgba(255, 255, 255, .16), rgba(255, 255, 255, .16));
}


/* ============================================================================
   27. IL MOCKUP DEL COMMITTENTE — il sito diventa blu navy su bianco

   Il committente ha consegnato due schermate disegnate: una parte della home e
   una parte della scheda di un appartamento. Il sito va portato li'.

   QUELLO CHE CAMBIA, detto in una riga: il colore portante passa dal teal al
   NAVY, i fondi notte spariscono, la decorazione sparisce, e la pagina diventa
   bianca con schede dal bordo sottile.

   PERCHE' UNA SEZIONE IN CODA E NON UN FOGLIO NUOVO. Questo foglio e'
   append-only da ventisei sezioni, ed e' una scelta che ha gia' pagato: la §37
   del pannello ha portato tutto in verde e la §48 lo ha riportato in blu
   toccando un blocco di token. Vale la stessa cosa qui — e in piu' garantisce
   che nessuna pagina resti indietro, perche' chi non viene toccato eredita
   comunque il colore nuovo.

   L'ORDINE DI QUESTA SEZIONE: prima il colore, poi la tipografia, poi la forma,
   poi i pezzi nuovi che il mockup mostra e che qui non esistevano.
   ========================================================================= */

/* ---------------------------------------------------------- 27.1  IL COLORE

   Si riscrivono i token CRUDI, non quelli semantici, e la ragione e' aritmetica:
   nel foglio ci sono 90 usi dei token semantici (`--fill`, `--accent`) e 166 usi
   diretti dei colori (`--blue`, `--blue-3`, `--night`). Riscrivendo i crudi si
   muovono tutti e due, perche' i semantici sono definiti da loro.

   I VALORI SONO LETTI A OCCHIO DAL MOCKUP: sono un punto di partenza onesto, non
   un dato. Quando arriva il file sorgente del disegno si correggono qui, in un
   posto solo. */
:root {
    --blue:    #1c3d6e;   /* IL colore: bottoni, riempimenti, marchio          */
    --blue-2:  #2a5490;   /* la variante chiara, per i gradienti               */
    --blue-3:  #1c3d6e;   /* il navy come TESTO su fondo chiaro — 10.4:1       */
    --blue-4:  #a8c6ea;   /* il navy come TESTO su fondo navy — 7.3:1          */
    --blue-l:  #eff4f9;   /* la fascia pallida, e il fondo delle rassicurazioni */

    /* I FONDI SCURI DIVENTANO NAVY. Nel mockup di scuro c'e' solo il pie' di
       pagina, ed e' blu: il nero-verde della notte non c'e' piu' da nessuna
       parte. `--night` regge il pie' di pagina, le bande scure e il velo
       dell'hero, quindi cambiarlo qui li porta tutti insieme. */
    --night:   #1c3d6e;
    --night-2: #234878;
    --deep:    #16325c;

    /* Il testo. Il mockup ha titoli quasi neri con una punta di blu, e corrente
       grigio-azzurro: non e' il nero-verde di prima. */
    --ink:     #16233a;
    --ink-2:   #55606f;
    --ink-3:   #7a8698;

    --paper:   #f7f9fb;
    --paper-2: #eff4f9;

    --line:    #e3e8ef;
    --line-2:  #b9c3d1;

    /*
        IL BRONZO SE NE VA, e non diventa navy.

        Serviva a fare il filetto sotto le testate di sezione e gli occhielli: nel
        mockup quel filetto non c'e', e le testate sono titolo e basta. Portarlo al
        navy vorrebbe dire riempire il sito di trattini blu che il disegno non ha —
        peggio che lasciarlo dov'era. Diventa trasparente, e la §27.3 toglie anche
        il filetto che lo disegnava.
    */
    --bronze:   transparent;
    --bronze-2: transparent;
}

/* Il passaggio del mouse su un riempimento pieno: nel mockup il bottone navy si
   scurisce, non si schiarisce. */
:root {
    --fill-2:      #16325c;
    --fill-top:    var(--blue);       /* niente gradiente: il bottone e' piatto */
    --fill-sheen:  none;
    --fill-glow:   none;
    --fill-glow-2: none;
}

/* --------------------------------------------------- 27.2  LA TIPOGRAFIA

   Il serif del mockup ha contrasto MEDIO e sta dritto: non e' il display ad alto
   contrasto che il sito usava. Fraunces e' variabile e ci arriva — ma va tarata,
   non lasciata dov'era.

   `opsz` bassa e `SOFT` a zero addolciscono il contrasto; `WONK` a zero toglie le
   forme storte, che sono la firma di Fraunces e nel mockup non ci sono. Il peso
   resta normale: nel disegno i titoli non sono grassi.

   La CSP dice `font-src 'self'`, quindi non si puo' aggiungere il serif del
   mockup anche volendo: si avvicina quello che c'e'. */
h1, h2, h3, h4,
.h1, .h2, .h3, .h4,
.hero__t, .shead__t {
    font-variation-settings: 'opsz' 40, 'SOFT' 0, 'WONK' 0;
    font-weight: 400;
    letter-spacing: -0.011em;
    color: var(--ink);
}

/* Il mono non compare nel mockup: ne' nei riferimenti, ne' nelle etichette, ne'
   nelle cifre. Le pagine pubbliche passano alla stessa sans di tutto il resto. */
:root { --m: var(--t); }

/* --------------------------------------------------------- 27.3  LA FORMA

   Raggio piccolo e costante, bordi sottili, niente ombre se non dove il mockup ne
   ha una vera — cioe' la sola scheda di ricerca sospesa sull'hero. */
:root {
    --radius:    6px;
    --radius-s:  4px;
    --radius-l:  8px;
    --radius-xl: 10px;
}

/* Il filetto bronzo sotto le testate di sezione: via. Restava disegnato dalla
   §26 anche con il bronzo trasparente, perche' il secondo gradiente e' grigio. */
.shead {
    background-image: none;
    border-bottom: 0;
}

/*
    IL BORDO TAGLIATO NON ESISTE PIU'.

    Era la firma del disegno precedente — una banda che finiva in diagonale — e
    nel mockup ogni fascia finisce dritta. Si spegne qui invece che cancellare le
    §22 e §24: quelle restano scritte, con le loro ragioni, per chi un giorno
    volesse rileggerle.
*/
.sec--cut::after,
.sec--cut::before,
.hero--cut::after { display: none; }

.sec--cut { clip-path: none; }

/* ------------------------------------------------- 27.4  LA TESTATA A DUE RIGHE

   Il marchio a sinistra, alto quanto la testata; a destra in colonna i recapiti
   sopra e il menu sotto. E' la forma del mockup, ed e' anche la sola che trova
   posto per telefono e mail — che su un sito di affitti sono quello che si cerca
   per primo, e che qui non comparivano sopra il piede di pagina. */
.top {
    background: var(--w);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: inset 0 -1px 0 var(--line);
}

.top.sm { box-shadow: inset 0 -1px 0 var(--line); }

.top__in {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-5, 1.5rem);
    min-height: 84px;
    padding-block: .625rem;
}

.top__side {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: .375rem;
    min-width: 0;
}

.top__row {
    display: flex;
    align-items: center;
    gap: .5rem;
}

/* --- il marchio ----------------------------------------------------------- */

.brand { gap: .625rem; }

/* Il quadrato pieno del mockup: navy, angoli appena smussati, glifo di linea
   bianco. Il gradiente e l'ombra di prima appartenevano a un disegno che qui non
   c'e' piu'. */
.brand__m {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.375rem;
    height: 2.375rem;
    flex: 0 0 auto;
    border-radius: var(--radius-s);
    background: var(--blue);
    background-image: none;
    box-shadow: none;
    font-size: 0;
}

.brand__m svg {
    width: 1.375rem;
    height: 1.375rem;
    fill: none;
    stroke: #fff;
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Il nome in maiuscoletto spaziato, come nel mockup, e su due righe quando c'e'
   posto: «CASE COSTA / TOSCANA». Il ritorno a capo lo decide la larghezza. */
.brand__n {
    font-family: var(--t);
    font-size: .8125rem;
    font-weight: 600;
    line-height: 1.2;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--blue);
    max-width: 9rem;
    white-space: normal;
}

/* --- i recapiti ----------------------------------------------------------- */

.tcon {
    display: flex;
    align-items: center;
    gap: 1.125rem;
    margin: 0;
    font-size: .75rem;
    color: var(--ink-2);
}

.tcon__i {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
    color: inherit;
    text-decoration: none;
}

.tcon__i:hover { color: var(--blue-3); }

.tcon__i svg {
    width: .875rem;
    height: .875rem;
    fill: none;
    stroke: var(--blue);
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* --- il menu -------------------------------------------------------------- */

/* Nel mockup le voci sono testo nudo e la corrente porta un filetto sotto:
   niente pastiglia al passaggio del mouse, niente fondo. */
.nav__link {
    border-radius: 0;
    font-size: .8125rem;
    color: var(--ink);
}

.nav__link:hover { background-color: transparent; color: var(--blue-3); }

.nav__link::after {
    left: 0;
    right: 0;
    height: 2px;
    border-radius: 0;
    background: var(--blue);
}

.menu__button { border-radius: var(--radius-s); box-shadow: none; }

.menu__panel { border-radius: var(--radius); box-shadow: var(--sh-2); }

.menu__link { border-radius: var(--radius-s); }

/* Il bottone pieno della testata: navy, piatto, angoli piccoli. */
.cta {
    border-radius: var(--radius-s);
    background: var(--blue);
    background-image: none;
    box-shadow: none;
}

.cta:hover { background: var(--fill-2); }

/*
    SUL TELEFONO LA TESTATA TORNA UNA RIGA.

    Due righe a 390 vogliono dire marchio, recapiti e menu in verticale: ottanta
    pixel di testata prima che cominci la pagina. I recapiti spariscono — il
    numero e' dentro il pannello, dove e' un bersaglio da premere e non una riga
    da leggere — e restano marchio e menu, che e' quello che la vecchia testata
    faceva bene.
*/
@media (max-width: 56.24rem) {
    .top__in { min-height: 64px; }
    .top__side { flex-direction: row; align-items: center; gap: .5rem; }
    .tcon { display: none; }
    .brand__n { max-width: none; font-size: .75rem; }
}

/* =========================================================================
   27.5  I PEZZI NUOVI DEL MOCKUP

   Sei blocchi che nel sito non esistevano, perche' il disegno precedente non ne
   aveva bisogno: la scheda di ricerca, i quattro motivi, i servizi, le
   informazioni utili, la scheda della spalla e le rassicurazioni.

   Sono scritti QUI e non nei fogli di pagina perche' ognuno serve almeno due
   schermate — la ricerca sta sulla home e sul catalogo, la scheda della spalla
   sull'immobile e domani sull'esperienza — e un blocco copiato in due fogli e'
   due blocchi che divergono al primo ritocco.

   Leggono solo i token di ruolo, come tutto il resto del progetto: su fondo
   bianco, carta o azzurro pallido sono corretti senza un selettore in piu'.
   ========================================================================= */

/* --- 27.5.1  la scheda di ricerca disponibilita' -------------------------- */

/*
    Nel mockup e' una scheda bianca sospesa sul bordo basso della fotografia:
    quattro colonne — arrivo, partenza, ospiti, e il bottone.

    NON E' DECORAZIONE. Il catalogo filtra gia' per `from`, `to` e `guests` — la
    clausola NOT EXISTS su `disponibilita` — quindi questa e' una scorciatoia
    verso una ricerca che esiste, non una promessa che nessuno mantiene. Resta un
    modulo GET, come la barra dei filtri: una lista filtrata deve restare un
    indirizzo che si manda per messaggio.
*/
.sbox {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr)) auto;
    align-items: end;
    gap: 1rem;
    padding: 1.25rem;
    border-radius: var(--radius);
    background: var(--card);
    box-shadow: var(--sh-2);
}

.sbox__f { display: flex; flex-direction: column; gap: .375rem; min-width: 0; }

.sbox__l {
    font-size: .75rem;
    font-weight: 500;
    color: var(--text-3);
}

.sbox__i {
    width: 100%;
    min-height: 2.75rem;
    padding: 0 .75rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-s);
    background: var(--ground);
    color: var(--text);
    font: inherit;
    font-size: .875rem;
}

.sbox__i:focus-visible { outline: 2px solid var(--focus); outline-offset: -1px; }

.sbox__go { min-height: 2.75rem; white-space: nowrap; }

/*
    SOSPESA SULL'HERO. Il margine negativo la fa salire sopra la fotografia, come
    nel mockup; sotto i 60rem torna una scheda normale in colonna, perche' quattro
    campi affiancati a 390 sono quattro campi da 60 pixel.
*/
.sbox--float {
    position: relative;
    z-index: 2;
    margin-top: -3.5rem;
}

@media (max-width: 59.99rem) {
    .sbox { grid-template-columns: 1fr; }
    .sbox--float { margin-top: -2rem; }
    .sbox__go { width: 100%; }
}

/* --- 27.5.2  i quattro motivi -------------------------------------------- */

/* Quattro colonne, ognuna un riquadro con l'icona, un titolo e una didascalia.
   Tutto centrato dentro la colonna, come nel mockup. */
.feats {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 1.5rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.feats__i {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .625rem;
    text-align: center;
}

.feats__ic {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.5rem;
    height: 3.5rem;
    border: 1px solid var(--hair);
    border-radius: var(--radius-xl);
}

.feats__ic svg {
    width: 1.5rem;
    height: 1.5rem;
    fill: none;
    stroke: var(--accent);
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.feats__t { font-weight: 600; font-size: .875rem; line-height: 1.3; color: var(--heading); }
.feats__d { font-size: .8125rem; line-height: 1.4; color: var(--text-2); }

@media (max-width: 47.99rem) {
    .feats { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; }
}

/* --- 27.5.3  i servizi inclusi ------------------------------------------- */

/* Griglia a tre colonne che scorre per righe: icona di linea e etichetta. */
.svcs {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: .75rem 1.25rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.svcs__i {
    display: flex;
    align-items: center;
    gap: .5rem;
    font-size: .875rem;
    color: var(--text);
}

.svcs__i svg {
    width: 1.125rem;
    height: 1.125rem;
    flex: 0 0 auto;
    fill: none;
    stroke: var(--accent);
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

@media (max-width: 47.99rem) { .svcs { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* --- 27.5.4  le informazioni utili --------------------------------------- */

/* Una colonna, con un piccolo rombo come punto elenco: e' il segno del mockup, e
   si disegna ruotando un quadrato invece di portarsi dietro un'altra icona. */
.useful { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }

.useful__i {
    position: relative;
    padding-left: 1.375rem;
    font-size: .875rem;
    color: var(--text);
}

.useful__i::before {
    content: '';
    position: absolute;
    left: .1875rem;
    top: .5rem;
    width: .4375rem;
    height: .4375rem;
    transform: rotate(45deg);
    border: 1px solid var(--accent);
}

/* --- 27.5.5  la scheda della spalla -------------------------------------- */

/*
    «Verifica la disponibilita'»: titolo, campi in colonna, bottone a tutta
    larghezza e una nota che dice che non si paga niente online — che e' la cosa
    che chi guarda sta cercando di capire.

    APPICCICATA MENTRE SI SCORRE, perche' la colonna del testo e' lunga e il
    bottone deve restare raggiungibile. `align-self: start` e' necessario: dentro
    una griglia, un elemento alto quanto la riga non ha dove appiccicarsi.
*/
.bcard {
    align-self: start;
    position: sticky;
    top: calc(var(--top-h) + 1rem);
    padding: 1.25rem;
    border: 1px solid var(--hair);
    border-radius: var(--radius);
    background: var(--card);
}

.bcard__t {
    margin: 0 0 1rem;
    font-family: var(--d);
    font-size: 1.125rem;
    font-weight: 400;
}

.bcard__form { display: grid; gap: .75rem; }

.bcard__form .cta { width: 100%; justify-content: center; margin-top: .25rem; }

.bcard__note {
    margin: .875rem 0 0;
    font-size: .8125rem;
    line-height: 1.45;
    color: var(--text-2);
}

@media (max-width: 63.99rem) { .bcard { position: static; } }

/* --- 27.5.6  le rassicurazioni ------------------------------------------- */

/* Tre righe: un cerchio con l'icona a sinistra, titolo e didascalia a destra.
   Fondo azzurro pallido, senza bordo: nel mockup e' l'unico blocco che ha un
   fondo proprio dentro la spalla. */
.assure {
    list-style: none;
    margin: 1rem 0 0;
    padding: 1.25rem;
    border-radius: var(--radius);
    background: var(--blue-l);
    display: grid;
    gap: 1rem;
}

.assure__i { display: flex; align-items: flex-start; gap: .75rem; }

.assure__ic {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    flex: 0 0 auto;
    border: 1px solid var(--line-2);
    border-radius: 50%;
}

.assure__ic svg {
    width: 1.125rem;
    height: 1.125rem;
    fill: none;
    stroke: var(--accent);
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.assure__b { display: flex; flex-direction: column; gap: .125rem; min-width: 0; }
.assure__t { font-weight: 600; font-size: .8125rem; color: var(--heading); }
.assure__d { font-size: .8125rem; line-height: 1.4; color: var(--text-2); }

/* --- 27.5.7  la scheda di un appartamento -------------------------------- */

/*
    La scheda del mockup: bordo sottile, fotografia in cima, poi titolo, una riga
    di dati con le icone, e un bottone pieno a tutta larghezza in fondo.

    IL BOTTONE NON E' UN SECONDO COLLEGAMENTO. La scheda intera e' gia' un'ancora
    sola — `.property-card__link`, che i test contano — e aggiungerne un altro
    triplicherebbe le fermate del tabulatore e romperebbe quel conteggio. Quello
    che sembra un bottone e' disegnato dentro l'unica ancora che c'e'.
*/
.property-card {
    border: 1px solid var(--hair);
    border-radius: var(--radius);
    background: var(--card);
    overflow: hidden;
}

.property-card:hover { border-color: var(--line-2); }

.property-card__link { display: flex; flex-direction: column; height: 100%; }

.property-card__go {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 2.5rem;
    margin-top: auto;
    border-radius: var(--radius-s);
    background: var(--fill);
    color: var(--fill-text);
    font-size: .8125rem;
    font-weight: 500;
}

.property-card__link:hover .property-card__go { background: var(--fill-2); }

/* ------------------------------------------- 27.6  LA RIGA DEI RECAPITI DEL PIEDE

   Tre gruppi affiancati in cima al piede: dove siamo, che numero abbiamo, a quale
   indirizzo ci si scrive. E' l'unica parte del piede che il mockup mostra — la sua
   immagine e' tagliata li' sotto — e sta SOPRA le quattro colonne, non al loro
   posto: quelle sono navigazione vera.

   Il cerchio intorno all'icona e' sottile, non pieno: su fondo navy un cerchio
   pieno bianco sarebbe il punto piu' luminoso della pagina, e nel mockup non lo
   e'. */
.fcon {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1.5rem;
    list-style: none;
    margin: 0;
    padding: 0 0 var(--space-6, 2rem);
}

.fcon__i { display: flex; align-items: center; gap: .75rem; min-width: 0; }

.fcon__ic {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    flex: 0 0 auto;
    border: 1px solid rgba(255, 255, 255, .32);
    border-radius: 50%;
}

.fcon__ic svg {
    width: 1.125rem;
    height: 1.125rem;
    fill: none;
    stroke: #fff;
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.fcon__b { display: flex; flex-direction: column; gap: .0625rem; min-width: 0; }

.fcon__t { font-weight: 600; font-size: .875rem; color: #fff; }

.fcon__d {
    font-size: .8125rem;
    color: rgba(255, 255, 255, .78);
    text-decoration: none;
    overflow-wrap: anywhere;
}

a.fcon__d:hover { color: #fff; text-decoration: underline; }

/* Su un telefono tre gruppi affiancati sono tre colonne da 110 pixel: uno sotto
   l'altro, con il respiro fra loro. */
@media (max-width: 47.99rem) {
    .fcon { grid-template-columns: 1fr; gap: 1rem; }
}

/* ------------------------------------ 27.7  LA TESTATA NON GALLEGGIA PIU' SULLA FOTO

   `hero-page` faceva due cose: toglieva il riempimento in cima al corpo e rendeva
   la testata trasparente, cosi' che la fotografia dell'hero le passasse sotto e
   la pagina si aprisse con l'immagine a tutto schermo. Era la firma del disegno
   precedente e aveva il suo velo apposta (§23) per tenere leggibile il menu sopra
   un cielo chiaro.

   NEL MOCKUP LA TESTATA E' UNA FASCIA BIANCA PIENA e la fotografia comincia
   SOTTO. Non e' un dettaglio di gusto: la testata del mockup porta i recapiti e
   due righe di roba, e sopra una fotografia diventerebbe illeggibile o costringerebbe
   a un velo cosi' forte da spegnere l'immagine — cioe' a peggiorare tutte e due le
   cose per tenerne una che il disegno non chiede.

   Il corpo si riprende il suo riempimento, o la prima fascia finirebbe sotto la
   testata fissa. */
/*
    E LA TESTATA DIVENTA APPICCICATA INVECE CHE FISSA.

    Fissa, la testata esce dal flusso e il corpo deve compensarla con un
    riempimento pari alla sua altezza — che e' `--top-h`, un numero scritto a mano.
    Misurato: la testata nuova e' alta 128 e il token diceva 124, quindi l'hero
    passava sotto di quattro pixel. Ogni volta che la testata cambia contenuto quel
    numero va rincorso, e prima o poi non lo si rincorre.

    Appiccicata, la testata resta nel flusso: si misura da sola, spinge la pagina
    della sua altezza vera, e continua a restare in cima mentre si scorre — che era
    l'unica cosa per cui serviva fissarla.
*/
body,
body.hero-page { padding-top: 0; }

.hero-page .top:not(.sm),
.hero-page .top,
.top {
    position: sticky;
    top: 0;
    background: var(--w);
    border-bottom: 0;
    box-shadow: inset 0 -1px 0 var(--line);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

/* Il velo che la §23 metteva dietro il menu sopra la fotografia non serve piu':
   non c'e' piu' niente da leggere sopra un'immagine. */
.hero-page .top::before { display: none; }

/* `--top-h` non regge piu' il riempimento del corpo — non ce n'e' — ma lo leggono
   ancora le cose che si appiccicano SOTTO la testata, come la scheda della spalla
   (§27.5.5) e la barra della scheda immobile. Resta, con il valore vero. */
:root { --top-h: 88px; }

@media (max-width: 56.24rem) { :root { --top-h: 64px; } }

/* La testata non ha riempimento proprio: l'altezza la decide la riga dentro, che
   e' l'unica cosa che sa quante righe di contenuto porta. Misurato: senza questa
   regola `.top` era 92 a 390 mentre la sua riga ne misurava 64 — ventotto pixel di
   bianco che nessuno aveva chiesto, ereditati da una versione in cui la testata
   era una riga sola. */
/*
    E QUI SERVE LA STESSA SPECIFICITA', non una in meno.

    `.hero-page .top:not(.sm)` (0,3,1) e' la regola che rendeva la testata
    trasparente sopra la fotografia, e non si limitava al fondo: riassegnava
    `--text`, `--heading` e `--accent` al BIANCO, perche' li' sotto c'era
    un'immagine. Con la testata bianca del mockup quella riassegnazione significa
    testo bianco su fondo bianco — una testata invisibile sulla sola pagina che il
    committente ha disegnato.

    Trovato misurando: `.top` sulla home era alta 128 contro le 88 delle altre, e
    tirando il filo e' venuto fuori anche il colore.
*/
.top,
.hero-page .top,
.hero-page .top:not(.sm) {
    padding: 0;
    --text:    var(--ink);
    --text-2:  var(--ink-2);
    --heading: var(--ink);
    --accent:  var(--blue-3);
    --focus:   var(--blue-3);
    --line-2:  #b9c3d1;
    color: var(--ink);
    background: var(--w);
    box-shadow: inset 0 -1px 0 var(--line);
}

/* E il token segue l'altezza vera, perche' lo leggono le cose che si appiccicano
   sotto: la scheda della spalla e la barra della scheda immobile. */
@media (max-width: 56.24rem) { :root { --top-h: 64px; } }

/* --------------------------- 27.8  LE COSTOLE DELLA SCHEDA, DOVE DEVONO STARE

   La §27.5.7 ha dato alla scheda immobile la forma del mockup — filetto sottile,
   raggio 6, finto bottone in fondo — ma non le regole STRUTTURALI senza cui quella
   forma non sta in piedi. Sono finite in tre fogli di pagina diversi
   (home.css, catalog.css, experiences.css), identiche, perche' ognuno di quei
   fogli ne aveva bisogno e nessuno poteva scriverle qui.

   Tre regole copiate in tre posti sono tre regole che divergono al primo ritocco,
   ed e' esattamente il difetto che questo giro doveva togliere: fino a ieri il
   sito aveva DUE schede immobile diverse, una sulla home e una sul catalogo.
   ========================================================================= */

/* La fotografia tiene la sua proporzione: senza, una scheda con una foto verticale
   e una con una orizzontale hanno due altezze diverse e la griglia si scalza. */
.property-card__frame { aspect-ratio: 3 / 2; }

/* Il corpo si allunga fino in fondo alla scheda. E' quello che permette al
   `margin-top: auto` del finto bottone di spingerlo in basso: senza, in una
   griglia di schede di testo diverso i bottoni stanno a quattro altezze diverse. */
.property-card__body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

/*
    E IL BOTTONE E' UN FLEX, non un blocco.

    `.card__b > span { display: block }` (§10) lo batte per specificita' — e' due
    classi contro una — e un blocco non centra il proprio testo. La regola qui e'
    scritta con la stessa forza: la scheda porta tutte e due le classi, quindi
    `.card__b > .property-card__go` vale 0,2,0 come quella che deve superare, e
    vince perche' viene dopo.
*/
.card__b > .property-card__go,
.property-card__go { display: flex; }

/*
    L'OMBRA E IL SOLLEVAMENTO SE NE VANNO.

    `.card` (§10) posa un'ombra a riposo e solleva di 8px sotto il mouse: era il
    disegno di prima, e nel mockup le schede hanno un filetto e nient'altro. Si
    spegne QUI e non in un foglio di pagina, o la scheda tornerebbe a essere due
    cose diverse fra home e catalogo.
*/
.property-card,
.property-card:hover {
    box-shadow: none;
    transform: none;
}

/* I pittogrammi della riga dati — ospiti, superficie, camere. Erano disegnati
   solo nel foglio del catalogo, quindi sulla home la stessa scheda mostrava le
   etichette senza le loro icone. */
.property-card__fact {
    display: inline-flex;
    align-items: center;
    gap: .3125rem;
}

.property-card__fact svg {
    width: .9375rem;
    height: .9375rem;
    flex: 0 0 auto;
    fill: none;
    stroke: var(--accent);
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.property-card__data {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .375rem .875rem;
}

/* =========================================================================
   27.9  I RESIDUI DEL DISEGNO PRECEDENTE

   Quello che la rimappatura dei token non poteva raggiungere: valori letterali
   scritti a mano, forme che il mockup non ha, e una dichiarazione che la §27.1 ha
   reso invalida senza che nessuno se ne accorgesse.
   ========================================================================= */

/*
    PRIMA DI TUTTO: `--fill-sheen` E `--fill-glow` TORNANO OMBRE VALIDE.

    La §27.1 li aveva messi a `none` per spegnere il riflesso e l'alone dei bottoni
    del disegno precedente. Ma `none` dentro un ELENCO di ombre non e' un valore
    legale: `box-shadow: none, 0 2px 8px rgba(...)` e' una dichiarazione invalida, e
    il browser la scarta INTERA — compresa la seconda ombra, che invece serviva.

    Sono dieci dichiarazioni nel foglio, e fra loro ci sono i comandi del visore
    delle fotografie, che galleggiano sopra un'immagine e senza ombra non si
    staccano da niente. Erano sparite per sbaglio, non per disegno.

    Un'ombra trasparente e' invece perfettamente legale e non disegna niente: fa lo
    stesso lavoro di `none` senza portarsi via le altre.
*/
:root {
    --fill-sheen:  0 0 0 0 transparent;
    --fill-glow:   0 0 0 0 transparent;
    --fill-glow-2: 0 0 0 0 transparent;
}

/* Il filetto che separava la fascia azzurra dal bianco era scritto a mano in TEAL
   — `rgba(21, 95, 125, .10)` — quindi la rimappatura dei token non poteva
   vederlo: restava l'unica riga del vecchio colore su una pagina navy. */
.sec--blue { box-shadow: inset 0 1px 0 var(--hair); }

/*
    L'OCCHIELLO, UNO SOLO PER TUTTO IL SITO.

    Nel mockup e' maiuscoletto spaziato piccolo e grigio, senza nessun segno
    davanti. La §26 gli disegnava un trattino, che era la firma di un disegno in
    cui le bande erano capitoli numerati.

    Sta QUI e non nel foglio della home, dov'era finito: l'occhiello della home e
    quello del catalogo devono essere la stessa cosa, e due copie della stessa
    regola in due fogli divergono al primo ritocco.
*/
.snum {
    font-family: var(--t);
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--text-3);
}

.snum::before { content: none; }

/*
    UN RAGGIO SOLO PER TUTTI I CAMPI.

    `.form__field` era a 12px e `.sbox__i` a 4: due campi sulla stessa schermata —
    quelli del modulo e quelli della scheda di ricerca — con due forme diverse. La
    §3 della specifica ne vuole uno solo.
*/
.form__field,
.form__field:focus { border-radius: var(--radius-s); }

/* Le pastiglie perdono la forma a pillola: il mockup ha un raggio solo. */
.chip, .tag { border-radius: var(--radius-s); }

/*
    LE FOTOGRAFIE PERDONO LA CORNICE.

    «Le fotografie hanno lo stesso raggio del contenitore, senza cornice» — §3
    della specifica. Il disegno precedente le incorniciava con un anello interno
    per staccarle dal fondo bianco; il mockup non lo fa, e su una scheda che ha
    gia' un filetto tutto intorno quell'anello e' un secondo bordo a due pixel dal
    primo.
*/
.card__ph::after,
.experience-card__ph::after,
.pmosaic__tile::after { box-shadow: none; }

/*
    IL PREZZO PASSA ALLA SANS.

    Era in Fraunces, perche' nel disegno precedente il prezzo era l'elemento piu'
    forte della scheda e il serif glielo diceva. Nel mockup non c'e' un solo numero
    in serif: il serif e' dei titoli, i numeri e l'interfaccia sono in Inter. Il
    peso tiene il prezzo forte senza cambiargli alfabeto.
*/
.card__p {
    font-family: var(--t);
    font-weight: 600;
    letter-spacing: -0.015em;
}

/*
    IL BOTTONE FANTASMA SULLE BANDE SCURE.

    `.cta--ghost` e' un bottone di solo contorno, e sulle bande navy prendeva il
    contorno dal token del testo chiaro: un rettangolo quasi invisibile. Sulle
    bande scure diventa bianco di contorno, che e' quello che il mockup fa con i
    comandi secondari sopra una fotografia.
*/
.sec--dark .cta--ghost,
.sec--deep .cta--ghost,
.hero .cta--ghost {
    border-color: rgba(255, 255, 255, .45);
    color: #fff;
}

.sec--dark .cta--ghost:hover,
.sec--deep .cta--ghost:hover,
.hero .cta--ghost:hover {
    border-color: #fff;
    background: rgba(255, 255, 255, .10);
}

/*
    UN RAGGIO SOLO, DAVVERO.

    La §3 della specifica dice «6px per schede, campi e bottoni». Erano rimaste due
    misure: i campi a 6 e i bottoni a 4, perche' i bottoni leggevano `--radius-s`.
    Due raggi sulla stessa riga — il campo e il bottone che gli sta accanto dentro
    la scheda di ricerca — sono la differenza che si vede senza saper dire cosa si
    sta vedendo.
*/
.cta,
.sbox__i,
.sbox__go,
.property-card__go,
.form__field,
.form__field:focus { border-radius: var(--radius); }

/* =========================================================================
   27.10  QUATTRO CORREZIONI DOPO LA VERIFICA

   Trovate rileggendo il lavoro con le misure in mano, non guardandolo.
   ========================================================================= */

/*
    LA CORNICE NON BASTAVA SPEGNERLA: QUEL PSEUDO-ELEMENTO FA DUE COSE.

    La §27.9 ha tolto l'anello con `box-shadow: none`, e in `parts/experiences.css`
    e' stata cancellata la riga che invece lo spegneva del tutto — con la
    motivazione che il foglio comune la duplicasse. Non la duplicava: `::after`
    porta ANCHE un gradiente bianco in fondo alla fotografia, che sfumava
    l'immagine nel corpo della scheda. Spenta l'ombra e riacceso l'elemento, quel
    velo bianco e' tornato su ogni fotografia delle esperienze.

    Il mockup non sfuma niente: la fotografia finisce dove finisce, e sotto
    comincia il corpo della scheda con il suo filetto. Si spegne l'elemento, non
    una sua proprieta'.
*/
.card__ph::after,
.experience-card__ph::after { display: none; }

/*
    LE PASTIGLIE ERANO RIMASTE L'UNICA COSA A 4px.

    La §27.9 le ha date `--radius-s` e subito dopo ha portato campi e bottoni a
    `--radius`: due misure sulla stessa schermata, che e' esattamente il difetto
    che quella sezione voleva chiudere. Colpa mia, e si vede solo misurando —
    quattro pixel contro sei su una pastiglia alta ventidue.
*/
.chip, .tag { border-radius: var(--radius); }

/*
    LO STESSO COMPONENTE, UN CORPO SOLO.

    `.sbox__i` componeva a 14px sulla scheda di ricerca della home e a 16px negli
    stessi campi del catalogo: e' un componente solo, e due corpi diversi lo
    trasformano in due componenti che si somigliano.

    SEDICI E NON QUATTORDICI, ed e' una scelta e non una media: sotto i 16px iOS
    ingrandisce la pagina da solo quando si tocca un campo, e da li' non torna
    indietro. Un modulo che fa saltare lo zoom al primo tocco e' peggio di due
    pixel di differenza con il mockup.
*/
.sbox__i { font-size: 1rem; }

/*
    UNA SCHEDA, UNA SCALA.

    Misurato a 1440 sulla STESSA scheda: sulla home il corpo aveva 22/24/24 di
    riempimento, il titolo 20,8px e il prezzo 27,5; sul catalogo 16/18/18, 17 e
    21,6. E' un componente solo — da questo giro e' letteralmente lo stesso partial
    — e due scale lo rendono di nuovo due schede che si somigliano, che e' il
    difetto che questo giro doveva chiudere.

    VINCE LA SCALA PIU' PICCOLA, per due ragioni. E' quella del mockup, dove la
    scheda e' compatta e il titolo non compete con i titoli di sezione. Ed e'
    quella che regge una griglia da quattro colonne: la scala grande era tarata su
    tre.

    Scritta con `.property-card` davanti, cosi' vale ovunque la scheda compaia
    senza che ogni pagina debba ridichiararla.
*/
.property-card .property-card__body { padding: 18px 20px 20px; }

.property-card .property-card__title {
    font-size: 1.0625rem;
    line-height: 1.3;
}

.property-card .card__p {
    margin-top: 12px;
    font-size: 1.35rem;
    letter-spacing: -0.015em;
}

.property-card .card__p--request { font-size: 1.0625rem; }

/* =========================================================================
   27.11  LE SCHEDE SI FANNO PIU' FITTE

   Il mockup mette QUATTRO schede in riga sulla home, e le disegna compatte: la
   fotografia e' un quarto di riga, non un terzo. Qui la griglia si fermava a tre,
   e a 1440 ogni scheda era larga 421 pixel con una fotografia alta 280 — misurato,
   la scheda intera ne misurava 554, cioe' due terzi dello schermo per un solo
   annuncio.

   Una quarta colonna sopra i 1400 non e' una densita' forzata: e' quello che il
   disegno chiede, e sotto restano le tre di prima, che a 1100 sono giuste.

   Vale per gli immobili e per le esperienze insieme, perche' da questo giro sono
   la stessa scheda.
   ========================================================================= */
@media (min-width: 87.5rem) {
    .grid--3 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/*
    IL RITMO DENTRO LA SCHEDA.

    Misurato a 1440 su una scheda alta 507: la fotografia ne occupava 205 e il
    corpo 300 — piu' della fotografia, per cinque righe di testo. Il mockup fa il
    contrario: la fotografia domina e il corpo e' compatto.

    Non si toglie niente — il luogo, il prezzo, i dati e il riferimento servono, e
    il riferimento serve proprio quando si telefona. Si stringe il RITMO, che era
    quello di un disegno con tre schede larghe 421 e adesso ne ha quattro da 310.

    La riga dei dati portava 14 di margine PIU' 13 di riempimento sopra il filetto:
    ventisette pixel per staccare una riga da quella sopra. Dodici bastano, e il
    filetto fa gia' da stacco.
*/
.property-card .property-card__top { font-size: .8125rem; }

.property-card .property-card__title { margin-top: 6px; }

.property-card .card__p { margin-top: 8px; }

.property-card .card__d {
    margin: 10px 0 12px;
    padding-top: 10px;
    font-size: .8125rem;
}

/* =========================================================================
   27.12  LE ULTIME MISURE

   Trovate dal passaggio di rifinitura, e tutte e cinque stavano qui: sono token
   o componenti condivisi, quindi nessun foglio di pagina poteva chiuderle senza
   farle divergere.
   ========================================================================= */

/*
    IL GRIGIO DELLE DIDASCALIE ERA SOTTO IL MINIMO, e lo hanno misurato tutti e
    quattro separatamente.

    `--ink-3: #7a8698` da' 3,69:1 sul bianco, 3,50 sulla carta e 3,33 sulla banda
    azzurra: il minimo per il testo corrente e' 4,5. Non e' un caso limite — colora
    le meta delle schede, gli occhielli, le etichette dei campi della scheda di
    ricerca e le etichette della scheda dati: cioe' quasi tutto il testo piccolo
    del sito.

    `#66707f` e' il primo valore che passa su TUTTI E TRE i fondi chiari (5,01 /
    4,75 / 4,53) senza diventare un secondo colore del testo: a 15px di distanza
    dal nero dei titoli resta chiaramente «secondario».

    Sulle bande scure `--text-3` e' un'altra dichiarazione (bianco al 55%) e non
    c'entra.
*/
:root { --ink-3: #66707f; }

/*
    IL BERSAGLIO DI «VEDI TUTTI» ERA ALTO 31 PIXEL.

    Il minimo tattile che questo progetto si e' dato e' 44. La freccia e' definita
    in TRE punti del foglio — §3, §24 e §27 — e il pavimento vero veniva dal
    `min-height: 28px` del secondo, che nessuna delle altre due rialzava. Si chiude
    qui, dove vince, invece che in uno dei tre fogli di pagina che la usano.

    Il riempimento cresce solo in verticale: la freccia sta in fondo a una testata
    di sezione, e allargarla in orizzontale la staccherebbe dal bordo su cui e'
    allineata.
*/
@media (hover: none) {
    .arrow { min-height: 44px; padding-block: 12px; }

    /* Misurato 124 x 42,3: un pelo sotto, e basta il riempimento. */
    .cta--sm { min-height: 44px; }
}

/*
    L'ETICHETTA DELLA SCHEDA DATI ERA A 9,5 PIXEL.

    Il pavimento che il mockup si da' e' 11. Nove virgola cinque maiuscoletto
    spaziato non e' una scelta tipografica: e' la misura a cui un'etichetta smette
    di essere leggibile e diventa una texture.
*/
.sp dt, .dossier__label { font-size: .6875rem; }

/*
    IL TITOLO DI BANDA ARRIVAVA A 52,8 PIXEL.

    `h2 { clamp(1.9rem, 4.2vw, 3.3rem) }` e' la scala del disegno precedente, dove
    i titoli di banda erano capitoli. La specifica del mockup mette i titoli di
    sezione fra 28 e 32: a 1440 il tetto scende a 2rem, e il minimo resta quello
    che serve a un telefono.
*/
h2, .h2 { font-size: clamp(1.5rem, 2.2vw, 2rem); }
