/* The landing page for erwindegroot.nl.

   The palette is deliberately the one both apps already use - dark chrome, a single warm
   yellow accent - so arriving here and then opening either app feels like one place rather
   than three. The values come from the PhotoSpiralysis site (--bg #101216, --accent
   #ffe000) and from Fractalysis' app.css, which reach for the same yellow from opposite
   directions; #ffe000 is the one that holds up as text on a dark ground, so it wins. */
:root {
    --bg: #101216;
    --bg-card: rgba(24, 27, 33, 0.72);
    --bg-card-hover: rgba(32, 36, 44, 0.86);
    --border: rgba(255, 255, 255, 0.09);
    --border-hover: rgba(255, 224, 0, 0.45);
    --text: #e8eaed;
    --text-dim: #9aa0ac;
    --accent: #ffe000;
    color-scheme: dark;
}

* {
    box-sizing: border-box;
}

/* Text for screen readers and nothing else. Clipped to a single pixel rather than hidden
   with display:none or visibility:hidden, both of which take it out of the accessibility
   tree along with the layout - which would defeat the entire point. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

html,
body {
    margin: 0;
    min-height: 100%;
}

body {
    /* Two soft pools of colour, one per app, sitting where the cards sit. They are the
       whole background if the canvas never starts, and they stay underneath it when it
       does - the canvas is drawn with a low alpha and lets these through. */
    background:
        radial-gradient(60rem 40rem at 12% 18%, rgba(255, 60, 140, 0.12), transparent 60%),
        radial-gradient(55rem 38rem at 88% 78%, rgba(0, 150, 255, 0.12), transparent 60%),
        var(--bg);
    color: var(--text);
    font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    -webkit-font-smoothing: antialiased;
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
}

#backdrop {
    position: fixed;
    inset: 0;
    z-index: -1;
    width: 100%;
    height: 100%;
    /* Barely there. Enough that the page is not static, far too faint to compete with the
       card artwork, which is the thing that is meant to be looked at. */
    opacity: 0.32;
    pointer-events: none;
}

main {
    width: 100%;
    max-width: 64rem;
    margin: 0 auto;
    padding: clamp(2.5rem, 8vh, 6rem) 1.5rem 2rem;
    display: flex;
    flex-direction: column;
    flex: 1;
    gap: clamp(2rem, 6vh, 3.5rem);
}

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

.masthead {
    text-align: center;
}

.masthead h1 {
    margin: 0;
    /* The heading is now a whole domain on one unbreakable line, so it has to be sized to
       fit rather than left to wrap. 8.5vw is the rate at which it still clears the page
       padding on a 320px screen - the narrowest phone worth caring about - and it is a
       steeper ramp than the old 6vw, so on an ordinary 375px phone the name comes out
       slightly larger than it was before the domain was added, not smaller. */
    font-size: clamp(1.6rem, 8.5vw, 3.25rem);
    font-weight: 600;
    letter-spacing: -0.02em;
    line-height: 1.1;
}

/* "www." and ".nl" - the domain around the name. Smaller, lighter and dimmer, so the eye
   reads the name first and the address second, from one line of type. Sitting on the same
   baseline is what keeps it looking like an address rather than a logo with superscripts. */
.host-affix {
    font-size: 0.4em;
    font-weight: 500;
    letter-spacing: 0;
    color: var(--text-dim);
}

/* Margins rather than typed spaces: a space here would be a place the line could break. */
.host-affix-lead {
    margin-right: 0.45em;
}

.host-affix-trail {
    margin-left: 0.45em;
}

.tagline {
    margin: 0.85rem auto 0;
    max-width: 34rem;
    color: var(--text-dim);
    font-size: clamp(0.95rem, 1.6vw, 1.075rem);
    line-height: 1.55;
}

/* --- app cards -------------------------------------------------------------------- */

.apps {
    display: grid;
    /* min(19rem, 100%) rather than a bare 19rem: a plain minmax floor is a hard minimum,
       so on a screen narrower than 19rem plus the page padding the track refuses to shrink
       and the cards hang off the right edge. Capping the floor at the container's own width
       lets the single column shrink with the viewport instead. */
    grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
    gap: 1.5rem;
}

