
/* --------------------------------------------------------------------
 * typeface
 *
 * the font-family descriptor takes a single family name - a fallback
 * list here is invalid & makes the browser drop the whole @font-face
 * rule. the fallback belongs on the body rule below.
 *
 * satoshi is the typeface of the portfolio PDF (see 'design.font' in
 * render/pdf.lua) & of the CV, so the webpage & the printed documents
 * read as one body of work. the italic is a face of the family, not a
 * slanted regular: a title of a work quoted in a text leans without the
 * browser having to fake it.
 */
@font-face {
    font-family: "Satoshi";
    src: url('/static/font/Satoshi-Regular-5b76e2ff2ee0.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Satoshi";
    src: url('/static/font/Satoshi-Italic-e5944df535de.woff2') format('woff2');
    font-weight: 400;
    font-style: italic;
    font-display: swap;
}

/* --------------------------------------------------------------------
 * tokens
 *
 * the palette is the one of the portfolio PDF
 * ('classes/portfolio/init.lua'): the text is ink; everything that
 * explains the text rather than being it - the year & category beside a
 * title, a caption, a folio - steps back through two greys; & a hairline
 * separates the bands of a page. keeping the same four values is what
 * lets the two builds look like one document.
 *
 * '--measure' caps the column on a wide display. 'render/web.lua' knows
 * it as well, because it has to tell the browser how wide an image gets
 * (see '_img_sizes' there): change it here & there together.
 */
:root {
    --ink: #111111;
    --shade: #5f5f5f;
    --muted: #8c8c8c;
    --rule: #c9c9c9;
    --paper: #ffffff;
    --measure: 660px;

    /* the measure of a quotation: how far it steps in from the text
     * around it & how wide it then is. named, because the line that
     * says where a quotation comes from has to take the very same
     * measure - see '.pquotesource' below. the indent is what the
     * browser gives a blockquote by itself & chota does not reset;
     * stating it here only makes it something both rules can reach. */
    --quote-indent: 40px;
    --quote-width: 50%;
}

html {
    /* chota sets 62.5% (so 1rem = 10px). satoshi renders a little larger
     * than the previous font at the same size, so scale the root down
     * here - every rem/em based size below follows, no need to touch
     * them. */
    font-size: 60%;
    height: 100%;
    margin: 0;
    padding: 0;
}

body {
    margin: 0;
    background-color: var(--paper);
    color: var(--ink);
    font-family: "Satoshi", sans-serif;
    font-weight: 400;
    font-style: normal;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

::selection {
    background-color: var(--ink);
    color: var(--paper);
}

/* --------------------------------------------------------------------
 * links
 *
 * a link carries an underline in the colour of the hairline rather than
 * of the text: it is there to be found, not to shout. the underline
 * darkens with the word on hover.
 */
a {
    color: var(--ink);
    text-decoration: underline;
    text-decoration-color: var(--rule);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.18em;
    transition: color .18s ease, text-decoration-color .18s ease;
}
a:visited { color: var(--ink); }
a:hover { color: var(--shade); text-decoration-color: var(--shade); }
a:active { color: var(--muted); }

/* the breadcrumb, the footer, a label: navigation is not marked with an
 * underline, it is just words that react. */
.nava {
    color: var(--ink);
    text-decoration: none;
}
a.nava:hover {
    color: var(--shade);
    text-decoration: none;
}

/* --------------------------------------------------------------------
 * the column
 *
 * one narrow measure, centred. the bottom padding is a little smaller
 * than the top one, so that the last line of a page does not look as if
 * it floated free of the page when it is scrolled to.
 */
#main {
    width: 40%;
    max-width: var(--measure);
    margin: 0 auto;
    padding: 7vh 0 6vh;
}

/* --------------------------------------------------------------------
 * the head of a page
 *
 * the breadcrumb, & - on the page of a work - the year & category at the
 * other end of the same line: the head of a page of the portfolio PDF,
 * where a hairline follows the title & closes the band.
 */
h1.pagehead {
    font-size: 5.2rem;
    font-weight: 500;
    line-height: 1.05;
    letter-spacing: -0.015em;
    margin: 0 0 2.8rem;
    padding-bottom: 1.4rem;
    border-bottom: 1px solid var(--rule);
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 2rem;
}

/* year & category of a work, at the right end of its breadcrumb.
 *
 * 'margin-left: auto' rather than a 'justify-content' on the head: it
 * puts the facts at the right end of the line they are on, which is the
 * breadcrumb's line while both fit, & a line of their own once the
 * column gets too narrow for the two together. a 'space-between' would
 * drop them back to the left edge in that second case, under the
 * breadcrumb, which is the arrangement this is getting away from.
 *
 * a shade darker & a size larger than the rest of the apparatus: they
 * say what the work is, which is more than a folio or a caption says. */
.pheadfacts {
    margin-left: auto;
    color: var(--shade);
    font-size: 1.7rem;
    white-space: nowrap;
}

/* a section heading inside a page - the terms, which are read rather
 * than looked at. the title of a page is the breadcrumb above. */
h1 {
    font-size: 3.4rem;
    font-weight: 500;
    letter-spacing: -0.01em;
    margin: 0 0 2rem;
}
h2 {
    font-size: 2.5rem;
    font-weight: 500;
    letter-spacing: -0.01em;
    margin: 5rem 0 1.4rem;
}
h3 {
    font-size: 1.9rem;
    font-weight: 500;
    margin: 3.2rem 0 1rem;
    color: var(--shade);
}

/* --------------------------------------------------------------------
 * the running text
 *
 * justified, the way the columns of the portfolio are, & hyphenated: in
 * a measure this narrow, justification without hyphens is a row of
 * rivers rather than a text. the last line of a paragraph stays flush
 * left, which is right for a text & where a quotation's source line
 * overrules it below.
 */
p {
    font-size: 2.2rem;
    line-height: 1.55;
    margin: 0 0 1.7rem;
    text-align: justify;
    text-justify: inter-word;
    text-align-last: left;
    -ms-text-align-last: left;
    -moz-text-align-last: left;
    hyphens: auto;
    -webkit-hyphens: auto;
    hyphenate-limit-chars: 7 3 3;
    orphans: 2;
    widows: 2;
}

/* browsers move a <figure> out of the <p> the documentation wrapped it
 * in & leave the empty paragraph behind; it has nothing to say. */
p:empty {
    display: none;
}

/* the first paragraph of a work & the line of the about page: a lead, a
 * little larger than the text it introduces, the way the portfolio sets
 * the first paragraph of a work. */
.worktext > p:first-of-type,
.plead {
    font-size: 2.8rem;
    line-height: 1.38;
    margin-bottom: 2.8rem;
}

/* a line that is a label rather than a text: the offprint in a caption,
 * a name under a cover, the address at the foot of a page. it is set
 * flush left & does not hyphenate, so that a place name is never broken
 * in half to justify a line of six words. */
.psmall,
.pworkfacts,
.pimgcaption,
.pquotesource,
#footnotes p {
    font-size: 1.5rem;
    line-height: 1.4;
    text-align: left;
    text-align-last: left;
    hyphens: none;
    -webkit-hyphens: none;
}

