/* ===================================================================
   Fotolab - a darkroom for photographers
   Version: 1.0.3
   Author: Reveloni
   For: Reveloni CMS 7.0.0+ (built and tested on 7.6.9)

   Changelog:
   1.0.0 - first release.
   1.0.1 - wider page: container 1140 -> 1440px (on a 1920px screen the
           side margins drop from 20% to 12.5% each); the text inside a
           print sheet is a centred column of about 72 characters, while
           the contact sheets keep the full width.
   1.0.2 - the loupe works on nested pages too (/parent/child): it took
           the large file from a relative 'uploads/' address, which
           resolves under the page path there, and showed a black
           glass. The folder now comes from the thumbnail's absolute
           address.
   1.0.3 - wider text column: about 115 characters per line instead of
           about 75 - at 75 the text stood as a narrow pillar on a
           desktop screen. Phones are unchanged.

   THE ONE RULE: THE PHOTOGRAPH IS NEVER TOUCHED.
   A photographer judges colour and tone, so nothing in this theme tints,
   blurs, blends or decorates a photo. The colour scheme lives around the
   pictures, never on them. The development effect is the only thing that
   ever puts a filter on an image, it runs once, and it ends at
   filter: none - checked by test on every photo after it settles.

   The page is a darkroom:
   - the gallery is a CONTACT SHEET: film strips with perforations and
     edge numbers, each photo shown whole inside its 3:2 frame, so upright
     and square pictures are never cropped;
   - page cards are PRINTS with a white border lying on the table;
   - the hero is the ENLARGER EASEL: four blades frame the lit paper;
   - the lightbox is the light table: neutral black, no scheme colour.

   What the panel drives:
   - colour scheme = the GREASE PENCIL: frame loops on the contact sheet,
     the stroke under headings, the circled step on the test strip. The
     pencil never carries text; where text needs the scheme (links,
     buttons) it is mixed toward the ink so it stays legible on all 48
     schemes;
   - animation slider = how strongly photos develop (read by head.html)
     and how far the easel blades travel; at 0 photos appear at once;
   - animations off = no development at all, photos are there at once;
   - footer colour is the panel's, taken halfway to film black, with film
     perforation on its edge (see FOOTER);
   - dark mode = the SAFELIGHT: the room goes dark and red-amber, the
     enlarger light on the easel stays white, the photos stay true.
   =================================================================== */

/* ===================================================================
   TOKENS
   =================================================================== */

:root {
    --fl-table: #e0e0dd;
    --fl-paper: #fbfbf8;
    --fl-paper-2: #f0efea;
    --fl-rule: #cfcec9;
    --fl-ink: #1d1e20;
    --fl-ink-soft: #55575b;

    /* The scheme as grease pencil (ornament) and as legible ink (text). */
    --fl-pencil: color-mix(in srgb, var(--primary) 78%, var(--fl-ink));
    --fl-mark: color-mix(in srgb, var(--primary) 30%, var(--fl-ink));
    --fl-on-mark: var(--fl-paper);

    /* Fixed materials - they do not change with the scheme or the mode. */
    --fl-base: #2a2927;          /* enlarger baseboard */
    --fl-light: #fffdf5;         /* the enlarger's white light */
    --fl-sheet: #0c0c0c;         /* contact sheet */
    --fl-rebate: #1c1b1a;        /* film edge */
    --fl-hole: #e6e3dc;          /* perforation */
    --fl-film-base: #0f0e0d;     /* inside a frame, around the photo */
    --fl-edge: #c3bdb0;          /* edge printing */
    --fl-blade: #d9d5ca;         /* easel blade enamel */
    --fl-blade-tick: #8f8a7f;

    /* Development: strength 0..1 and duration, overwritten by head.html
       from the panel slider. */
    --fl-dev-s: 1;
    --fl-dev-time: 1.15s;
    --fl-travel: calc(var(--animation-offset, 20px) * 4);

    --fl-sans: 'Instrument Sans', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --fl-serif: 'Instrument Serif', Georgia, 'Times New Roman', serif;
    --fl-mono: 'DM Mono', ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;

    /* Aliases some engine markup and inline styles expect. */
    --text: var(--fl-ink);
    --text-light: var(--fl-ink-soft);
    --bg-light: var(--fl-paper-2);
}

html[data-theme="dark"] {
    --fl-table: #0e0a09;
    --fl-paper: #1b1412;
    --fl-paper-2: #241a17;
    --fl-rule: #3d2b25;
    --fl-ink: #f2e4d8;
    --fl-ink-soft: #cdb3a2;

    --fl-pencil: color-mix(in srgb, var(--primary) 62%, #f2e4d8);
    --fl-mark: color-mix(in srgb, var(--primary) 45%, #f2e4d8);
    --fl-on-mark: #150e0c;

    --fl-safelight: #c8401f;
}

/* ===================================================================
   BASE
   =================================================================== */

* {
    box-sizing: border-box;
}

body {
    margin: 0;
    font-family: var(--fl-sans);
    font-size: 1.0625rem;
    line-height: 1.65;
    color: var(--fl-ink);
    background: var(--fl-table);
    -webkit-font-smoothing: antialiased;
}

/* The safelight. A static fixed layer with no blending, so it is
   composited once and costs nothing while scrolling (Preparat audit). */
html[data-theme="dark"] body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(900px 620px at 88% -8%, rgba(200, 64, 31, 0.30), transparent 62%),
        radial-gradient(760px 520px at 4% 108%, rgba(200, 64, 31, 0.13), transparent 60%);
}

h1,
h2,
h3,
h4 {
    font-family: var(--fl-serif);
    font-weight: 400;
    line-height: 1.12;
    color: var(--fl-ink);
    letter-spacing: -0.005em;
}

a {
    color: var(--fl-mark);
}

.container {
    max-width: 1440px;
    margin: 0 auto;
    padding: 0 20px;
}

/* ===================================================================
   HEADER - sticky, fleet convention. No position: relative here: it
   would silently override sticky. No overflow either: the engine renders
   the dropdown inside the header (Zest 1.0.1).
   =================================================================== */

header {
    position: sticky;
    top: 0;
    z-index: 100;
    background: var(--fl-paper);
    border-bottom: 1px solid var(--fl-rule);
}

html[data-theme="dark"] header {
    background: #140f0d;
}

/* The dropdown anchors here: top: 100% is the bottom of the bar. */
header .container {
    position: relative;
}

.header-content {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    padding: 0.75rem 0;
}

.logo-container img {
    height: 80px;
    width: auto;
    display: block;
}

.site-title {
    font-family: var(--fl-serif);
    font-size: 1.6rem;
    line-height: 1.1;
    color: var(--fl-ink);
}

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

/* Menu control: a 35 mm film cassette with its leader out. The engine
   emits <span>glyph</span><span>Menu</span>; the glyph is hidden with
   font-size: 0 and the span is redrawn, as everywhere in the fleet. */
.hamburger-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.85rem;
    font-family: var(--fl-sans);
    font-weight: 500;
    font-size: 0.95rem;
    color: var(--fl-ink);
    background: transparent;
    border: 1px solid var(--fl-rule);
    border-radius: 2px;
    padding: 0.5rem 1rem 0.5rem 0.85rem;
    cursor: pointer;
}

