/*
 * Gram theme — Modernist.
 *
 * Flat, flush left, zero radius, strong 2px rules; a single red accent and
 * photographs in pure black and white. One stylesheet, no framework.
 *
 * One webfont, self-hosted: Satoshi, from css/fonts/. Nothing is fetched from
 * a third party — a photo blog that exists to keep its visitors off other
 * people's servers must not hand out their IP addresses for a typeface. (This
 * is why the design system's Archivo, which only existed on Google's CDN, is
 * not what is loaded here.)
 *
 * Light and dark are both first-class: a full-bleed photo grid on the wrong
 * background looks broken, and phones are in dark mode half the time.
 */

/*
 * Satoshi © Indian Type Foundry, designed by Deni Anggara — fontshare.com.
 *
 * The variable cut, one file per style: 42 KB covers every weight the theme
 * asks for (300–900), where the static cuts would have been a request each for
 * regular, medium, bold and black. Both faces are declared as the *same*
 * family with a weight range, so plain font-weight: 700 works and nothing has
 * to name a per-weight family — unlike the vendor CSS that ships with the
 * download, which gives every cut its own family name.
 *
 * The italic is only fetched if something on the page is actually italic.
 */
@font-face {
	font-family: "Satoshi";
	src: url("fonts/Satoshi-Variable.woff2") format("woff2");
	font-weight: 300 900;
	font-style: normal;
	font-display: swap;
}

@font-face {
	font-family: "Satoshi";
	src: url("fonts/Satoshi-VariableItalic.woff2") format("woff2");
	font-weight: 300 900;
	font-style: italic;
	font-display: swap;
}

:root {
	--gram-bg: #f3f2f2;
	--gram-media: #d7d3d3;
	--gram-ink: #201e1d;
	--gram-muted: #605d5d;
	/* Structural rule between sections; --gram-edge is the hard black edge. */
	--gram-rule: rgba(32, 30, 29, 0.4);
	--gram-edge: #201e1d;
	--gram-accent: #ec3013;
	/* Accent at paragraph size needs a deeper ramp step for contrast. */
	--gram-accent-ink: #ae1800;
	--gram-overlay: rgba(32, 30, 29, 0.72);
	/* One column width for every view: the grid, the feed, a post, the about
	   page. Switching tabs must not change how wide the page is. */
	--gram-shell: 620px;
	--gram-gap: 4px;
	/* Satoshi, with the system grotesque stack behind it: the fallback is what
	   the page is set in for the first paint and if the woff2 never arrives, so
	   it has to be a grotesque too, not a serif. */
	--gram-font: "Satoshi", system-ui, -apple-system, "Segoe UI", Roboto,
		"Helvetica Neue", Arial, sans-serif;
}

@media (prefers-color-scheme: dark) {
	:root {
		--gram-bg: #201e1d;
		--gram-media: #000000;
		--gram-ink: #f3f2f2;
		--gram-muted: #9b9797;
		--gram-rule: rgba(243, 242, 242, 0.35);
		--gram-edge: #f3f2f2;
		--gram-accent: #ec3013;
		--gram-accent-ink: #ff9783;
		--gram-overlay: rgba(0, 0, 0, 0.72);
	}
}

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

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

body {
	margin: 0;
	background: var(--gram-bg);
	color: var(--gram-ink);
	font-family: var(--gram-font);
	font-size: 15px;
	line-height: 1.55;
	-webkit-font-smoothing: antialiased;
}

img,
video {
	max-width: 100%;
}

a {
	color: inherit;
	text-decoration: none;
}

.gram-shell {
	max-width: var(--gram-shell);
	margin: 0 auto;
	padding: 0 24px;
}