/* a caption is centred under its picture & steps back into grey; it is
 * the one text of a page that is not the work itself. */
.pimgcaption {
    margin-top: 1.2rem;
    color: var(--shade);
    text-align: center;
    text-align-last: center;
}

/* the year & category under the name of a work in the grid of covers. */
.pworkfacts {
    color: var(--shade);
}

/* the address at the foot of the about page: a label, set off from the
 * text above it by a hairline & the same step back into grey as a
 * caption. */
.pcontact {
    margin-top: 4.5rem;
    padding-top: 1.4rem;
    border-top: 1px solid var(--rule);
    font-size: 1.7rem;
    color: var(--shade);
    text-align: left;
    text-align-last: left;
}

/* --------------------------------------------------------------------
 * quotations
 *
 * a quotation is set a little smaller than the text around it, indented,
 * & in the italic of the family - three small signals that together say
 * "this is somebody else speaking" without a rule or a box. */
blockquote {
    background-color: transparent;
    border: none;
    padding: 0;
    margin: 3rem 0 3rem var(--quote-indent);
    width: var(--quote-width);
}
blockquote p {
    font-size: 1.9rem;
    line-height: 1.45;
    font-style: italic;
}
blockquote p:last-child {
    margin-bottom: 0;
}

/* the line that names the source of a quotation - "(excerpt from tiny
 * manifesto)" & the like. it belongs to the quotation above it rather
 * than to the page, so it stands in the measure of the quotation &
 * centres itself in that. centred across the whole column instead it
 * would sit well to the right of the quotation it names, since a
 * quotation is indented & only half as wide.
 *
 * a block, although djot writes it as a span: 'text-align' has nothing
 * to act on until the line is one. */
.pquotesource {
    display: block;
    margin-left: var(--quote-indent);
    width: var(--quote-width);
    text-align: center;
    text-align-last: center;
    color: var(--shade);
}

