/* Photography section — Phase 1 minimal styles. * * Phase 2 will introduce the masonry / grid / chronological mode toggle, * darkroom lightbox treatment, and richer card hover states. For now the * goal is structural correctness and clean defaults so a single photo * page reads end-to-end. * * Style scope: applied via $if(photography)$ gate in templates/partials/head.html, * so these rules only load on photography pages. */ /* --------------------------------------------------------------------------- * Single-photo page * --------------------------------------------------------------------------- */ .photo-entry { max-width: 60rem; margin: 0 auto; } /* Single-photo entries keep their own centred header; series pages use the * essay frontmatter grid instead (see .photo-series-frontmatter below). */ .photo-header { margin-bottom: 2.25rem; text-align: center; } .photo-header .page-title { margin-bottom: 0.9rem; } .photo-header .meta-row { justify-content: center; } .photo-header .photo-captured { margin-top: 0.35rem; } /* --------------------------------------------------------------------------- * Series frontmatter — the essay header, with photographs in it. * * A series landing has an essay's shape, so it uses an essay's grid: * [ mark ] [ title block ] [ mark ]. The left slot, which on an essay holds * the monogram, holds the lead frame; the right, which holds the epistemic * figure, holds the series' statistics. Both are marks in the same sense — * small, fixed-width, and about the thing rather than part of it. * --------------------------------------------------------------------------- */ .photo-series-frontmatter { align-items: start; } .photo-series-lead { margin: 0; } .photo-series-lead img { width: 100%; height: auto; display: block; background: var(--bg-muted); cursor: zoom-in; /* A quiet mount, so a photograph in the mark column reads as a plate rather than as a picture that failed to be full width. */ border: 1px solid var(--border); padding: 0.3rem; } /* Statistics, in the register of /build/ and /stats/ — this site already * treats counting things as worth showing. Tabular figures so the values * form a column, small-caps labels so they stay subordinate to them. */ .photo-series-stats { margin: 0; font-family: var(--font-sans); font-size: 0.7rem; line-height: 1.5; font-variant-numeric: tabular-nums; display: flex; flex-direction: column; gap: 0.15rem; } .photo-series-stats .stat-row { display: grid; grid-template-columns: 3.6rem minmax(0, 1fr); gap: 0.4rem; align-items: baseline; } .photo-series-stats dt { font-variant: all-small-caps; letter-spacing: 0.08em; color: var(--text-faint); } .photo-series-stats dd { margin: 0; color: var(--text-muted); overflow-wrap: anywhere; } /* Below the grid's breakpoint the marks stack under the title, where a * fixed narrow column makes no sense. */ @media (max-width: 900px) { .photo-series-frontmatter .frontmatter-mark-slot--left { max-width: 22rem; margin: 0 auto; } .photo-series-stats { align-items: center; } .photo-series-stats .stat-row { grid-template-columns: 3.6rem auto; justify-content: center; } } .photo-figure { margin: 1rem 0 1.25rem; text-align: center; } .photo-figure img { max-width: 100%; height: auto; cursor: zoom-in; } .photo-caption { margin-top: 0.5rem; font-style: italic; color: var(--text-muted); font-size: 0.95em; } .photo-captured { color: var(--text-muted); font-size: 0.95em; } .photo-location { font-style: italic; } /* Color palette strip (Phase 1 stub; populated by Phase 3 auto-extraction * once tools/extract-palette.py lands). Renders only when frontmatter has * a non-empty palette: list. */ .photo-palette { display: flex; gap: 2px; margin: 1rem 0 1.5rem; height: 0.75rem; } .photo-swatch { flex: 1; border-radius: 1px; } /* Per-photo metadata block — camera, lens, exposure, etc. Two-column * description list, definition-list semantics for accessibility. */ .photo-meta { display: grid; grid-template-columns: max-content 1fr; gap: 0.25rem 1rem; margin: 1.25rem 0; padding: 0.75rem 1rem; border-left: 2px solid var(--border-muted); background: var(--bg-muted); font-size: 0.9em; } .photo-meta dt { font-weight: 600; color: var(--text-muted); text-transform: lowercase; letter-spacing: 0.02em; } .photo-meta dd { margin: 0; } .photo-license { color: inherit; } .photo-external-links { margin: 0.75rem 0 1.25rem; font-size: 0.9em; color: var(--text-muted); } .photo-external-links .meta-label { margin-right: 0.5rem; text-transform: lowercase; letter-spacing: 0.02em; } .photo-external-link { color: var(--text); } /* --------------------------------------------------------------------------- * Section landing — /photography/ * --------------------------------------------------------------------------- */ .photography-intro { max-width: 36rem; margin: 0 auto 2rem; color: var(--text-muted); } .photography-empty { text-align: center; color: var(--text-faint); font-style: italic; margin: 4rem auto; } /* --------------------------------------------------------------------------- * Mode toggle UI * --------------------------------------------------------------------------- */ .photography-header { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: 1.5rem; } /* Mode toggle. * * These are anchors, so they were inheriting body-link underlines and reading * as a row of links inside a box; the active one inverted to a solid slab of * ink, which shouted louder than anything else on a page of photographs. The * register here follows .list-count-btn in item-card.css — the site's * existing quiet control — in small caps, with the active state carried by * ink and border rather than by fill. */ .photography-controls { display: flex; align-items: center; justify-content: center; gap: 0.5rem; margin: 0 0 1.75rem; } .photography-mode-toggle { display: inline-flex; gap: 0.3rem; flex-wrap: wrap; justify-content: center; } .photography-mode-toggle .mode-btn { padding: 0.3rem 0.75rem; background: none; border: 1px solid var(--border); border-radius: 2px; color: var(--text-faint); cursor: pointer; font-family: var(--font-sans); font-size: 0.7rem; font-variant: all-small-caps; letter-spacing: 0.09em; line-height: 1.6; text-decoration: none; transition: color var(--transition-fast), border-color var(--transition-fast); } .photography-mode-toggle .mode-btn:hover { color: var(--text); border-color: var(--text-muted); text-decoration: none; } .photography-mode-toggle .mode-btn.is-active { color: var(--text); border-color: var(--text-muted); font-weight: 600; } .photography-mode-toggle .mode-btn:focus-visible { outline: 2px solid var(--text-muted); outline-offset: 2px; } @media (prefers-reduced-motion: reduce) { .photography-mode-toggle .mode-btn { transition: none; } } /* --------------------------------------------------------------------------- * Grid container — base + per-mode rules. * * The same .photography-grid markup feeds three layout strategies; the * data-photography-mode attribute (set by photography-modes.js) keys all * per-mode rules. JS also sets inline grid-row spans on each card in * masonry mode; clearing the attribute clears the inline style. * --------------------------------------------------------------------------- */ .photography-grid { list-style: none; padding: 0; margin: 0; display: grid; } /* Masonry — variable-height cells respecting native aspect ratios. * grid-auto-rows + JS-applied grid-row spans synthesize true masonry. */ .photography-grid[data-photography-mode="masonry"] { /* 16rem, not 22rem. The grid lives inside the 800px body measure, where * 22rem tracks left room for only two columns — near enough to * chronological's single wide column that, with a corpus shot entirely in * one orientation, the two modes were indistinguishable. At 16rem three * columns fit, and the three modes read as three different things: * many small squares, several native-proportion frames, one large column. */ grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr)); grid-auto-rows: 8px; /* must match ROW_UNIT in photography-modes.js */ gap: 8px; /* must match ROW_GAP in photography-modes.js */ align-items: stretch; } .photography-grid[data-photography-mode="masonry"] .photo-card-img { width: 100%; height: auto; display: block; } /* Uniform grid — a dense contact sheet. * * Masonry and grid were nearly indistinguishable in practice: both drew * captioned cards at similar widths, and with a corpus shot mostly in one * orientation masonry's variable heights never got to vary. The two modes * now answer different questions. Masonry is for reading the collection — * native proportions, captions always legible. Grid is for scanning it — * tighter tiles, square crops, and the caption withheld until you single a * frame out. Nothing on screen competes with the pictures. */ .photography-grid[data-photography-mode="grid"] { grid-template-columns: repeat(auto-fill, minmax(min(100%, 12rem), 1fr)); gap: 0.4rem; } .photography-grid[data-photography-mode="grid"] .photo-card-link { position: relative; display: block; } .photography-grid[data-photography-mode="grid"] .photo-card-img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; display: block; background: var(--bg-muted); } /* The caption rides over the frame rather than below it, so tiles stay * flush and the grid reads as one field of images. * * Hidden with opacity and transform, never display/visibility: the title is * the accessible name of the surrounding link, and removing it from the box * tree would take it out of the accessibility tree with it. This way it is * always announced and merely not painted. */ .photography-grid[data-photography-mode="grid"] .photo-card-meta { position: absolute; inset: auto 0 0 0; display: flex; flex-direction: column; align-items: flex-start; gap: 0.05rem; margin: 0; padding: 1.75rem 0.6rem 0.5rem; font-size: 0.78em; color: #f2f2f2; background: linear-gradient( to top, rgba(0, 0, 0, 0.78) 0%, rgba(0, 0, 0, 0.55) 45%, rgba(0, 0, 0, 0) 100% ); opacity: 0; transform: translateY(0.35rem); transition: opacity 140ms ease, transform 140ms ease; pointer-events: none; } .photography-grid[data-photography-mode="grid"] .photo-card-link:hover .photo-card-meta, .photography-grid[data-photography-mode="grid"] .photo-card-link:focus-visible .photo-card-meta, .photography-grid[data-photography-mode="grid"] .photo-card:focus-within .photo-card-meta { opacity: 1; transform: translateY(0); } .photography-grid[data-photography-mode="grid"] .photo-card-title { font-weight: 500; line-height: 1.25; /* Two lines maximum: a long title must not grow a scrim tall enough to * cover the frame it is describing. */ display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .photography-grid[data-photography-mode="grid"] .photo-card-date { color: rgba(255, 255, 255, 0.72); font-size: 0.92em; } /* A hover-only affordance is no affordance on a touchscreen. Where hover * does not exist, the caption is simply always shown. */ @media (hover: none) { .photography-grid[data-photography-mode="grid"] .photo-card-meta { opacity: 1; transform: none; } } @media (prefers-reduced-motion: reduce) { .photography-grid[data-photography-mode="grid"] .photo-card-meta { transition: none; } } /* Chronological — single column, large, contemplative. */ .photography-grid[data-photography-mode="chronological"] { grid-template-columns: minmax(0, 48rem); justify-content: center; gap: 3rem; } .photography-grid[data-photography-mode="chronological"] .photo-card-img { width: 100%; height: auto; display: block; } .photography-grid[data-photography-mode="chronological"] .photo-card-meta { margin-top: 0.75rem; font-size: 1em; } /* Series lead image. * * A series landing sets `photo:` to one of its own children's filenames — * the frame that should introduce the set. No file is duplicated: photo-url * resolves against the containing directory, and a landing lives in the same * directory as its children, so it addresses the child's image directly. * Deliberately not lazy-loaded; it is the first thing on the page. */ .photo-series-lead img { width: 100%; height: auto; display: block; background: var(--bg-muted); cursor: zoom-in; } /* --------------------------------------------------------------------------- * Card chrome — shared across all three modes * --------------------------------------------------------------------------- */ .photo-card { margin: 0; } .photo-card-link { display: block; text-decoration: none; color: inherit; overflow: hidden; } .photo-card-img { background: var(--bg-muted); } .photo-card-meta { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; margin-top: 0.4rem; font-size: 0.9em; } .photo-card-title { font-weight: 500; } .photo-card-date { color: var(--text-muted); font-variant-numeric: oldstyle-nums; flex-shrink: 0; } /* --------------------------------------------------------------------------- * Darkroom lightbox — photography pages only * * Augments the existing lightbox (static/css/images.css) when the JS * detects body[data-page-type="photography"] and adds the .darkroom * class to the overlay. Non-photography lightbox styling is unaffected. * --------------------------------------------------------------------------- */ .lightbox-overlay.darkroom { background: #000; } .lightbox-overlay.darkroom .lightbox-vignette { position: absolute; inset: 0; pointer-events: none; background: radial-gradient( ellipse at center, rgba(0, 0, 0, 0) 35%, rgba(0, 0, 0, 0.55) 100% ); transition: opacity 200ms ease; } .lightbox-overlay:not(.darkroom) .lightbox-vignette, .lightbox-overlay:not(.darkroom) .lightbox-info-toggle, .lightbox-overlay:not(.darkroom) .lightbox-info-panel { display: none; } .lightbox-overlay.darkroom .lightbox-caption { color: rgba(255, 255, 255, 0.78); font-style: italic; font-size: 0.95em; max-width: 48rem; margin: 1rem auto 0; text-align: center; } .lightbox-overlay.darkroom .lightbox-close { color: rgba(255, 255, 255, 0.8); } /* "i" toggle button — top-right corner alongside the close button. */ .lightbox-info-toggle { position: absolute; top: 0.75rem; right: 3.5rem; z-index: 2; width: 2rem; height: 2rem; display: flex; align-items: center; justify-content: center; background: transparent; border: 1px solid rgba(255, 255, 255, 0.4); border-radius: 50%; color: rgba(255, 255, 255, 0.8); font-size: 1rem; cursor: pointer; transition: background 120ms ease, border-color 120ms ease; } .lightbox-info-toggle:hover, .lightbox-info-toggle[aria-pressed="true"] { background: rgba(255, 255, 255, 0.12); border-color: rgba(255, 255, 255, 0.7); } /* Info panel — slides in from below; muted serif for the EXIF strip. */ .lightbox-info-panel { position: absolute; bottom: 0; left: 50%; transform: translate(-50%, 100%); max-width: 48rem; width: calc(100% - 2rem); padding: 1rem 1.25rem; background: rgba(0, 0, 0, 0.85); color: rgba(255, 255, 255, 0.85); border-top: 1px solid rgba(255, 255, 255, 0.12); transition: transform 220ms ease; pointer-events: none; } .lightbox-overlay.is-info-visible .lightbox-info-panel { transform: translate(-50%, 0); pointer-events: auto; } .lightbox-info-panel dl { display: grid; grid-template-columns: max-content 1fr; gap: 0.25rem 1.25rem; margin: 0; font-size: 0.9em; } .lightbox-info-panel dt { font-weight: 500; color: rgba(255, 255, 255, 0.55); text-transform: lowercase; letter-spacing: 0.04em; } .lightbox-info-panel dd { margin: 0; color: rgba(255, 255, 255, 0.92); } /* --------------------------------------------------------------------------- * Map page — /photography/map/ * * The viewport is sized in viewport-relative units so the map fills * a useful portion of the screen without dominating it; the * surrounding page chrome (header, attribution paragraph, footer) * stays visible at typical desktop heights. * * Leaflet's own stylesheet handles tile / control / popup styling; * these rules cover only the page-level shell, the tooltip content * we render via tooltipHtml(), and the no-pin / error fallback states. * --------------------------------------------------------------------------- */ .photography-map { height: 70vh; min-height: 32rem; margin: 1.5rem 0 1rem; border: 1px solid var(--border); background: var(--bg-muted); } .photography-map--empty, .photography-map--error { display: flex; align-items: center; justify-content: center; text-align: center; padding: 2rem; } .photography-map-empty, .photography-map-error, .photography-map-fallback { max-width: 32rem; margin: 0 auto; color: var(--text-muted); font-style: italic; } .photography-map-note { margin: 0.5rem 0 2rem; color: var(--text-faint); font-size: 0.85em; max-width: 48rem; } .photography-map-note code { background: var(--bg-muted); padding: 0 0.3em; border-radius: 2px; font-size: 0.9em; } /* Tooltip content — rendered by photography-map.js inside Leaflet's * default tooltip wrapper. The wrapper class .photography-map-tooltip-wrap * lets us tighten Leaflet's default padding without bleeding into the * other Leaflet popups. */ .leaflet-tooltip.photography-map-tooltip-wrap { background: var(--bg); color: var(--text); border: 1px solid var(--border); border-radius: 2px; padding: 0; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); font-family: inherit; } /* Leaflet draws tooltip arrows via ::before; recolor to match. */ .leaflet-tooltip.photography-map-tooltip-wrap::before { border-top-color: var(--border); } .photography-map-tooltip { width: 14rem; max-width: 16rem; } .photography-map-tooltip-img { display: block; width: 100%; height: 8rem; object-fit: cover; margin: 0; border-bottom: 1px solid var(--border); } .photography-map-tooltip-title { padding: 0.4rem 0.6rem 0.1rem; font-weight: 500; line-height: 1.2; } .photography-map-tooltip-date { padding: 0 0.6rem 0.4rem; color: var(--text-muted); font-size: 0.85em; font-variant-numeric: oldstyle-nums; } /* --------------------------------------------------------------------------- * By-year index — /photography/by-year/ * * A simple narrow column of years, each link a small card with the * year (large oldstyle figures) and a count (muted small caps). * --------------------------------------------------------------------------- */ .photography-header--narrow { flex-direction: column; align-items: flex-start; gap: 0.25rem; } .photography-by-year-back { margin: 0 0 1rem; color: var(--text-muted); font-size: 0.9em; } .photography-by-year-list { list-style: none; padding: 0; margin: 1rem 0 2rem; max-width: 24rem; } .photography-by-year-item { margin: 0; } .photography-by-year-link { display: flex; justify-content: space-between; align-items: baseline; padding: 0.5rem 0; border-bottom: 1px solid var(--border-muted); text-decoration: none; color: inherit; transition: color 120ms ease; } .photography-by-year-link:hover { color: var(--text); } .photography-by-year-year { font-size: 1.4em; font-variant-numeric: oldstyle-nums; } .photography-by-year-count { color: var(--text-muted); font-size: 0.85em; text-transform: lowercase; letter-spacing: 0.04em; } .photography-by-year-count::before { content: ""; display: inline-block; width: 0; } .photography-by-year-count::after { content: " photographs"; } /* --------------------------------------------------------------------------- * Contact sheet — /photography/contact-sheet/ * * Frame-numbered grid in the visual register of an analog contact * print: small uniform thumbnails, thin white border per frame, * frame number in the corner via a CSS counter. The dark page * backdrop is a soft "film-room" gray rather than full black so * the white frames don't ring against the surrounding page chrome * too hard. * --------------------------------------------------------------------------- */ .photography-contact-sheet { counter-reset: contact-frame-no; list-style: none; padding: 1.5rem; margin: 1rem 0 2rem; background: #1c1c1c; border: 1px solid #2a2a2a; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 11rem), 1fr)); gap: 1.25rem; } .contact-frame { counter-increment: contact-frame-no; margin: 0; background: #f5f3ee; padding: 0.5rem 0.5rem 0.4rem; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); transition: transform 120ms ease; } .contact-frame:hover { transform: scale(1.015); } .contact-frame-link { display: block; color: #1c1c1c; text-decoration: none; position: relative; } .contact-frame-img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; display: block; background: #2a2a2a; } .contact-frame-link::before { /* Frame number — analog convention: oldstyle figures, mono, * small, top-left of the print under the image. */ content: counter(contact-frame-no, decimal-leading-zero); position: absolute; top: -1.4em; left: 0; font-family: var(--font-mono, monospace); font-size: 0.75em; color: rgba(245, 243, 238, 0.55); letter-spacing: 0.05em; } .contact-frame-label { display: block; margin-top: 0.35rem; font-size: 0.75em; color: #4a4a4a; line-height: 1.2; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* --------------------------------------------------------------------------- * Series landing — /photography// * --------------------------------------------------------------------------- */ /* The lede. Takes .essay-summary's size and leading so a series reads like * an essay's frontmatter, but stays centred in its column and drops the rule * and label — a bordered box does not centre, and "Summary" is redundant * above two lines of prose. text-wrap: balance keeps the ragged edges even * instead of leaving one word alone on the last line. */ .photo-series-abstract { margin: 1.1rem auto 0; font-family: var(--font-serif); font-size: 0.95rem; line-height: 1.65; color: var(--text-muted); max-width: 32rem; text-align: center; text-wrap: balance; } .photo-series-prose { max-width: 36rem; margin: 1.5rem 0; color: var(--text); } /* --------------------------------------------------------------------------- * Slideshow * * Shares the darkroom overlay's ground and vignette so the two full-screen * views read as one idea. Chrome is deliberately faint and sits away from the * picture: at this size the photograph is the only thing that should hold * attention, and the controls are there to be found, not noticed. * --------------------------------------------------------------------------- */ body.slideshow-open { overflow: hidden; } .slideshow-overlay { /* Inherits position, layout, and — critically — the opacity/visibility * hiding from .lightbox-overlay in images.css. Only the differences are * declared here; redeclaring display would override the base class's * hiding mechanism and pin the overlay open. */ z-index: 1000; gap: 1.25rem; padding: 2.5rem 1.5rem 1.5rem; background: #000; } .slideshow-figure { margin: 0; display: flex; flex-direction: column; align-items: center; gap: 0.9rem; max-width: min(100%, 1600px); min-height: 0; } .slideshow-img { max-width: 100%; /* Leave room for caption and control bar without them ever overlapping the frame — the photograph is never cropped to fit chrome. */ max-height: calc(100vh - 11rem); width: auto; height: auto; object-fit: contain; cursor: pointer; display: block; } .slideshow-caption { text-align: center; color: rgba(255, 255, 255, 0.8); font-family: var(--font-serif); display: flex; flex-direction: column; gap: 0.15rem; max-width: 48rem; } .slideshow-title { font-size: 0.98rem; } .slideshow-date { font-size: 0.82rem; color: rgba(255, 255, 255, 0.5); font-variant-numeric: oldstyle-nums; } .slideshow-bar { display: flex; align-items: center; gap: 0.5rem; } .slideshow-btn { background: none; border: 1px solid rgba(255, 255, 255, 0.22); border-radius: 2px; color: rgba(255, 255, 255, 0.7); font-size: 0.9rem; line-height: 1; padding: 0.4rem 0.7rem; cursor: pointer; transition: color 120ms ease, border-color 120ms ease; } .slideshow-btn:hover { color: #fff; border-color: rgba(255, 255, 255, 0.55); } .slideshow-btn:focus-visible { outline: 2px solid rgba(255, 255, 255, 0.75); outline-offset: 2px; } .slideshow-close { position: absolute; top: 1rem; right: 1.25rem; font-size: 1.4rem; padding: 0.15rem 0.6rem; } .slideshow-counter { margin: 0 0 0 0.6rem; color: rgba(255, 255, 255, 0.45); font-family: var(--font-sans); font-size: 0.72rem; font-variant-numeric: tabular-nums; letter-spacing: 0.06em; } @media (max-width: 540px) { .slideshow-img { max-height: calc(100vh - 13rem); } .slideshow-counter { display: none; } } @media (prefers-reduced-motion: reduce) { .slideshow-btn { transition: none; } } /* --------------------------------------------------------------------------- * Map pins * * A div icon rather than Leaflet's default bitmap: it carries the number of * photographs at a place, sizes itself to that, and gives the map no image * that has to resolve. The palette is the site's, so a pin reads as part of * the page rather than as part of Leaflet. * --------------------------------------------------------------------------- */ .photography-map-pin { display: flex; align-items: center; justify-content: center; background: none; border: none; } .photography-map-pin span { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; border-radius: 50%; background: var(--text); color: var(--bg); font-family: var(--font-sans); font-size: 0.7rem; font-variant-numeric: tabular-nums; line-height: 1; box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.65); transition: transform var(--transition-fast); } .photography-map-pin:hover span { transform: scale(1.12); } /* Thumbnail strip inside a place tooltip — a glance at what is there, capped at four so the tooltip stays a tooltip. */ .photography-map-strip { display: flex; gap: 2px; margin-bottom: 0.4rem; } .photography-map-strip-img { width: 46px; height: 46px; object-fit: cover; display: block; background: var(--bg-muted); } .photography-map-tooltip--group .photography-map-tooltip-meta { font-family: var(--font-sans); font-size: 0.68rem; color: var(--text-faint); font-variant-numeric: tabular-nums; } @media (prefers-reduced-motion: reduce) { .photography-map-pin span { transition: none; } }