/* vanese.uk - a port of the Squarespace theme this site ran on until 2026.
   Monochrome by design: the theme's palette is pure greyscale (--black-hsl
   0,0%,0%, --white-hsl 0,0%,100%, --darkAccent 30.2% = #4d4d4d, --lightAccent
   96.9% = #f7f7f7) and nothing here adds colour.

   --darkAccent has exactly one job on these two pages, and it is not text: it
   is the flat overlay the theme paints over the portrait. The port used it to
   grey out the footer's copyright and the contribution notes, which the
   original sets in plain black, and did not paint the overlay at all - so the
   one place the colour belonged was the one place it was missing.

   The numbers below are not estimates. While vanese.uk was still on
   Squarespace, the theme published nearly all of them, and each is cited on
   the rule that uses it. Three sources, in order of authority:

     tweakJSON     embedded in the page HTML - the theme's own settings
     the theme CSS static1.squarespace.com/static/versioned-site-css/
                   .../site.css?nocustom=true
     measurement   computed styles off the live pages in headless Chrome, for
                   the few values the linked CSS does not account for. Those
                   say "measured" rather than naming a rule.

   maxPageWidth (1400px) does two separate jobs, and they are easy to confuse:

     - It caps the content of a page section, via static.css's
         .page-section > .content-wrapper { max-width: var(--maxPageWidth);
                                            margin: 0 auto }
       The computed box-sizing is content-box, so the cap is on the content and
       the two gutters sit outside it: a band is 1400 + 2 * --gutter wide,
       centred. It does NOT cap the header, whose header-width tweak is "Full".
     - It is the cap term in the type formula, baked into the clamps at the
       foot of this file.

   Below 1400px of content the cap does not bind, which is why measuring at a
   1280px viewport cannot tell a correct model from a wrong one. Check any
   change here at 1920 as well.

   styles.test.mjs asserts these values. Change one there and here together. */

@import url("/css/fonts.css");

:root {
  --ink: #000;
  --ground: #fff;
  --hairline: #f7f7f7;
  /* --darkAccent-hsl: 0,0%,30.19607843%, measured as rgb(77, 77, 77). Only the
     portrait overlay uses it. */
  --overlay: #4d4d4d;

  --body: "EB Garamond", Garamond, "Times New Roman", serif;
  --display: "Cormorant Garamond", "EB Garamond", Garamond, serif;

  /* tweakJSON "pagePadding":"4vw", uncapped at both ends. Measured: 51.2px of
     inset at a 1280px viewport. The narrow query below moves it to 6vw. */
  --gutter: 4vw;

  /* tweakJSON "maxPageWidth":"1400px". See the note above: this caps a band's
     content, not the band, and not the header. */
  --page: 1400px;

  /* .page-section.vertical-alignment--middle ... .section-height--medium
       > .content-wrapper { padding-top: 6.6vmax; padding-bottom: 6.6vmax }
     Verbatim: vmax is a real unit, and approximating it in vw only introduces
     error. It follows the viewport's longer edge, so on a landscape desktop it
     is the width and on a phone it is the height. Measured: 84.48px at
     1280x800 and 55.704px at 390x844. Two adjacent bands give 13.2vmax
     between their contents without anyone having to add a margin. */
  --section-pad: 6.6vmax;

  /* .sqs-block { padding: 17px }, applied vertically only where a section
     holds more than one block, so two adjacent blocks are 34px apart and the
     group is inset 17px from the section's content box. A fixed 17px at every
     viewport. It is not a spacing choice of ours; it is where most of the
     whitespace pinned further down actually comes from. */
  --block-gap: 17px;

  /* The header's own box. See the note on .band's minimum height: this is the
     term that makes the first band's minimum mean what the theme's does.
     3vw of padding top and bottom around a 50px logo. */
  --header-height: calc(6vw + 50px);
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--body);
  /* --normal-text-size-value: 1.2. See the type note at the foot of the file:
     the cap is 19.36px and a 1280px viewport gives 19.07px. The floor is what
     the theme's height-driven mobile branch comes to, measured at 18.03px on a
     390x844 phone. */
  font-size: clamp(1.125rem, 1rem + 0.24vw, 1.21rem);
  line-height: 1.6; /* --body-font-line-height: 1.6em */
  font-kerning: normal;
  text-rendering: optimizeLegibility;
}

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