.hamburger-btn:hover {
    border-color: var(--fl-ink);
}

.hamburger-btn span:first-child {
    position: relative;
    display: block;
    width: 15px;
    height: 17px;
    margin-top: 3px;
    font-size: 0;
    border: 2px solid currentColor;
    border-radius: 3px;
}

/* spool end */
.hamburger-btn span:first-child::before {
    content: "";
    position: absolute;
    top: -6px;
    left: 3px;
    width: 5px;
    height: 4px;
    border-radius: 1px 1px 0 0;
    background: currentColor;
}

/* film leader with perforation */
.hamburger-btn span:first-child::after {
    content: "";
    position: absolute;
    top: 3px;
    left: 100%;
    width: 9px;
    height: 7px;
    background:
        linear-gradient(currentColor, currentColor) top / 100% 1.5px no-repeat,
        linear-gradient(currentColor, currentColor) bottom / 100% 1.5px no-repeat,
        repeating-linear-gradient(90deg, currentColor 0 1.5px, transparent 1.5px 3.5px) center / 100% 2px no-repeat;
    transition: width 0.2s ease;
}

.hamburger-btn:hover span:first-child::after {
    width: 12px;
}

.dropdown-menu {
    display: none;
    position: absolute;
    top: 100%;
    right: 0;
    z-index: 60;
    min-width: 250px;
    max-height: 70vh;
    overflow-y: auto;
    overscroll-behavior: contain;
    touch-action: pan-y;
    background: var(--fl-paper);
    border: 1px solid var(--fl-rule);
    box-shadow: 0 16px 34px -12px rgba(0, 0, 0, 0.4);
    padding: 0.4rem;
}

.dropdown-menu.active {
    display: block;
}

.dropdown-menu ul {
    display: block;
    list-style: none;
    margin: 0;
    padding: 0;
}

.dropdown-menu li {
    display: block;
}

.dropdown-menu a {
    display: block;
    padding: 0.55rem 0.85rem;
    color: var(--fl-ink);
    text-decoration: none;
}

.dropdown-menu a:hover {
    background: var(--fl-paper-2);
}

/* The current page is ticked in grease pencil. */
.dropdown-menu a.active {
    box-shadow: inset 3px 0 0 var(--fl-pencil);
}

/* Subpages (7.6.8 put the home page's subpages under Home). */
.dropdown-menu .dropdown-submenu,
.dropdown-menu .dropdown-subsubmenu {
    padding: 0 0 0 0.9rem;
}

.dropdown-submenu a,
.dropdown-subsubmenu a {
    font-size: 0.92rem;
    padding: 0.4rem 0.85rem;
    color: var(--fl-ink-soft);
}

/* ===================================================================
   BREADCRUMBS
   =================================================================== */

.breadcrumbs {
    padding: 0.8rem 0;
    font-size: 0.86rem;
    color: var(--fl-ink-soft);
}

.breadcrumbs a {
    color: var(--fl-mark);
    text-decoration: none;
}

.breadcrumbs a:hover {
    text-decoration: underline;
}

.breadcrumbs span {
    margin: 0 0.45rem;
}

/* ===================================================================
   HERO - the enlarger easel. A dark baseboard, a cone of white light,
   the lit paper, and four blades framing it. The paper and the title
   are fixed colours: the enlarger light is white in any room and in any
   scheme, so the title never depends on the palette.
   =================================================================== */

.hero {
    position: relative;
    overflow: hidden;
    padding: clamp(3rem, 7vw, 5.5rem) 20px clamp(3.2rem, 7.5vw, 6rem);
    background:
        radial-gradient(ellipse 70% 95% at 50% 0%, #3a3835 0%, transparent 70%),
        var(--fl-base);
}

/* The cone of light from the lens. */
.hero:not(.hero--has-bg)::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    width: min(1180px, 160%);
    height: 100%;
    transform: translateX(-50%);
    clip-path: polygon(45% 0, 55% 0, 97% 100%, 3% 100%);
    background: linear-gradient(to bottom, rgba(255, 251, 238, 0.03), rgba(255, 251, 238, 0.14));
    pointer-events: none;
    z-index: 0;
}