.gram-sr {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

.gram-skip {
	position: absolute;
	left: -9999px;
	top: 0;
	z-index: 100;
	padding: 0.5rem 1rem;
	background: var(--gram-bg);
	border: 2px solid var(--gram-edge);
}

.gram-skip:focus {
	left: 0;
}

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

::selection {
	background: #ffc4b8;
	color: #201e1d;
}

/* ----------------------------------------------------------------- Masthead */

/*
 * The site name, and on a listing the post count beside it. This used to be a
 * sticky bar of its own above the tab row; two stacked bars for two pieces of
 * text is a lot of furniture above a photograph, so it is now simply the first
 * line of the header block.
 */
.gram-masthead {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 1rem;
	padding: 26px 0 14px;
}

/* An <h1> on a listing, a plain <div> elsewhere — hence the reset. */
.gram-masthead-title {
	margin: 0;
	font-size: 26px;
	font-weight: 800;
	letter-spacing: -0.02em;
	line-height: 1.1;
	text-transform: uppercase;
}

.gram-masthead-meta {
	flex: none;
	color: var(--gram-muted);
	font-size: 10.5px;
	font-weight: 600;
	letter-spacing: 0.16em;
	text-transform: uppercase;
}

@media (max-width: 640px) {
	.gram-shell {
		padding: 0 16px;
	}

	.gram-masthead {
		padding: 18px 0 10px;
	}

	.gram-masthead-title {
		font-size: 21px;
	}
}

/* ------------------------------------------------------------------- Header */

/*
 * The same column as everything it sits above, so the centre axis and the two
 * edges hold across a view switch.
 */
.gram-head {
	max-width: var(--gram-shell);
	margin: 0 auto;
	padding: 0 24px;
}

/* ------------------------------------------------------------ View switcher */

/* Labels under a rule: the active one is underlined in the accent. An outlined
   pill at this size reads as "disabled"; a rule reads as "here". */
.gram-views {
	display: flex;
	gap: 26px;
	margin: 0;
	border-bottom: 2px solid var(--gram-rule);
}

.gram-view {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	/* 48px of touch target, drawn as type over a rule rather than as a button. */
	min-height: 48px;
	padding: 16px 0 10px;
	border: 0;
	border-bottom: 3px solid transparent;
	margin-bottom: -2px;
	background: none;
	color: var(--gram-muted);
	font-family: inherit;
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	cursor: pointer;
	transition: color 0.12s linear, border-color 0.12s linear;
}

.gram-view:hover {
	color: var(--gram-ink);
	border-bottom-color: var(--gram-rule);
}

.gram-view.is-active {
	color: var(--gram-ink);
	border-bottom-color: var(--gram-accent);
}

.gram-view.is-active:active {
	border-bottom-color: var(--gram-accent-ink);
}

/* On a phone the tabs split the row evenly. */
@media (max-width: 640px) {
	.gram-views {
		gap: 0;
	}

	.gram-view {
		flex: 1 1 0;
		/* Three even thirds read better centred than flush left. */
		justify-content: center;
		gap: 6px;
	}
}

/* ---------------------------------------------------------------- Tag filter */

.gram-filters {
	display: flex;
	gap: 6px;
	margin: 0;
	padding: 16px 0 18px;
	list-style: none;
	/* Horizontal on a phone: the row scrolls rather than wrapping into a block
	   that pushes the first photograph off the screen. */
	overflow-x: auto;
	scrollbar-width: none;
}

.gram-filters::-webkit-scrollbar {
	display: none;
}

.gram-filter-chip {
	flex: none;
	display: inline-block;
	padding: 5px 10px;
	border: 1px solid var(--gram-rule);
	color: var(--gram-ink);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.04em;
	white-space: nowrap;
}

.gram-filter-chip:hover {
	border-color: var(--gram-ink);
}

.gram-filter-chip.is-active {
	background: var(--gram-accent);
	border-color: var(--gram-accent);
	color: #fff;
}

.gram-filter-count {
	color: var(--gram-muted);
	font-weight: 600;
}

.gram-filter-chip.is-active .gram-filter-count {
	color: #ffc4b8;
}

@media (max-width: 640px) {
	.gram-filters {
		padding: 12px 16px;
		margin: 0 -16px;
	}
}

/* A category listing has no chip in the row above, so it says so in one line. */
.gram-listing-note {
	margin: 0 0 18px;
	color: var(--gram-muted);
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.14em;
	text-transform: uppercase;
}

/* --------------------------------------------------------------------- Grid */

.gram-grid {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	/* minmax(0, 1fr), not 1fr: an intrinsically wide tile would otherwise push
	   its own track past a third of the row. */
	grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: var(--gram-gap);
}

/* Edge to edge on phones — at that width the padding costs the only dimension
   a photograph has. */
@media (max-width: 640px) {
	.gram-grid {
		--gram-gap: 2px;
	}

	.gram-shell:has(> .gram-grid) {
		padding: 0;
	}
}

.gram-tile {
	position: relative;
	min-width: 0;
	overflow: hidden;
}

.gram-tile-link {
	display: block;
	position: relative;
	aspect-ratio: 1 / 1;
	overflow: hidden;
	background: var(--gram-media);
}

.gram-tile-image {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

.gram-tile-placeholder {
	display: block;
	width: 100%;
	height: 100%;
	background: var(--gram-media);
}

.gram-tile-overlay {
	position: absolute;
	top: 8px;
	right: 8px;
	display: flex;
	gap: 6px;
	color: #fff;
	filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
	pointer-events: none;
}

.gram-tile-icon {
	display: inline-flex;
}

/* Desktop hover dim, so a grid of photographs still feels clickable. */
@media (hover: hover) {
	.gram-tile-link::after {
		content: "";
		position: absolute;
		inset: 0;
		background: rgba(32, 30, 29, 0);
		transition: background 0.12s linear;
	}

	.gram-tile-link:hover::after {
		background: rgba(32, 30, 29, 0.22);
	}
}

.gram-empty {
	padding: 4rem 24px;
	color: var(--gram-muted);
}

/* --------------------------------------------------------------- Pagination */

.gram-pagination {
	display: flex;
	justify-content: space-between;
	gap: 1rem;
	margin: 0 0 3rem;
	padding: 18px 24px 0;
}

.gram-pagination-link {
	color: var(--gram-accent-ink);
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.14em;
	text-transform: uppercase;
}

.gram-pagination-link:hover {
	color: var(--gram-ink);
}

.gram-pagination-link:only-child {
	margin-left: auto;
}

.gram-pagination-link[rel="prev"]:only-child {
	margin-left: 0;
	margin-right: auto;
}

/* ------------------------------------------------------------ Post and feed */

.gram-post-card {
	/* width, not just max-width: an auto cross-axis margin makes a flex item
	   shrink to fit its content, and .gram-feed is a column flex container. With
	   the media out of flow the widest thing left in a card is its caption text,
	   which would otherwise set the column width post by post. */
	width: 100%;
	max-width: var(--gram-shell);
	margin: 0 auto;
	/* On a post's own page the card *is* the column, so it carries the column's
	   side padding — the same 24px as the header, the grid and the pagination.
	   The media's edges are what the eye measures the column by, and a
	   full-bleed card sits 48px wider than the tab row above it. In the feed
	   .gram-feed carries the padding instead; see there. */
	padding: 0 24px;
	/* A tile links to a card inside the feed; clear the sticky top bar (56px
	   plus its rule) so the post does not open with its top edge underneath it. */
	scroll-margin-top: 72px;
}

.gram-carousel {
	position: relative;
	background: var(--gram-media);
}

.gram-track {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scrollbar-width: none;
	/* One frame for the whole post: see the note in php/post-card.php. */
	aspect-ratio: var(--gram-ratio, 1);
}

.gram-track::-webkit-scrollbar {
	display: none;
}

.gram-slide {
	position: relative;
	flex: 0 0 100%;
	scroll-snap-align: center;
}

/*
 * Absolute, not height: 100%. A percentage height on the media resolves against
 * the slide's content box, which an in-flow image first grows to its own full
 * height — 620px wide at 3:4 is 827px tall, in a 349px frame. The media then fits
 * itself neatly into that oversized box and the track, which scrolls in x and
 * therefore clips in y, cuts the bottom off. Taken out of flow it sizes against
 * the slide's own box instead, and object-fit letterboxes inside the frame.
 */
.gram-image,
.gram-video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: contain;
	display: block;
}

.gram-slide-duration {
	position: absolute;
	top: 8px;
	right: 8px;
	padding: 3px 7px;
	background: var(--gram-overlay);
	color: #fff;
	font-size: 10.5px;
	font-weight: 600;
	letter-spacing: 0.08em;
	pointer-events: none;
}

.gram-carousel-arrow {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	display: grid;
	place-items: center;
	width: 36px;
	height: 36px;
	border: 0;
	background: var(--gram-overlay);
	color: #fff;
	cursor: pointer;
	padding: 0;
}

.gram-carousel-prev { left: 0; }
.gram-carousel-next { right: 0; }

/* Touch users swipe; the arrows are for pointers only. */
@media (hover: none) {
	.gram-carousel-arrow {
		display: none !important;
	}
}

.gram-dots {
	list-style: none;
	display: flex;
	gap: 4px;
	margin: 0;
	padding: 10px 0 0;
}

.gram-dot {
	width: 14px;
	height: 3px;
	background: var(--gram-rule);
	transition: background 0.12s linear;
}

.gram-dot.is-active {
	background: var(--gram-accent);
}

.gram-post-body {
	padding: 14px 0 0;
}

.gram-post-footer {
	max-width: var(--gram-shell);
	margin: 0 auto;
	padding: 0 24px 3rem;
}

/* --------------------------------------------------------------------- Feed */

.gram-feed-shell {
	padding-bottom: 3rem;
}

/*
 * One centred column, posts separated by a rule. No cards, no borders on three
 * sides, nothing floating: the rule is what says "this post ended".
 */
.gram-feed {
	display: flex;
	flex-direction: column;
	max-width: var(--gram-shell);
	margin: 0 auto;
	/* The column's side padding lives here rather than on the cards, because the
	   rule that separates two posts is a card border: on the card it would be
	   drawn outside that padding and run 24px past both the photos and the tab
	   row's rule. */
	padding: 0 24px;
	/* Loading newer posts inserts them above what is on screen, and gram.js
	   moves the scroll position by exactly their height to keep the reader in
	   place. Scroll anchoring would compensate for the same insertion again. */
	overflow-anchor: none;
}

.gram-feed .gram-post-card {
	padding-left: 0;
	padding-right: 0;
	padding-bottom: 26px;
	border-bottom: 2px solid var(--gram-rule);
}

.gram-feed .gram-post-card + .gram-post-card {
	padding-top: 26px;
}

.gram-feed .gram-post-card:last-child {
	border-bottom: 0;
}

/* Edge to edge on phones, like the grid: at that width the padding costs the
   only dimension a photograph has. The caption keeps a margin of its own. */
@media (max-width: 640px) {
	.gram-feed,
	.gram-post-card {
		padding-left: 0;
		padding-right: 0;
	}

	.gram-post-body {
		padding-left: 16px;
		padding-right: 16px;
	}
}

.gram-feed-end {
	margin: 2.5rem auto 0;
	max-width: var(--gram-shell);
	padding: 0 24px;
	color: var(--gram-muted);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
}

.gram-feed-shell .gram-pagination {
	max-width: var(--gram-shell);
	margin-left: auto;
	margin-right: auto;
}

/* The way back towards newer posts, above the column instead of below it —
   that is where the posts it loads appear. */
.gram-feed-top {
	margin-bottom: 18px;
	padding-top: 0;
}

.gram-caption {
	overflow-wrap: anywhere;
	text-wrap: pretty;
}

/*
 * A caption written in Bludit's own editor arrives as HTML, so the elements
 * TinyMCE can emit need to look deliberate here rather than fall back to the
 * browser's defaults — which give a paragraph a 1em margin and would push the
 * date away from the text.
 */
.gram-caption > :first-child {
	margin-top: 0;
}

.gram-caption > :last-child {
	margin-bottom: 0;
}

.gram-caption p,
.gram-caption ul,
.gram-caption ol,
.gram-caption blockquote,
.gram-caption pre,
.gram-caption figure,
.gram-caption table {
	margin: 0 0 0.75em;
}

.gram-caption h2,
.gram-caption h3,
.gram-caption h4,
.gram-caption h5,
.gram-caption h6 {
	margin: 1.25em 0 0.4em;
	font-size: 1.05em;
	font-weight: 700;
	line-height: 1.3;
}

.gram-caption ul,
.gram-caption ol {
	padding-left: 1.25em;
}

.gram-caption li {
	margin-bottom: 0.2em;
}

.gram-caption blockquote {
	padding-left: 0.9em;
	border-left: 2px solid var(--gram-border);
	color: var(--gram-text-muted);
}

.gram-caption code {
	padding: 0.1em 0.35em;
	border-radius: 4px;
	background: var(--gram-bg-media);
	font-size: 0.9em;
}

.gram-caption pre {
	padding: 0.75em 0.9em;
	border-radius: 8px;
	background: var(--gram-bg-media);
	overflow-x: auto;
}

.gram-caption pre code {
	padding: 0;
	background: none;
}

.gram-caption img {
	height: auto;
	border-radius: 8px;
}

.gram-caption hr {
	margin: 1.25em 0;
	border: 0;
	border-top: 1px solid var(--gram-border);
}

/* An editor's table can be wider than the column; let it scroll, not the page. */
.gram-caption table {
	display: block;
	width: 100%;
	overflow-x: auto;
	border-collapse: collapse;
}

.gram-caption th,
.gram-caption td {
	padding: 0.35em 0.6em;
	border: 1px solid var(--gram-border);
	text-align: left;
}

.gram-caption a {
	color: var(--gram-accent-ink);
	text-decoration: underline;
	text-underline-offset: 2px;
}

.gram-caption a:hover {
	color: var(--gram-accent);
}

.gram-tags {
	list-style: none;
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	margin: 10px 0 0;
	padding: 0;
}

.gram-tags a {
	color: var(--gram-muted);
	font-size: 10.5px;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
}

.gram-tags a:hover {
	color: var(--gram-accent-ink);
}

.gram-post-date {
	margin: 10px 0 0;
	color: var(--gram-muted);
	font-size: 10.5px;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.14em;
}

.gram-post-nav {
	display: flex;
	justify-content: space-between;
	gap: 1rem;
	margin-top: 2rem;
	padding-top: 1.25rem;
	border-top: 2px solid var(--gram-rule);
	font-size: 11px;
	font-weight: 700;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--gram-muted);
}