/* The theme underlines at 0.2em with a flat 1px rule, at every size. Measured
   live: 8.7296px under a 43.648px heading and 3.8144px under a 19.072px article
   link, both exactly 0.2em, and 1px thick in both places. Leaving the thickness
   at `auto` lets the browser scale it, which puts a 2-3px rule under the
   headings.

   Every rule that draws an underline below uses text-decoration-line, not the
   text-decoration shorthand: the shorthand resets thickness and offset back to
   `auto`, so it would quietly undo both of these. */
a { color: inherit; text-decoration: none; text-underline-offset: 0.2em; text-decoration-thickness: 1px; }
a:hover, a:focus-visible { text-decoration-line: underline; }
a:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

/* Visually hidden, still read aloud - the social links are icons only. */
.sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip { position: absolute; left: -9999px; }
.skip:focus {
  left: var(--gutter);
  top: 0.5rem;
  z-index: 10;
  padding: 0.5rem 0.75rem;
  background: var(--ground);
  border: 1px solid var(--ink);
}

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

/* tweakJSON "header-width":"Full", so no max-width: the header runs the whole
   viewport, inset only by the page padding. "header-vert-padding":"3vw". */
.site-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 3vw var(--gutter);
}

/* .header-title { margin-right: 2.5vw }. Measured live: 32px at a 1280px
   viewport and 48.1px at 1920. The port had clamp(1.5rem, 5vw, 4.5rem), which
   is 64px and 72px, so the nav started 32px and 24px too far right. Nobody had
   measured it: the header was checked for its padding, its logo height and its
   full-bleed width, and the gap between the two was never one of them. */
.brand {
  display: flex;
  align-items: center;
  gap: 2.5vw;
}

/* tweakJSON "header-logo-height":"50px" - a height, not a width. The mark is
   549x118, so this sets it 233px wide. */
.site-header .logo img { height: 50px; width: auto; }
.site-header .logo:hover { text-decoration-line: none; }