/* The lit paper on the easel. */
.hero .container {
    position: relative;
    z-index: 1;
    max-width: 860px;
    margin: 0 auto;
    padding: clamp(2.2rem, 5vw, 3.6rem) clamp(1.4rem, 4vw, 3rem);
    text-align: center;
    background: radial-gradient(ellipse 80% 95% at 50% 45%, var(--fl-light) 0%, #f3f1e9 70%, #e5e2d8 100%);
    box-shadow: 0 0 70px rgba(255, 246, 220, 0.16), 0 22px 40px -22px rgba(0, 0, 0, 0.7);
}

.hero h1 {
    margin: 0;
    font-family: var(--fl-serif);
    font-weight: 400;
    font-size: clamp(2.3rem, 5.2vw, 4.2rem);
    line-height: 1.05;
    letter-spacing: -0.012em;
    color: #1d1e20;
}

/* A pencil stroke under the title - the scheme's mark on the print. */
.hero h1::after {
    content: "";
    display: block;
    width: min(70%, 9em);
    height: 12px;
    margin: 0.35em auto 0;
    background-color: color-mix(in srgb, var(--primary) 78%, #1d1e20);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 14' preserveAspectRatio='none'%3E%3Cpath d='M3 9C40 5 80 11 120 7S200 4 237 8' fill='none' stroke='black' stroke-width='3.4' stroke-linecap='round'/%3E%3C/svg%3E") center / 100% 100% no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 14' preserveAspectRatio='none'%3E%3Cpath d='M3 9C40 5 80 11 120 7S200 4 237 8' fill='none' stroke='black' stroke-width='3.4' stroke-linecap='round'/%3E%3C/svg%3E") center / 100% 100% no-repeat;
}

/* The easel blades: two L-shaped blades, top-left and bottom-right,
   resting 4px onto the paper. Each is one pseudo-element made of three
   background layers (ruler ticks, one bar, the other bar). */
.hero .container::before,
.hero .container::after {
    content: "";
    position: absolute;
    inset: -9px;
    pointer-events: none;
    background-repeat: no-repeat;
    filter: drop-shadow(0 3px 3px rgba(0, 0, 0, 0.35));
}

.hero .container::before {
    background-image:
        repeating-linear-gradient(90deg, var(--fl-blade-tick) 0 1px, transparent 1px 12px),
        linear-gradient(var(--fl-blade), var(--fl-blade)),
        linear-gradient(var(--fl-blade), var(--fl-blade));
    background-size: calc(100% - 30px) 4px, 100% 13px, 13px 100%;
    background-position: 26px 2px, 0 0, 0 0;
}

.hero .container::after {
    background-image:
        repeating-linear-gradient(90deg, var(--fl-blade-tick) 0 1px, transparent 1px 12px),
        linear-gradient(var(--fl-blade), var(--fl-blade)),
        linear-gradient(var(--fl-blade), var(--fl-blade));
    background-size: calc(100% - 30px) 4px, 100% 13px, 13px 100%;
    background-position: 4px calc(100% - 2px), 0 100%, 100% 0;
}

/* Hero with a photo (homepage, engine owns .hero-bg-media and the tint
   on ::after). The photo IS the projected image: the paper goes
   transparent and the blades frame it. */
.hero.hero--has-bg {
    padding-top: clamp(5rem, 12vw, 9rem);
    padding-bottom: clamp(5rem, 12vw, 9rem);
}

.hero--has-bg .container {
    background: none;
    box-shadow: none;
}

.hero--has-bg h1 {
    color: #fff;
}

.hero--has-bg h1::after {
    background-color: color-mix(in srgb, var(--primary) 70%, #fff);
}

/* ===================================================================
   THE EASEL CLOSES - first page of a visit only (head.html sets the
   flag), and only with animations on. The blades slide in from outside
   the paper, then the enlarger lamp comes on: the paper and title go
   from dim to lit, the cone of light appears. On the homepage with a
   photo, the photo develops under the lamp instead.
   =================================================================== */

@keyframes fl-blade-a {
    from { transform: translate(calc(var(--fl-travel) * -1), calc(var(--fl-travel) * -1)); opacity: 0; }
    30% { opacity: 1; }
    to { transform: none; opacity: 1; }
}

@keyframes fl-blade-b {
    from { transform: translate(var(--fl-travel), var(--fl-travel)); opacity: 0; }
    30% { opacity: 1; }
    to { transform: none; opacity: 1; }
}

@keyframes fl-lamp {
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes fl-expose {
    from { filter: brightness(0.32); }
    to { filter: brightness(1); }
}

html[data-fl-easel="run"] body[data-animations-enabled="true"] .hero .container::before {
    animation: fl-blade-a 1s cubic-bezier(0.2, 0.75, 0.25, 1) 0.1s backwards;
}

html[data-fl-easel="run"] body[data-animations-enabled="true"] .hero .container::after {
    animation: fl-blade-b 1s cubic-bezier(0.2, 0.75, 0.25, 1) 0.1s backwards;
}

html[data-fl-easel="run"] body[data-animations-enabled="true"] .hero:not(.hero--has-bg) .container {
    animation: fl-expose 0.45s ease-out 1s backwards;
}

html[data-fl-easel="run"] body[data-animations-enabled="true"] .hero:not(.hero--has-bg)::before {
    animation: fl-lamp 0.45s ease-out 1s backwards;
}

html[data-fl-easel="run"] body[data-animations-enabled="true"] .hero--has-bg .hero-bg-media img {
    animation: fl-develop calc(var(--fl-dev-time) * 1.4) cubic-bezier(0.35, 0.1, 0.3, 1) 0.9s backwards;
}

/* ===================================================================
   DEVELOPMENT - the main effect. A photo rises out of white paper when
   the engine marks its block as visible (.animated): shadows first,
   highlights last, as in the tray. Contrast collapses everything to
   mid-grey and a strong brightness pushes it to white; as both relax,
   the darkest tones leave white first and the highlights stay clipped
   until the end. The last keyframe is filter: none - the photo settles
   exactly as uploaded.
   =================================================================== */

@keyframes fl-develop {
    0% {
        filter: contrast(calc(1 - var(--fl-dev-s))) brightness(calc(1 + 1.2 * var(--fl-dev-s)));
    }
    40% {
        filter: contrast(calc(1 - 0.55 * var(--fl-dev-s))) brightness(calc(1 + 1.1 * var(--fl-dev-s)));
    }
    75% {
        filter: contrast(calc(1 - 0.15 * var(--fl-dev-s))) brightness(calc(1 + 0.35 * var(--fl-dev-s)));
    }
    100% {
        filter: none;
    }
}

body.js-ready[data-animations-enabled="true"] .gallery-grid.animated .gallery-item img,
body.js-ready[data-animations-enabled="true"] .page-card.animated img.page-card-image {
    animation: fl-develop var(--fl-dev-time) cubic-bezier(0.35, 0.1, 0.3, 1) backwards;
    animation-delay: calc(0.25s + var(--fl-i, 0) * 0.14s);
}

/* Frame by frame along the strip. */
.gallery-item:nth-child(2) { --fl-i: 1; }
.gallery-item:nth-child(3) { --fl-i: 2; }
.gallery-item:nth-child(4) { --fl-i: 3; }
.gallery-item:nth-child(5) { --fl-i: 4; }
.gallery-item:nth-child(6) { --fl-i: 5; }
.gallery-item:nth-child(7) { --fl-i: 6; }
.gallery-item:nth-child(8) { --fl-i: 7; }
.gallery-item:nth-child(n+9) { --fl-i: 8; }

.page-card:nth-child(3n+2) { --fl-i: 1; }
.page-card:nth-child(3n+3) { --fl-i: 2; }

/* ===================================================================
   SCROLL REVEAL - the engine's animation contract, all three rules.
   script.js adds .animated as blocks enter and .js-ready to <body>.
   Without the reset rule the offset is applied and never removed
   (Tarasy/Laminat/Preparat 1.0.4). opacity: 0 is safe without JS: the
   engine's <noscript> override uses !important.
   =================================================================== */

.animate-on-scroll {
    opacity: 0;
    transform: translateY(var(--animation-offset, 20px));
    transition: opacity 0.6s ease, transform 0.6s ease;
}

body.js-ready[data-animations-enabled="true"] .animate-on-scroll.animated {
    opacity: 1;
    transform: translateY(0);
}

body:not([data-animations-enabled="true"]) .animate-on-scroll {
    opacity: 1;
    transform: none;
}

/* The contact sheet lands quickly so the development is what you watch. */
.gallery-grid.animate-on-scroll {
    transition-duration: 0.35s;
}

/* ===================================================================
   THE PRINT SHEET - page content
   =================================================================== */

main > article > .container,
main > .container {
    padding-top: clamp(1.4rem, 3.5vw, 2.6rem);
}

.content-section {
    counter-reset: fl-frame;
    background: var(--fl-paper);
    padding: clamp(1.4rem, 4vw, 3rem);
    margin-bottom: clamp(1.6rem, 4vw, 2.8rem);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.14), 0 18px 38px -24px rgba(0, 0, 0, 0.45);
}

html[data-theme="dark"] .content-section {
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.6), 0 18px 38px -20px rgba(0, 0, 0, 0.9);
}

.content-section h2,
.content-section h3 {
    margin: 1.5em 0 0.55em;
}

.content-section h2 {
    font-size: clamp(1.8rem, 3.4vw, 2.4rem);
}

.content-section h3 {
    font-size: clamp(1.35rem, 2.4vw, 1.65rem);
}

.content-section > div > h2:first-child,
.content-section > div > h3:first-child {
    margin-top: 0.3em;
}

/* The pencil stroke under a section heading. */
.content-section h2::after,
.contact-form h2::after,
.testimonials-section h2::after {
    content: "";
    display: block;
    width: min(100%, 5.5em);
    height: 10px;
    margin-top: 0.25em;
    background-color: var(--fl-pencil);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 14' preserveAspectRatio='none'%3E%3Cpath d='M3 8C42 11 84 5 124 8S196 10 237 6' fill='none' stroke='black' stroke-width='3.4' stroke-linecap='round'/%3E%3C/svg%3E") center / 100% 100% no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 14' preserveAspectRatio='none'%3E%3Cpath d='M3 8C42 11 84 5 124 8S196 10 237 6' fill='none' stroke='black' stroke-width='3.4' stroke-linecap='round'/%3E%3C/svg%3E") center / 100% 100% no-repeat;
}

.content-section a:not(.btn):not(.cta-button):not(.price-tag) {
    color: var(--fl-mark);
    text-decoration: underline;
    text-decoration-color: var(--fl-pencil);
    text-decoration-thickness: 2px;
    text-underline-offset: 0.2em;
}

/* Safety net for images pasted into the text: never wider than the page. */
.content-section img {
    max-width: 100%;
    height: auto;
}

/* The text column (1.0.1, widened in 1.0.3): about 115 characters per
   full line (1.0.1 had about 75, which stood as a narrow pillar on a
   desktop screen); without it the 1440px page would stretch lines to
   about 150. The engine wraps the page text in the one child of
   .content-section that has neither a class nor an inline text-align
   (the price and CTA rows carry one), so that wrapper becomes a centred
   column; the contact sheets above and below it keep the whole width of
   the print. */
.content-section > div:not([class]):not([style*="text-align"]) {
    max-width: 80ch;
    margin-left: auto;
    margin-right: auto;
}

/* ===================================================================
   CONTACT SHEET - the gallery. Both .gallery-grid instances (above and
   below the text) are sheets; frames touch side by side so each row reads
   as one strip of film. The frame is 3:2 and the photo is shown WHOLE in
   it on the film base - the one place the theme refuses to crop.
   =================================================================== */

.gallery-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 14px 0;
    padding: 14px 12px;
    margin: 0 0 2rem;
    background: var(--fl-sheet);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 14px 30px -18px rgba(0, 0, 0, 0.6);
}

.gallery-item {
    position: relative;
    overflow: hidden;
    padding: 19px 5px 27px;
    cursor: zoom-in;
    counter-increment: fl-frame;
    background-color: var(--fl-rebate);
    background-image:
        repeating-linear-gradient(90deg, var(--fl-hole) 0 11px, transparent 11px 26px),
        repeating-linear-gradient(90deg, var(--fl-hole) 0 11px, transparent 11px 26px);
    background-size: 100% 8px, 100% 8px;
    background-position: 7px 5px, 7px calc(100% - 5px);
    background-repeat: no-repeat;
}

.gallery-item > picture {
    display: block;
}

.gallery-grid img,
.gallery-item img {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 3 / 2;
    object-fit: contain;
    object-position: center;
    background: var(--fl-film-base);
}

/* Edge printing: frame number, arrow, half-frame number. Numbers and a
   letter only, so nothing to translate. The counter runs across both
   sheets of a page, matching the lightbox order. */
.gallery-item::after {
    content: counter(fl-frame) "\00A0\00A0\25B8\00A0\00A0" counter(fl-frame) "A";
    position: absolute;
    left: 10px;
    bottom: 14px;
    font-family: var(--fl-mono);
    font-weight: 500;
    font-size: 9px;
    line-height: 1;
    letter-spacing: 0.08em;
    color: var(--fl-edge);
    pointer-events: none;
}

/* The grease-pencil loop a photographer draws round a keeper. Hover
   only, drawn in the scheme colour, loose and slightly skewed. */
.gallery-item::before {
    content: "";
    position: absolute;
    inset: 6px 1px 11px;
    z-index: 2;
    border: 3px solid var(--fl-pencil);
    border-radius: 18px 26px 22px 30px / 24px 18px 28px 20px;
    opacity: 0;
    transform: rotate(-1.6deg) scale(1.05);
    transition: opacity 0.2s ease, transform 0.25s ease;
    pointer-events: none;
}

.gallery-item:nth-child(even)::before {
    transform: rotate(1.3deg) scale(1.05);
}

.gallery-item:hover::before {
    opacity: 0.95;
    transform: rotate(-1.6deg) scale(1);
}

.gallery-item:nth-child(even):hover::before {
    transform: rotate(1.3deg) scale(1);
}

/* The loupe (created by head.html, mouse only). A chrome ring round a
   magnified view of the ~1920px file the lightbox also uses. */
.fl-loupe {
    position: fixed;
    left: 0;
    top: 0;
    z-index: 1500;
    width: 200px;
    height: 200px;
    border-radius: 50%;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s ease;
    background-color: var(--fl-film-base);
    background-repeat: no-repeat;
    box-shadow:
        inset 0 0 0 1px rgba(0, 0, 0, 0.45),
        inset 0 0 18px rgba(0, 0, 0, 0.25),
        0 0 0 4px #ecebe7,
        0 0 0 5px #8f8d88,
        0 0 0 8px #cfcdc8,
        0 0 0 9px #6f6d68,
        0 16px 34px rgba(0, 0, 0, 0.45);
}

.fl-loupe.is-on {
    opacity: 1;
}

/* ===================================================================
   PRINTS - page cards lie on the table with a white border.
   =================================================================== */

.onepage-sections,
.subpages-sections {
    padding: clamp(1.6rem, 4vw, 3rem) 0;
}

.page-cards-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: clamp(1.4rem, 3vw, 2.2rem);
}