.gram-post-nav-right {
	margin-left: auto;
}

.gram-post-nav-link:hover {
	color: var(--gram-ink);
}

/* --------------------------------------------------------------- About page */

/*
 * The one text page of a site made of photographs. The site column is also a
 * good measure for prose, so it needs no width of its own.
 */
.gram-about {
	max-width: var(--gram-shell);
	margin: 0 auto;
	padding: 30px 24px 3rem;
}

.gram-about-title {
	margin: 0;
	font-size: 30px;
	font-weight: 800;
	letter-spacing: -0.02em;
	line-height: 1.15;
}

.gram-about-lead {
	margin: 12px 0 0;
	color: var(--gram-muted);
	font-size: 15px;
}

/* Prose, not a caption: a whole page of 15px runs short of air. */
.gram-about-body {
	margin-top: 22px;
	padding-top: 22px;
	border-top: 2px solid var(--gram-rule);
	font-size: 16px;
	line-height: 1.65;
}

@media (max-width: 640px) {
	.gram-about {
		padding: 22px 16px 2.5rem;
	}

	.gram-about-title {
		font-size: 25px;
	}
}

/* ----------------------------------------------------------------- 404 page */

.gram-notfound {
	padding: 5rem 24px;
	max-width: var(--gram-shell);
	margin: 0 auto;
}