.app-card {
    display: flex;
    flex-direction: column;
    text-decoration: none;
    color: inherit;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 1rem;
    overflow: hidden;
    /* The card is a link, so it is one element; its children are spans laid out as blocks
       rather than divs, because a div inside an <a> is legal but a span is what this
       actually is - inline content given a box. */
    backdrop-filter: blur(12px);
    transition: transform 220ms ease, border-color 220ms ease, background-color 220ms ease, box-shadow 220ms ease;
}

.app-card:hover,
.app-card:focus-visible {
    transform: translateY(-4px);
    background: var(--bg-card-hover);
    border-color: var(--border-hover);
    box-shadow: 0 1.25rem 2.5rem rgba(0, 0, 0, 0.45);
}

.app-card:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

/* 4:3, because that is the shape of both pictures. Neither is cropped by CSS: the
   PhotoSpiralysis photo is 4:3 as exported and has content it cannot lose at both edges -
   the bear's ears at the top, the watermark at the bottom - and the Fractalysis render was
   cropped to 4:3 when it was resized for the web. So object-fit never has a decision to
   make, at any card width. */
.app-art {
    display: block;
    position: relative;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    background: #07080a;
    /* A hairline between art and text, so the card still reads as two parts on a screen
       where the artwork happens to be dark at the bottom edge. */
    border-bottom: 1px solid var(--border);
}

/* There is deliberately no gradient scrim over the bottom of the artwork. One looked right
   over the generated pictures these cards used to carry, and wrong over real ones: it
   greys out the bright corner of the Fractalysis render and dims the watermark along the
   bottom of the PhotoSpiralysis photo. The hairline border above does the separating. */

.art-image {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 400ms ease;
}

.app-card:hover .art-image,
.app-card:focus-visible .art-image {
    transform: scale(1.04);
}

.app-body {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    padding: 1.35rem 1.4rem 1.5rem;
    flex: 1;
}

.app-name {
    font-size: 1.3rem;
    font-weight: 600;
    letter-spacing: -0.01em;
}

.app-blurb {
    color: var(--text-dim);
    font-size: 0.95rem;
    line-height: 1.55;
    /* Pushes the call to action to the bottom, so the two cards line their "Open the app"
       rows up with each other even when one blurb runs a line longer. */
    flex: 1;
}

.app-go {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    margin-top: 0.35rem;
    color: var(--accent);
    font-size: 0.95rem;
    font-weight: 600;
}

.go-arrow {
    width: 1.05em;
    height: 1.05em;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    transition: transform 220ms ease;
}

.app-card:hover .go-arrow,
.app-card:focus-visible .go-arrow {
    transform: translateX(0.25rem);
}

/* --- about strip ------------------------------------------------------------------ */

/* Two short notes under the cards, one per app, saying in plain words what each one is
   for. They exist because the cards alone give a search engine almost nothing to index: a
   name, a blurb and a picture. This is where the page says "Mandelbrot set" and "Droste
   effect" in sentences, and links to the page that says more. Laid out on the same grid
   as the cards, so each note sits under the app it describes when there are two columns. */
.about {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
    gap: 1.5rem 2.5rem;
}

.about h2 {
    margin: 0 0 0.5rem;
    font-size: 1.05rem;
    font-weight: 600;
    letter-spacing: -0.01em;
}

.about p {
    margin: 0 0 0.6rem;
    color: var(--text-dim);
    font-size: 0.95rem;
    line-height: 1.6;
}

.about a {
    color: var(--accent);
    text-decoration: none;
    font-size: 0.95rem;
    font-weight: 600;
    border-bottom: 1px solid transparent;
    transition: border-color 180ms ease;
}

.about a:hover,
.about a:focus-visible {
    border-bottom-color: var(--accent);
}

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

.colophon {
    margin-top: auto;
    padding-top: 1rem;
    text-align: center;
    color: var(--text-dim);
    font-size: 0.875rem;
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
}

