/* Shared shell for every page: palette, typography, header, footer, buttons, image
 * frames and the grid primitives.
 *
 * This used to be five near-identical <style> blocks, one per page, ~80% duplicated. As
 * one external file it is fetched once and then served from cache for the rest of the
 * visit, and — because no page carries inline CSS or a style="" attribute any more — the
 * CSP can say style-src 'self' with no 'unsafe-inline'.
 *
 * Page-specific rules live in assets/<page>.css, loaded after this file so the cascade
 * resolves the handful of deliberate per-page overrides (.btn.saf, .sec-lead widths).
 *
 * Mobile-first: the unprefixed rules are the phone layout, and min-width queries add the
 * wider ones. That ordering is why the phone — the slowest device and the smallest
 * viewport — never has to parse rules meant for a desktop and then undo them.
 */

/* fonts.css is linked separately from the HTML rather than @import-ed here. An @import
 * cannot start until this file has been fetched and parsed, which would serialise two
 * round trips before any text can paint; two <link> tags fetch in parallel. */

:root{
  /* Pantone-based palette — Martini Olive ground.
   *
   * The ground was #726A4E. At that value the palette's own accents did not meet WCAG AA
   * against it: paprika text measured 1.28:1 (the garment PRICES were effectively
   * invisible), the "View on Vinted" call to action 2.62:1, and all body copy 3.47:1
   * against a 4.5:1 requirement.
   *
   * Darkening the ground rather than lightening each accent is the deliberate choice:
   * there are three accent hues sitting on it, and lightening them individually washes
   * the spice palette out toward cream and loses what makes the design its own. A deeper
   * olive keeps every accent's chroma and lifts all of them at once. It is also already
   * in the visual language — the frames and vignettes use #3D2B24 chicory.
   *
   * To revert the whole thing, put --void back to #726A4E (and --void-2/-3 to #655D43 /
   * #585138). Nothing else depends on the new values.
   */
  --void:#4A4433; --void-2:#413C2D; --void-3:#383426;

  /* CHROME: the header and the footer, one colour, 24 Aug 2026.
   *
   * The header used to be rgba(74,68,51,.94) — which is --void at 94% ON --void, compositing
   * to #4A4433 exactly. The bar was measurably the same colour as the page it floated over:
   * 1.0:1. The backdrop blur and the alpha were both doing nothing visible, and a 1px border
   * was the only thing marking where the page stopped and the chrome began. The footer was
   * barely better at 1.28:1 — LESS separation than a section band gets (1.36:1).
   *
   * #221F16 is 1.70:1 against the body and 1.25:1 against the band, which is the point: it
   * has to clear BOTH, or a banded section sitting above the footer bleeds into it. The
   * darker ground also lifts every colour that sits on the chrome — nav links from 4.89:1 to
   * 7.25:1, magenta from 4.70:1 to 7.97:1.
   *
   * Header and footer share it deliberately. Two near-identical darks 1.07:1 apart are not a
   * hierarchy anyone perceives, they are two values to keep in step. */
  --chrome:#221F16;

  /* Alternating section band. Sections were separated only by a 1px dark rule that is
   * effectively invisible on a dark ground, so the page read as one undifferentiated
   * column.
   *
   * This is a VALUE step in the same warm olive family, not a complementary hue, and that
   * was measured rather than assumed: a literal complement of the ground is a slate blue
   * (#33394A), which separates from the ground by only 1.19:1 — LESS distinct than this
   * warm option at 1.36:1 — while introducing a cool hue that fights paprika, saffron and
   * amber and undercuts the spice identity the whole design is built on. A darker band of
   * the same hue reads as "new section" immediately and lifts text contrast on top of it
   * (body copy goes from 6.22:1 to 8.44:1).
   *
   * If a cool band is ever wanted, this is the one variable to change. */
  --band:#353022;

  /* paprika is a FILL colour, not a text colour. It is a mid-luminance saturated red that
     cannot reach 4.5:1 on any ground dark enough to be readable — it tops out around
     3.5:1 even on near-black. So it keeps its job as a background (the Czech-In label,
     button fills) where dark text sits ON it, and small accent text uses --magenta
     instead, which clears AA at 5.57:1 here. */
  --paprika:#D3502F; --chilli:#B8431F; --saffron:#DCE775; --turmeric:#B8431F;
  /* magenta was #E8A93C, which measured 4.70:1 on --void — over the 4.5 AA floor, but only
     just, and it is the accent for small text across the whole site. On a DARK ground an
     accent gains contrast by getting lighter, not darker: #F0BC5C reads 5.57:1 on the body,
     7.55:1 on the band and 9.45:1 on the chrome, and carries --void on top of it at 5.57:1
     for the filled hover states. Same amber, a comfortable margin instead of a graze. */
  --magenta:#F0BC5C; --olive:#DCE775; --bone:#F2EFE6; --bone-dim:#D6CFB8;
  --avocado:#8A8B32; --chicory:#3D2B24; --tangerine:#D3502F; --lime:#DCE775;

  /* Single source of truth for the gutter, so the header, sections and footer stay in
     line when it tightens on a phone. */
  --gutter:20px;

  /* ---------- type scale ----------
   *
   * Eight steps, replacing the twenty fixed px sizes and ten separate clamp() expressions
   * that had accumulated across these stylesheets — 11.5, 12.5, 13.5, 14.5, 15.5 and 16.5
   * all existed, a half-pixel apart, doing nothing different from each other. Steps that
   * small are not a scale; they are decisions taken one at a time, months apart.
   *
   * In rem, and that is the point of the exercise. Every size on this site used to be px,
   * which browser ZOOM scales fine — but the separate "Font size" preference in Chrome,
   * Firefox and Safari does nothing to px text at all. A visitor who has moved that off 16
   * because they are tired of squinting was still being handed 14px here. 1rem is whatever
   * they set. Structural padding stays in px on purpose: it should not grow with the text.
   *
   * The fluid steps use clamp(min, rem + vw, max) rather than plain vw so they still
   * respond to that preference at every viewport width — a pure-vw heading ignores it.
   */
  --fs-2xs:.75rem;     /* 12px — captions, tags, chips, crumbs, counts, the privacy line */
  --fs-xs:.8125rem;    /* 13px — nav, buttons, footer links, small labels */
  --fs-s:.875rem;      /* 14px — card titles, dense secondary prose */
  --fs-m:1rem;         /* 16px — THE BODY DEFAULT, and every form control */
  --fs-l:1.0625rem;    /* 17px — long-form body and page ledes */
  --fs-xl:clamp(1.25rem,.95rem + 1.4vw,1.625rem);   /* 20 → 26 */
  --fs-2xl:clamp(1.5rem,1.1rem + 2.2vw,2.125rem);   /* 24 → 34 */
  --fs-3xl:clamp(2rem,1.1rem + 4.6vw,3.75rem);      /* 32 → 60 */

  /* Leading rises with line length and falls with size. */
  --lh-tight:1.15;   /* display headings */
  --lh-snug:1.45;    /* card titles, short labels */
  --lh-read:1.65;    /* body copy */
  --lh-long:1.72;    /* the article, which is the longest measure on the site */

  /* Two faces, two jobs. See the .prose rule below for which text gets which. */
  --mono:'IBM Plex Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
  --sans:'IBM Plex Sans',ui-sans-serif,system-ui,-apple-system,'Segoe UI',sans-serif;

  /* ---------- measure ----------
   *
   * These are NOT the ch values the site used to carry, and the difference is the whole
   * point. `ch` is the width of the digit zero, and in a MONOSPACE face that is also the
   * width of every other character — so `max-width:66ch` on mono put exactly 66 characters
   * on a line. In a proportional face the zero is one of the widest glyphs there is: at
   * 66ch, Plex Sans fits 83 characters, measured. Every measure on the site was silently
   * 25% too wide the moment the prose changed face.
   *
   * Calibrated against Plex Sans at --fs-l: 9.6px per ch, 7.64px per average character.
   * 54ch lands at about 68 characters and 48ch at about 60 — inside the 55-75 band where
   * reading speed and comprehension both hold up, rather than the width at which people
   * start skimming in an F-pattern.
   */
  --measure:54ch;     /* ~68 characters — long-form body */
  --measure-s:48ch;   /* ~60 characters — ledes, notes, captions under a heading */
}