.nav, .social {
  display: flex;
  align-items: center;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* .header-layout-nav-left .header-nav-item:not(:last-child)
   { margin-right: 1.1vw }, which the social actions take too. Measured live at
   14.08px on a 1280px viewport and 21.12px on a 1920px one. The floor is ours:
   below 768px the original swaps to a hamburger and has nothing to copy. */
.nav { gap: max(0.75rem, 1.1vw); }

/* Squarespace sets the nav in the body face at the body size:
   --site-navigation-font-font-size-value: var(--normal-text-size-value), so
   19.072px at a 1280px viewport, no letter-spacing. The port drew it at 13px
   with 0.06em of tracking, which was recorded as a deliberate departure and is
   not one worth keeping - it is the largest remaining visual difference
   between the two sites.

   text-transform stays, and is not a departure: the original's link text is
   literally "ABOUT" and "ARTICLES" with text-transform: none, so uppercasing
   "About" here renders the same word. Keeping the labels in sentence case in
   views.mjs keeps them readable to a screen reader that spells out capitals. */
.nav a {
  font-family: var(--body);
  text-transform: uppercase;
  white-space: nowrap;
}

/* The active item's underline is a background gradient on the theme, not a
   text-decoration: it sits at the bottom of the padding box rather than an em
   below the baseline, which puts it lower and keeps it clear of the descender
   in "Articles". padding-bottom: 0.1em is the theme's own. */
.nav a[aria-current="page"] {
  padding-bottom: 0.1em;
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 100% 1px;
  background-position: 0 100%;
  background-repeat: no-repeat;
}

/* .header-actions--right .header-actions-action--social .icon
   { width: 40px; height: 40px }, fixed at every width, with the glyph 32.2px
   inside it. The svg is 32px in the markup and these 4px make up the box.
   display: block on the svg because an inline one sits on a text baseline and
   makes the box 39.5px tall instead of 40. */
.social { gap: max(0.75rem, 1.1vw); }
.social a { display: block; padding: 0.25rem; }
.social svg { display: block; }
.social a:hover { text-decoration-line: none; opacity: 0.65; }

/* ------------------------------------------------------------------ main --- */

/* main is a bare stack. Every block on both pages is a .band, which is this
   theme's page section: a minimum height, its own padding, and its content
   centred in whatever height it ends up with. Two adjacent bands therefore put
   13.2vmax between their contents with no margins involved, and the first band
   puts 6.6vmax under the header. */
main { display: flow-root; }

/* .page-section.section-height--medium { min-height: 66vh }, plus 6.6vmax of
   padding top and bottom, and vertical-alignment--middle. The minimum height
   is not decoration: at 1280x800 the live intro section is 528px tall and
   centres a ~400px block inside it, and on a tall viewport it sets the whole
   page rhythm. */
.band {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 66vh;
  padding: var(--section-pad) var(--gutter);
  /* content-box on the original, so the cap is on the content and the gutters
     are outside it. Everything here is border-box, hence the addition.
     Measured live at 1920px wide: 1554px, centred. */
  max-width: calc(var(--page) + 2 * var(--gutter));
  margin-inline: auto;
}

/* The theme's header is transparent and absolutely positioned, and the first
   section pads itself by exactly the header's height to make room for it -
   126.766px at 1280, 165.172px at 1920, both the header's own box. So on the
   original the header sits *inside* the first section's 66vh minimum. The port
   keeps the header in flow, which is the same thing to look at and a different
   thing to measure: without this, the first band asks for a full 66vh below a
   header that has already taken its share, and any first band shorter than that
   gets centred in a box too tall for it. Live at 390 that put the articles
   intro 35px lower here than there. Nothing on a wide screen is short enough
   for the minimum to bind, so this only ever shows on a phone. */
main > .band:first-child { min-height: calc(66vh - var(--header-height)); }

/* Home: content-width--narrow, i.e. .content { width: 50% } of the 92vw content
   box, horizontally centred. The bio sits left and the portrait right, tops
   aligned - the original is an image block in card layout with
   image-position-right, where the bio is the image's caption.

   The split inside the card is two theme variables:
     --image-block-card-image-width: 50%
     --image-block-card-image-card-separation: 9%
   and the rules that read them give the image
     width: calc(var(--image-block-card-image-width) - <separation>/2)
   and the caption the remainder, so each column is 45.5% and the gap between
   them is 9%. Measured live: 267.9 + 52.98 at 1280 and 318.5 + 63 at 1920. The
   port's clamp(1.5rem, 4.3vw, 3.5rem) with flex: 1 1 0 gave 55.04/266.9 and
   56/322 - close at 1280, and drifting further apart above it, which is the
   shape of every error a 1280-only measurement leaves behind. */
.intro {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 9%;
  width: 50%;
  margin-inline: auto;
}

.intro > * { flex: 0 0 45.5%; min-width: 0; }

/* .sqsrte-large, p, .sqsrte-small { margin: 1rem 0 }, so a flat 16px rather
   than the port's 0.9em, which tracked the type and came to 17.16px. */
.bio p { margin: 0 0 1rem; }
.bio p:last-child { margin-bottom: 0; }

/* The bio ends in three empty paragraphs on the original - three of the same
   1rem margin and 1.6em line box as every other paragraph, the last one's
   bottom margin zeroed by the theme. They are not decoration by accident: they
   set how far the intro section runs on past the last line of the bio, and
   dropping them made this section 140px shorter than the original's.
   Reproduced as the height they occupy rather than as three empty <p>s, which
   would be markup a screen reader has to walk past. Measured live: 139.5px at
   1280 and 134.5px at 390. */
.bio { padding-bottom: calc(3 * (1rem + 1.6em)); }

/* The theme paints a flat wash over the portrait and nothing else: the image
   block renders

     <div class="sqs-image-shape-container-element" style="position: relative">
       <img>
       <div class="image-overlay"></div>

   and .image-overlay computes to background rgb(77, 77, 77) at opacity 0.1,
   filling the image exactly. The port drew the image bare, which is why it read
   warmer and more saturated than the original: measured over the portrait,
   17.8% mean saturation there against 20.7% here.

   It is the overlay, not the file. Both sides serve the same pixels under the
   same Display P3 profile - the ICC blocks are byte-identical and the two source
   images differ by 0.47% RMSE, which is JPEG noise. An earlier round read the
   same symptom as a stripped colour profile; that was a real bug in the importer
   and worth the fix, but it was never this.

   A pseudo-element rather than the theme's empty <div>, since it is paint and
   not content. The banner and the Instagram thumbnails carry no overlay on the
   original, so they get none here. */
.portrait { position: relative; display: block; }
.portrait::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--overlay);
  opacity: 0.1;
  pointer-events: none;
}