.page-card {
    position: relative;
    display: flex;
    flex-direction: column;
    background: var(--fl-paper);
    padding: 12px 12px 1.3rem;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.14), 0 14px 28px -18px rgba(0, 0, 0, 0.5);
    transition: box-shadow 0.25s ease;
}

.page-card:hover {
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.16), 0 20px 36px -18px rgba(0, 0, 0, 0.6);
}

html[data-theme="dark"] .page-card {
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.6), 0 14px 28px -16px rgba(0, 0, 0, 0.9);
}

.page-card > picture {
    display: block;
}

/* The engine puts .page-card-image straight on the <img> (and on the
   placeholder <div>) - so it is sized here, on the image itself. */
.page-card-image {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 3 / 2;
    object-fit: cover;
    object-position: center;
    margin: 0 0 1rem;
    background: var(--fl-paper-2);
}

.page-card-content {
    display: flex;
    flex-direction: column;
    flex: 1;
    padding: 0 0.35rem;
}

.page-card-title {
    font-family: var(--fl-serif);
    font-weight: 400;
    font-size: 1.5rem;
    line-height: 1.12;
    margin: 0 0 0.5rem;
    color: var(--fl-ink);
}

.page-card-excerpt {
    margin: 0 0 1rem;
    font-size: 0.97rem;
    color: var(--fl-ink-soft);
}