.colophon a {
    color: var(--text-dim);
    text-decoration: none;
    border-bottom: 1px solid transparent;
    transition: color 180ms ease, border-color 180ms ease;
}

.colophon a:hover,
.colophon a:focus-visible {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

.dot {
    opacity: 0.5;
}

/* The Ko-fi "Tip Me" button floats in the bottom-left corner, 160px wide and reaching
   115px up from the bottom edge. On a wide screen the centred colophon never comes near
   it, but below about 42rem the footer text spreads out far enough to run underneath the
   button when the page is scrolled to its end, and the email address is the thing that
   gets hidden. Enough extra padding that the footer clears the button instead. */
@media (max-width: 42rem) {
    .colophon {
        padding-bottom: 5.5rem;
    }
}

/* --- article pages ---------------------------------------------------------------- */

/* The privacy policy and the two app pages: a heading, a line of introduction, then short
   titled sections. They borrow the landing page's palette and type and narrow the column
   to a comfortable reading width; the rest of this is sizes for elements the landing page
   never uses. */
.page {
    max-width: 40rem;
    margin: 0 auto;
    width: 100%;
}

.page h1 {
    margin: 0 0 1.25rem;
    font-size: clamp(1.75rem, 5vw, 2.25rem);
    font-weight: 600;
    letter-spacing: -0.02em;
    line-height: 1.15;
}

.page > p,
.section p,
.section li {
    margin: 0;
    color: var(--text-dim);
    line-height: 1.65;
}

.page > p {
    margin-bottom: 2rem;
}

/* The picture at the top of an app page is the same 4:3 file its card uses, at the
   column's full width. width/height on the img reserve the space, as on the cards. */
.page figure {
    margin: 0 0 2rem;
    border-radius: 1rem;
    overflow: hidden;
    border: 1px solid var(--border);
    background: #07080a;
}

.page figure img {
    display: block;
    width: 100%;
    height: auto;
}

.section {
    margin-bottom: 1.75rem;
}

.section h2 {
    margin: 0 0 0.5rem;
    font-size: 1.15rem;
    font-weight: 600;
    letter-spacing: -0.01em;
}

/* Questions in a FAQ: a step below the section headings, a step above the text. */
.section h3 {
    margin: 1rem 0 0.25rem;
    font-size: 1rem;
    font-weight: 600;
}

.section p + p,
.section p + ul {
    margin-top: 0.75rem;
}

.section ul {
    margin: 0;
    padding-left: 1.25rem;
}

.section li + li {
    margin-top: 0.35rem;
}

.section a,
.page > p a {
    color: var(--text);
    text-decoration: none;
    border-bottom: 1px solid var(--border-hover);
    transition: color 180ms ease, border-color 180ms ease;
}

.section a:hover,
.section a:focus-visible,
.page > p a:hover,
.page > p a:focus-visible {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

/* The one link on an app page that opens the app: a pill in the accent colour, so it
   reads as the thing to press rather than one more link in the running text. The dark
   text on it is the same value photospiralysis.com uses on its yellow buttons. */
.cta {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    margin: 0 0 2rem;
    padding: 0.7rem 1.3rem;
    border-radius: 999px;
    background: var(--accent);
    color: #1a1608;
    font-weight: 700;
    text-decoration: none;
    transition: filter 180ms ease, transform 180ms ease;
}

.cta:hover,
.cta:focus-visible {
    filter: brightness(1.06);
    transform: translateY(-1px);
}

.cta:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

.page > .updated {
    margin-bottom: 0;
    font-size: 0.85rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

/* The landing page's colophon sits at the foot of the viewport because that page is
   short. An article is not, so its footer follows the text instead. */
.page + .colophon {
    margin-top: 3rem;
}

/* --- motion ----------------------------------------------------------------------- */

/* The backdrop animation and the card lift both stop for anyone who has asked for less
   motion. site.js checks the same query before starting its animation loop. */
@media (prefers-reduced-motion: reduce) {
    #backdrop {
        display: none;
    }

    .app-card,
    .art-image,
    .go-arrow {
        transition: none;
    }

    .app-card:hover,
    .app-card:focus-visible {
        transform: none;
    }
}