/* Gallery strip: content-width--wide, i.e. the full content box. The block
   carries sqs-gallery-thumbnails-per-row-4, whose rule is
   .sqs-gallery-design-grid-slide { width: 25% }, so it is four across and the
   fifth post wraps onto a row of its own at a quarter width. The aspect comes
   from sqs-gallery-aspect-ratio-three-four-vertical (padding-bottom: 133.333%),
   and the inline block style sets every gutter to 0px.

   It is also the second block in its section: an empty spacer block sits above
   it whose own content is 17px tall, and both blocks carry 17px of padding. 4 *
   17 = 68px above the first row, measured live at 1280 and 1920 alike, and one
   block-gap below it. */
.featured ul {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0;
  margin: calc(4 * var(--block-gap)) 0 var(--block-gap);
  padding: 0;
  list-style: none;
}
.featured img {
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  background: var(--hairline);
  transition: opacity 0.2s ease;
}
.featured a:hover img, .featured a:focus-visible img { opacity: 0.78; }

/* --------------------------------------------------------------- articles --- */

/* The intro line and the banner are two blocks in one section, so they sit in
   the same column and there is no section gap between them: content-width--
   medium, i.e. .content { width: 75% }, centred. */
/* The intro line is the middle of three children of one RTE block:
     <h4> empty     margin 0 0 2rem, line box 1.3889em
     <p>  the line  margin 1rem 0, so its top margin collapses into the h4's
     <p>  empty     margin 1rem 0 0, line box 1.6em
   and then the block's own 17px, and another 17px on the image block below it,
   before the banner starts. The h4 is heading size 4, whose size value is 1.2,
   the same as body text, so its em is the body's em; its 1.3889 line-height is
   the theme's heading formula, 1.4 * (1 + (1 - 1.2) / 25).

   Written out as the space it comes to, since the empty elements themselves are
   nothing a reader should have to walk past. It lands within 0.01px of the
   original at 1280, 1920 and 390. */
.articles-intro {
  width: 75%;
  margin-inline: auto;
  margin-block: calc(2rem + 1.3889em) calc(1rem + 1.6em + 2 * var(--block-gap));
  text-align: center;
}

.banner {
  width: 75%;
  margin-inline: auto;
  /* Last block in a two-block section, so it takes the same 17px below. */
  margin-bottom: var(--block-gap);
}

/* The publication list is its own section: content-width--narrow (50%) with
   horizontal-alignment--left, so it occupies the left half of the page rather
   than being centred. */