.page-card-link {
    align-self: flex-start;
    margin-top: auto;
    font-weight: 600;
    font-size: 0.95rem;
    color: var(--fl-mark);
    text-decoration: underline;
    text-decoration-color: var(--fl-pencil);
    text-decoration-thickness: 2px;
    text-underline-offset: 0.25em;
}

.page-card-link:hover {
    text-decoration-thickness: 3px;
}

/* ===================================================================
   PLACEHOLDERS - pages without a photo get darkroom objects instead of
   the engine's emoji. Three compositions on 3n+1 / 3n+2 / 3n+3, which
   are mutually exclusive by construction; the base rule sets only what
   all three share, so nothing leaks between them (Cyberpunk 1.0.12).
   =================================================================== */

.page-card-placeholder {
    position: relative;
    overflow: hidden;
    font-size: 0;
    line-height: 0;
    background-color: var(--fl-paper-2);
}

.page-card-placeholder span {
    display: none;
}

.page-card-placeholder::before,
.page-card-placeholder::after {
    content: "";
    position: absolute;
    pointer-events: none;
}

/* 1 - the test strip: six exposures on the upper part, the seconds
   pencilled on the white margin below, the right step circled. */
.page-card:nth-child(3n+1) .page-card-placeholder {
    background-color: #f7f6f1;
    background-image: linear-gradient(90deg,
        #ecebe6 0 16.66%, #cfcdc6 16.66% 33.33%, #a8a59c 33.33% 50%,
        #77746c 50% 66.66%, #494742 66.66% 83.33%, #22211f 83.33% 100%);
    background-size: 100% 76%;
    background-position: 0 0;
    background-repeat: no-repeat;
}

.page-card:nth-child(3n+1) .page-card-placeholder::before {
    left: calc(50% - 7px);
    width: calc(16.66% + 14px);
    top: 8%;
    bottom: 3%;
    border: 3px solid var(--fl-pencil);
    border-radius: 50%;
    transform: rotate(-5deg);
}

.page-card:nth-child(3n+1) .page-card-placeholder::after {
    content: "2 4 8 16 32 64";
    left: 0;
    right: 0;
    bottom: 8%;
    padding: 0 calc(8.33% - 0.4em);
    font-family: var(--fl-mono);
    font-weight: 500;
    font-size: 12px;
    line-height: 1;
    text-align: justify;
    text-align-last: justify;
    color: #4a4c50;
}

/* 2 - an unexposed sheet on the easel, a focus cross in pencil. */
.page-card:nth-child(3n+2) .page-card-placeholder {
    background-color: var(--fl-base);
}

.page-card:nth-child(3n+2) .page-card-placeholder::before {
    inset: 17% 19%;
    background:
        linear-gradient(var(--fl-pencil), var(--fl-pencil)) center / 24px 3px no-repeat,
        linear-gradient(var(--fl-pencil), var(--fl-pencil)) center / 3px 24px no-repeat,
        #f7f6f1;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.45);
}

.page-card:nth-child(3n+2) .page-card-placeholder::after {
    inset: calc(17% - 6px) calc(19% - 6px);
    background:
        linear-gradient(var(--fl-blade), var(--fl-blade)) 0 0 / 100% 9px no-repeat,
        linear-gradient(var(--fl-blade), var(--fl-blade)) 0 0 / 9px 100% no-repeat,
        linear-gradient(var(--fl-blade), var(--fl-blade)) 0 100% / 100% 9px no-repeat,
        linear-gradient(var(--fl-blade), var(--fl-blade)) 100% 0 / 9px 100% no-repeat;
    -webkit-mask:
        linear-gradient(#000, #000) 0 0 / 58% 62% no-repeat,
        linear-gradient(#000, #000) 100% 100% / 58% 62% no-repeat;
    mask:
        linear-gradient(#000, #000) 0 0 / 58% 62% no-repeat,
        linear-gradient(#000, #000) 100% 100% / 58% 62% no-repeat;
    filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.4));
}

/* 3 - a strip of negatives, the middle frame crossed out. */
.page-card:nth-child(3n+3) .page-card-placeholder {
    background-color: var(--fl-rebate);
    background-image:
        repeating-linear-gradient(90deg, var(--fl-hole) 0 10px, transparent 10px 23px),
        repeating-linear-gradient(90deg, var(--fl-hole) 0 10px, transparent 10px 23px);
    background-size: 100% 7px, 100% 7px;
    background-position: 6px 7%, 6px 93%;
    background-repeat: no-repeat;
}

.page-card:nth-child(3n+3) .page-card-placeholder::before {
    left: 4%;
    right: 4%;
    top: 20%;
    bottom: 20%;
    background:
        linear-gradient(to bottom, transparent 58%, rgba(0, 0, 0, 0.22) 58%),
        repeating-linear-gradient(90deg, #c5c2b9 0 30%, transparent 30% 35%);
}

.page-card:nth-child(3n+3) .page-card-placeholder::after {
    left: 36.2%;
    width: 27.6%;
    top: 20%;
    bottom: 20%;
    background:
        linear-gradient(to top right, transparent calc(50% - 1.6px), var(--fl-pencil) calc(50% - 1.6px) calc(50% + 1.6px), transparent calc(50% + 1.6px)),
        linear-gradient(to bottom right, transparent calc(50% - 1.6px), var(--fl-pencil) calc(50% - 1.6px) calc(50% + 1.6px), transparent calc(50% + 1.6px));
}

/* ===================================================================
   BUTTONS, PRICE
   =================================================================== */

.btn,
.cta-button,
button[type="submit"],
input[type="submit"] {
    display: inline-block;
    font-family: var(--fl-sans);
    font-weight: 600;
    font-size: 0.98rem;
    line-height: 1.2;
    padding: 0.85rem 1.6rem;
    border: 0;
    border-radius: 2px;
    color: var(--fl-on-mark);
    background: var(--fl-mark);
    box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2);
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.2s ease;
}

.btn:hover,
.cta-button:hover,
button[type="submit"]:hover,
input[type="submit"]:hover {
    background: var(--fl-ink);
    color: var(--fl-paper);
}

.price-tag {
    display: inline-block;
    font-family: var(--fl-mono);
    font-weight: 500;
    font-size: 1.05rem;
    padding: 0.5rem 1rem;
    color: var(--fl-ink);
    background: var(--fl-paper);
    border: 1.5px solid var(--fl-ink);
    box-shadow: 4px 4px 0 var(--fl-pencil);
    text-decoration: none;
}

a.price-tag:hover {
    box-shadow: 2px 2px 0 var(--fl-pencil);
}

/* ===================================================================
   TESTIMONIALS - notes pencilled on the back of prints
   =================================================================== */

.testimonials-section {
    padding: clamp(2rem, 5vw, 3.5rem) 0;
}

/* The engine writes color: var(--primary) inline on this heading; raw
   scheme colour must never carry text in this theme (pale schemes fall
   far below 3:1), so this is the one justified !important. */
.testimonials-section h2 {
    font-family: var(--fl-serif);
    font-weight: 400;
    font-size: clamp(1.8rem, 3.4vw, 2.4rem);
    color: var(--fl-ink) !important;
}

.testimonials-section h2::after {
    margin-left: auto;
    margin-right: auto;
}

.testimonials-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: clamp(1.2rem, 3vw, 2rem);
    margin-top: 1.5rem;
}

.testimonial-card {
    background: var(--fl-paper);
    color: var(--fl-ink);
    padding: 1.5rem 1.5rem 1.3rem;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.14), 0 12px 24px -18px rgba(0, 0, 0, 0.5);
}

.testimonial-rating {
    margin: 0 0 0.5rem;
    letter-spacing: 0.12em;
    color: var(--fl-mark);
}

.testimonial-text {
    margin: 0 0 0.9rem;
    font-family: var(--fl-serif);
    font-style: italic;
    font-size: 1.22rem;
    line-height: 1.4;
}

.testimonial-author {
    margin: 0;
    font-weight: 600;
    font-size: 0.92rem;
    color: var(--fl-ink-soft);
}

/* ===================================================================
   CONTACT FORM - a lab order slip
   =================================================================== */

.contact-form {
    max-width: 760px;
    margin: clamp(1.6rem, 4vw, 2.8rem) auto;
    padding: clamp(1.4rem, 4vw, 2.6rem);
    background: var(--fl-paper);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.14), 0 18px 38px -24px rgba(0, 0, 0, 0.45);
}

