/*
 * nachtblume.at — Austria by city.
 *
 * Not one class name in the file, and that is the point rather than an aesthetic. The
 * markup carries data-attributes it needs anyway — data-role says what a thing is,
 * data-state says how it currently stands — and every selector here reads one of those.
 * A class vocabulary is the most mechanical thing a competitor can grep across domains;
 * a site that has none cannot share one.
 *
 * Written atomically: each rule sets as few properties as it can and the cascade does the
 * composing, so a value lives in one place. Neighbours variously keep their CSS inside the
 * layout, write modules, write BEM, or compose utility classes — which
 * of those a site does is one of the axes they are kept apart on.
 *
 * The name is a night flower, and the sheet follows it: muted greens, a pale gold for the
 * one thing that should be looked at, and corners rounded on two of four — a petal is not
 * a rounded rectangle.
 */

/* ---- ground ------------------------------------------------------------- */

/*
 * Charcoal ground, green kept for the things that are green: links and the mark.
 *
 * The first palette put the same muted green on the ground, the masthead, the wordmark
 * and the links at once — and a green mark on a green bar is a mark nobody sees. Worse
 * for a catalogue of people: photographs are the page, and a coloured ground tints every
 * one of them. A neutral dark leaves the faces the only colour above the fold.
 *
 * The greens below are unchanged in hue and only ever used as ink, so the site still
 * reads as its name — a night flower — rather than as a green room.
 */
:root {
    --night: #14161a;
    --leaf: #1c1f24;
    --edge: #2b2f36;
    --paper: #eceef1;
    --dim: #a2a8b2;
    --stem: #7fc08f;
    --pollen: #d8c98a;
    /* Two corners of four, everywhere. Written once so the shape is a decision rather
       than a value retyped at every box — and so flipping the diagonal is one edit. */
    --petal: 14px 3px 14px 3px;
}

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

body {
    /* A column at least as tall as the window, with the sheet taking the slack: without
       it the colophon sits wherever the content happened to end, and a short page — the
       crossing, a 404, an empty city — leaves a band of ground below the footer. */
    min-height: 100vh;
    display: flex;
    flex-direction: column;

    background: var(--night);
    color: var(--paper);
    /* A humanist face, asked for by name from what the reader already has. Nothing is
       fetched: a webfont is one request every page of every site makes to one host. */
    font: 16px/1.7 "Segoe UI", Candara, "Gill Sans", Optima, "Trebuchet MS", sans-serif;
}

a { color: var(--stem); text-decoration: none; }
a:hover { color: var(--pollen); }
img { display: block; max-width: 100%; }

h1 { font-size: clamp(1.5rem, 4vw, 2.1rem); font-weight: 600; line-height: 1.25; }
h2 { font-size: 1.1rem; font-weight: 600; letter-spacing: 0.02em; }
h1, h2 { margin-block: 0 0.6em; }
p { margin-block: 0 1em; }

/* Focus stays visible and stays off the page edge — the outline is what a keyboard
   reader navigates by, and the default one disappears against a dark background. */
:focus-visible { outline: 2px solid var(--pollen); outline-offset: 3px; }

/* ---- masthead ----------------------------------------------------------- */

[data-role="masthead"] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* Allowed to wrap: the wordmark and the two links together come close to the width
       of a small phone, and a flex item does not shrink below its content — one long
       word would otherwise push the page wider than the screen. */
    flex-wrap: wrap;
    gap: 12px 20px;
    padding: 18px 22px;
    border-bottom: 1px solid var(--edge);
    background: var(--leaf);
}

[data-role="wordmark"] {
    display: flex;
    align-items: center;
    gap: 9px;
    /* Paper, not stem. Not a contrast fix — the green measured 7.74:1 against the bar and
       passed AAA. It is a fix for the mark reading as decoration: green lettering on a
       green-tinted masthead sat at the same weight as the petal beside it, and the name
       stopped being the loudest thing in the corner. Paper puts the word first and leaves
       the colour to the flower, where nothing has to be read off it. */
    color: var(--paper);
    /* Lowercase as a decision, not as typed text: the name is written in capitals
       everywhere it is prose, and only the mark is quiet. */
    text-transform: lowercase;
    font-size: 1.25rem;
    font-weight: 600;
    letter-spacing: 0.04em;
}

/* The flower keeps the green; the word does not. A shape carries a low-contrast colour
   where lettering cannot — nothing has to be read off it. */
[data-role="mark"] { color: var(--stem); }

[data-role="menu"] {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 18px;
    font-size: 0.92rem;
}

[data-role="menu"] a { color: var(--dim); }
[data-role="menu"] a:hover { color: var(--paper); }
[data-role="menu"] a[data-state="current"] { color: var(--paper); box-shadow: 0 1px 0 var(--pollen); }