.gram-notfound-code {
	margin: 0;
	font-size: 72px;
	font-weight: 800;
	letter-spacing: -0.03em;
	line-height: 1;
	color: var(--gram-accent);
}

.gram-notfound-text {
	margin: 12px 0 24px;
}

.gram-notfound-link {
	display: inline-block;
	padding: 12px 20px;
	background: var(--gram-accent);
	color: #fff;
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 0.14em;
	text-transform: uppercase;
}

.gram-notfound-link:hover {
	background: #dd2b0f;
}

/* ------------------------------------------------------------------- Footer */

.gram-footer {
	border-top: 2px solid var(--gram-edge);
	margin-top: 2rem;
	padding: 24px 0 40px;
	color: var(--gram-muted);
	font-size: 10.5px;
	font-weight: 600;
	letter-spacing: 0.14em;
	text-transform: uppercase;
}

.gram-footer-inner {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
}

.gram-footer-text {
	margin: 0;
}

.gram-footer-links {
	display: flex;
	gap: 16px;
}

.gram-footer-links a:hover {
	color: var(--gram-ink);
}

@media (prefers-reduced-motion: reduce) {
	* {
		animation-duration: 0.01ms !important;
		transition-duration: 0.01ms !important;
		scroll-behavior: auto !important;
	}
}