.content-section .contact-form {
    box-shadow: none;
    padding-left: 0;
    padding-right: 0;
}

.contact-form h2 {
    margin: 0 0 1.2rem;
    font-size: clamp(1.8rem, 3.4vw, 2.4rem);
}

.form-group {
    margin-bottom: 1.1rem;
}

.contact-form label {
    display: block;
    margin-bottom: 0.3rem;
    font-size: 0.86rem;
    font-weight: 500;
    color: var(--fl-ink-soft);
}

.contact-form input[type="text"],
.contact-form input[type="email"],
.contact-form input[type="tel"],
.contact-form input[type="number"],
.contact-form textarea,
.contact-form select {
    width: 100%;
    font: inherit;
    color: var(--fl-ink);
    background: transparent;
    border: 0;
    border-bottom: 1.5px solid var(--fl-rule);
    border-radius: 0;
    padding: 0.55rem 0.1rem;
}

.contact-form textarea {
    min-height: 130px;
    resize: vertical;
    border: 1.5px solid var(--fl-rule);
    padding: 0.6rem 0.7rem;
}

.contact-form input:focus,
.contact-form textarea:focus,
.contact-form select:focus {
    border-color: var(--fl-pencil);
}

.success {
    padding: 0.7rem 0.9rem;
    margin-bottom: 1rem;
    font-weight: 500;
    color: #1d5d34;
    background: #e5f1e8;
}