/* ---- the split ---------------------------------------------------------- */

[data-role="sheet"] {
    width: 100%;
    max-width: 1080px;
    margin: 0 auto;
    padding: 26px 22px 60px;
    /* Takes the slack in the body column, so the colophon is pushed to the bottom of the
       window on a short page rather than floating in the middle of it. */
    flex: 1;
}

/* One column by default; two only where the page opened a narrow one. Keyed on the
   child rather than on data-page, so a page gets the second column by filling it and
   never by being named in this file. */
@media (min-width: 900px) {
    [data-role="sheet"]:has([data-column="narrow"]) {
        display: grid;
        /* The narrow column is fixed and the wide one takes the rest: a fractional
           split would let the figures column grow with the window, and a rate card
           600px wide is a table of two words and a lot of air. */
        grid-template-columns: minmax(0, 1fr) 260px;
        gap: 44px;
        align-items: start;
    }

    [data-column="narrow"] { position: sticky; top: 24px; }
}

/* Without min-width the wide column refuses to shrink below its widest child — one long
   profile name or an unbroken URL and the whole grid grows past the viewport. */
[data-column="wide"] { min-width: 0; }

[data-column="narrow"] {
    min-width: 0;
    margin-top: 34px;
    font-size: 0.94rem;
}

@media (min-width: 900px) {
    [data-column="narrow"] { margin-top: 0; }
}

/* ---- opening ------------------------------------------------------------ */

/*
 * A heading and a sentence, and then the listing. There was a large opening photograph
 * here and it has been removed: it was the first card of the list at four times the size,
 * so it told the reader nothing the list does not, and it pushed the listing itself off
 * the first screen. Everything that belonged to it — the two-column placement, the 300px
 * track — went with it.
 */
[data-role="lead"] { color: var(--dim); max-width: 58ch; }

/* ---- listings: <dl> pairs ----------------------------------------------- */

/*
 * A listing here is a definition list, not a grid of cards: the name is the term and
 * what is known about it is the description. The grid is on the <dl> and the pairs are
 * placed into it, so a name and its detail stay in one cell without a wrapper element
 * between them — which is what would turn this back into a card.
 */
[data-role="entries"] {
    display: grid;
    /* min() so the track can go below its floor on a narrow phone. At a plain 150px a
       390px screen minus 44px of padding cannot fit two columns and the grid collapses
       to one, leaving a single 3:4 thumbnail filling the whole screen. */
    grid-template-columns: repeat(auto-fill, minmax(min(150px, 100%), 1fr));
    gap: 20px 16px;
    margin-block: 18px 30px;
}

/*
 * A pair is ONE grid item: `<div data-role="entry">` holding the `dt` and its `dd`.
 *
 * Without it the two halves are siblings and a grid fills its cells in source order, so
 * the photograph lands in one column and its age in the next, beside somebody else's
 * face. The `dl` stays a `dl` — a `div` between the list and its pairs is allowed by the
 * definition-list grammar precisely so a pair can be grouped.
 *
 * ⚠️ `grid-auto-flow: column` was tried instead and is WRONG, though it does pair them:
 * with the flow going sideways the grid never wraps, so twenty profiles become twenty
 * columns and the page scrolls off to the right, dragging every neighbouring block with
 * it. The pair was fixed and the listing broke. Do not put it back.
 */
[data-role="entry"] { min-width: 0; }

[data-role="entry-name"] { min-width: 0; }

[data-role="entry-name"] a {
    display: block;
    color: var(--paper);
}

[data-role="entry-name"] img {
    width: 100%;
    height: auto;
    aspect-ratio: 3 / 4;
    object-fit: cover;
    border-radius: var(--petal);
    background: var(--leaf);
}

/*
 * A card whose photograph is missing keeps the same box anyway. `previewAlias` is
 * nullable, the `img` sits behind an `{% if %}`, and without a stand-in that card has no
 * picture at all: its name floats to the top of the row while its neighbours' names sit
 * 200px lower, against a column of faces. Drawn rather than fetched — an empty frame in
 * the same ink as a loading one.
 */
[data-role="entry-name"] a::before {
    content: "";
    display: block;
    aspect-ratio: 3 / 4;
    border-radius: var(--petal);
    background: var(--leaf);
}

/* …unless the photograph is there, in which case the image itself is the box. */
[data-role="entry-name"] a:has(img)::before { display: none; }