/* --------------------------------------------------------------------
 * footnotes
 *
 * the notes of a work, lifted out of the text & set under a hairline at
 * the foot of the page - the way the portfolio collects them under the
 * text columns. */
#footnotes {
    margin-top: 5rem;
    color: var(--shade);
}
#footnotes hr {
    border: none;
    border-top: 1px solid var(--rule);
    margin: 0 0 2rem;
}
#footnotes ol {
    padding-left: 2.4rem;
}
#footnotes p {
    text-align: left;
    text-align-last: left;
}
.footnote-ref {
    text-decoration: none;
}
.footnote-ref sup {
    font-size: 0.7em;
    color: var(--shade);
}
.footnote-back {
    text-decoration: none;
    color: var(--muted);
}

/* --------------------------------------------------------------------
 * pictures & sound
 */
img {
    margin: auto;
    display: block;
    /* every img carries its intrinsic width/height, so the browser can
     * reserve space for it before it loads. these two keep that reserved
     * box responsive instead of pinning it to the attribute pixels. */
    max-width: 100%;
    height: auto;
}

/* a documentation picture takes the measure of the text column, whatever
 * the picture it was made from happened to be.
 *
 * 'max-width' alone only ever shrinks: a source that is smaller than the
 * column (a video still, a screenshot) then stands at its own pixel
 * width & sits visibly narrower than the pictures around it - on a wide
 * display only, since on a phone the column is narrower than the picture
 * anyway, which is why this only shows there. the widths that
 * 'render/web.lua' generates are unaffected, they never upscale a source
 * & the browser still picks the sharpest one it has.
 *
 * '.img-w-25/50/75' stay in charge of the pictures they are put on: a
 * class outweighs this rule. */
figure img {
    width: 100%;
}

.img-w-75 {
    width: 75%;
}

.img-w-50 {
    width: 50%;
}

.img-w-25 {
    width: 25%;
}

/* figure must not change its content width/padding/margin */
figure {
    max-width: 100%;
    min-width: 100%;
    padding: 0;
    margin: 4.5rem 0;
}

iframe {
    width: 100%;
}

audio {
    max-height: 100%;
    width: 100%;
    max-width: 1000px;
    min-width: 150px;
    margin: auto;
    object-fit: contain;
}

/* doesn't work for all browsers (e.g. not for firefox), but at least style some of them */
audio::-webkit-media-controls-play-button,
audio::-webkit-media-controls-panel {
    background-color: white;
    color: white;
}

/* add extra styling support for firefox */
@supports (-moz-appearance:none) {
    audio {
        filter: grayscale(100%) invert(100%) contrast(200%);
    }
}

/* prevent overlap of button and caption */
.audiodiv figcaption {
    margin-top: 2rem;
}

/* the name of the photographer, on the picture it belongs to: white in
 * the head of the picture, where the credit line of a plate of the
 * portfolio sits. a slow, quiet appearance, & a slight shadow rather
 * than a dark scrim so the line stays readable over a bright
 * photograph without dimming the photograph itself. */
.overlaycontainer_copyright {
    position: relative;
}

.overlay_copyright {
    position: absolute;
    inset: 0;
    opacity: 0;
    transition: opacity .5s ease;
}

.overlaytext_copyright {
    position: absolute;
    top: 0;
    left: 2%;
    color: white;
    font-size: 1.5em;
    text-align: left;
    text-shadow: 0 1px 3px rgba(0, 0, 0, .65);
}

.overlaycontainer_copyright:hover .overlay_copyright,
.overlaycontainer_copyright:focus-within .overlay_copyright {
    opacity: 1;
}

/* --------------------------------------------------------------------
 * the grid of works
 *
 * two covers to a row inside the column, one on a phone. every cover is
 * cropped to the same shape while the images are generated ('aspect' in
 * 'render/web.lua'), so the rows line up whether the source was
 * portrait or landscape.
 *
 * the name & the facts stand under the cover rather than across it: a
 * label that only exists on hover does not exist on a touchscreen, & a
 * portfolio is read there as often as here.
 */
.worksgrid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 5rem 3.4rem;
}

.workdiv {
    margin: 0;
}

.workimglink {
    display: block;
    color: var(--ink);
    text-decoration: none;
}