.publications {
  width: 50%;
}

/* --heading-2-size-value: 2.8, giving a 46.24px cap and 43.65px at a 1280px
   viewport; the floor is the theme's mobile branch, measured at 34.23px on a
   390x844 phone.

   The line-height is NOT --heading-font-line-height. That is only the input:
   the theme scales it per heading size, with
     h2 { line-height: calc(var(--heading-font-line-height)
                            * (1 + (1 - var(--heading-2-size-value)) / 25)) }
   = 1.4em * (1 + (1 - 2.8) / 25) = 1.2992. Measured live at 56.7075px on a
   43.648px heading.

   h1,h2,h3,h4 { margin: 2rem 0 }, and that top margin is also what separates
   one publication from the next once it collapses through the section. */
.publication h2 {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(2.14rem, 1rem + 2.16vw, 2.89rem);
  line-height: 1.2992;
  margin: 2rem 0;
}
.publication h2 a { text-decoration-line: underline; }

/* Squarespace zeroes the outer margins of an RTE block's first and last child,
   and the list is one block. It matters here and not there: .publications is a
   flex item, so it establishes a block formatting context and the heading's
   2rem cannot collapse out through it - without this the whole list sits 32px
   below where the original starts it. */
.publication:first-child h2 { margin-top: 0; }
.publication:last-child .article:last-child { margin-bottom: 0; }

.publication ul { margin: 0; padding: 0; list-style: none; }

/* Each article is a paragraph in an RTE block on the original, and the theme
   sets .sqsrte-large, p, .sqsrte-small { margin: 1rem 0 }. Margins, not
   padding, so adjacent rows collapse to 16px and the heading's 2rem wins where
   they meet. */
.article { margin: 1rem 0; }
.article > a { text-decoration-line: underline; }

/* .note carries no rule. On the original the contribution note is a bare text
   node inside the paragraph - "<a>...</a> (contributed)" - so it renders at the
   paragraph's own size and colour: 19.072px, black, upright. The port set it in
   grey italic at 0.9em, which is a treatment the theme does not have. The span
   stays in views.mjs as the hook Phase 2 will edit through. */

/* ---------------------------------------------------------------- footer --- */

/* The footer is its own section, and a section-height--small one, which is
   half a medium section on both counts: { min-height: 33vh } and
   > .content-wrapper { padding: 3.3vmax 0 }. Measured live at 1280x800 it is
   264px tall, so it is the minimum height doing the work, not the padding. */
.site-footer {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 33vh;
  padding: calc(var(--section-pad) / 2) var(--gutter);
  /* A page section like any other, so it takes the same cap. */
  max-width: calc(var(--page) + 2 * var(--gutter));
  margin-inline: auto;
  text-align: center;
}

/* content-width--narrow, like the intro and the publication list. */
.footer-content { width: 50%; margin-inline: auto; }

/* Two paragraphs in an RTE block, so their 1rem margins collapse to 16px
   between them. The port had a bare anchor and a paragraph with a 0.9rem top
   margin, which came to 14.4px. */
.site-footer p { margin: 1rem 0; }
.site-footer p:first-child { margin-top: 0; }
.site-footer p:last-child { margin-bottom: 0; }

/* The original's footer link is body text: the body face at the body size,
   sentence case, underlined. The port had it at 13px uppercase with tracking
   and no underline at all. */
.site-footer a { text-decoration-line: underline; }

/* class="sqsrte-small", and --small-text-size-value is 1, so the whole type
   formula collapses to a flat 16px at every viewport - it does not track the
   body size. Measured live at 16px/25.6px at 1280, 1920 and 390 alike. Black,
   like everything else on the page: the port set it in #4d4d4d at 15px. */
.copyright { font-size: 1rem; line-height: 1.6; }

.notfound { text-align: center; }
.notfound h1 { font-family: var(--display); font-weight: 400; }