.error {
    padding: 0.7rem 0.9rem;
    margin-bottom: 1rem;
    font-weight: 500;
    color: #8e1f14;
    background: #f7e3df;
}

html[data-theme="dark"] .success {
    color: #bfe7c8;
    background: #17301f;
}

html[data-theme="dark"] .error {
    color: #ffc9bf;
    background: #3b1511;
}

/* ===================================================================
   FOOTER - the end of the roll. A strip of film runs along its top edge
   and the panel's footer colour is taken halfway to film black.
   Why: with the footer colour on "auto" the engine uses --secondary,
   and on the pale schemes white text on it measured as low as 1.61:1
   (ice), 1.92 (mint), 2.45 (sun) - 16 of 48 schemes failed. Mixed 50%
   toward #121110 the worst of all 48 is 5.06:1. Footer colours picked
   in the panel keep their hue, one step deeper.
   =================================================================== */

footer {
    position: relative;
    margin-top: clamp(3rem, 6vw, 4.5rem);
    padding: 3rem 0 2rem;
    background: color-mix(in srgb, var(--footer-bg, var(--secondary)) 50%, #121110);
    color: #fff;
}

footer::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: -22px;
    height: 22px;
    pointer-events: none;
    background-color: var(--fl-rebate);
    background-image: repeating-linear-gradient(90deg, var(--fl-hole) 0 11px, transparent 11px 26px);
    background-size: 100% 8px;
    background-position: 7px 7px;
    background-repeat: no-repeat;
}

footer h3,
footer h4 {
    color: #fff;
}

footer a {
    color: #fff;
    text-decoration: underline;
    text-underline-offset: 0.18em;
}

.footer-content {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: clamp(1.4rem, 4vw, 3rem);
}

.footer-section h3,
.footer-section h4 {
    margin: 0 0 0.7rem;
    font-size: 1.35rem;
}

.footer-section p,
.footer-section li {
    margin: 0 0 0.45rem;
}

.footer-menu {
    list-style: none;
    margin: 0;
    padding: 0;
}

.footer-menu-text {
    opacity: 0.9;
}

.social-links {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin: 0.2rem 0 0 -10px;
}

.social-links a {
    color: #fff;
}

.copyright {
    margin-top: 1.8rem;
    padding-top: 1.1rem;
    border-top: 1px solid rgba(255, 255, 255, 0.25);
    font-size: 0.88rem;
    opacity: 0.92;
}

/* ===================================================================
   DARK-MODE CONTROL - fixed bottom right, fleet convention. Drawn as the
   darkroom SAFELIGHT: by day its red glass is dark, switched on it glows
   - which is exactly what dark mode is here.
   =================================================================== */

.dark-mode-toggle {
    position: fixed;
    bottom: 20px;
    right: 20px;
    z-index: 1000;
    width: 50px;
    height: 50px;
    padding: 0;
    font-size: 0;
    line-height: 0;
    cursor: pointer;
    border: 1px solid var(--fl-rule);
    border-radius: 50%;
    background: var(--fl-paper);
    box-shadow: 0 8px 18px -8px rgba(0, 0, 0, 0.5);
}