.cover-image-work {
    display: block;
    width: 100%;
    height: auto;
    /* the covers are already cropped to this shape while the images are
     * generated ('aspect' in 'render/web.lua'); the ratio is repeated
     * here so that the reserved box is right before they arrive & the
     * rows stay level. */
    aspect-ratio: 3 / 4;
    object-fit: cover;
    transition: transform .55s cubic-bezier(.22, .61, .36, 1);
}

.worktitle {
    display: block;
    margin-top: 1.2rem;
    font-size: 1.7rem;
    transition: color .2s ease;
}

.workimglink .pworkfacts {
    display: block;
    margin-top: 0.2rem;
}

/* the name & the facts are already under the cover, so a fade over the
 * picture only washes it out - there is nothing left for it to reveal.
 * the cover answers the pointer by coming forward a little instead: a
 * stronger, livelier sign than a change in brightness, & one that does
 * not touch the picture's colour. */
.workimglink:hover .cover-image-work,
.workimglink:focus-visible .cover-image-work {
    transform: scale(1.05);
}

.workimglink:hover .worktitle {
    color: var(--shade);
}

/* --------------------------------------------------------------------
 * the foot of a page
 *
 * a hairline, & the two ends of a line: the copyright on the left, the
 * terms on the right - the foot of a page of the portfolio PDF, where
 * the name & the folio stand the same way.
 */
.pagefoot {
    margin-top: 6rem;
    padding-top: 1.4rem;
    border-top: 1px solid var(--rule);
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    column-gap: 2rem;
    row-gap: 0.6rem;
    font-size: 1.5rem;
    color: var(--muted);
}
.pagefoot a {
    color: inherit;
    text-decoration: none;
}
.pagefoot a:hover {
    color: var(--ink);
}

/* --------------------------------------------------------------------
 * the cover, the index page
 *
 * the name at the head, & the three ways into the site standing in the
 * middle of the page with nothing around them: no address, no folio, no
 * labels - just the glyphs. each is a letter of a word, & each speaks
 * its word only when the pointer reaches it, so the cover reads as
 * three marks until one of them is asked.
 */
#indexcontainer {
    min-height: calc(100vh - 13vh);
    min-height: calc(100svh - 13vh);
    display: flex;
    flex-direction: column;
}

.indexname {
    font-size: 1.5rem;
    color: var(--muted);
    letter-spacing: 0.08em;
}

.indexnav {
    display: flex;
    flex-direction: column;
    row-gap: 1rem;
    margin-top: auto;
    margin-bottom: auto;
}

.indexitem {
    display: grid;
    grid-template-columns: 5.5rem auto;
    align-items: baseline;
    column-gap: 1.5rem;
    color: var(--ink);
    text-decoration: none;
}

.indexglyph {
    font-size: 7.5rem;
    line-height: 1;
    transition: color .25s ease, transform .25s ease;
}

.indexlabel {
    font-size: 1.7rem;
    color: var(--shade);
    /* the word is held back until the pointer reaches its letter: the
     * cover only offers the three glyphs, & each one names itself when
     * asked. the reserved column keeps the glyphs on one edge, so the
     * page does not shift as a word arrives. */
    opacity: 0;
    transform: translateX(-0.4rem);
    transition: color .25s ease, opacity .25s ease, transform .25s ease;
}

.indexitem:hover .indexglyph,
.indexitem:focus .indexglyph {
    color: var(--shade);
    transform: translateX(0.6rem);
}

.indexitem:hover .indexlabel,
.indexitem:focus .indexlabel {
    color: var(--ink);
    opacity: 1;
    transform: translateX(0);
}

/* --------------------------------------------------------------------
 * narrower displays
 */
@media only screen and (max-width: 700px) {
    #main {
        width: 97%;
    }

    p {
        font-size: 1.8rem;
    }

    .worktext > p:first-of-type,
    .plead {
        font-size: 2.2rem;
    }

    h1.pagehead {
        font-size: 4rem;
    }

    blockquote p {
        font-size: 1.6rem;
    }

    .indexglyph {
        font-size: 6rem;
    }
}

@media only screen and (max-width: 860px) and (min-width: 700px) {
    #main {
        width: 70%;
    }
}

@media only screen and (max-width: 599px) {
    .worksgrid {
        grid-template-columns: 1fr;
        gap: 4.5rem 0;
    }

    .indexnav {
        row-gap: 0.4rem;
    }
}

/* a touchscreen has no pointer to hover with, so anything that only
 * appears on hover appears never. the photographer's name & the words
 * beside the letters of the index are simply there. */
@media (hover: none) {
    .overlay_copyright {
        opacity: 1;
    }

    .indexlabel {
        opacity: 1;
        transform: none;
    }
}