/* ---------------------------------------------------------------- narrow --- */

@media (max-width: 767px) {
  /* tweakJSON "mobile-header-vert-padding":"6vw",
     "header-mobile-logo-max-height":"30px". */
  /* Measured: the live content is inset 23.4px at 390px wide, i.e. 6vw. The
     theme defines --sqs-mobile-site-gutter: 6vw but applies it outside the
     stylesheet the page links, so this one is pinned by measurement. */
  /* 6vw top and bottom, then the stacked rows: the 30px logo, the 1rem gap
     inside .brand, one line of nav, the 1.25rem gap in .site-header, and the
     40px social row. Measured: 181.6px at 390. The 1.6em is the nav's line box
     and resolves where the token is used, against the band's inherited body
     size, not against the root's 16px. */
  :root { --gutter: 6vw; --header-height: calc(12vw + 30px + 1rem + 1.6em + 1.25rem + 40px); }

  .site-header { flex-direction: column; gap: 1.25rem; padding: 6vw var(--gutter); }
  .site-header .logo img { max-height: 30px; }
  .brand { flex-direction: column; gap: 1rem; }

  /* Every column goes full width, as the theme's own mobile rules do. */
  .intro, .publications, .articles-intro, .banner, .footer-content { width: 100%; }

  /* Once a section stacks, the theme pads the top of its first block as well,
     which it does not do on a wide screen. Measured live at 390: the portrait,
     the first publication heading and the articles intro line all start 17px
     lower than they would at 1280, where they sit flush with the section's
     content box. .articles-intro takes the same 17px at the foot of this
     query, added to the space it already carries. */
  .intro, .publications { margin-top: var(--block-gap); }

  /* The image leads on a narrow screen, as it does in the original theme,
     where the bio is that image's caption. */
  /* gap goes back to 0 rather than staying at 9%: a percentage row-gap resolves
     against an indefinite height, so it would silently be zero anyway, and the
     separation belongs on the portrait where it can resolve against the width.
     .design-layout-card.sqs-narrow-width .image-card-wrapper
       { margin-top: var(--image-block-card-image-card-separation) }, the same
     9% the two columns are split by, measuring 30.9px at 390px wide. */
  .intro { flex-direction: column-reverse; align-items: stretch; gap: 0; }
  .intro > * { flex: none; }
  .portrait { margin-bottom: 9%; }

  /* .sqs-layout .sqs-gallery-design-grid-slide { width: 50% !important } under
     the theme's mobile query. The fifth image sits alone on the third row at
     half width; the original never stretched it. */
  /* The spacer block above the gallery is not laid out at this width, so what
     is left above the first row is the gallery block's own 17px. */
  .featured ul { grid-template-columns: repeat(2, 1fr); margin-top: var(--block-gap); }

  /* 74.03px above the intro line at 390 against 58.49px at 1280: the same
     block padding as above, on top of the empty <h4>. */
  .articles-intro { margin-top: calc(2rem + 1.3889em + var(--block-gap)); }
}

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

/* For the record, the expression the clamps above replace. Squarespace sizes
   type from a unitless size value V, under
   @media (min-width: 768px), (orientation: landscape):

     font-size: min( calc((V - 1) * 1.2vw + 1rem),
                     max( calc((V - 1) * 0.012 * 1400px + 1rem),
                          calc(V * 1rem) ) )

   With --base-font-size 16px and maxPageWidth 1400px that is a straight ramp
   in the viewport width, capped at 1400px wide:

     body  V = 1.2  ->  min(0.24vw + 16px, 19.36px)   19.07px at 1280px
     h2    V = 2.8  ->  min(2.16vw + 16px, 46.24px)   43.65px at 1280px

   Under 768px in portrait the theme swaps the viewport width for
   min(100vh, 900px), which is a quirk of the original rather than a design
   intent, and is not reproduced. */