[data-role="entry-name"] span {
    display: block;
    margin-top: 7px;
    font-weight: 600;
    /* Names are data and vary without limit; the tile cannot be sized for the longest
       one, so the longest one is cut instead of pushing its neighbour off the row. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

[data-role="entry-name"] a:hover img { opacity: 0.85; }

/*
 * The neighbours are a footnote, not a second catalogue: four faces at the size the
 * listing uses, however wide the column they land in.
 *
 * Without a ceiling `1fr` shares the whole width between four tiles — 180px each in the
 * wide column, and a 3:4 thumbnail at that width is 240px tall, so the tail of the page
 * outweighs the person it belongs to. Measured before this line: 477px tiles, 1094px of
 * block. `auto-fill` fills the row and stops at the size; nothing stretches.
 */
[data-role="nearby"] [data-role="entries"] {
    grid-template-columns: repeat(auto-fill, minmax(min(120px, 100%), 150px));
    justify-content: start;
}

/* Inside its own pair now, so it simply follows its term. */
[data-role="entry-detail"] {
    margin-top: 2px;
    color: var(--dim);
    font-size: 0.86rem;
}

/* An empty listing is the same element in another state — no second name for it. */
[data-role="entries"][data-state="empty"] {
    display: block;
    padding: 22px;
    border: 1px solid var(--edge);
    border-radius: var(--petal);
    color: var(--dim);
}

/* ---- places, rates, traits: two-column pairs ---------------------------- */

[data-role="places"],
[data-role="rate"],
[data-role="traits"] {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 2px 12px;
    margin-bottom: 24px;
}

[data-role="places"] dt,
[data-role="rate"] dt,
[data-role="traits"] dt {
    min-width: 0;
    padding: 6px 0;
    border-top: 1px solid var(--edge);
}

[data-role="places"] dd,
[data-role="rate"] dd,
[data-role="traits"] dd {
    padding: 6px 0;
    border-top: 1px solid var(--edge);
    color: var(--dim);
    text-align: right;
    /* Figures line up column-wise only if they are tabular; proportional digits make a
       column of counts look ragged. */
    font-variant-numeric: tabular-nums;
}

[data-role="places"] dt a { color: var(--paper); }

/* The index page shows a note under each city; the sidebar version does not, so the
   note is what the wider treatment keys on rather than a separate list name. */
[data-role="place-note"] {
    display: block;
    color: var(--dim);
    font-size: 0.84rem;
    line-height: 1.5;
}

[data-role="places"][data-state="index"] { font-size: 1rem; }
[data-role="places"][data-state="index"] dt { padding-block: 12px; }
[data-role="places"][data-state="index"] dd { padding-block: 12px; }

/* ---- trail and paging --------------------------------------------------- */

[data-role="trail"] {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 14px;
    color: var(--dim);
    font-size: 0.86rem;
}

[data-role="paging"] {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 26px;
}

/* ---- buttons ------------------------------------------------------------ */

/*
 * Buttons are links in a state, so they are styled by that state. Three appear:
 * `go` carries the page forward, `quiet` offers the way that is not the point, and
 * `call` is the one thing on a profile the site wants pressed.
 */
[data-state="go"],
[data-state="quiet"],
[data-state="call"],
[data-role="paging"] a {
    display: inline-block;
    padding: 10px 18px;
    border: 1px solid var(--edge);
    border-radius: var(--petal);
    color: var(--paper);
    font-size: 0.94rem;
}

[data-state="go"],
[data-state="call"],
[data-role="paging"] a:not([data-state="quiet"]) {
    border-color: transparent;
    background: var(--stem);
    color: var(--night);
    font-weight: 600;
}

[data-state="go"]:hover,
[data-state="call"]:hover,
[data-role="paging"] a:not([data-state="quiet"]):hover {
    background: var(--pollen);
    color: var(--night);
}

[data-state="quiet"] { color: var(--dim); }
[data-state="quiet"]:hover { color: var(--paper); border-color: var(--stem); }

/* ---- gate pages: verify, interstitial, 404 ------------------------------ */

[data-role="gate"] {
    max-width: 34rem;
    margin: 8vh auto;
    padding: 30px 26px;
    border: 1px solid var(--edge);
    border-radius: var(--petal);
    background: var(--leaf);
    text-align: center;
}

[data-role="gate"] p { color: var(--dim); }

/* The portrait keeps the crossing part of the profile rather than a screen in front of
   it. Asymmetric corners, as the `radius` axis has it — a petal is not a rounded box. */
[data-role="gate-portrait"] {
    width: 170px;
    aspect-ratio: 3 / 4;
    margin: 0 auto 16px;
    overflow: hidden;
    border-radius: var(--petal);
    background: var(--tint, var(--edge));
}

[data-role="gate-portrait"] img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

[data-role="gate-who"] { margin: 0 0 18px; }

[data-role="gate-who"] dt {
    font-weight: 600;
    font-size: 1.05rem;
}

[data-role="gate-who"] dd {
    margin: 2px 0 0;
    color: var(--dim);
    font-size: .9rem;
}

/* The line naming what is ahead reads before the neutral one about the foreign site: it
   is the answer to "what will I see", and the visitor is deciding right here. */
[data-role="ahead"] {
    margin-block: 0 14px;
    color: var(--ink, inherit);
}

[data-role="choices"] {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    justify-content: center;
    margin-block: 22px 14px;
}

[data-role="note"] { font-size: 0.84rem; }

/* ---- prose pages -------------------------------------------------------- */

[data-page="prose"] [data-column="wide"] { max-width: 62ch; }
[data-page="prose"] h2 { margin-top: 1.6em; color: var(--stem); }

/* ---- profile: gallery and viewer ---------------------------------------- */

[data-role="gallery"] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(150px, 100%), 1fr));
    gap: 12px;
    margin-block: 18px 30px;
}