*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}

body{
  background:var(--void);
  color:var(--bone);
  font-family:var(--mono);
  font-size:var(--fs-m);
  line-height:var(--lh-read);
  -webkit-font-smoothing:antialiased;
  position:relative;
  overflow-x:hidden;
}

/* ---------- the two jobs text does here ----------
 *
 * Almost everything on this site is SCANNED — the wordmark, the nav, prices, tags,
 * captions, section headings, button labels. Monospace is excellent at that: fixed advance
 * widths make columns line up and labels read as machined rather than typed. That is the
 * whole visual idea and it does not move.
 *
 * A smaller share is READ — the article, a restaurant review, a product description, the
 * lede under a page title. Monospace is measurably worse at that: every glyph occupies the
 * same advance, so the outline of a word — the thing a fluent reader recognises before
 * decoding any letter — flattens into a picket fence.
 *
 * shop.css made this call for product blurbs and page ledes and left a note saying the
 * diary, restaurant and article pages "keep the all-mono setting until they are looked at
 * properly". This is that, promoted here and extended to the rest of the site. Plex Sans is
 * Plex Mono's sibling, drawn by the same team on the same skeleton, so the two sit together
 * rather than looking like an accident.
 *
 * text-wrap:pretty is the other half of the job: it is what stops a paragraph ending on a
 * single orphaned word. Browsers that do not support it ignore the declaration.
 */
.prose,
.lede, .sec-lead, .sub,
.rev p, .rev .order, .note p,
.post p, .plate p,
.entry-body p, .entry-body li,
.cat-note, .group-note, .shop-cta p,
.guide-body p, .manifesto p, .vinted-cta p,
.holding p, .how-steps li, .how-note,
.notfound .sub, .foot-lede{
  font-family:var(--sans);
  font-size:var(--fs-m);
  line-height:var(--lh-read);
  text-wrap:pretty;
}

/* A heading that breaks with one word alone on the last line looks like a mistake, and on
   this site — where h1 runs to 60px — it looks like a large one. Ignored where unsupported. */
h1,h2,h3,.nm{text-wrap:balance}

/* Form controls inherit nothing by default, and Safari on iOS zooms the whole viewport
   whenever a focused field is under 16px. The wardrobe's search box and five filter selects
   were 12.5px, so tapping the most-used control on the site threw the visitor into a zoomed
   page they then had to pinch back out of. 1rem is the fix and the only fix. */
input,select,textarea,button{font-family:inherit;font-size:var(--fs-m)}