.dark-mode-toggle::before {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 24px;
    height: 19px;
    transform: translate(-50%, -58%);
    border-radius: 12px 12px 3px 3px;
    background: #3a3634;
}

.dark-mode-toggle::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    width: 15px;
    height: 10px;
    transform: translate(-50%, -38%);
    border-radius: 8px 8px 2px 2px;
    background: #6b2e25;
    transition: background-color 0.3s ease, box-shadow 0.3s ease;
}

.dark-mode-toggle:hover::after {
    background: #8a3a2e;
}

html[data-theme="dark"] .dark-mode-toggle {
    background: #1b1210;
    border-color: #5a2a20;
}

html[data-theme="dark"] .dark-mode-toggle::after {
    background: #ff5b37;
    box-shadow: 0 0 10px 3px rgba(255, 91, 55, 0.75), 0 0 24px 8px rgba(255, 91, 55, 0.3);
}

/* ===================================================================
   LIGHTBOX - the light table: neutral black, no scheme colour near the
   photo. Hidden until .active, fixed above everything (the engine emits
   it at the end of <body> on every page with a gallery).
   =================================================================== */

.lightbox {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100vh;
    height: 100dvh;
    z-index: 2000;
    background: rgba(10, 10, 10, 0.96);
    flex-direction: column;
    justify-content: center;
    align-items: center;
    cursor: pointer;
}

.lightbox.active {
    display: flex;
}

.lightbox img {
    max-width: 92%;
    max-height: 82vh;
    max-height: 82dvh;
    box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.08);
    cursor: default;
}

.lightbox-close {
    position: absolute;
    top: 16px;
    right: 20px;
    font-size: 2.2rem;
    line-height: 1;
    color: #e8e5de;
    background: none;
    border: 0;
    cursor: pointer;
}

.lightbox-counter {
    margin-top: 1rem;
    font-family: var(--fl-mono);
    font-size: 0.9rem;
    letter-spacing: 0.12em;
    color: #bdb7ab;
}

.lightbox-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    font-size: 2.2rem;
    line-height: 1;
    padding: 0.7rem 1rem;
    color: #e8e5de;
    background: rgba(255, 255, 255, 0.08);
    border: 0;
    border-radius: 2px;
    cursor: pointer;
}

.lightbox-nav:hover {
    background: rgba(255, 255, 255, 0.18);
}

.lightbox-prev {
    left: 20px;
}

.lightbox-next {
    right: 20px;
}

/* ===================================================================
   FOCUS - five selectors, fleet convention
   =================================================================== */

a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible {
    outline: 3px solid var(--fl-pencil);
    outline-offset: 2px;
}

/* ===================================================================
   NARROW SCREENS
   =================================================================== */

@media (max-width: 720px) {
    .logo-container img {
        height: 56px;
    }

    .site-title {
        font-size: 1.35rem;
    }

    .gallery-grid {
        grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
        gap: 10px 0;
        padding: 10px 8px;
    }

    .gallery-item {
        padding: 15px 3px 22px;
        background-image:
            repeating-linear-gradient(90deg, var(--fl-hole) 0 9px, transparent 9px 21px),
            repeating-linear-gradient(90deg, var(--fl-hole) 0 9px, transparent 9px 21px);
        background-size: 100% 6px, 100% 6px;
        background-position: 5px 4px, 5px calc(100% - 4px);
    }

    .gallery-item::after {
        left: 7px;
        bottom: 11px;
        font-size: 7.5px;
    }

    .hero .container::before,
    .hero .container::after {
        inset: -7px;
        background-size: calc(100% - 24px) 3px, 100% 10px, 10px 100%;
    }

    .hero .container::before {
        background-position: 20px 2px, 0 0, 0 0;
    }

    .hero .container::after {
        background-position: 4px calc(100% - 2px), 0 100%, 100% 0;
    }

    .fl-loupe {
        display: none;
    }
}

/* ===================================================================
   REDUCED MOTION - no development, no easel, no reveal offset.
   =================================================================== */

@media (prefers-reduced-motion: reduce) {
    .animate-on-scroll,
    body.js-ready[data-animations-enabled="true"] .animate-on-scroll.animated {
        opacity: 1;
        transform: none;
        transition: none;
    }

    .gallery-item img,
    .page-card-image,
    .hero .container,
    .hero .container::before,
    .hero .container::after,
    .hero::before,
    .hero-bg-media img {
        animation: none !important;
    }
}

/* ===================================================================
   PRINT - white paper, plain type; photos exactly as they are.
   =================================================================== */

@media print {
    body {
        background: #fff;
        color: #000;
    }

    body::before,
    .dark-mode-toggle,
    .hamburger-menu,
    .fl-loupe,
    .hero::before,
    .hero .container::before,
    .hero .container::after,
    .hero h1::after,
    footer::before,
    .gallery-item::before,
    .page-card-placeholder {
        display: none !important;
    }

    .animate-on-scroll {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }

    img {
        animation: none !important;
        filter: none !important;
    }

    header,
    footer,
    .hero,
    .hero .container,
    .content-section,
    .page-card,
    .testimonial-card,
    .contact-form {
        background: none !important;
        color: #000 !important;
        box-shadow: none !important;
    }

    .hero h1,
    footer a,
    footer h3 {
        color: #000 !important;
    }

    .gallery-grid {
        background: none;
        box-shadow: none;
        gap: 8px;
        padding: 0;
    }

    .gallery-item {
        background: none;
        padding: 0 0 14px;
        break-inside: avoid;
    }

    .gallery-item::after {
        color: #555;
        bottom: 1px;
        left: 0;
    }

    .page-card,
    .content-section {
        break-inside: avoid;
        border: 1px solid #999;
    }
}