[data-role="gallery"] img {
    width: 100%;
    height: auto;
    aspect-ratio: 3 / 4;
    object-fit: cover;
    border-radius: var(--petal);
    background: var(--leaf);
}

[data-role="gallery"] a:hover img { opacity: 0.85; }

/*
 * `margin: auto` is what centres a modal dialog, and the reset at the top of this sheet
 * sets `margin: 0` on everything — so the viewer opened pinned to the top-left corner
 * with the photograph hanging out of it and the backdrop dimming only the far side.
 * Restored explicitly rather than by exempting `dialog` from the reset: the reset is the
 * one rule every other element here depends on.
 *
 * `display: grid` alongside, because `overflow: visible` on a box whose child is capped
 * at 88vh let the arrows sit outside the dialog and outside its hit area.
 */
[data-role="viewer"] {
    margin: auto;
    display: grid;
    place-items: center;
    max-width: 96vw;
    max-height: 96vh;
    padding: 0;
    border: 0;
    border-radius: var(--petal);
    background: transparent;
    overflow: visible;
}

/* A dialog that is not open must not take part in the grid above: `display: grid` on the
   element itself overrides the browser's `display: none` for the closed state, and the
   viewer would sit in the page as a stray picture. */
[data-role="viewer"]:not([open]) { display: none; }

[data-role="viewer"]::backdrop { background: rgb(10 14 11 / 0.92); }

[data-role="viewer"] img {
    max-width: 96vw;
    /* Room left below for the arrows, which sit inside the dialog: at 96vh the controls
       would be pushed off the bottom of a short landscape screen. */
    max-height: 88vh;
    width: auto;
    height: auto;
    margin: 0 auto;
    border-radius: var(--petal);
}

[data-role="viewer"] button {
    position: absolute;
    border: 0;
    border-radius: 50%;
    background: rgb(32 42 34 / 0.9);
    color: var(--paper);
    /* Forty-four square is the smallest target a thumb hits reliably; the glyphs inside
       are much smaller than that and would otherwise be a target only a mouse can use. */
    width: 44px;
    height: 44px;
    font-size: 1.6rem;
    line-height: 1;
    cursor: pointer;
}

[data-role="viewer"] button:hover { background: var(--stem); color: var(--night); }
[data-role="viewer"] button[value="prev"] { left: 10px; top: 50%; transform: translateY(-50%); }
[data-role="viewer"] button[value="next"] { right: 10px; top: 50%; transform: translateY(-50%); }
[data-role="viewer"] button[value="close"] { right: 10px; top: 10px; font-size: 1.3rem; }

/* ---- profile: contact and reviews --------------------------------------- */

[data-role="contact"] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-bottom: 26px;
}

[data-role="teaser"] {
    min-width: 0;
    color: var(--dim);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.06em;
}

[data-role="reviews"] { margin-block: 14px 30px; }

[data-role="review-head"] {
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid var(--edge);
    font-weight: 600;
}

[data-role="review-body"] { color: var(--dim); }
[data-role="review-body"] p { margin-block: 6px 0; }

[data-role="review-by"] { font-size: 0.84rem; opacity: 0.8; }

[data-role="review-reply"] {
    margin-top: 10px !important;
    padding-left: 14px;
    /* The reply is indented by a rule on one side rather than by a box: it is the same
       kind of text as the review above it, one step in. */
    border-left: 2px solid var(--edge);
    font-size: 0.9rem;
}

/* ---- colophon ----------------------------------------------------------- */

[data-role="colophon"] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px 20px;
    padding: 22px;
    border-top: 1px solid var(--edge);
    background: var(--leaf);
    color: var(--dim);
    font-size: 0.86rem;
}

[data-role="colophon"] p { margin: 0; }

/* Motion is decoration here — the hover fades and nothing else — so honouring the
   preference costs the page nothing. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { transition: none !important; animation: none !important; }
}