/* Full-page film grain. Fixed and pointer-events:none so it never intercepts a tap. */
body::before{
  content:"";position:fixed;inset:0;z-index:9999;pointer-events:none;
  opacity:.055;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

a{color:inherit;text-decoration:none}
::selection{background:var(--magenta);color:var(--void)}

/* Keyboard users need to see where they are. The design has no focus styling at all
   otherwise, and :focus-visible keeps it off mouse clicks. */
:focus-visible{outline:2px solid var(--magenta);outline-offset:3px}

img{max-width:100%;height:auto}

/* THE PAGE WIDTH, one token (24 Aug 2026).
 *
 * Was a hard 1280px, which on a 1920 display left 320px of dead margin down each side and
 * capped the shelves at three product cards per row — a shop showing three things across
 * two-thirds of the screen. 1600 is the ceiling because past roughly that width a row of
 * cards costs more eye travel than the extra card is worth; it is a reading limit, not a
 * technical one.
 *
 * The header and the footer read the same token, so the wordmark can never sit inboard of
 * the content beneath it. That drift is the reason this is a variable and not three
 * numbers. */
:root{--wrap:1600px;--head-col:820px}
.wrap{
  max-width:var(--wrap);margin:0 auto;width:100%;
  /* max() not calc(): in landscape on a notched phone the inset can exceed the gutter, and
     the content must clear the notch; in portrait, and everywhere else, both insets are 0
     and this is exactly var(--gutter). */
  padding-left:max(var(--gutter),env(safe-area-inset-left,0px));
  padding-right:max(var(--gutter),env(safe-area-inset-right,0px));
}

/* Lets a keyboard or screen-reader user jump past the header nav. Off-screen until
   focused. */
.skip{
  position:absolute;left:-9999px;top:0;z-index:200;
  background:var(--magenta);color:var(--void);padding:10px 16px;
  font-size:var(--fs-2xs);letter-spacing:.06em;text-transform:uppercase;
}
.skip:focus{left:0}

/* Text only a screen reader sees. Used to carry the "opens on Vinted, new tab"
   warning that the repeated visible "View on Vinted" labels used to carry. */
.vh{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;
}

/* The header is sticky, so an anchor jump would otherwise land with the target's first
   line hidden underneath it. Applied globally rather than per-id so any anchor added
   later gets the clearance for free. */
html{scroll-behavior:smooth}
:target,[id]{scroll-margin-top:88px}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

/* ---------- header ---------- */

header.site{
  border-bottom:1px solid rgba(242,239,230,.18);
  position:sticky;top:0;z-index:100;
  /* SAFE AREA. The viewport meta now carries viewport-fit=cover, so the page fills the
     screen including the band behind the status bar and the notch. Without this padding the
     bar would stop below that band and page content would scroll through it — the same
     symptom this file was already fixing, from a different cause.
     env() falls back to 0px everywhere that has no inset, so this is inert on desktop.
     It also makes the env(safe-area-inset-bottom) rule in shop.css start resolving to a
     real value; it was written before the page opted into the safe area and had been a
     no-op since. */
  padding-top:env(safe-area-inset-top,0px);
  /* OPAQUE, AND NO backdrop-filter. Both were here until 24 Aug 2026 and both had to go.
   *
   * On iPhone Safari the page painted THROUGH and ABOVE this bar: the hero's saffron
   * highlight drew as sharp glyphs in the strip above the header while the page scrolled.
   * Sharp glyphs are a paint-order failure, not translucency — 6% alpha tints, it does not
   * redraw text.
   *
   * The cause is an interaction, not a single rule. backdrop-filter renders by sampling
   * everything painted beneath it, and body::before is a fixed, full-viewport grain layer
   * with mix-blend-mode:overlay at z-index 9999 (see the note there). A blend layer over the
   * whole page forces one blending group, and WebKit then hands the backdrop-filtered
   * element the wrong snapshot to sample. The bar is the one element on the site doing both
   * things at once, which is why it is the one element that broke.
   *
   * Opaque removes the class of bug rather than a symptom of it: with nothing to sample
   * there is no snapshot to get wrong, on any engine or version. What is lost is the soft
   * darkening as content passed under the bar — a nicety two days old. The separation the
   * bar actually needs comes from --chrome being 1.70:1 against the page, which is
   * untouched.
   *
   * If the blur is ever wanted back, it cannot come back alone: the grain overlay would have
   * to go, or the header would need to be lifted out of that blending group. */
  background:var(--chrome);
}

/* One row at every width: brand | section links | contact icons. The bar used to wrap onto
   two rows below 641px, because the brand shared row one with a coordinate stamp and left
   the nav no space. The stamp is gone, and the icons now sit outside nav.main so they stay
   pinned to the right edge even when the links themselves have to scroll. */
.bar{
  display:flex;align-items:center;
  gap:10px;min-height:52px;
  flex-wrap:nowrap;
  /* The phone nav panel hangs off this box with position:absolute; top:100%. */
  position:relative;
}

.brand{
  flex:0 0 auto;
  /* Enlarged 24 Aug 2026, without touching the 52/64px bar. The room was there vertically —
     the box was 44px around 17px of text — so the gain comes from line-height:1 tightening
     the line box and the font growing into the space that frees. The binding constraint is
     WIDTH, not height: at 375px the bar already carries the menu button (44), the language
     pill (71), WhatsApp (41), gaps (30) and gutters (40), leaving ~149px. 15px lands the
     wordmark near 126px and keeps ~23px of slack for a longer language name in the pill;
     16px was measured at ~135px and spends nearly all of it, which is a squeeze one
     translation away from wrapping.
     RE-MEASURED 25 Aug 2026, and the earlier figure was pessimistic. Laid out at 375px the
     bar spends 272px on its four controls, 30px on gaps and 40px on gutters, leaving 35px
     genuinely free. 16px costs 9 of it and the wider WhatsApp target below costs 13, so 13
     remain — enough for a longer language code, and the pill is the only thing that grows
     with translation. */
  font-weight:600;font-size:1rem;line-height:1;letter-spacing:0;text-transform:uppercase;
  /* The wordmark is two words and one slash in a monospace face — 16 characters, 101px on
     a 375px bar. Pulling the spaces in around the slash reads as a tighter lockup rather
     than a smaller logo. */
  word-spacing:-.34em;
  /* Without this the three words wrapped to two lines inside a fixed-height bar and
     spilled over the border. */
  white-space:nowrap;
  /* The wordmark is a link home, and it was 20px tall on a phone and 23px on a desktop —
     under the 24px WCAG 2.2 target minimum at every width, which is UX-AUDIT item 7.2.
     The text cannot grow without eating the bar, so the TARGET grows instead: the box is
     centred on the same baseline and only its hit area changes. */
  display:flex;align-items:center;min-height:24px;
}
.brand b{color:var(--magenta)}

/* The mobile nav: a horizontal strip that scrolls if the links outrun the viewport.
   Chosen over a hamburger because there are only four links — a disclosure widget would
   add a tap to reach every one of them, and needs JavaScript this site otherwise does
   not have (script-src is 'none'). */
nav.main{
  display:flex;gap:14px;
  /* margin-left:auto rather than justify-content:flex-end. With flex-end, a nav too narrow
     for its labels overflows towards its START edge — the first link slides left underneath
     the wordmark, overlapping it and staying unreachable, because a scroll container cannot
     scroll backwards past its origin. Pushing the whole box right with auto margin instead
     keeps the labels laid out from the left, so the overflow lands at the right edge where
     overflow-x can actually scroll to it. */
  flex:0 1 auto;min-width:0;margin-left:auto;justify-content:flex-start;
  font-size:var(--fs-2xs);letter-spacing:0;text-transform:uppercase;
  overflow-x:auto;
  scrollbar-width:none;
}
nav.main::-webkit-scrollbar{display:none}
nav.main a{
  /* Raised from .68 (25 Aug 2026): at 13px on this ground the resting links read as greyed
     out rather than merely secondary. .8 keeps the active link's full-opacity magenta as the
     thing that stands out, which is where the contrast should be spent. */
  color:var(--bone);opacity:.8;
  transition:opacity .15s,color .15s;
  /* flex:0 0 auto matters: as a shrinkable item the label would be squeezed and clipped
     silently, and nav.main would never report an overflow to scroll. */
  flex:0 0 auto;white-space:nowrap;
  /* 44px of vertical tap target, per the WCAG target-size guidance. */
  display:flex;align-items:center;min-height:44px;
}
nav.main a:hover{opacity:1;color:var(--magenta)}
nav.main a.active{opacity:1;color:var(--magenta)}

/* The contact icon — WhatsApp alone since 24 Aug 2026. Vinted left the bar: it is a
   channel, not a destination, and how-it-works and the footer both still link it. It lives
   outside nav.main so the scroll container holding the section links cannot carry it off
   the right edge — contact is always reachable. */
/* NO DIVIDER any more (24 Aug 2026). The hairline here separated "where you can go on this
   site" from "where you can reach me", and that reasoning expired twice over: the section
   links moved behind the menu button below 641px, and the Vinted icon left the bar
   entirely. What was left was a rule between the language pill and a single WhatsApp icon —
   two controls of the same kind — with the pill's own border sitting 10px away, so the eye
   read two vertical lines and no grouping. */
.bar-ico{
  flex:0 0 auto;
  display:flex;align-items:center;
  padding-left:9px;
}
a.ico{
  opacity:.8;
  color:var(--bone);
  min-width:31px;min-height:44px;
  display:flex;align-items:center;justify-content:center;
  transition:opacity .15s,color .15s;
}
a.ico:hover{opacity:1;color:var(--magenta)}
/* WhatsApp's own green, because that recognition is the whole value of an icon. Clears AA
   comfortably on this ground (~6:1). */
a.wa{color:#25D366}
a.wa:hover{color:#3BE87C}

/* ONE ROW at every width, with the section links behind a disclosure below 641px
 * (24 Aug 2026). This replaces the two-row phone bar.
 *
 * THE MEASUREMENT THAT FORCED IT. Laid out flat at 375px the bar wants 505px: wordmark 101,
 * the three labels 222, the language pill 71, WhatsApp 41, gaps and gutters 70. Dropping
 * the Vinted icon returned 41px against a 130px deficit, so "just fit it on one row" was
 * never available — the row was always going to clip two of the three links, which is worse
 * than the second row it was meant to replace. Something had to leave the bar, and the only
 * honest candidate was the labels: they are the widest item and the only one that survives
 * being one tap away.
 *
 * WHAT STAYS VISIBLE, AND WHY THOSE TWO. The language pill and WhatsApp are NOT behind the
 * disclosure. WhatsApp is the contactPoint in this site's structured data and the one link
 * a phone opens as an app — burying the primary contact channel behind a tap to save 41px
 * would be the same mistake as the old layout that hid it below 641px. The language pill is
 * the control a visitor who cannot read the current page needs FIRST; a non-English speaker
 * should not have to guess that a hamburger holds the way out. Both are single controls, so
 * both cost a tap either way — a disclosure buys nothing here and hides something.
 *
 * WHY A <details> AND NOT A SCRIPTED MENU. script-src is 'none'. The language switcher
 * already proves the pattern works: keyboard, screen readers, and the open/close state all
 * come from the browser. The trade is real — every section link now costs one extra tap on
 * a phone, which is exactly the objection that kept the links inline while there was room
 * for them. There is no longer room for them.
 *
 * WHY THE BUTTON IS ON THE LEFT. The hairline before .bar-ico already divides "where you
 * can go on this site" from "where you can reach me". The menu is navigation, so it belongs
 * on the navigation side of that line, and grouping it with WhatsApp would put a nav control
 * inside the contact cluster.
 *
 * The sibling selector below is why nav.main sits AFTER the <details> in the markup: CSS can
 * only reach forwards, so `details[open] ~ nav` is what opens the panel. Keeping the nav
 * outside the <details> rather than inside it also means the desktop rule is a plain
 * display:flex — no fighting the UA's closed-details hiding, which is not reliably
 * overridable across engines. */
@media (max-width:640px){
  .menu{flex:0 0 auto;order:-1}
  .menu>summary{
    list-style:none;cursor:pointer;
    display:flex;align-items:center;justify-content:center;
    /* 44px square: the WCAG target-size minimum, same as the nav links it replaces. */
    min-width:44px;min-height:44px;
    margin-left:-11px;   /* optical: pulls the glyph back to the page gutter */
    color:var(--bone);opacity:.8;
    transition:opacity .15s,color .15s;
  }
  .menu>summary::-webkit-details-marker{display:none}
  .menu[open]>summary,.menu>summary:hover{opacity:1;color:var(--magenta)}
  /* The glyphs grow, the targets do not (25 Aug 2026). Both boxes were already 44px tall
     around an 18px glyph — the height is the tap target, not padding, so there was never any
     margin here to trade away. The room was simply unused: 24px inside a 44px box still
     leaves 10px of clearance on every side and the 52px bar does not move.
     The two icons bracket the bar at opposite ends, so they are sized as a pair. */
  .menu>summary svg{width:24px;height:24px}
  a.ico svg{width:24px;height:24px}
  /* And the WhatsApp target itself was 31px WIDE against the 44px minimum — under the target
     size on one axis, which is a defect rather than a preference. It costs 13px of the 35px
     the bar has spare. */
  a.ico{min-width:44px}
  /* The panel. Full-bleed under the bar rather than a floating card: it is the whole of the
     site's navigation, and a 375px-wide sheet has no reason to pretend to be narrower. */
  nav.main{
    display:none;
    position:absolute;left:0;right:0;top:100%;
    flex-direction:column;gap:0;
    padding:4px var(--gutter) 8px;
    /* Fully opaque, and NOT the header's translucent ground: this panel sits over the hero,
       and at .98 alpha the display heading behind it still read through the links. A menu
       you can see the page through is a menu that is hard to read. */
    background:var(--void);
    border-bottom:1px solid rgba(242,239,230,.18);
    /* Above the hero's own stacking context, below nothing. */
    z-index:99;
    box-shadow:0 12px 24px rgba(0,0,0,.28);
    /* The bar is the scroll container's parent; the panel must not inherit its overflow. */
    overflow:visible;
  }
  .menu[open] ~ nav.main{display:flex}

  /* TWO PANELS CANNOT BE OPEN AT ONCE.
   *
   * <details> elements do not coordinate: opening the menu leaves the language switcher
   * openable, and both panels drop from the same bar. The nav panel sits at z-index 99 and
   * the language menu at 60, so the nav covered the top of the language list — English,
   * Русский and Français were behind it. A visitor who cannot read the current page could
   * open the picker and not find their language, which is the one audience that control
   * exists for.
   *
   * Hiding the control while the menu is open makes the broken state unreachable rather
   * than merely survivable, and it needs no JavaScript — there is none loaded site-wide,
   * and script-src forbids the inline kind. The pill returns the instant the menu closes.
   * The sibling selector works because .lang follows .menu in the markup. */
  .menu[open] ~ .lang{display:none}
  nav.main a{
    min-height:48px;
    border-bottom:1px solid rgba(242,239,230,.08);
    font-size:var(--fs-xs);letter-spacing:.04em;
  }
  nav.main a:last-child{border-bottom:0}
}

/* SMALL PHONES (359px and under: the SE-class 320px screen, and nothing newer).
 *
 * The bar carries four controls that are now 44 + 134 + 71 + 44 = 293px of content. At 375px
 * that fits with 13px to spare; at 320 it overruns by 22 and the page scrolls sideways, which
 * is worse than any of the sizes that caused it. Measured before this round, 320px was
 * already 1px over — the width was never really there.
 *
 * The 22px comes back out of SPACING, not out of the controls: nothing shrinks, no label is
 * dropped, and the language code — the thing a visitor who cannot read the page is looking
 * for — stays visible. The gutters and the gaps are the only part of a bar nobody taps. */
@media (max-width:359px){
  .bar{gap:6px;padding-inline:14px}
  /* Spacing alone gave back 18 of the 22, and the last 4 had to come from somewhere. The
     wordmark goes back to the 15px it was until this round: it is the one item here that is
     read rather than tapped, so shrinking it costs no target, and 320px screens do not get
     the extra point of brand that a modern phone does. */
  .brand{font-size:.9375rem}
  .lang{margin-left:auto}
  .lang>summary{padding:0 7px}
}

/* Anchor clearance. The header is one row at every width now — 52px on a phone, 64px from
   641px up — so the 88px global value clears both and the 112px phone override that the
   second row needed is gone with it. */

@media (min-width:641px){
  :root{--gutter:30px}
  .menu{display:none}
  .bar{min-height:64px;gap:24px}
  .brand{font-size:1.0625rem;letter-spacing:.04em}
  nav.main{
    /* 15px, up from 13px (25 Aug 2026). There is no padding left to reclaim here — the desktop
       links already sit at min-height:0 — so the size has to come from the type itself, and the
       64px bar has the room. Stopped short of 16px: that is within a hair of the 17px wordmark
       and the two start competing for the same rank. */
    gap:24px;font-size:.9375rem;letter-spacing:.04em;overflow-x:visible;
  }
  nav.main a{min-height:0}
  .bar-ico{padding-left:18px}
  /* The WhatsApp glyph ships at 18px in the markup, which is right on a 52px phone bar and
     under-reads beside a 15px label on a 64px one. Sized in CSS rather than in the SVG
     attributes so it stays 18px on a phone and no generated page has to be touched. */
  a.ico svg{width:22px;height:22px}
}

/* ---------- image frames ---------- */

.frame{position:relative;overflow:hidden;border:1px solid rgba(58,30,22,.18)}
/* Photos are wrapped in <picture> for the WebP/JPEG choice. display:contents takes the
   wrapper out of the layout entirely, so .ph below still positions against .frame exactly
   as it did when the <img> was a direct child. */
.frame picture{display:contents}
.frame .ph{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:1}
.frame .grain{
  position:absolute;inset:0;z-index:2;opacity:.2;mix-blend-mode:overlay;pointer-events:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g2'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.7' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g2)'/%3E%3C/svg%3E");
}
.frame .cap{
  position:absolute;left:10px;bottom:9px;z-index:3;
  font-size:var(--fs-2xs);letter-spacing:.06em;color:var(--bone);text-transform:uppercase;
  background:rgba(58,30,22,.6);padding:3px 8px;
}
.frame::after{content:"";position:absolute;inset:0;box-shadow:inset 0 0 60px 10px rgba(58,30,22,.55);mix-blend-mode:multiply;pointer-events:none}

/* Restaurant names sit ON the photograph, in the same bottom-left slot the garment
   captions use, rather than floating above it as loose text. It is a name rather than a
   descriptor, so it is set larger and heavier than a plain .cap — and gets a gradient
   scrim instead of a flat chip, because a restaurant photo is busy and unpredictable
   where a flat-lay garment shot is not. */
.frame .cap.rname{
  left:0;right:0;bottom:0;
  font-size:var(--fs-m);font-weight:600;letter-spacing:.03em;
  padding:26px 12px 10px;
  background:linear-gradient(to top,rgba(28,24,16,.88),rgba(28,24,16,.55) 55%,transparent);
}

/* A frame that actually contains a photograph dials the grain and vignette back, so the
   treatment reads as a tint rather than smothering the image. */
.frame:has(.ph) .grain{opacity:.06}
.frame:has(.ph)::after{box-shadow:inset 0 0 40px 6px rgba(58,30,22,.18)}

/* spice-duotone placeholder grounds, hue set per element */
.sp-mag{background:radial-gradient(85% 85% at 35% 25%,#E4713F,transparent 55%),linear-gradient(150deg,#D3502F,#3D2B24)}
.sp-pap{background:radial-gradient(85% 85% at 40% 30%,#E4713F,transparent 55%),linear-gradient(150deg,#C24420,#2E1F19)}
.sp-saf{background:radial-gradient(85% 80% at 45% 35%,#E4EF9A,transparent 55%),linear-gradient(150deg,#B7C13F,#5C5E1E)}
.sp-oli{background:radial-gradient(85% 85% at 35% 30%,#A9AA4E,transparent 55%),linear-gradient(150deg,#8A8B32,#3A3A14)}
.sp-chi{background:radial-gradient(85% 85% at 55% 30%,#E4713F,transparent 55%),linear-gradient(150deg,#C24420,#2E1F19)}
.sp-plum{background:radial-gradient(85% 85% at 40% 30%,#6E6A4B,transparent 55%),linear-gradient(150deg,#4A3B30,#241812)}

/* ---------- page furniture ---------- */

.page-head{border-bottom:1px solid rgba(58,30,22,.22);padding:38px 0 30px;position:relative}
.crumb{font-size:var(--fs-2xs);letter-spacing:.1em;text-transform:uppercase;color:var(--magenta);margin-bottom:18px}

/* Breadcrumb links measured 15px tall — under the 24px WCAG 2.2 target-size minimum, on a
   control that is one of two ways back out of a shelf or a post. Same fix already used for
   .brand and for .trust-n: the TEXT stays exactly where it is on its baseline, and only the
   hit area grows. inline-flex rather than a block so the crumb still reads as one line with
   its separators. */
.crumb a{display:inline-flex;align-items:center;min-height:24px}
@media (any-pointer:coarse){.crumb a{min-height:32px}}
/* HEADING COLUMN. The H1 and the lede beneath it used to carry unrelated caps — 20ch and
   --measure-s — so a shelf heading measured 720px above a 490px lede, two ragged right
   edges for no reason. One token, one edge. Left-aligned, NOT centred like the Diaries
   article: on a listing page a full-width grid defines the column, and a heading that
   centres would float away from the first card it introduces. Text aligns to whatever
   defines the column beneath it — on an article that is the prose itself, which is why
   that one page type centres. */
.page-head h1{font-weight:300;font-size:var(--fs-3xl);line-height:var(--lh-tight);letter-spacing:-.01em;max-width:var(--head-col);margin-bottom:18px}
.page-head h1 b{font-weight:600;color:var(--magenta)}
.page-head .lede{font-size:var(--fs-l);color:var(--bone-dim);max-width:var(--head-col)}
.page-head .lede b{color:var(--magenta);font-weight:500}

/* Light type on a dark ground optically GAINS weight — the glyphs bleed outward into the
   background rather than the reverse — so a weight that is elegant at 60px is frail at 32.
   The h1 clamps down to 32px on a phone and keeps its 300 there, which is the one width
   where the headline stops looking deliberate and starts looking thin. 400 below the
   breakpoint, 300 above it, where there is enough mass for the light weight to read. */
@media (max-width:640px){
  .page-head h1,.art-head h1,.hero h1,.notfound h1{font-weight:400}
}

@media (min-width:641px){
  .page-head{padding:52px 0 40px}
}

/* A light hairline, not the old rgba(58,30,22,.22) — a dark rule on a dark ground is
   invisible, which is half of why the sections ran together. */
section{border-bottom:1px solid rgba(242,239,230,.10)}

/* Alternating bands give the page a rhythm, so scrolling past a section boundary is
   obvious without having to read the heading. */
main > section:nth-of-type(even){background:var(--band)}


/* padding-block, NOT the `padding: 22px 0 44px` shorthand this used to be.
 *
 * .sec-inner is always applied alongside .wrap (`class="wrap sec-inner"`), and .wrap sets
 * `padding: 0 var(--gutter)`. The shorthand here reset the horizontal padding back to 0,
 * so every section on every page ran flush to both edges of the screen — text touching
 * the bezel on a phone. Same bug applied to .hero-inner in index.css.
 * Setting only the block axis leaves .wrap's inline gutter intact. */
.sec-inner{padding-block:22px 44px}

/* Stacked on a phone. The old rule was flex with `justify-content:space-between` at every
   width, and .expo carried white-space:nowrap — so at 375px the exposition text ran off
   the right edge of the screen and was simply cut off. */
.sec-top{
  display:flex;flex-direction:column;gap:10px;
  padding:22px 0 20px;margin-bottom:26px;
  /* Warm amber rather than the old near-invisible dark rule: this line under the section
     heading is the marker someone scanning the page actually sees. */
  border-bottom:1px solid rgba(232,169,60,.22);
}
.sec-top h2{font-weight:500;font-size:var(--fs-2xl);line-height:var(--lh-tight);letter-spacing:-.01em;text-transform:uppercase}
.sec-top .expo{
  font-size:var(--fs-2xs);letter-spacing:.06em;color:var(--bone-dim);
  line-height:1.9;opacity:.85;
  /* Deliberately NOT nowrap at this width — that is what caused the overflow. */
  text-align:left;
}

@media (min-width:721px){
  .sec-inner{padding-block:22px 60px}
  .sec-top{flex-direction:row;justify-content:space-between;align-items:flex-start;gap:20px;padding:22px 0 26px;margin-bottom:34px}
  .sec-top .expo{text-align:right;white-space:nowrap}
}

.sec-lead{max-width:var(--measure-s);color:var(--bone-dim);margin-bottom:32px}
.sec-lead b{color:var(--magenta);font-weight:600}

/* ---------- seller rating strip (homepage) ---------- */

/* Deliberately NOT a card: no border, no panel, no background. It sits in the flow above
   the lead like a byline, because a boxed-off rating reads as an advertisement for itself,
   which is the opposite of what a trust signal needs to do. The only emphasis is the
   number, and the only colour is the saffron on the stars.

   It wraps rather than scrolls at narrow widths — three short phrases, so on a phone it
   becomes two lines and stays entirely readable. */
/* 14px was the gap to the sentence that used to follow this strip; that sentence is gone and
   the strip now sits directly above the shelf tiles, which need the same air the sentence had
   below it. */
/* The whole strip is one link (24 Aug 2026). It was a div holding a separate "Read the
   reviews" anchor; the stars and the number — the part a thumb reaches for first — were not
   clickable, and the anchor took its own row on a phone to repeat what the count said.
   inline-flex, not flex: the strip should be exactly as wide as its own content, so the
   hit area does not run the width of the page. */
a.trust{
  display:inline-flex;flex-wrap:wrap;align-items:baseline;gap:8px 14px;
  margin-bottom:30px;font-size:var(--fs-s);color:var(--bone-dim);
  /* Still not a card: no border, no background, per the note above. The affordance is the
     arrow on the count plus the hover, which is enough on a line that reads as a byline. */
  text-decoration:none;
  transition:color .15s;
}
a.trust:hover,a.trust:focus-visible{color:var(--bone)}
a.trust:hover .trust-n,a.trust:focus-visible .trust-n{color:var(--bone)}
/* The count carries the arrow, so it carries the underline too — one unambiguous target
   rather than an underline dragged across the rating itself. */
.trust-n{
  color:var(--magenta);font-weight:500;
  text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px;
  /* Baseline-aligned text is 23px tall, under the 24px WCAG 2.2 minimum; grow the hit area
     without moving the text off the baseline. */
  display:inline-flex;align-items:center;min-height:24px;
}
@media (any-pointer:coarse){.trust-n{min-height:44px}}
.trust-rate{display:inline-flex;align-items:baseline;gap:8px;white-space:nowrap}
/* letter-spacing because ★ glyphs are drawn tight and read as a smear at small sizes. */
.trust .stars{color:var(--magenta);letter-spacing:.08em;font-size:1.2em}
.trust-rate b{color:var(--bone);font-weight:600;font-size:1.15em}
.trust-n{white-space:nowrap}

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

.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  font-size:var(--fs-xs);letter-spacing:.06em;text-transform:uppercase;
  padding:12px 20px;min-height:44px;
  border:1px solid var(--bone);color:var(--bone);
  transition:background .15s,color .15s,border-color .15s;
}
.btn:hover{background:var(--bone);color:var(--void)}
.btn.mag{border-color:var(--magenta);color:var(--magenta)}
.btn.mag:hover{background:var(--magenta);color:var(--void)}
.btn.saf{border-color:var(--saffron);background:var(--saffron);color:var(--void)}
.btn.saf:hover{background:var(--turmeric);border-color:var(--turmeric);color:var(--void)}

.cta-row{margin-top:30px;display:flex;gap:12px;flex-wrap:wrap}
/* The home page puts a cta-row ABOVE the image grid as well as below it, and the rule
   above only set a top margin — so the "Open Vinted closet" buttons sat flush against the
   first row of photographs. Only applied when something follows, so the trailing cta-row
   at the end of a section does not add space on top of the section's own padding. */
.cta-row:not(:last-child){margin-bottom:30px}

/* ---------- grids shared by several pages ---------- */

/* One column by default; widen as the viewport allows.

   Every track is minmax(0,1fr) rather than a bare 1fr, and that is load-bearing, not
   style. A bare 1fr is minmax(auto,1fr), so each column refuses to shrink below its
   item's min-content width. The cards contain `.piece .frame`, which sizes itself from
   `aspect-ratio:4/5` with no intrinsic width to work from — its min-content resolves to
   a fixed 560px, so three columns demanded 1712px inside a 1220px container and the
   grid ran off the right edge of the page on desktop. minmax(0,…) lets the tracks
   shrink to the space that actually exists. */
/* Two-up. Used by the homepage diary strip, which carries the two newest posts: a pair in a
   three-track grid would leave one cell empty and read as a card that failed to load. */
.grid2{display:grid;grid-template-columns:minmax(0,1fr);gap:12px}
@media (min-width:641px){.grid2{grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}}

.grid3{display:grid;grid-template-columns:minmax(0,1fr);gap:12px}
@media (min-width:521px){.grid3{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (min-width:801px){.grid3{grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}}
/* Cards share the grid's rows instead of stacking their own, so a three-line title lifts
   the whole row's title band rather than shoving that one card's meta line and button out
   of step with its neighbours. Four rows because every card is frame + meta + tags + ask.
   Pre-subgrid browsers ignore the block and get the ragged column this replaces. */
@supports (grid-template-rows:subgrid){
  .grid3>.piece{display:grid;grid-template-rows:subgrid;grid-row:span 4;row-gap:0}
}

.split{display:grid;grid-template-columns:1fr;gap:16px}
@media (min-width:721px){.split{grid-template-columns:1fr 1fr}}

/* min-width:0 so a card can never push its track wider than the track was given —
   the aspect-ratio frame below has a 560px min-content width it would otherwise
   insist on. Pairs with the minmax(0,1fr) tracks in .grid3. */
.piece{display:flex;flex-direction:column;min-width:0}
.piece .frame{aspect-ratio:4/5}
.piece .meta{padding:12px 2px 0;display:flex;justify-content:space-between;align-items:baseline;gap:10px}
/* min-width:0 is load-bearing. Without it the name keeps its max-content width, and a
   long garment title pushes the nowrap price clean off the right edge of a 375px screen
   instead of wrapping. */
.piece .meta .nm{font-size:var(--fs-s);font-weight:500;line-height:var(--lh-snug);color:var(--bone);flex:1 1 auto;min-width:0}
.piece .meta .pr{font-size:var(--fs-s);color:var(--magenta);white-space:nowrap;flex:0 0 auto}
.piece .tags{padding:6px 2px 0;font-size:var(--fs-2xs);letter-spacing:.04em;color:var(--bone-dim);text-transform:uppercase}
/* Every card used to end in a "View on Vinted →" line — six of them on the home page,
   twenty-four on the wardrobe. The card is already the link, so the repetition was noise.
   One small corner mark carries the "this leaves the site" meaning instead, and the link's
   accessible name carries it in words via .vh. */
.piece .ext{
  position:absolute;top:8px;right:8px;z-index:2;
  width:22px;height:22px;display:flex;align-items:center;justify-content:center;
  background:rgba(10,9,8,.62);border:1px solid rgba(242,239,230,.28);
  color:var(--bone);font-size:var(--fs-2xs);line-height:1;
  transition:background .15s,border-color .15s,color .15s;
}
.piece:hover .ext,.piece:focus-visible .ext{background:var(--magenta);border-color:var(--magenta);color:var(--void)}
.piece:hover .frame{outline:2px solid var(--magenta);outline-offset:2px}

.plate{border:1px solid rgba(58,30,22,.2);display:flex;flex-direction:column;background:var(--void-2)}
.plate .frame{aspect-ratio:16/9;border:none;border-bottom:1px solid rgba(58,30,22,.2)}
.plate .pbody{padding:20px}
.plate h3{font-size:var(--fs-l);font-weight:600;line-height:var(--lh-snug);text-transform:uppercase;letter-spacing:.02em;margin-bottom:6px;color:var(--bone)}
.plate .cz{font-size:var(--fs-2xs);color:var(--olive);letter-spacing:.04em;margin-bottom:12px}
.plate p{color:var(--bone-dim);margin-bottom:14px}
.plate .more{font-size:var(--fs-xs);letter-spacing:.06em;text-transform:uppercase;color:var(--magenta)}
.plate a.more:hover{color:var(--magenta)}
a.plate{transition:transform .15s}
a.plate:hover{transform:translateY(-2px)}
a.plate:hover h3{color:var(--magenta)}
a.plate h3{transition:color .15s}

/* ---------- diary post card ---------- */

/* Shared, not page-specific: the same card appears in the homepage's diary strip and in
   every rail on diaries.html. It lives here rather than in diaries.css because index.html
   does not load diaries.css and should not have to.

   Close cousin of .plate, which it does not reuse: .plate is a fixed two-up feature with a
   16/9 crop and generous padding, sized for one or two items in a .split. A rail card is
   narrower, denser, and has to hold its height against neighbours in a scrolling track —
   hence flex:1 on the body and the .more pinned to the bottom, so a two-line title and a
   four-line title still produce cards that line up. */
.post{
  display:flex;flex-direction:column;
  border:1px solid rgba(58,30,22,.2);background:var(--void-2);
  transition:transform .15s,border-color .15s;
}
.post .frame{aspect-ratio:16/10;border:none;border-bottom:1px solid rgba(58,30,22,.2)}
.post .pbody{padding:16px 18px 18px;display:flex;flex-direction:column;flex:1 1 auto}
.post h3{
  font-size:var(--fs-m);font-weight:600;line-height:var(--lh-snug);letter-spacing:-.01em;
  color:var(--bone);margin-bottom:8px;transition:color .15s;
}
/* --fs-s rather than the --fs-m the .prose rule gives it: a rail card is 280-320px wide,
   and 16px there would run about thirty characters to the line. */
.post p{font-size:var(--fs-s);color:var(--bone-dim);margin-bottom:14px}
/* margin-top:auto is what aligns the read-links across a row of uneven cards. */
.post .more{margin-top:auto;font-size:var(--fs-2xs);letter-spacing:.06em;text-transform:uppercase;color:var(--magenta)}
.post:hover{transform:translateY(-2px);border-color:rgba(232,169,60,.35)}
.post:hover h3{color:var(--magenta)}
.post:hover .frame{filter:brightness(1.06)}
/* A card is a link, so it must show a focus ring — and the ring goes round the whole card
   rather than the text inside it, which is what a keyboard user is actually moving between. */
.post:focus-visible{outline:2px solid var(--magenta);outline-offset:3px}

/* The larger variant used by the Tram 22 grid at the top of diaries.html. Same card, more
   air, a wider crop — it is the front page of the journal. */
.post-lg .frame{aspect-ratio:4/3}
.post-lg h3{font-size:var(--fs-l)}

/* minmax(0,1fr) for the same reason as .grid3 above. */
.grid4{display:grid;grid-template-columns:minmax(0,1fr);gap:12px}
@media (min-width:521px){.grid4{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (min-width:1001px){.grid4{grid-template-columns:repeat(4,minmax(0,1fr));gap:16px}}

@media (prefers-reduced-motion:reduce){
  .post:hover{transform:none}
}

/* ---------- footer ----------
 *
 * Rebuilt 17 Aug 2026, and shared verbatim with the Spirituality page: same markup, same
 * class names, same spacing and type steps. Only the colours differ per page, and here they
 * come from the site palette rather than that page's void.
 *
 * What the old footer had that this one does not: a wordmark (the header already carries it),
 * a flat list of seven links with no grouping, and WhatsApp as the only channel. What it gains:
 * three category columns whose headings are themselves links, sub-navigation into each area
 * (fourteen internal links rather than seven, which is the internal linking search engines
 * actually read), and two small channel chips instead of one wide labelled link. */

footer{background:var(--chrome);border-top:1px solid rgba(242,239,230,.10);padding:36px 0 18px}
.foot{padding-bottom:22px;border-bottom:1px solid rgba(242,239,230,.14)}

/* The wordmark returns to the footer (24 Aug 2026), reversing the note above. What changed:
   the header is now a 53px sticky bar whose wordmark is 16 characters, and a visitor who has
   scrolled 14,000px down a shelf page and landed here has no brand in view at the moment
   they decide whether to trust the place. It anchors the footer above the columns rather
   than sitting in the basement beside the small print — a wordmark that reads as a footnote
   is worse than no wordmark. */
.foot-brand{
  display:inline-flex;align-items:center;min-height:32px;margin-bottom:18px;
  font-weight:600;font-size:var(--fs-s);letter-spacing:.04em;text-transform:uppercase;
  word-spacing:-.34em;white-space:nowrap;color:var(--bone);
}
.foot-brand b{color:var(--magenta)}
.foot-brand:hover{color:var(--magenta)}

/* One sentence, identical on every page including Spirituality — it describes the site, not
   whichever page you happen to be standing on, which is also what makes it useful to a search
   engine reading any single page in isolation. */
/* Sentence and channels share one line, chips pushed right and centred against the text block —
   the claim and the way to act on it, together. They wrap beneath it when there is no room. */
.foot-intro{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:16px 32px}
/* No max-width. The 52ch cap that used to be here is the right rule for a column of prose and
   the wrong one for a single 140-character sentence with 700px of empty footer beside it: it
   was folding one line of copy into three while the row it sits in had room to spare. The
   sentence now takes the width the row gives it, and the chips beside it set the limit. */
.foot-lede{font-size:var(--fs-s);color:var(--bone-dim);line-height:1.6;flex:1 1 34ch}

.foot-nav{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:22px 32px;margin-top:20px}
.foot-col{display:flex;flex-direction:column}
/* The column heading is a destination, not a label: land on the hub, or skip straight past it
   to the thing you actually wanted. */
.foot-col .head{
  font-size:var(--fs-2xs);text-transform:uppercase;letter-spacing:.09em;
  color:var(--bone-dim);opacity:.75;padding:4px 0 8px
}
/* No aria-current in here: index.html is the single source of this block and the generators
   copy it verbatim into every page, so a per-page marker cannot survive a build. The header
   nav carries .active + aria-current for the module, which is where the marker belongs. */
.foot-col .head:hover{color:var(--magenta);opacity:1}

/* 36px rather than the 44px these links used to carry. They sit in a single vertical column
   with nothing tappable beside them, so the only mis-tap available is the neighbouring row in
   the same list, and 36px plus the row gap clears a fingertip. It is the difference between a
   footer that fits a phone screen and one that does not. */
.foot-col .item{display:flex;align-items:center;min-height:36px;font-size:var(--fs-xs);color:var(--bone);opacity:.82}
.foot-col .item:hover{color:var(--magenta);opacity:1}

.foot-bottom{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px 24px;padding-top:18px}
.foot-reach{display:flex;gap:10px;flex:0 0 auto}
/* One sentence, not two spans pushed to opposite ends. Privacy leads because it is the part
   a visitor has a stake in; how the site was made is a footnote to it. */
/* Icon plus one word. At footer scale "WhatsApp me" was carrying no information the green mark
   did not already carry, and email deserved to be a control rather than an address to copy. */
.chip{
  display:inline-flex;align-items:center;gap:7px;min-height:38px;padding:8px 16px 8px 13px;
  border-radius:100px;font-size:var(--fs-xs);font-weight:600;
  border:1px solid rgba(242,239,230,.16);color:var(--bone);
  transition:background .3s,border-color .3s,color .3s
}
/* #25D366 at full strength measures 4.89:1 on this ground — the same reasoning that set the
   old .wa-link to opacity 1 rather than .68. */
.chip.wa{color:#25D366;border-color:rgba(37,211,102,.40)}
.chip.wa:hover{background:rgba(37,211,102,.12);border-color:#25D366}
.chip.mail:hover{background:rgba(242,239,230,.08);border-color:rgba(242,239,230,.42)}
.wa-ico{flex:0 0 auto}

/* No tracking, no cookies, no analytics — so there is nothing to disclose in a banner. This
   line is the whole privacy notice, and it keeps the contrast it was corrected to. */
.foot-note{font-size:var(--fs-2xs);color:var(--bone-dim);opacity:.8;letter-spacing:.03em}

/* Two columns before one: three lists stacked single-file is a long scroll of footer, and 340
   rather than 380 because a 375px iPhone is the median phone here, not an edge case. */
@media (max-width:700px){
  .foot-nav{grid-template-columns:repeat(2,minmax(0,1fr));gap:22px 20px}
}
@media (max-width:340px){
  .foot-nav{grid-template-columns:1fr}
}

/* ---------- tap targets, keyed to the pointer rather than the width ----------
 *
 * Two rules above drop links from 44px to their own text height at 641px — one in the
 * header, one here. Both were written as "wide screen means mouse", and 641px up is
 * exactly where the tablets are: measured at 768px and at 1024px, every header nav link
 * and every footer link came out 21px tall, under the 24px WCAG 2.2 target minimum, on
 * the one class of device with no cursor to aim with.
 *
 * any-pointer:coarse, not pointer:coarse. It asks whether ANY available input is coarse,
 * so a touchscreen laptop and a tablet with a trackpad attached both keep the large
 * targets; pointer:coarse asks only about the primary input and would surrender them the
 * moment a mouse is plugged in. Nobody is harmed by a link being easy to hit.
 *
 * Placed after both breakpoint blocks deliberately — these selectors have the same
 * specificity as the ones they are correcting, so source order is what decides. */
@media (any-pointer:coarse){
  nav.main a{min-height:44px}
  .foot-col .item{min-height:40px}
  .brand{min-height:44px}
}

/* Honour the OS-level "reduce motion" setting: kill the smooth scroll and the hover
   translate, which are the only movement on the site. */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.01ms !important;animation-iteration-count:1 !important;transition-duration:.01ms !important}
  a.plate:hover{transform:none}
}

/* ---------- motion ----------
 *
 * Everything below is CSS. There is deliberately no animation library on these pages.
 *
 * spirituality.html gets its movement from GSAP, ScrollTrigger, three.js and Lenis pulled
 * from unpkg and cdnjs — roughly a quarter of a megabyte of JavaScript from two
 * third-party origins. That is a defensible trade on one showpiece page. It is not one
 * here: wardrobe.html carries 159 cards and 3,300 DOM nodes, and the whole reason this
 * site self-hosts its fonts is that the footer's "no cookies, no trackers, no analytics"
 * is meant literally. Scroll-linked reveals no longer need any of it.
 *
 * The pattern matters as much as the effect. The BASE state is the finished state — cards
 * are visible, at full opacity, in their final position — and the animation is added
 * inside @supports on top. Support for scroll-driven animations is Chrome/Edge 115+ and
 * Safari 18+, with Firefox still behind a flag; in Firefox, and in any browser that has
 * never heard of this, the feature query simply does not match and the page renders as it
 * does today. Nothing is ever hidden waiting for a script that might not arrive.
 */
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .piece,.post,.plate,.tile,.vitem,.rev,.holding,.how{
      animation:lift linear both;
      animation-timeline:view();
      /* The range ends inside ENTRY, not inside cover, and that distinction is the whole
         correctness of this rule. `cover` is measured against the element's full pass
         across the viewport, so on a short screen with a tall element the range can still
         be mid-way while the element is sitting still in front of the reader — text parked
         at 60% opacity. `entry 100%` is reached the moment the element has finished
         entering, which is the last instant at which an entrance animation should still be
         running. */
      animation-range:entry 0% entry 100%;
    }
    @keyframes lift{
      from{opacity:0;transform:translateY(16px)}
      to{opacity:1;transform:none}
    }

    /* The section rules draw themselves as their heading arrives. It is the one flourish
       that is native to a monospace layout — a rule being typed rather than a thing
       sliding in. */
    /* Same correction as above, and it mattered more here: measured at 375px, a section
       heading parked just below the fold sat at clip-path:inset(0 47% 0 0) — half the words
       wiped, and staying wiped, because `cover 26%` was still a long scroll away. A wipe
       that does not finish is not an effect, it is missing text. */
    .sec-top,.rail-top{
      animation:rule-in linear both;
      animation-timeline:view();
      animation-range:entry 0% entry 90%;
    }
    @keyframes rule-in{
      from{clip-path:inset(0 100% 0 0)}
      to{clip-path:inset(0 0 0 0)}
    }
  }
}

/* Cross-document view transitions. The site is same-origin, server-rendered and almost
   script-free, which is the ideal case for these: a card on the wardrobe and the
   photograph at the top of its category page are the same object, and the browser can
   tween between them across a real navigation with no JavaScript at all.
   Unsupported browsers get an ordinary navigation, which is what they get today. */
@view-transition{navigation:auto}
@media (prefers-reduced-motion:reduce){
  @view-transition{navigation:none}
}
::view-transition-old(root),::view-transition-new(root){animation-duration:.22s}

/* A hover underline that grows from the left rather than a border that blinks on. It is
   drawn as a background image so it sits under the descenders instead of clipping them,
   and it animates, which a border-bottom cannot. */
.post h3,.plate h3,.tile h3{
  background-image:linear-gradient(var(--magenta),var(--magenta));
  background-repeat:no-repeat;
  background-position:0 100%;
  background-size:0 1px;
  transition:background-size .22s ease,color .15s;
}
.post:hover h3,a.plate:hover h3,.tile:hover h3,
.post:focus-visible h3,a.plate:focus-visible h3,.tile:focus-visible h3{background-size:100% 1px}

/* ---------------------------------------------------------------- language ---- */
/* The switcher. A <details> disclosure holding real links, written into every page by
 * scripts/build-i18n.mjs — which is what knows the current path, and therefore what each
 * link should point at.
 *
 * <details> rather than a <select> or a JS dropdown: it opens, closes, and takes keyboard
 * focus with no script at all, on a site that ships almost none. A <select> full of
 * navigation is also a well-known screen-reader annoyance — it announces as a form control
 * and fires on arrow-key browsing.
 *
 * It sits OUTSIDE .bar-ico, on the left of that hairline. The divider means "site links to
 * the left, ways to reach me on the right", and a language control is a site control — it
 * spent its first day grouped with Vinted and WhatsApp, which is a category error.
 *
 * No flags anywhere. A flag is a country and a language is not: Spanish is not Spain, and
 * German is not only Germany. Each language is named in ITSELF, because a visitor who
 * cannot read English cannot find "Czech" in an English list. */
.lang{position:relative;flex:0 0 auto;margin-left:14px}
.lang>summary{
  list-style:none;cursor:pointer;
  display:flex;align-items:center;gap:7px;
  /* 40px, up from 34 (25 Aug 2026). Every other control in this bar is a 44px target and the
     pill was the one under it — the smallest thing to hit, and the one a visitor who cannot
     read the page needs first. It stops short of 44 on purpose: at 44 the pill outweighs the
     wordmark beside it and starts reading as the bar's primary control. The change is
     vertical only, so it costs the 375px bar no width. */
  min-height:40px;padding:0 10px;
  /* A pill, not a bare word. The border is what makes it read as a control rather than as
     one more nav link, and the shading lifts it off the header bar without the volume of a
     filled button — which would out-shout the nav this header exists to serve. */
  border:1px solid rgba(242,239,230,.34);
  border-radius:999px;
  background:rgba(242,239,230,.06);
  color:var(--bone);
  font-size:var(--fs-2xs);letter-spacing:.04em;text-transform:uppercase;
  /* Full opacity where nav.main sits at .68: the contrast IS the prominence. */
  opacity:1;
  transition:background .15s,border-color .15s,color .15s;
}
.lang>summary::-webkit-details-marker{display:none}
.lang>summary:hover,.lang[open]>summary{
  color:var(--magenta);
  border-color:var(--magenta);
  background:rgba(232,169,60,.12);
}
.lang-globe{flex:none;opacity:.9}
.lang-caret{flex:none;opacity:.7;transition:transform .15s}
.lang[open] .lang-caret{transform:rotate(180deg)}
/* The code is the phone label; see the media query at the end of this block. */
.lang-code{display:none}

.lang-menu{
  position:absolute;top:calc(100% + 6px);right:0;z-index:60;
  min-width:100%;padding:6px 0;
  background:#221f1b;
  border:1px solid rgba(242,239,230,.16);border-radius:10px;
  box-shadow:0 14px 32px rgba(0,0,0,.4);
  display:flex;flex-direction:column;
}
.lang-menu a,.lang-menu .lang-here{
  padding:9px 18px;color:var(--bone);
  text-decoration:none;white-space:nowrap;font-size:14px;
}
.lang-menu a{opacity:.82}
.lang-menu a:hover{opacity:1;color:var(--magenta);background:rgba(242,239,230,.06)}
/* Where you are now. Highlighted rather than ticked: a checkmark is a second thing to
   decode at 11px, while colour plus a marker rail reads before you have finished looking.
   It is a <span>, so it does not respond to hover — deliberately, since there is nowhere
   for it to take you. */
.lang-menu .lang-here{
  color:var(--magenta);opacity:1;font-weight:600;
  background:rgba(232,169,60,.1);
  box-shadow:inset 3px 0 0 var(--magenta);
  cursor:default;
}

/* Phones. The full language name is what makes the control findable, but at 375px the bar
   is already carrying a wordmark, three nav links and two icons — so the globe keeps the
   meaning and the code says where you are, in about half the width. */
@media (max-width:640px){
  .lang{margin-left:auto}
  .lang>summary{padding:0 9px;gap:5px}
  .lang-name{display:none}
  .lang-code{display:inline}
}

/* The menu hangs from the RIGHT edge of its button at every width. Anchoring it left
 * instead pushes it off a 375px screen — measured: it ended at 412px on a 375px viewport,
 * because the control sits hard against the right margin on a phone. Right-anchored, it
 * grows inwards, where there is always room. */
