/*
 * SCOPING, and why every rule below carries a prefix.
 *
 * This file is loaded by the shell as well as by the standalone page, because the item view can be
 * swapped into the main region at any moment and a stylesheet that arrives with its markup paints
 * the wrong thing first. The consequence is that a bare `main { max-width: 58rem }` in here was
 * capping the WHOLE application, and bare `table`, `th`, `section` and `footer` rules were quietly
 * restyling the admin and people views too.
 *
 * So: `body.item-page` for page chrome that belongs to the standalone page alone, and `.item-view`
 * for everything that belongs to the view itself and should travel with it. A stylesheet loaded
 * globally may only style what it owns by name.
 */

/*
 * Buffet -- the item view's own styles.
 *
 * Lifted out of item.php so that ONE definition serves both places the item view is rendered: its
 * canonical standalone URL (/item.php, which §9's embeds, oEmbed, JSON-LD and unfurls all want to
 * keep) and the application shell, where it is a view like any other. Two copies of this file would
 * be two item views, and the one nobody is looking at is the one that rots (CLAUDE.md §5).
 *
 * Loaded alongside app.css rather than instead of it: the shell's layout rules key off .app-shell
 * and friends, which a standalone item page simply does not have, so linking both is safe in either
 * direction and keeps the palette in one place.
 */

/* The palette and type of the upload page, because they are two views of one application and a
   dev environment that looks assembled from parts reads as one. System fonts: the page should look
   like the OS it is used on rather than like a website pretending to be an application. */
:root
{
	/* The same declaration app.css carries, for the same reason, and through the same token (D351):
	   without it the UA paints its own parts -- a <select>'s dropdown, the autofill highlight, the
	   scrollbars -- with its LIGHT appearance whatever the palette says. Neither of these two
	   standalone pages had it at all, so both had app.css's old bug in the dark scheme; the token's
	   `light dark` fallback fixes that and lets a theme dark in both schemes say so here too. */
	color-scheme: var( --ua-color-scheme, light dark );

	--page-background: #f4f4f2;
	--page-background-translucent: rgb( 244 244 242 / 0.6 );
	--card-background: #ffffff;
	--ink: #16181c;
	--ink-quiet: #5c626c;
	--ink-very-quiet: #acafb4;
	--rule: #d8d8d4;
	--accent: #2f6f4f;
	--accent-quiet: #e6efe9;
	--warning: #8a2b1f;
	--warning-quiet: #f7e8e5;
}

@media ( prefers-color-scheme: dark )
{
	:root
	{
		--page-background: #17181a;
		--page-background-translucent: rgb( 23 24 26 / 0.6 );
		--card-background: #1f2124;
		--ink: #eceef1;
		--ink-quiet: #a2a8b2;
		--ink-very-quiet: #55575b;
		--rule: #32353a;
		--accent: #6fbf95;
		--accent-quiet: #23302a;
		--warning: #f0907f;
		--warning-quiet: #33221f;
	}
}

*
{
	box-sizing: border-box;
}

body.item-page
{
	margin: 0;
	padding: 2.5rem 1.5rem 4rem;
	background: var( --page-background );
	color: var( --ink );
	font: var( --text-md )/1.55 var( --font-ui, system-ui, sans-serif );
	-webkit-font-smoothing: antialiased;
}

body.item-page main
{
	max-width: 58rem;
	margin: 0 auto;
}

body.item-page a
{
	color: var( --accent );
}

.breadcrumb
{
	margin: 0 0 1.5rem;
	font-size: var( --text-base );
	color: var( --ink-quiet );
}

.item-view h1
{
	margin: 0 0 .35rem;
	font-size: var( --text-2xl );
	letter-spacing: -.015em;
	line-height: 1.2;
	word-break: break-word;
}

.urn
{
	margin: 0 0 1.25rem;
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: var( --text-sm );
	color: var( --ink-quiet );
	word-break: break-all;
}

.badges
{
	display: flex;
	flex-wrap: wrap;
	gap: .5rem;
	margin: 0 0 1.75rem;
}

/* ---------------------------------------------------------------------------------------------
   Badges and the pressable .control-pill are defined ONCE, in app.css, under the heading "THE
   VOCABULARY RULE" -- round is pressable, square is stated. This file used to carry a second
   .badge block of its own, and two definitions of one thing is exactly how they drift (D129); the
   grid and the shell were still drawing bordered pills after this page had stopped.

   The affordance vocabulary below obeys the same rule from the other side: `ready` and `offerable`
   are round, bordered and hoverable because they can be acted on, and `blocked` deliberately
   borrows the BADGE shape, because it is a statement and not a disabled control. A greyed-out
   button invites a press and then refuses it; a statement never makes the offer.

   The one thing restated here is the item page's taller, upper-case badge: this page shows four of
   them in a row under the title, where the shell shows them inline in dense grid cards.
   --------------------------------------------------------------------------------------------- */

.item-view .badge
{
	padding: .2rem .5rem;
	font-size: var( --text-xs );
	text-transform: uppercase;
}

/* The preview, which is the whole point of the gate. Fixed aspect from the stored dimensions, so
   the page does not reflow when the image arrives -- and a placeholder of the same shape while
   there is nothing to show, for the same reason. */
.preview
{
	margin: 0 0 2rem;
	border: 1px solid var( --rule );
	border-radius: .75rem;
	background: var( --card-background );
	overflow: hidden;
}

/* ---------------------------------------------------------------------------------------------
   HOW BIG MEDIA IS ALLOWED TO GET, AND WHY EVERY SLOT IS THE SAME SIZE.

   Two requirements, and the second one changed the answer to the first.

   The cap is half the viewport height. A 2160x3840 portrait thumbnail at `width: 100%` in a 58rem
   column is about 1,900 pixels tall: it fills the viewport twice over and pushes every fact about
   the item below the fold. Half the height leaves the title, the badges and the controls on screen
   with the picture, so the page reads as an item with a preview rather than a preview with a page.

   The MATCHING is the newer requirement: the poster box and the video box sit side by side and must
   be the same size, with their contents the same size too. That rules out letting each medium keep
   its own shape -- a 640 px thumbnail beside a 1080p rendition gives two boxes of different heights
   and the row reads as broken rather than paired.

   So `.tool-media` is a fixed-shape box, `--item-aspect` comes from the item (itemview.php computes
   one ratio for the whole item, not one per file), and the content fills it with `object-fit:
   contain` -- centred, never distorted, letterboxed where the shapes disagree.

   NOTE, because it reverses what this comment said an hour ago: content now DOES scale up. The
   earlier rule was "nothing is ever scaled up, an upscaled preview looks like a mistake", and that
   was right when each medium sized itself. It is incompatible with "the img and the video are the
   same size", and matched boxes are worth more here than pixel-exactness on a 640 px thumbnail that
   is being shown at roughly 460 px anyway. Stated rather than quietly changed.
   --------------------------------------------------------------------------------------------- */

.item-view { --media-max-height: 50vh; }

/* Adjacent boxes, collapsing to a column when there is not room for two.
   
   `auto-fit` with a `minmax` floor rather than a media query, and that is not laziness: the item
   view is rendered both standalone and inside the shell, where a 16.5rem sidebar eats the width. A
   viewport media query would collapse at the wrong moment in one of the two contexts, and a
   container query would need a containment context declared up the tree for a layout this simple.
   `auto-fit` responds to the space actually available, in both. */
.tool-areas
{
	display: grid;
	grid-template-columns: repeat( auto-fit, minmax( 21rem, 1fr ) );
	gap: 1rem;

	/* Stretch, not start: the boxes on a row match each other's height, and the shorter one grows
	   rather than leaving a ragged step between two panels that are obviously a pair. The media
	   boxes inside already match (see .tool-media); it was only the panels around them that did not,
	   because one carries three controls and the other carries one. */
	align-items: stretch;
	margin: 1.5rem 0;
}

/* Inside the grid the areas are cells, so their own vertical margins would fight the gap. */
/* A stretched cell needs its own column layout, or the extra height lands as a gap in the middle
   rather than below the controls. Header, then media, then a spacer that absorbs the difference, so
   the controls of both boxes sit on the same line as each other. */
.tool-areas .tool-area
{
	margin: 0;
	display: flex;
	flex-direction: column;
	/* A grid item's minimum width is its content's unless told otherwise, so a box whose contents
	   have a floor wider than its column grows PAST the column and every full-width field inside it
	   follows -- the Rights and credits area did exactly that at 1400px, its credits row's three
	   8rem minimums adding up to more than a 21rem column (found by Jon on a screen, 2026-09-06).
	   Zero says: the column decides, and the contents wrap or shrink to it. */
	min-width: 0;
}

/* WHAT MAKES TWO SIDE-BY-SIDE TOOL AREAS LINE UP AT THE BOTTOM (D137), and it moved.

   It used to sit on `.tool-controls`, a DIRECT child of the tool area -- which meant that adding a
   second controls block beside the pill row silently ended the alignment, with nothing failing and
   nothing to read. ui-design.md §4.2 names that trap in advance and this is the fix it prescribes:
   one footer holds the pills and the action rows, and the footer takes the `auto` itself. */
/* ...AND ONLY WHERE THERE IS A MEDIA BOX TO LINE UP UNDER (Jon, 2026-09-05).

   The alignment exists for the pair of matched media boxes: two artefacts side by side, and the
   controls of both on one line beneath them. A surface with no artefact -- Web renditions is a
   header, a row of size chips and one offer -- stretched to its neighbour's height all the same, and
   the `auto` then pushed its one action row to the floor of the cell with a screen's worth of
   nothing between the chips and the tool that makes them. So the footer flows up to sit under the
   content, and drops to the foot only in an area that holds a .tool-media box.

   `:has()`, and this is the first use of it in the project: the question is "does this area draw
   an artefact", and the markup that answers it is decided by four different branches of the
   surface template (a player with rungs, a picture, a loop, a fallback preview), so an attribute
   set on the section would be a fifth copy of that decision. The degradation is benign -- an engine
   without `:has()` ignores the rule and every footer flows up, which is the new behaviour for the
   common case and a ragged bottom edge for the pair, not a broken page. */
.tool-areas .tool-area:has( .tool-media ) > .tool-area-footer { margin-top: auto; }

.tool-media
{
	display: grid;
	place-items: center;
	/* Both set once on .tool-areas, from the item's own dimensions. 16/9 only if nothing was known. */
	aspect-ratio: var( --item-aspect, 16 / 9 );

	/* THE WIDTH IS THE INTERESTING LINE, and the first version of it was wrong.
	   
	   `aspect-ratio` with `width: 100%` and `max-height` does NOT shrink a tall box to fit -- width
	   resolves first, the aspect gives a height taller than the cap, and the cap then clamps the box
	   out of the shape it was told to keep. A portrait item (a phone screen recording in this library
	   is 720x1556) got a wide short box with the video letterboxed in the middle of it.
	   
	   Capping the WIDTH at "as wide as this aspect can be while staying under the height cap" makes
	   both constraints hold at once, so a tall item gets a tall narrow box, centred, and a wide one
	   fills the column. Both boxes on the row still match each other, which is the requirement. */
	width: min( 100%, calc( var( --media-max-height ) * var( --item-aspect-decimal, 1.7778 ) ) );

	margin-inline: auto;

	/* Room between the artefact and the tools under it. They were all but touching, which read as
	   one block and gave the controls no standing of their own. */
	margin-bottom: 1.1rem;

	overflow: hidden;
	border-radius: 4px;
	background: #111;
}

.tool-media img,
.tool-media video
{
	display: block;
	width: 100%;
	height: 100%;
	/* contain, never cover: a thumbnail is evidence about the item, and cropping it to fill a box
	   would be the interface editing the picture. */
	object-fit: contain;
	background: #000;
}

/* A placeholder inside the box rather than instead of it, so a tier-3 item's area is the same size
   as everything else and the row still lines up. */
.tool-media .preview-placeholder
{
	margin: 0;
	padding: 1rem 1.25rem;
	max-width: 28rem;
	text-align: center;
	color: #d8d8d4;
	background: transparent;
	font-size: var( --text-base );
	line-height: 1.5;
}

/* The same two lines the card shows, at the size this panel has room for (D171). The media box has
   a near-black ground of its own, so the extension takes a light ink rather than --ink, which is
   dark in the light theme and would vanish into it. */
.tool-media .preview-placeholder[data-kind="file"]
{
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: .35rem;
	font-size: var( --text-lg );
}

.tool-media .preview-placeholder .no-preview-extension { color: #f0f0ee; }
.tool-media .preview-placeholder .no-preview-noun { color: #b6b6b2; }

.preview-caption
{
	padding: .7rem .9rem;
	border-top: 1px solid var( --rule );
	font-size: var( --text-sm );
	color: var( --ink-quiet );
}

.preview-placeholder
{
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 12rem;
	padding: 2rem 1.5rem;
	text-align: center;
	color: var( --ink-quiet );
	font-size: var( --text-md );
}

.preview[data-state="dead"] .preview-placeholder,
.preview[data-state="failed"] .preview-placeholder
{
	color: var( --warning );
}

.item-view section
{
	margin: 0 0 2rem;
}

/* ---------------------------------------------------------------------------------------------
   TWO BOXES SIDE BY SIDE: title-and-description, and sharing (Jon, 2026-08-31 -- D237).

   Neither needs the full measure, and stacked at 100% width they read as two chapters of a long page
   rather than as the two things a person actually does to an item.

   NO GRID IS DECLARED HERE, and no box either. The markup carries `class="tool-areas item-columns"`,
   so the columns are the .tool-areas grid a few hundred lines above -- `repeat( auto-fit,
   minmax( 21rem, 1fr ) )`, which collapses its empty tracks and therefore gives two children 50%
   each wherever 42rem of room exists and stacks them where it does not -- and each child carries
   `tool-area`, so its border, radius, ground and padding are the definition every other box on this
   page already uses.

   Borrowing both is not merely reuse, it is the better tool. The first version of this band declared
   a grid of its own behind a `min-width: 72rem` media query and hand-rolled a box with a .6rem radius
   against .tool-area's 8px: a viewport breakpoint can only GUESS at the width available, which inside
   the shell is the window minus a 16.5rem sidebar and outside it is a 58rem measure, while auto-fit
   measures it; and two boxes that differ by a radius and a pixel of padding are the pair-that-drifts
   this file warns about elsewhere. Both were deleted rather than corrected (Jon, 2026-08-31).

   What is left below is the only thing genuinely about being IN a column: three descendants carry a
   `max-width` from the days they were full-page-wide sections, and inside a half-width box that is a
   second, smaller measure imposed on top of the column's own.
   --------------------------------------------------------------------------------------------- */

.item-view .item-columns .details-form,
.item-view .item-columns .share-note,
.item-view .item-columns .tool-area-summary { max-width: none; }

/* My marks stand BENEATH the details form since 2026-09-20 (they stood above the title), and
   .details-state's own margin is `0 0 1rem` -- room below it, for a block with a form under it. Here
   the form is above, so the room moves to the top. By adjacency, so the state ladder, which still
   stands above the form, keeps the margin it has. */
.details-form + .details-state { margin: 1rem 0 0; }

/* Save is the last thing in the box, BELOW the marks, so it stands outside the form it submits (the
   `form` attribute ties it back -- itemview.php says why). Outside the form it loses
   `.details-form > .control-pill { justify-self: end }`, and this row gives the same edge back. */
.details-save-row { display: flex; justify-content: flex-end; margin-top: 1rem; }


/* ---------------------------------------------------------------------------------------------
   THE IN-DEPTH DISCLOSURE (Jon, 2026-08-31 -- D237): five tables of stored fact, folded away.

   The container is transparent and only the summary has a ground, so the tables inside keep sitting
   on the page background exactly as they did when they were top-level sections -- each table already
   draws its own card, and a card inside a card is a border nobody asked for.

   The marker is drawn rather than native: a <summary> laid out as a grid loses the browser's own
   triangle in more than one engine, and a disclosure with no visible affordance is a heading people
   do not know they can press. `list-style` is the standards route out of the native marker and
   ::-webkit-details-marker is what Safari reads -- both, as .details-area already does.
   --------------------------------------------------------------------------------------------- */

.item-depth
{
	margin: 0 0 2rem;
	border: 1px solid var( --rule );
	border-radius: .6rem;
}

.item-depth-summary
{
	position: relative;
	display: grid;
	gap: .15rem;
	padding: .75rem 1rem .8rem 2.1rem;
	cursor: pointer;
	background: var( --card-background );
	border-radius: .6rem;
	list-style: none;
}

.item-depth-summary::-webkit-details-marker { display: none; }

.item-depth[ open ] .item-depth-summary
{
	border-radius: .6rem .6rem 0 0;
	border-bottom: 1px solid var( --rule );
}

/* The chevron. A rotated corner rather than a glyph, for the reason ui-design.md §4 gives about
   codepoint coverage: a triangle from the font stack is a shape the reader's fonts decide. */
.item-depth-summary::before
{
	content: "";
	position: absolute;
	left: 1rem;
	top: 1.15rem;
	width: .4rem;
	height: .4rem;
	border-right: 1.5px solid currentColor;
	border-bottom: 1.5px solid currentColor;
	transform: rotate( -45deg );
	opacity: .65;
}

.item-depth[ open ] .item-depth-summary::before
{
	top: 1.05rem;
	transform: rotate( 45deg );
}

.item-depth-summary:focus-visible
{
	outline: 2px solid var( --accent );
	outline-offset: 2px;
}

.item-depth-title
{
	font-weight: 600;
	font-size: var( --text-md );
	letter-spacing: -.005em;
}

.item-depth-hint
{
	font-size: var( --text-sm );
	color: var( --ink-quiet );
}

.item-depth-body
{
	padding: 1.25rem 1rem .25rem;
}

/* The last section's 2rem bottom margin would otherwise sit inside the box as dead space. */
.item-depth-body > section:last-child { margin-bottom: .75rem; }

.item-view h2
{
	margin: 0 0 .7rem;
	font-size: var( --text-lg );
	letter-spacing: -.005em;
}

.item-view table
{
	width: 100%;
	border-collapse: collapse;
	background: var( --card-background );
	border: 1px solid var( --rule );
	border-radius: .6rem;
	overflow: hidden;
	font-size: var( --text-base );
}

.item-view caption
{
	padding: 0 0 .55rem;
	text-align: left;
	font-size: var( --text-sm );
	color: var( --ink-quiet );
}

.item-view th, .item-view td
{
	padding: .55rem .8rem;
	text-align: left;
	vertical-align: top;
	border-top: 1px solid var( --rule );
}

.item-view thead th
{
	border-top: 0;
	font-size: var( --text-xs );
	text-transform: uppercase;
	letter-spacing: .04em;
	color: var( --ink-quiet );
	white-space: nowrap;
}

.item-view tbody th
{
	width: 13rem;
	font-weight: 600;
	white-space: nowrap;
}

.mono
{
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: var( --text-sm );
	word-break: break-all;
}

.quiet
{
	color: var( --ink-quiet );
}

.warn
{
	color: var( --warning );
}

.table-scroll
{
	overflow-x: auto;
}

.item-view footer
{
	margin-top: 2.5rem;
	padding-top: 1rem;
	border-top: 1px solid var( --rule );
	font-size: var( --text-sm );
	color: var( --ink-quiet );
}

/* The player. max-width rather than a fixed size so a 2160p rendition does not overflow the
   column on a laptop, and width/height on the element so the layout does not jump when the
   metadata arrives. */
/* Three tones, three degrees of alarm, and the quietest is the most common: a format with no chain
   yet is the system working as designed (§8.4 tier 3), and it must not wear the colour of a fault. */
.outcome
{
	margin: 1.5rem 0;
	padding: .9rem 1.1rem;
	border: 1px solid var( --outcome-edge, #d8d8d8 );
	border-left: 3px solid var( --outcome-accent, #999 );
	border-radius: 4px;
	background: var( --outcome-ground, #fafafa );
}
.outcome h2 { margin: 0 0 .4rem; font-size: var( --text-md ); }
.outcome p { margin: 0 0 .4rem; font-size: var( --text-base ); line-height: 1.5; }
.outcome-next { font-weight: 500; }
.outcome-assurance { opacity: .85; font-style: italic; }
.outcome-technical { margin-bottom: 0; opacity: .6; font-size: var( --text-sm ); }

/* The disclosure that holds the technical line (and, in the compact size, everything but the
   headline). A summary is a control, so it wears the pill's cursor and nothing else: it is not a
   button and must not look like one. */
.outcome-more { margin-top: .2rem; }
.outcome-more summary { cursor: pointer; font-size: var( --text-sm ); opacity: .7; }
.outcome-more summary:hover { opacity: 1; }
.outcome-more > p { margin-top: .4rem; }

/* COMPACT (D309): once any recipe of the item's chain has produced something, the banner is a
   line, not a panel -- the tool areas below carry each surface's state. Same tone colours, a
   third of the room. */
.outcome-compact { margin: .8rem 0; padding: .45rem .8rem; }
.outcome-compact .outcome-line { margin: 0; font-size: var( --text-base ); }
.outcome-compact .outcome-line strong { font-weight: 600; }

.outcome[data-tone="notice"]  { --outcome-accent: #4a7fb5; --outcome-ground: #f5f8fb; --outcome-edge: #dbe4ee; }
.outcome[data-tone="warning"] { --outcome-accent: #b5822a; --outcome-ground: #fdf9f1; --outcome-edge: #ede0c8; }
.outcome[data-tone="error"]   { --outcome-accent: #a51d2d; --outcome-ground: #fdf3f4; --outcome-edge: #eed3d6; }

.player { margin: 1.5rem 0; }
/* Both the player's video and the turntable's are inside a .tool-media box now, which owns the
   shape and the cap. Nothing left to say here beyond not fighting it. */
.loop-view { margin: 0; }

/* Where the player will be. Same footprint as the video so the layout does not jump when the
   first rung lands, and the poster behind it at low contrast so the panel says what the item is
   rather than just that it is busy. */
/* Where the player will be. It fills its .tool-media box exactly, so the placeholder and the video
   it becomes are the same size and the layout does not jump when the first rung lands -- which is
   what the box was for. The poster sits behind at low contrast, so the panel says what the item is
   rather than only that it is busy. */
.player-pending
{
	display: flex; align-items: center; justify-content: center;
	width: 100%; height: 100%;
	background: #111 center / contain no-repeat;
}
.player-pending p
{
	max-width: 32rem; margin: 0; padding: 1rem 1.4rem; text-align: center;
	background: rgba( 0, 0, 0, 0.62 ); color: #f2f2f2; border-radius: 4px; font-size: var( --text-base );
}

/* ---------------------------------------------------------------------------------------------
   RENDITIONS: the rung switcher, the image sizes, the turntable/interactive view selector.

   A peer set, and that is what distinguishes everything below from an action row. Its members are
   compared with EACH OTHER, they share one explanation, and choosing one is choosing what the
   preview shows rather than changing the world -- so they stay pills, and `Encode for web` and
   `Build viewer and turntable`, which are compared against not doing them, did not (§4.2, D291).

   See the note above .badge in app.css for the rule: round is pressable, square is stated.
   --------------------------------------------------------------------------------------------- */

.renditions
{
	display: flex;
	flex-wrap: wrap;
	gap: .4rem;
	align-items: center;
	margin: .6rem 0 0;
	padding: 0;
}

/* ---------------------------------------------------------------------------------------------
   WEIGHT, which is the other half of the vocabulary and the half the first pass got wrong.

   Square-versus-round said which things are pressable. It did not say which things are LOUD, and
   the result was inverted: "Encode for web" -- the only thing on the page that does anything -- was
   set at .84rem in a thin outline, while DRAFT and VIDEO sat next to it in high-contrast pills. The
   page shouted its metadata and whispered its verb (decisions D135).

   THE LADDER THIS BLOCK USED TO HOLD NOW LIVES IN TWO PLACES, and that is the shape of §4.2 rather
   than a scattering. Every offer became an action row, so the four states an offer can be in moved
   with it: `offerable` is a `.control-pill` with an accent tone, and `working`, `ready` and
   `blocked` are `.badge[data-state]` -- both defined once in app.css, beside the classes they are
   variants of. What is left here is the part that never was an offer.

   The rule the ladder implements is unchanged and is the thing to keep: A STATUS LABEL MUST NEVER
   OUT-WEIGH A CONTROL ON THE SAME ROW.

     current     solid accent fill.  The rendition you are watching.
     ready       outlined, normal weight.  Available, not urgent.
     working     outlined, quiet, swept.  In hand; nothing to do.
     absent      dashed.  A rung the ladder could carry and was not asked for.
   --------------------------------------------------------------------------------------------- */

.rendition
{
	display: inline-flex;
	align-items: baseline;
	gap: .35rem;
	padding: .34rem .8rem;
	border: 1px solid var( --rule );
	border-radius: 999px;
	font-size: var( --text-base );
	font-family: inherit;
	line-height: 1.4;
	text-decoration: none;
	color: var( --ink );
	background: var( --card-background );
}

/* THE ATTRIBUTE IS NOT THE EFFECT (D288): `.rendition` sets `display`, and an author display beats
   the user-agent's `[hidden] { display: none }` at any specificity -- so a chip the module has not
   revealed yet (the Quick Look anchor, D404) would sit on screen, hidden in name only. */
.rendition[hidden]
{
	display: none;
}

/* The Quick Look anchor must contain an <img> as its first child -- that is Apple's rule for what
   makes `rel="ar"` open AR rather than download -- so the chip carries the item's own thumbnail at
   the size of a letter, which also happens to read as "this one opens a picture of the thing". */
.rendition .ar-quicklook-glyph
{
	width: 1.1em;
	height: 1.1em;
	object-fit: cover;
	border-radius: 999px;
	align-self: center;
}

/* A rendition IS pressable -- it swaps what the player is showing -- so it keeps its hover. Nothing
   that merely STATES something ever gets one: the highlight is the single cue this interface
   reserves for "you can press this", and spending it on a thing that does nothing teaches a person
   to distrust every other highlight on the page. */
.rendition[data-state="ready"]:hover
{
	border-color: var( --ink-quiet );
}

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

.rendition[aria-current="true"]
{
	border-color: var( --accent );
	background: var( --accent );
	color: var( --page-background );
	font-weight: 600;
}

.rendition-note
{
	font-variant: all-small-caps;
	letter-spacing: .03em;
	opacity: .75;
}

/* ---------------------------------------------------------------------------------------------
   `working`: the sweep.
　
   A slow light travelling left to right, rather than the opacity pulse this used to be. The
   difference is not decorative: a pulse has no direction, and a blinking control reads as an alarm
   as readily as as progress. A sweep advances, which is the thing being claimed -- and it is the
   honest amount of information to claim, because the queue does not report a percentage (FFmpeg's
   stderr progress is parsed for §10's events, not stored on the job).

   It is a background animation only. Nothing moves in the layout, so a row of these cannot reflow
   the page, and the label stays readable throughout. The keyframes it rides on are declared in
   app.css beside `.badge[data-state="working"]`, which is the other half of the same state.
   --------------------------------------------------------------------------------------------- */

.rendition[data-state="working"]
{
	color: var( --ink-quiet );
	border-color: var( --rule );
	background-color: color-mix( in srgb, var( --ink-quiet ) 8%, var( --card-background ) );
	background-image: linear-gradient
	(
		100deg,
		transparent 25%,
		color-mix( in srgb, var( --accent ) 26%, transparent ) 48%,
		transparent 71%
	);
	background-size: 220% 100%;
	background-repeat: no-repeat;
	animation: affordance-sweep 2.1s linear infinite;
}

/* Respect the reader. A sweeping chip is decoration, and vestibular disorders are not rare.
   Reduced motion keeps the tint -- so the state is still legible -- and stops the movement. */
@media ( prefers-reduced-motion: reduce )
{
	.rendition[data-state="working"]
	{
		animation: none;
		background-image: none;
	}
}

/* A failure is not a greyed-out thing: it wants a different colour, because the action it asks for
   is to go and read the job log rather than to wait. */
.rendition[data-state="failed"],
.rendition[data-state="dead"]
{
	color: var( --warning );
	border-color: color-mix( in srgb, var( --warning ) 45%, transparent );
	background: var( --warning-quiet );
}

/* Waiting rungs the ladder could carry but has not been asked for. Still dashed: unlike an
   affordance, a rung is not individually startable -- one video-web job produces every rung at once
   (D99) -- so it is genuinely an absence rather than an offer. */
.rendition[aria-disabled="true"] { cursor: default; }

.rendition[data-state="absent"]
{
	border-style: dashed;
	color: var( --ink-quiet );
	background: transparent;
}

/* ---------------------------------------------------------------------------------------------
   The outcome line after a POST to /action.php, which arrives as ?started= or ?refused=.
   --------------------------------------------------------------------------------------------- */

.action-outcome
{
	margin: 1rem 0 0;
	padding: .55rem .85rem;
	border-radius: 4px;
	font-size: var( --text-base );
	background: var( --accent-quiet );
	color: var( --accent );
}

.action-outcome[data-tone="refused"]
{
	background: var( --warning-quiet );
	color: var( --warning );
}

/* ---------------------------------------------------------------------------------------------
   TOOL AREAS.

   One per surface a chain declares: a title, a sentence, the artefact, and the controls that make
   and use it. The card exists to say "these things belong together" -- the poster player, the rung
   switcher and the encode button are one tool, and they were three unrelated regions of the page
   before (D133).

   A container query rather than a media query would be better here and is deliberately not used:
   the page has one column at every width this serves, so a container query would be machinery
   answering a question nobody is asking.
   --------------------------------------------------------------------------------------------- */

.tool-area
{
	margin: 1.5rem 0;
	padding: 1rem 1.15rem 1.15rem;
	border: 1px solid var( --rule );
	border-radius: 8px;
	background: var( --card-background );
}

.tool-area-header { margin: 0 0 .85rem; }

.tool-area-title
{
	margin: 0;
	font-size: var( --text-md );
	font-weight: 600;
	letter-spacing: 0;
	text-transform: none;
	color: var( --ink );
}

/* ---------------------------------------------------------------------------------------------
   Heading, then metadata. The same shape as the page title above, one level down.

   This replaced a sentence of prose under every heading. The sentence described the KIND of thing
   -- "renditions a browser can play, and the source of every embed" -- which is worth knowing once
   and is identical on every video in the installation; it now lives on the header's title
   attribute. What sits here instead is what is true of THIS artefact: how its picture was chosen,
   how big it is, what codec it is in.

   Badges, so square and unpressable per the vocabulary rule in app.css. Nothing in this row is a
   control and nothing in it should become one -- the controls are under the artefact, which is the
   arrangement D133 and D137 settled on and the reason the media boxes line up.
   --------------------------------------------------------------------------------------------- */
.tool-area-badges
{
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: .35rem;
	margin: .35rem 0 0;
}

/* The provenance badge is the one with something to say, so it is the one allowed a tint. `pending`
   is the state that earns attention: what is on screen is not what was asked for, and it is the
   only badge here that will change on its own. */
.tool-area-badges .badge[data-key-image-state="pending"]
{
	background: var( --warning-quiet );
	color: var( --warning );
}

.tool-area-badges .badge[data-key-image-state="borrowed"],
.tool-area-badges .badge[data-key-image-state="chosen"]
{
	background: var( --accent-quiet );
	color: var( --accent );
}

/* Kept for the surfaces that still render a sentence, and for anything a chain adds later. */
.tool-area-summary
{
	margin: .25rem 0 0;
	max-width: 46rem;
	font-size: var( --text-base );
	line-height: 1.5;
	color: var( --ink-quiet );
}

.tool-area-empty
{
	margin: .5rem 0;
	font-size: var( --text-base );
	color: var( --ink-quiet );
}

/* The controls row sits at the FOOT of its tool area, under the artefact, because the artefact is
   the answer to "what is the state of this" and the controls answer "what can I do about it" --
   which is the order a person asks them in. */
.tool-controls
{
	display: flex;
	flex-wrap: wrap;
	gap: .45rem;
	align-items: center;
	margin: .85rem 0 0;
	padding: 0;
}

/* Inside a tool area the artefact has already been introduced by the header, so its own outer
   framing is redundant -- two nested cards read as a mistake. */
.tool-area .preview { margin: 0; border: 0; background: transparent; overflow: visible; }
.tool-area .player { margin: 0; }
.tool-area .renditions { margin-top: .75rem; }

/* ---------------------------------------------------------------------------------------------
   THE SUNSETTING AREA: archive, remove, restore, erase.

   IT IS A TOOL AREA NOW, and that reverses D136 (see D278). The old argument was that "a tool area
   is where you make things; this is where you unmake them", so giving the two the same card would
   put "Erase permanently" in the same visual class as "Encode for web". It read well and it cost
   more than it was worth: this was the one region of the page drawn in a vocabulary of its own, so
   a reader had to learn a second convention in order to use the three controls that need the most
   care -- and the controls themselves are now .action-rows, which is the same vocabulary the tool
   areas use.

   What survives of D136 is the left rule and the position. The rule is a marker rather than a red
   box, because most of what is in here is reversible: archiving takes nothing away, removal takes
   nothing away and can be undone, and colouring the whole section as danger would teach a person to
   ignore the colour before they ever met the button that earns it. The position -- last on the page
   -- is the other half, and it is why a person scanning an item never passes "Erase" on the way to
   reading its checksum.
   --------------------------------------------------------------------------------------------- */

.lifecycle-area
{
	margin: 2.5rem 0 1rem;

	/* The one thing that separates it from the tool areas above, and it has to be in a colour
	   that is not --rule: a 3px left edge in the same grey as the 1px border around it reads as a
	   rendering artefact rather than as a mark. --ink-quiet is the quietest thing on the page that
	   is still legibly a decision, in both themes, and it is deliberately NOT --warning: most of
	   what is in here is reversible, and a section that cries danger teaches a person to ignore
	   the colour before they ever meet the button that earns it. */
	border-left: 3px solid var( --ink-quiet );
	border-radius: 0 8px 8px 0;
}

/* The box, the rows and the hairlines are .tool-area and .action-row in app.css. What is left here
   is the confirmation, which is the one thing in an action row that no other action row has. */
.lifecycle-confirm
{
	display: flex;
	align-items: baseline;
	gap: .45rem;
	margin: .55rem 0 0;
	font-size: var( --text-base );
	color: var( --ink );
	cursor: pointer;
}


/* ------------------------------------------------------------------------------------------------
   THE INTERACTIVE 3D VIEWER, and the chips that choose it (deferred.md C46, D283).

   The mount sits inside .tool-media beside the turntable video and must occupy exactly the box the
   video had -- the grid places both in the same cell, so switching views must not move the page a
   pixel. Everything here is layout and colour; nothing here decides whether the viewer runs.
   ------------------------------------------------------------------------------------------------ */

.model-viewer
{
	width: 100%;
	height: 100%;
	/* The rasteriser's own ground colour, so the canvas, the turntable's letterboxing and the key
	   image are one colour. Switching view should change what you can do, not what you are looking
	   at. Kept in step with MODEL_BACKGROUND_RGB in app/lib/geometry3d.php by hand -- two files, one
	   colour, and this comment is the only thing holding them together. */
	background: #16181d;
}

.model-viewer canvas
{
	display: block;
	width: 100%;
	height: 100%;
	/* The canvas is the control surface: a drag on it turns the model, so the browser must not
	   claim the gesture for scrolling first. `pan-y` keeps the page scrollable vertically on a
	   touch screen, which is the compromise that stops the viewer trapping the reader. */
	touch-action: pan-y;
}

.model-viewer canvas:focus-visible
{
	/* OrbitControls binds the arrow keys, so the canvas takes focus -- and a focusable thing with
	   no visible focus ring is a WCAG 2.4.7 failure, however decorative it looks. */
	outline: 2px solid #8ab4f8;
	outline-offset: -2px;
}

/* THE SPATIAL VIEWER (D358): the same box, the same swap, a second module. The mount takes the
   turntable's cell exactly as .model-viewer does; the unrolled panorama beneath it is an <img>
   inside .tool-media and inherits that box's fit. For an object movie the mount stays hidden and
   the reel's <video> is the control surface, which the cursor says while it is being turned. */

/* THE MARKDOWN STAGE (D447). NOT a .tool-media box, on purpose: that box keeps a PICTURE's aspect
   ratio on a near-black ground, and a page of prose has no aspect ratio and wants paper. This one
   is as wide as its column and as tall as reading wants, and the document scrolls inside it --
   the page outside cannot measure a frame that has no origin, so the height is chosen, not fitted.
   The ground is the token the frame's own stylesheet paints with, so there is no flash of another
   colour while the frame loads and no seam once it has. */
.markdown-stage
{
	height: min( 75vh, 52rem );
	min-height: 18rem;
	margin-bottom: .75rem;
	border: 1px solid var( --rule );
	border-radius: 6px;
	overflow: hidden;
	background: var( --card-background );
}

.markdown-frame
{
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
	background: var( --card-background );
}

/* THE PAGE READER (D574): a PDF, a deck or a text document as a scrolling column of page pictures
   (lib/pagereader.php draws it; pages-reader.js shows the bar and works it).

   Its area is a cell of the band like any other: the key image on the left, the reader on the
   right. (It took the whole row for an hour; Jon, on seeing it: "no need for this experimental
   layout". Expand and Full screen are there for the reader who wants the room.)

   Like the Markdown stage and unlike .tool-media, the box has no aspect ratio: it is as wide as
   its column and as tall as reading wants, and the pages scroll inside it. The ground is a desk,
   not paper -- the pages are paper, and they are drawn white by the worker whatever the theme --
   so the two do not run together and a page's edge is where the page ends.

   WIDTH IS THE ZOOM. A page is `--page-reader-zoom` times the column; 1 is fit-to-width, which is
   where reading starts. Past 1 the page is wider than the box and the box scrolls sideways; the
   auto margins centre a page that fits and collapse to zero for one that does not, which is what
   keeps its left edge reachable (centring with `align-items` would strand it off-screen).

   FIT PAGE turns the rule round: the page is as large as fits the box whole, which is what a slide
   wants and what a reader wants for a first look at a spread. Both limits are percentages of the
   box, so it holds in full screen without a second rule. */
.page-reader
{
	display: flex;
	flex-direction: column;
	margin-bottom: .75rem;
	border: 1px solid var( --rule );
	border-radius: 6px;
	overflow: hidden;
	background: #4a4d52;
}

.page-reader-bar
{
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: .4rem;
	padding: .4rem .6rem;
	border-bottom: 1px solid var( --rule );
	background: var( --card-background );
	color: var( --ink );
	font-size: var( --text-sm );
}

/* Where you are, and how you see it: two groups that wrap as wholes. A bar too narrow for one row
   breaks between them -- the first version broke wherever it ran out, minus on one line and plus on
   the next. */
.page-reader-bar-group
{
	display: inline-flex;
	align-items: center;
	gap: .4rem;
}

/* `hidden` loses to any author `display`, and the bar is drawn hidden until the script can honour
   it (the same trap .tool-media's children document below). */
.page-reader-bar[hidden] { display: none; }

.page-reader-position
{
	min-width: 6.5rem;
	text-align: center;
	font-variant-numeric: tabular-nums;
	color: var( --ink-quiet );
}

.page-reader-button
{
	padding: .2rem .65rem;
	border: 1px solid var( --rule );
	border-radius: 999px;
	background: var( --card-background );
	color: var( --ink );
	/* A <button> does not inherit its face (app.css .control-pill says how that was found). */
	font: inherit;
	font-size: var( --text-sm );
	white-space: nowrap;
	cursor: pointer;
}

.page-reader-button:hover { border-color: var( --ink-quiet ); }
.page-reader-button:focus-visible { outline: 2px solid var( --accent ); outline-offset: 2px; }
.page-reader-button[aria-pressed="true"] { border-color: var( --accent ); color: var( --accent ); }
.page-reader-button[disabled] { opacity: .45; cursor: default; }

.page-reader-pages
{
	/* The pages' offsets are measured from this box (pages-reader.js reads offsetTop). */
	position: relative;
	display: flex;
	flex-direction: column;
	gap: .75rem;
	/* The band's own media height (.item-view sets it), so the reader stands beside the key image
	   as a neighbour and not as a tower; a floor, because half a window is not always a page. */
	height: max( var( --media-max-height, 50vh ), 24rem );
	min-height: 18rem;
	padding: .75rem;
	overflow: auto;
	/* The reader's scroll ends at the reader: the page behind it does not take over at the last
	   page, which on a phone is the difference between reading and losing your place. */
	overscroll-behavior: contain;
}

.page-reader-pages:focus-visible { outline: 2px solid var( --accent ); outline-offset: -2px; }

.page-reader-page
{
	display: block;
	flex: none;
	width: calc( var( --page-reader-zoom, 1 ) * 100% );
	max-width: none;
	height: auto;
	margin-inline: auto;
	background: #fff;
	box-shadow: 0 1px 4px rgba( 0, 0, 0, .35 );
}

.page-reader[data-fit="page"] .page-reader-page
{
	width: auto;
	max-width: 100%;
	max-height: 100%;
}

/* Given the room, it takes it: the band's Expand (timeline.js marks the area) is a reader asking to read. */
.tool-band[data-focus-applied] .tool-area[data-focused] .page-reader-pages { height: min( 80vh, 64rem ); }

/* The guest page gives the reader most of the window, as it gives the Markdown frame (share.css):
   the title above it and a first glimpse of what is below. `dvh` where it exists. */
.page-reader-full-page .page-reader-pages
{
	height: calc( 100vh - 17rem );
	height: calc( 100dvh - 17rem );
	min-height: 22rem;
}

/* FULL SCREEN, on the reader and not on the pages, so the bar comes along. The pages take what the
   bar leaves; every percentage above then means the screen. */
.page-reader:fullscreen
{
	border: 0;
	border-radius: 0;
}

.page-reader:fullscreen .page-reader-pages
{
	flex: 1 1 auto;
	height: auto;
	min-height: 0;
}

.page-reader-note { margin-top: -.25rem; }

@media ( max-width: 46rem )
{
	/* A phone: less padding round the page, and the words that can wrap do. */
	.page-reader-pages { padding: .4rem; gap: .4rem; }
	.page-reader-position { min-width: 0; }
}

@media print
{
	/* Paper has no scroll box. The pages print one after another at the sheet's width. */
	.page-reader { border: 0; background: none; }
	.page-reader-bar { display: none; }
	.page-reader-pages { height: auto; overflow: visible; padding: 0; }
	.page-reader-page { width: 100%; box-shadow: none; break-inside: avoid; }
}

/* THE WEB LINK'S AREA (D561): a picture beside what it is a picture of. Two columns where there is
   room and one where there is not, by the space the area has and not by the viewport. The picture
   box is 16:9 like the card's, so the same capture looks the same in both; the placeholder inside
   it is app.css's `.card-link`, drawn larger by this box's own font size. */
.link-stage
{
	display: grid;
	grid-template-columns: repeat( auto-fit, minmax( min( 16rem, 100% ), 1fr ) );
	gap: 1rem 1.25rem;
	align-items: center;
	margin-bottom: .75rem;
}

.link-picture
{
	aspect-ratio: 16 / 9;
	border: 1px solid var( --rule );
	border-radius: 6px;
	overflow: hidden;
	background: var( --card-background );
	font-size: var( --text-lg );
}

.link-picture img
{
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.link-facts
{
	display: grid;
	gap: .45rem;
	min-width: 0;
}

.link-facts p
{
	margin: 0;
}

/* The site, large: it is where Open goes, read from the address and never from the page. */
.link-site
{
	font-size: var( --text-xl );
	font-weight: 650;
	overflow-wrap: anywhere;
}

.link-address
{
	color: var( --ink-quiet );
	font-size: var( --text-sm );
	overflow-wrap: anywhere;
}

.link-address-field
{
	width: 100%;
	min-width: 0;
	margin-top: .4rem;
	padding: .35rem .5rem;
	font: inherit;
	font-size: var( --text-sm );
}

/* THE FLASH STAGE (D436). One grid cell holds everything -- the poster or the sentence, the sealed
   frame over it once Play is pressed, and the status line over that -- so the box never changes
   size between the still and the running movie. */
.flash-stage > *
{
	grid-area: 1 / 1;
}

.flash-stage .flash-intro,
.flash-stage .flash-status
{
	margin: 0;
	padding: 1rem;
	max-width: 32rem;
	text-align: center;
	color: #d8d8d4;
	font-size: var( --text-sm );
}

.flash-stage .flash-status
{
	z-index: 2;
	border-radius: 4px;
	background: rgba( 0, 0, 0, .72 );
}

.flash-asked p
{
	margin: 0 0 .5rem;
	padding: .5rem .75rem;
	border-left: 3px solid #c98a1b;
	border-radius: 2px;
	background: rgba( 201, 138, 27, .12 );
	font-size: var( --text-sm );
}

.flash-viewer
{
	z-index: 1;
	width: 100%;
	height: 100%;
	background: #000;
}

.flash-viewer iframe
{
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
}

.spatial-viewer
{
	width: 100%;
	height: 100%;
	background: #16181d;
}

/* THE PANORAMA CLAIMS BOTH AXES, and this is the one place it must differ from `.model-viewer canvas`
   above (Jon, 2026-09-08). That rule keeps `pan-y` as a deliberate compromise -- the page stays
   scrollable through the viewer on a touch screen -- and this rule inherited the value by being
   written next to it. It is wrong here: `pan-y` tells the browser that vertical gestures belong to
   the page, so **a touch drag upward scrolled instead of tilting**, and tilt is not a nicety in a
   panorama. It is half the interaction, it has a range the QTVR data track declared, and a viewer
   that answers only horizontal drags is showing two thirds of the file.

   The cost is the one every map on the web pays: a touch that starts inside the picture no longer
   scrolls the page. It is bounded by the box -- the media area is capped at 50vh on the item page
   and 60vh on the guest page -- so there is always page above and below to scroll from.

   The object movie's reel already resolved the same question the same way, one rule below. */
.spatial-viewer canvas
{
	display: block;
	width: 100%;
	height: 100%;
	touch-action: none;
	cursor: grab;
}

.spatial-viewer canvas:focus-visible
{
	outline: 2px solid #8ab4f8;
	outline-offset: -2px;
}

.loop-view video[data-scrubbing="yes"]
{
	cursor: ew-resize;
	touch-action: none;
}

/* The chips are .rendition and inherit everything from it. The only difference is that these are
   buttons rather than links, and a button carries UA styling that would otherwise show through. */
.view-select .rendition
{
	font: inherit;
	background: none;
	cursor: pointer;
}

.view-select .rendition[disabled]
{
	cursor: default;
}

/* AND THE FILL THE RULE ABOVE TOOK AWAY, restated where it can win.

   `background: none` there is a button reset, and it also unset `.rendition[aria-current="true"]`'s
   accent fill: same specificity (0,2,0), later in the file, so it wins. The chip then drew with no
   fill and its label in `--page-background` -- white text on the page's own ground -- so THE VIEW
   YOU ARE LOOKING AT WAS THE ONE CHIP YOU COULD NOT READ. Measured on both doors: `Turn by hand`
   after a press came back `color: rgb(253,248,234)` over `background: rgba(0,0,0,0)` on /item.php,
   and the same on the guest page (2026-09-08).

   The fill is the only thing in this vocabulary that says which view is current -- the ladder above
   spends `current = solid accent` on exactly this -- so restoring it is restoring the state, not
   decoration. Kept as a separate rule rather than by deleting `background: none`, because that line
   is doing a real job for the three chips that are NOT current. */
.view-select .rendition[aria-current="true"]
{
	background: var( --accent );
}

/* THE TWO RULES THAT MAKE SWITCHING ACTUALLY SWITCH, and the first is a debt this stylesheet already
   owed before the viewer existed.

   `app.css` states the rule and this file broke it: the user-agent's `[hidden] { display: none }`
   loses to ANY author declaration of `display`, so `.tool-media video { display: block }` above means
   `video.hidden = true` does NOTHING. The turntable stayed on screen, paused, and the canvas was laid
   out underneath it and off the bottom of the panel -- which reads exactly as "the Interactive button
   stops the turntable and nothing more" (D288, reported by Jon).

   Any author rule that sets `display` on an element that is ever `hidden` owes a matching `[hidden]`
   rule. This is that rule, paid late. */
.tool-media img[hidden],
.tool-media video[hidden],
.tool-media .model-viewer[hidden],
.tool-media .spatial-viewer[hidden]
{
	display: none;
}

/* And the second: both views belong in the SAME grid cell. `.tool-media` is a grid with no explicit
   template, so two children become two rows -- which is why the canvas appeared below the video
   rather than in place of it, and why the media box was twice the height it should have been. One
   cell means switching views cannot move a single pixel of the page. */
.loop-view > [data-view]
{
	grid-area: 1 / 1;
	width: 100%;
	height: 100%;
}


/* ---------------------------------------------------------------------------------------------
   RIGHTS AND CREDITS (D343). A tool area beside the details box, in the details form's own
   vocabulary: the same grid, the same pill. What is new is the credits table, which is three
   columns -- role, name, link -- and reads as a table because it is one. The rules are here rather
   than in app.css because the area draws only on the item page (and on the guest page through
   share.css's own line), and app.css is the shell's sheet.
   --------------------------------------------------------------------------------------------- */
.rights-area .rights-line { margin: 0 0 0.35rem; }
.rights-area .rights-provenance { display: block; font-size: var( --text-base ); margin-bottom: 0.75rem; }
.rights-area .rights-ai-training { font-size: var( --text-base ); margin: 0 0 0.75rem; }
.rights-area .rights-attribution { margin: 0 0 1rem; display: grid; gap: 0.25rem; max-width: 46rem; }
.rights-area .rights-attribution textarea { font: inherit; font-size: var( --text-base ); }
.rights-area .credits-list { margin: 0 0 0.75rem; padding-left: 1.1rem; }
.rights-area .rights-conditions { margin: 0 0 0.75rem; white-space: pre-line; }

/* The rule that stood here (`.rights-form select, .rights-form input[ type="url" ] { font: inherit;
   padding: 0.3rem 0.5rem; min-width: 0 }`) retired on 2026-10-10, in two steps. The URL half made
   the Rightsholder link inherit its label's --text-base while every text field beside it read at
   --text-md ("one of the fields is not like the others", Jon) -- app.css's .details-form block now
   names url/email/number alongside text, and this later-loading sheet restating the field was
   exactly what overrode it. The select half then measured as the same kind of leftover: its padding
   beat app.css's select dressing, whose 1.9rem right-hand padding is where the chevron lives, so
   the rights selects ran their text under the arrow while Language beside them did not. Everything
   it declared, app.css's base select and .details-form rules now say. */

.credits-rows { display: grid; gap: 0.4rem; border: 0; padding: 0; margin: 0; }
.credits-rows legend { font-size: var( --text-base ); padding: 0; margin-bottom: 0.25rem; }
.credits-row,
.credits-row-head
{
	display: grid;
	/* Zero floors, so the row can never be wider than the area holding it (see .tool-areas
	   .tool-area); the narrow-screen rule below stacks the three long before that matters. */
	grid-template-columns: minmax(0, 11rem) minmax(0, 1fr) minmax(0, 1fr);
	gap: 0.4rem;
	align-items: center;
}
.credits-row-head { font-size: var( --text-sm ); }
.credits-row input[ type="text" ] { padding-block: 0.3rem; }

@media ( max-width: 40rem )
{
	.credits-row-head { display: none; }
	.credits-row { grid-template-columns: 1fr; }
}


/* ---------------------------------------------------------------------------------------------
   SIGNING (D369): the fixity and provenance table, and the sentence above it.

   Almost nothing here, deliberately. The table inherits `.item-view table` a few hundred lines up
   -- same ground, same rule, same radius, same cell padding as the Representations table it sits
   near -- so the only rules are the three this area adds: a checksum that reads as a hash rather
   than as prose, the provenance line under each file name, and the trust sentence.

   THE TRUST SENTENCE IS NOT A BADGE, AND THAT IS THE POINT (docs/design-signing.md §4). A
   self-signed certificate produces a signature that is valid and an issuer nobody recognises, so
   `.signing-trust` is quiet body text rather than a pill: a coloured badge reading "Signed" would
   tell the reader the claim has been vouched for by somebody, which is the one thing it has not.
   The only coloured state here is an EXPIRED certificate, which is a fault an operator must fix.
   --------------------------------------------------------------------------------------------- */
.signing-area .signing-state { margin: 0 0 0.35rem; }
.signing-area .signing-trust { font-size: var( --text-base ); margin: 0 0 0.9rem; }

/* The one sentence that is true of every row, said once above the table instead of in each cell
   (signing.php's cell logic says why). Same size as the trust sentence because it is the same kind
   of thing -- an explanation of the mechanism, not a fact about this item -- with a little more air
   under it, since the table starts immediately below. */
.signing-area .signing-what { font-size: var( --text-base ); margin: 0 0 1.1rem; }

.signing-area .signing-checksum
{
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: var( --text-sm );
	/* The cell shows the first 16 characters and the `title` carries all 64. `nowrap` rather than a
	   break: sixteen hex characters always fit, and a hash broken across two lines reads as two
	   values -- which was what the first version of this rule did, found by looking at it. */
	white-space: nowrap;
}

.signing-area .signing-provenance { display: block; font-size: var( --text-sm ); font-weight: 400; margin-top: 0.15rem; }
.signing-area .signing-footnote { font-size: var( --text-sm ); margin: 0.75rem 0 0; }
.signing-area .signing-receipt-link { white-space: nowrap; }

/* ON A PHONE THE TABLE HAS TO WRAP (Jon, 2026-09-11: "the signing tool area has issues on small
   screens"). It measured 631px inside a 451px box, and the whole excess was one inherited rule:
   `.item-view tbody th` is `white-space: nowrap` for the Representations table's short row labels,
   and here the row header also carries the provenance sentence, a block of prose that therefore
   could not break. The 13rem column width was the rest. Under the sheet breakpoint (46rem, app.css's
   figure rather than this file's 40rem, because between the two the box is still narrower than the
   table) the header wraps and takes its natural width, and the cells lose a little padding. The
   checksum keeps its `nowrap`: sixteen hex characters fit, and a hash broken in two reads as two
   values. `overflow-wrap: anywhere` on the provenance line is for a recipe name longer than the
   column, which no current recipe has and one day one will. */
@media ( max-width: 46rem )
{
	/* The column headings too: "How it can be proved" set in one line held its column at 300px and
	   squeezed the file column, prose and all, into a strip five lines deep. */
	.signing-area thead th, .signing-area tbody th { white-space: normal; width: auto; }
	.signing-area .signing-provenance { overflow-wrap: anywhere; }
	.signing-area th, .signing-area td { padding: .5rem .55rem; }
}


/* ---------------------------------------------------------------------------------------------
   TRANSCRIPTION (D374): the full-width tool area after Title and description.

   The text is WebVTT in a textarea, set in the monospace face, because the timing lines are the
   structure of the document: in a proportional face `00:01:02.500 --> 00:01:05.000` is a different
   width on every line, and those lines are what a person scans down to find the sentence to fix.
   The form drops .details-form's 46rem reading measure, because the area is full width for exactly
   this text (transcripts.php) and a measure would put it back in a column.
   --------------------------------------------------------------------------------------------- */
.transcript-area .transcript-state { margin: 0 0 0.75rem; font-size: var( --text-base ); }
.transcript-area .details-form.transcript-form { max-width: none; }

.transcript-area .transcript-text
{
	width: 100%;
	box-sizing: border-box;
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: var( --text-base );
	line-height: 1.5;
	resize: vertical;
}

/* THE PANEL THAT OPENS (Jon, 2026-09-14): the text, the language, Save and the downloads in one
   rounded box, so the editor reads as one whole and stops at its own edge instead of running into the
   offer row under it. A hairline in the rule colour and the page's off-white behind it -- a step
   quieter than the card it sits in -- and nothing heavier: it is a region, not a warning. */
.transcript-area .transcript-panel
{
	padding: 1rem 1.1rem 1.1rem;
	border: 1px solid var( --rule );
	border-radius: 0.75rem;
	background: var( --page-background );
}

.transcript-area .transcript-panel .transcript-text { background: var( --card-background ); }

.transcript-area .transcript-hint { margin: 0; font-size: var( --text-sm ); color: var( --ink-quiet ); }
.transcript-area .transcript-language input { max-width: 8rem; }
.transcript-area .transcript-downloads { margin: 0.75rem 0 0; font-size: var( --text-base ); }

/* A TEXT'S ROW IS A DISCLOSURE (D398): the <details> sits in the footer like any row, its <summary> IS
   the action row -- title, state, pill -- and the panel beneath it is the editor. The summary carries
   .action-row's flex, padding and rule; the marker is hidden as .details-area hides it; open, the rule
   moves from the summary to the foot of the panel so the row still ends where the next begins. */
.transcript-area .action-row-disclosure { margin: 0; padding: 0; border: 0; }
.transcript-area .action-row-disclosure > summary { display: flex; list-style: none; cursor: pointer; }
.transcript-area .action-row-disclosure > summary::-webkit-details-marker { display: none; }
.transcript-area .action-row-disclosure > summary:focus-visible { outline: 2px solid var( --accent ); outline-offset: 2px; }
.transcript-area .action-row-disclosure[open] > summary { border-bottom: 0; padding-bottom: .3rem; }
.transcript-area .action-row-disclosure[open] > .transcript-panel { margin: 0 0 .9rem; }
.transcript-area .action-row-disclosure[open]:not( :last-child ) { border-bottom: 1px solid var( --rule ); padding-bottom: .9rem; }
.transcript-area .transcript-state-note { font-size: var( --text-sm ); }

/* The editor's exits (D403): Cancel and Save corrections on one line, right-aligned like every
   details form's Save; the unsaved-edits dialog dressed as the move interstitial is. The summary's
   control column is a span, because a summary holds phrasing content; it is laid out as the div is. */
.transcript-area .transcript-actions { display: flex; justify-content: flex-end; gap: .5rem; margin: .75rem 0 0; }
.transcript-area summary > .action-row-controls { display: flex; flex-direction: column; gap: .4rem; }
.transcript-unsaved { padding: 0; border: 0; background: transparent; }
.transcript-unsaved::backdrop { background: rgba( 0, 0, 0, .35 ); }
.transcript-unsaved .interstitial { margin: 0; }
.transcript-unsaved .transcript-actions { justify-content: flex-end; }

/* A heading between rows (D403): small capitals like a table head, with the rule the rows already
   draw between them, so it reads as a divider and not as a section of its own. */
.tool-area-footer .tool-area-subheading { margin: 1.1rem 0 .2rem; font-size: var( --text-xs ); font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var( --ink-quiet ); }

/* The glossary's proposed fixes (D387): a bordered list above the editor's disclosure. One row is a
   checkbox, the timestamp as a link into the editor, the caption with the heard word marked and the
   replacement under it, and Skip at the end of the row. */
.transcript-area .transcript-fixes
{
	margin: 0 0 1rem;
	padding: 0.75rem 1rem;
	border: 1px solid var( --rule );
	border-radius: 0.5rem;
	background: var( --card-background );
}

.transcript-area .transcript-fixes-title { margin: 0 0 0.25rem; font-size: var( --text-md ); }
.transcript-area .transcript-fixes .transcript-hint { margin: 0 0 0.75rem; }
.transcript-area .transcript-fixes-list { list-style: none; margin: 0 0 0.75rem; padding: 0; }

.transcript-area .transcript-fix
{
	display: grid;
	grid-template-columns: auto auto 1fr auto;
	gap: 0.25rem 0.75rem;
	align-items: start;
	padding: 0.5rem 0;
	border-top: 1px solid var( --rule );
}

.transcript-area .transcript-fix:first-child { border-top: 0; }
.transcript-area .transcript-fix input[type="checkbox"] { margin: 0.2rem 0 0; }

.transcript-area .transcript-fix-line
{
	font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
	font-size: var( --text-sm );
	color: var( --ink-quiet );
	padding-top: 0.1rem;
	white-space: nowrap;
}

.transcript-area .transcript-fix-body { display: block; min-width: 0; font-size: var( --text-base ); line-height: 1.45; }
.transcript-area .transcript-fix-context { display: block; overflow-wrap: anywhere; }
.transcript-area .transcript-fix-context mark { padding: 0 0.15em; background: var( --warning-quiet ); color: inherit; border-radius: 0.2em; }
.transcript-area .transcript-fix-change { display: block; font-size: var( --text-base ); color: var( --ink-quiet ); }
.transcript-area .transcript-fix-skip { font-size: var( --text-sm ); }

.transcript-area .transcript-fixes-empty { margin: 0 0 0.75rem; font-size: var( --text-base ); color: var( --ink-quiet ); }

/* What the current correction changed (D388): a quiet line of "heard → corrected" pairs. */
.transcript-area .transcript-corrections { margin: 0 0 0.75rem; font-size: var( --text-base ); line-height: 1.5; }
.transcript-area .transcript-correction { white-space: nowrap; }
.transcript-area .transcript-correction s { color: var( --ink-quiet ); }

@media ( max-width: 40rem )
{
	.transcript-area .transcript-fix { grid-template-columns: auto auto 1fr; }
	.transcript-area .transcript-fix-skip { grid-column: 3; justify-self: end; }
}


/* ---------------------------------------------------------------------------------------------
   THE TOUCH-SCREEN FONT FLOOR, the item page.

   app.css states the rule and the whole argument for it: under a coarse pointer no text-entry
   control may compute below 16px, because iOS Safari zooms the page in on focusing one and never
   zooms back out. This file loads AFTER app.css, so every field it sizes here wins the tie against
   that floor and has to repeat it. The three here are the attribution box, the rights form
   and the WebVTT editor -- the last keeps its monospace face, which is about the timing lines and
   not about the size.
   --------------------------------------------------------------------------------------------- */

@media ( pointer: coarse )
{
	.rights-area .rights-attribution textarea,
	.transcript-area .transcript-text
	{
		font-size: max( 16px, 1em );
	}
}


/* ---------------------------------------------------------------------------------------------
   THE TIMELINE (D439), and the focus toggle every area in the band shares.

   The area is a cell of .tool-areas like the player beside it, so the box, the header and the footer
   are .tool-area's. What is declared here is the list -- a scroller of its own, because a recording
   with captions has hundreds of rows and the page must not become that long -- and the three
   vocabularies inside a row: the TIME is a control (it seeks), the KIND is a label, the rest is text.
   --------------------------------------------------------------------------------------------- */

.tool-area-header { position: relative; }

/* The focus button sits in the header's corner and out of the flow, so a header that gains one keeps
   the height it had and the media boxes beside each other still line up (D137). Drawn only where a
   script can honour it: timeline.js owns the behaviour, and without it the button would do nothing. */
.tool-area-focus
{
	position: absolute;
	top: -.15rem;
	right: -.25rem;
	padding: .15rem .5rem;
	border: 1px solid transparent;
	border-radius: 999px;
	background: transparent;
	color: var( --ink-quiet );
	font: inherit;
	font-size: var( --text-xs );
	cursor: pointer;
}

.tool-area-focus:hover,
.tool-area-focus:focus-visible { border-color: var( --rule ); color: var( --ink ); }

/* The Timeline's header carries TWO corner buttons -- Fullscreen (D533) and Expand -- and both took
   the same absolute corner, overlapping on every video item. Found when D614 made the area the main
   surface; the media button steps left of the other's widest word. */
.timeline-area .tool-area-focus[data-media-fullscreen] { right: 5.2rem; }

.tool-area-focus .tool-area-focus-restore { display: none; }
.tool-area-focus[aria-pressed="true"] .tool-area-focus-expand { display: none; }
.tool-area-focus[aria-pressed="true"] .tool-area-focus-restore { display: inline; }
.tool-area-focus[aria-pressed="true"] { border-color: var( --rule ); color: var( --ink ); }

/* Only the item page's band has focus, so the buttons elsewhere (none today) would be inert. */
.tool-areas:not( .tool-band ) .tool-area-focus { display: none; }

/* FOCUSED. `data-focus-applied` is set by the script AFTER it has marked which areas stay, so a page
   that arrives with ?focus= and no script hides nothing. Its value is how many areas are shown. */
.tool-band[data-focus-applied] > .tool-area:not( [data-focused] ) { display: none; }
.tool-band[data-focus-applied="1"] { grid-template-columns: minmax( 0, 1fr ); }
.tool-band[data-focus-applied="2"] { grid-template-columns: minmax( 0, 3fr ) minmax( 20rem, 2fr ); }

/* The Timeline focused takes what its words need and no more (D621, Jon: "about 60+ characters of
   content per line"): 60 characters at the list's size, plus the time column, the margin the up
   glyph borrows and the area's own padding. The player takes the rest. (D614's flip gave the
   transcript three quarters of the band, which set lines far past reading length.) */
.tool-band[data-focus-applied="2"][data-focus="timeline"] { grid-template-columns: minmax( 0, 1fr ) minmax( 20rem, 40rem ); }

@media ( max-width: 60rem )
{
	/* Both spellings, because the flipped rule above is the more specific and would win here otherwise. */
	.tool-band[data-focus-applied="2"],
	.tool-band[data-focus-applied="2"][data-focus="timeline"] { grid-template-columns: minmax( 0, 1fr ); }
}

.timeline-list
{
	list-style: none;
	margin: 0 0 .75rem;
	padding: 0;
	max-height: 22rem;
	overflow-y: auto;
	overscroll-behavior: contain;
	border-top: 1px solid var( --rule );
	/* offsetTop of a row is measured from here, which timeline.js relies on to scroll the list alone. */
	position: relative;
}

.tool-band[data-focus-applied] .timeline-list { max-height: min( 70vh, 52rem ); }

.timeline-row
{
	display: grid;
	grid-template-columns: 4.2rem 4.6rem minmax( 0, 1fr );
	gap: .5rem;
	align-items: baseline;
	padding: .4rem .25rem;
	border-bottom: 1px solid var( --rule );
	font-size: var( --text-sm );
}


/* The row's own grid would beat the `hidden` attribute (UA display: none loses to author display:
   grid), and `hidden` is how a non-chosen language's rows are parked (D614). Scoped to the list for
   the weight: a turn's `.timeline-row.timeline-turn { display: block }` comes later in this file at
   equal specificity and won, so another language's turns stayed on screen (D631, seen there). */
.timeline-list .timeline-row[hidden] { display: none; }

/* THE TOOLBAR AND ITS TABS (D631, Jon's layout): the language first, then one view at a time.
   Each tab shows only its own rows; everything else about a view goes with it. */
.timeline-toolbar { display: flex; flex-wrap: wrap; gap: .35rem; align-items: center; margin: 0 0 .6rem; }

.timeline-language-select
{
	padding: .2rem .45rem;
	border: 1px solid var( --rule );
	border-radius: .4rem;
	/* The longhand: the shorthand would wipe the chevron app.css draws as a background-image. */
	background-color: var( --card-background );
	color: var( --ink );
	font: inherit;
	font-size: var( --text-xs );
}

.timeline-tab
{
	padding: .2rem .65rem;
	border: 1px solid var( --rule );
	border-radius: 999px;
	background: transparent;
	color: var( --ink-quiet );
	font: inherit;
	font-size: var( --text-xs );
	cursor: pointer;
}

.timeline-tab[aria-pressed="true"] { background: var( --accent-quiet ); color: var( --ink ); border-color: transparent; }

.timeline-area[data-timeline-tab="transcript"] .timeline-row:not( [data-kind="turn"] ),
.timeline-area[data-timeline-tab="subtitles"] .timeline-row:not( [data-kind="caption"] ):not( [data-kind="speaker"] ),
.timeline-area[data-timeline-tab="chapters"] .timeline-row:not( [data-kind="chapter"] ):not( [data-kind="key-image"] ),
.timeline-area[data-timeline-tab="notes"] .timeline-row:not( [data-kind="note"] ) { display: none; }

/* The Transcript tab's head: the action for the chosen language -- Transcribe (again) for the
   source, Translate (again) for any other -- and, compact once a transcript exists, the button
   and its select alone. */
.timeline-transcript-head { margin: 0 0 .6rem; }
.timeline-area:not( [data-timeline-tab="transcript"] ) .timeline-transcript-head { display: none; }
.timeline-area[data-language-role="primary"] .timeline-offer[data-offer="translate"],
.timeline-area[data-language-role="translation"] .timeline-offer[data-offer="transcribe"],
.timeline-area[data-language-role="untranslated"] .timeline-offer[data-offer="transcribe"] { display: none; }
.timeline-transcript-head[data-compact] .action-row-text,
.timeline-transcript-head[data-compact] .timeline-offer[data-offer="translate"] select { display: none; }
.timeline-transcript-head[data-compact] .action-row { padding: 0; border: 0; }
.timeline-transcript-head[data-compact] .action-row > .action-row-controls { flex-direction: row; flex-wrap: wrap; align-items: center; margin-left: 0; }
.timeline-untranslated { display: none; margin: .4rem 0 0; color: var( --ink-quiet ); font-size: var( --text-sm ); }
.timeline-area[data-language-role="untranslated"] .timeline-untranslated { display: block; }

/* The tab's second row: Review and Reflow for Subtitles, the speakers for both transcript tabs. */
.timeline-subtoolbar { display: flex; flex-wrap: wrap; gap: .4rem .9rem; align-items: baseline; margin: 0 0 .6rem; }
.timeline-subtoolbar .timeline-reflow-form,
.timeline-subtoolbar .timeline-speakers-disclosure { margin: 0; }
.timeline-area:not( [data-timeline-tab="transcript"] ):not( [data-timeline-tab="subtitles"] ) .timeline-subtoolbar,
.timeline-area:not( [data-timeline-tab="subtitles"] ) .timeline-review-bar,
.timeline-area[data-timeline-tab="transcript"] .timeline-reflow-form { display: none; }

/* The add form belongs to Chapters and Notes, each with only what its kind takes. */
.timeline-area[data-timeline-tab="transcript"] .tool-area-footer,
.timeline-area[data-timeline-tab="subtitles"] .tool-area-footer,
.timeline-area[data-timeline-tab="chapters"] .timeline-kind-field,
.timeline-area[data-timeline-tab="chapters"] .timeline-add-to,
.timeline-area[data-timeline-tab="chapters"] .timeline-pin-button { display: none; }

/* Each tab says so when it has nothing of its kind. */
.timeline-tab-empty { display: none; }
.timeline-area[data-timeline-tab="subtitles"]:not( :has( .timeline-row[data-kind="caption"] ) ) .timeline-tab-empty[data-tab="subtitles"],
.timeline-area[data-timeline-tab="chapters"]:not( :has( .timeline-row[data-kind="chapter"] ) ) .timeline-tab-empty[data-tab="chapters"],
.timeline-area[data-timeline-tab="notes"]:not( :has( .timeline-row[data-kind="note"] ) ) .timeline-tab-empty[data-tab="notes"] { display: block; }

/* TWO VIEWS OF ONE TRANSCRIPT (D621): Text shows a paragraph per turn, Subtitles the timed rows --
   and each view's own tools go with it. Never by `hidden`, which the language switch owns. */
.timeline-area[data-transcript-view="text"] .timeline-row[data-kind="caption"],
.timeline-area[data-transcript-view="text"] .timeline-row[data-kind="speaker"],
.timeline-area[data-transcript-view="text"] .timeline-review-bar,
.timeline-area[data-transcript-view="text"] .timeline-reflow-form,
.timeline-area[data-transcript-view="subtitles"] .timeline-row[data-kind="turn"] { display: none; }


/* A turn (D621): the speaker's name, then what they said -- a paragraph to read, and for an editor
   in the focused area the textarea in its place. */
.timeline-row.timeline-turn { display: block; padding: .6rem .25rem .7rem; }
.timeline-turn .timeline-speaker-name { display: block; margin: 0 0 .25rem; }
.timeline-turn-text { margin: 0; line-height: 1.55; }

.timeline-area:not( [data-focused] ) .timeline-turn-form { display: none; }
.timeline-area[data-focused] .timeline-turn:has( .timeline-turn-form ) .timeline-turn-text { display: none; }

.timeline-turn-field
{
	display: block;
	width: 100%;
	box-sizing: border-box;
	padding: .45rem .55rem;
	border: 1px solid var( --rule );
	border-radius: .4rem;
	background: var( --card-background );
	color: inherit;
	font: inherit;
	font-size: var( --text-sm );
	line-height: 1.55;
	resize: vertical;
}

.timeline-turn-field:focus { outline: 2px solid var( --accent ); outline-offset: 1px; }
.timeline-turn-actions { display: flex; justify-content: space-between; align-items: center; margin: .35rem 0 0; }


/* A caption's displayed line breaks are real newlines in its text (D621). */
.timeline-cue-open { white-space: pre-line; }
.timeline-cue-open:focus-visible,
.timeline-cue-open[data-nav-focus]:focus,
.timeline-time[data-nav-focus]:focus { outline: 2px solid var( --accent ); outline-offset: 2px; border-radius: .2rem; }

/* Subtitles over the picture (D621): laid over the video exactly as the pins are, the words at
   the foot of the frame, sized from the picture's height by timeline.js. */
.timeline-subtitle-overlay
{
	position: absolute;
	display: flex;
	align-items: flex-end;
	justify-content: center;
	padding: 0 4% 5%;
	box-sizing: border-box;
	pointer-events: none;
	z-index: 3;
}

.timeline-subtitle-overlay[hidden] { display: none; }

/* The words can be dragged while their caption is open (D630): the one part of the overlay that
   takes the pointer, and only then. */
.timeline-subtitle-overlay[data-draggable] > span { pointer-events: auto; cursor: move; touch-action: none; outline: 1px dashed rgb( 255 255 255 / .6 ); outline-offset: 2px; }

/* The placement picker (D630): nine points in a 3x3 square, the current one filled. */
.timeline-placement { display: inline-grid; grid-template-columns: repeat( 3, .85rem ); gap: 2px; padding: 2px; border: 1px solid var( --rule ); border-radius: .25rem; align-self: center; }
.timeline-placement-point { width: .85rem; height: .55rem; padding: 0; border: 0; border-radius: 1px; background: var( --rule ); cursor: pointer; }
.timeline-placement-point:hover,
.timeline-placement-point:focus-visible { background: var( --ink-quiet ); }
.timeline-placement-point[aria-pressed="true"] { background: var( --accent ); }

.timeline-subtitle-overlay > span
{
	white-space: pre-line;
	text-align: center;
	line-height: 1.3;
	padding: .1em .45em;
	border-radius: .2em;
	background: rgb( 0 0 0 / .72 );
	color: #fff;
}

/* The quiet player's bar under the picture (D621): play, the clock, a scrubber -- nothing over it. */
.timeline-player-bar
{
	display: flex;
	align-items: center;
	gap: .6rem;
	margin: .4rem 0 0;
	font-size: var( --text-xs );
	color: var( --ink-quiet );
}

.timeline-player-clock { white-space: nowrap; font-variant-numeric: tabular-nums; }
.timeline-player-scrubber { flex: 1 1 auto; min-width: 4rem; accent-color: var( --accent ); }

/* A speaker's turn (D614): the header row where the voice changes. The name IS the seek button
   (D616) -- a separate time duplicated the first cue's. */
.timeline-row.timeline-speaker
{
	grid-template-columns: minmax( 0, 1fr );
	padding-top: .65rem;
	border-bottom: 0;
}

.timeline-speaker-name
{
	padding: 0;
	border: 0;
	background: transparent;
	font: inherit;
	font-weight: 600;
	color: var( --ink );
	text-align: left;
	cursor: pointer;
}

.timeline-speaker-name:hover,
.timeline-speaker-name:focus-visible { text-decoration: underline dotted var( --ink-quiet ); text-underline-offset: .2em; }

/* STICKY (D617, Jon: "so when scrolling it shows whose rows we are looking at"): the header holds
   the top of the scrolling list while its turn scrolls under it, and the next one takes its place.
   The ground must be opaque or the rows read through it. */
.timeline-row.timeline-speaker
{
	position: sticky;
	top: 0;
	z-index: 2;
	background: var( --card-background );
}

/* EDITING WANTS THE ROOM (D615, Jon): unfocused, the lane is a reader -- the rename rides only the
   expanded area, and the cue editor opens only there too (timeline.js refuses the press unfocused;
   the form itself is [hidden] at rest, D616). The forms are still server-drawn either way (the
   redraw path and the proofs see them); a page with no script keeps Edit as text in the manage
   block, which is open at rest. */
.timeline-area:not( [data-focused] ) .timeline-speakers-disclosure { display: none; }

/* A transcript row reads tight (D616): time and words, no kind column; the time never wraps. */
.timeline-row[data-kind="caption"] { grid-template-columns: 4.2rem minmax( 0, 1fr ); }
.timeline-time { white-space: nowrap; }

/* The words are the editor's door (D616): a button that reads as the text it holds. The pointer
   says so only where the press would do something -- the focused area. */
.timeline-cue-open
{
	display: block;
	width: 100%;
	padding: 0;
	border: 0;
	background: transparent;
	color: inherit;
	font: inherit;
	text-align: left;
	cursor: default;
}

.tool-area[data-focused] .timeline-cue-open { cursor: pointer; }
.tool-area[data-focused] .timeline-cue-open:hover,
.tool-area[data-focused] .timeline-cue-open:focus-visible { text-decoration: underline dotted var( --ink-quiet ); text-underline-offset: .2em; }

/* display: flex below would beat the UA's [hidden], and hidden-at-rest is the editor's whole state. */
.timeline-cue-form[hidden] { display: none; }

/* EDITING IN PLACE (D617): the row says it is being worked on, and the words keep their shape --
   no box around them, just the caret and the selection. */
.timeline-row[data-editing] { background: var( --accent-quiet ); }
/* Only while actually editable (D624): a bare [contenteditable] also matched "false", which hid the
   keyboard's focus outline on every row that had ever been opened. */
.timeline-cue-open[contenteditable]:not( [contenteditable="false"] ):focus { outline: none; }

/* While editing, the words go inline so the down glyph can hug the last word (D618); the up glyph
   borrows the left margin -- the body is the positioning context -- so nothing pushes the row in. */
.timeline-row[data-kind="caption"] .timeline-body { position: relative; }
.timeline-row[data-editing] .timeline-cue-open { display: inline; width: auto; }

.timeline-shift-glyph { display: none; }

.timeline-row[data-editing] .timeline-shift-glyph
{
	display: inline-grid;
	place-items: center;
	min-width: 1.35rem;
	height: 1.35rem;
	padding: 0;
	border: 1px solid var( --rule );
	border-radius: 999px;
	background: var( --card-background );
	color: var( --ink-quiet );
	font: inherit;
	font-size: var( --text-xs );
	line-height: 1;
	cursor: pointer;
	vertical-align: text-bottom;
}

.timeline-shift-glyph:hover,
.timeline-shift-glyph:focus-visible { border-color: var( --accent ); color: var( --ink ); }

.timeline-row[data-editing] .timeline-shift-up { position: absolute; left: -1.75rem; top: .1rem; }
.timeline-shift-down { margin-left: .4rem; }

/* The name is a simple line, its color saying it answers (D618, Jon) -- no box. */
.timeline-cue-form .timeline-voice-field
{
	border: 0;
	border-bottom: 2px solid var( --accent );
	border-radius: 0;
	background: transparent;
	padding: .1rem .1rem;
	font: inherit;
	color: inherit;
	flex: 0 1 11rem;
}

.timeline-cue-form .timeline-voice-field:focus { outline: none; border-bottom-color: var( --ink ); }

/* Cancel and Save on a bottom row of their own (D618). */
.timeline-cue-actions
{
	flex-basis: 100%;
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-top: .2rem;
}

/* The meter: the two numbers subtitling conventions watch (characters · per second), warning past
   the profile. At rest a crowded cue tints its time -- in the focused area, and in review (D620),
   because elsewhere it is an editing concern the reader should not be nagged by. */
.timeline-cue-meter { font-size: var( --text-xs ); color: var( --ink-quiet ); white-space: nowrap; align-self: center; }
.timeline-cue-meter[data-over] { color: var( --warning ); font-weight: 600; }
.timeline-area[data-timeline-tab="subtitles"] .timeline-row[data-cue-over] .timeline-time { border-color: var( --warning ); color: var( --warning ); }

/* The kind of flag as a word under the time, in its box (D632, Jon): `fast`, `wide`, `brief`, `long`,
   written by timeline.js from the catalogue into data-flag-label. */
.timeline-area[data-timeline-tab="subtitles"] .timeline-time[data-flag-label] { display: inline-flex; flex-direction: column; align-items: center; line-height: 1.15; }
.timeline-area[data-timeline-tab="subtitles"] .timeline-time[data-flag-label]::after { content: attr( data-flag-label ); font-size: .7em; letter-spacing: .02em; }

/* The Subtitles head (D632): the review, shown only when there is something flagged, and the captions
   on the player, moved up from the block below. */
.timeline-area:not( [data-timeline-tab="subtitles"] ) .timeline-subtitles-head { display: none; }
.timeline-player-captions { margin: 0 0 .6rem; }

/* The chosen text's downloads and Remove, on the far side of the Transcript tab's head (D632). */
.timeline-transcript-head[data-compact] { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem; }
.timeline-transcript-head[data-compact] .timeline-untranslated { flex-basis: 100%; order: 3; }
.timeline-text-tools-set { margin-left: auto; }
.timeline-text-tools { display: flex; flex-wrap: wrap; gap: .3rem .9rem; align-items: baseline; font-size: var( --text-xs ); color: var( --ink-quiet ); }
.timeline-text-tools[hidden] { display: none; }
.timeline-text-remove { display: inline; margin: 0; }

/* THE WARNING OFFERS ITS REPAIRS (D620): the fixes appear only while the meter says over. */
.timeline-cue-fixes { display: none; gap: .3rem; align-items: center; }
.timeline-cue-meter[data-over] + .timeline-cue-fixes { display: inline-flex; }

/* Review (D620): the bar under the switches -- the counts and the walk. */
.timeline-review-bar
{
	display: flex;
	flex-wrap: wrap;
	gap: .5rem;
	align-items: center;
	margin: 0 0 .6rem;
	padding: .35rem .5rem;
	border: 1px solid var( --rule );
	border-radius: .5rem;
}

.timeline-review-bar[hidden] { display: none; }
.timeline-review-summary { font-size: var( --text-xs ); color: var( --ink-quiet ); flex: 1 1 12rem; }

/* Reflow (D620): one quiet verb among the controls. */
.timeline-reflow-form { display: inline-block; margin: 0 0 .6rem; }

/* (D616's "tools follow their lanes" rule retired at D631: the tabs place the add form now.) */

/* The cue's editor and the rename-the-speakers form (D614). */
.timeline-cue-form
{
	display: flex;
	flex-wrap: wrap;
	gap: .35rem;
	margin: .3rem 0 0;
}

.timeline-cue-form .timeline-body-field { flex: 1 1 14rem; }
.timeline-voice-field { flex: 0 1 9rem; min-width: 7rem; }

.timeline-speakers-disclosure { margin: 0 0 .6rem; }

.timeline-speakers-form
{
	display: flex;
	flex-direction: column;
	gap: .4rem;
	margin: .4rem 0 0;
	align-items: flex-start;
}

.timeline-voice-row
{
	display: grid;
	grid-template-columns: minmax( 6rem, 11rem ) minmax( 0, 1fr );
	gap: .5rem;
	align-items: center;
	align-self: stretch;
	font-size: var( --text-sm );
}

.timeline-speakers-hint { margin: 0; font-size: var( --text-xs ); color: var( --ink-quiet ); }

/* The manage block (D614): the transcript's lifecycle rows, closed at rest at the area's foot. */
/* Raw VTT (D633): the chosen text's WebVTT, shown only when its button opened it, in the Transcript tab.
   (The block that sat here below the Timeline, D614-D632, is gone; everything it held is at the top.) */
.timeline-raw-vtt { display: none; margin: 0 0 .75rem; padding: .6rem .7rem; border: 1px solid var( --rule ); border-radius: .5rem; }
.timeline-area[data-timeline-tab="transcript"][data-raw-vtt="open"] .timeline-raw-vtt:not( [hidden] ) { display: block; }

/* The glossary's fixes at the top of the Transcript tab, boxed as the review is in Subtitles (D633). */
.timeline-fixes { margin: 0 0 .75rem; padding: .5rem .7rem; border: 1px solid var( --rule ); border-radius: .5rem; }
.timeline-area:not( [data-timeline-tab="transcript"] ) .timeline-fixes,
.timeline-area:not( [data-language-role="primary"] ) .timeline-fixes { display: none; }

.timeline-time
{
	justify-self: start;
	padding: .05rem .4rem;
	border: 1px solid var( --rule );
	border-radius: 6px;
	background: transparent;
	color: var( --accent );
	font: inherit;
	font-variant-numeric: tabular-nums;
	cursor: pointer;
}

.timeline-time:hover,
.timeline-time:focus-visible { border-color: var( --accent ); }

.timeline-kind { color: var( --ink-quiet ); font-size: var( --text-xs ); }

.timeline-row[data-kind="chapter"] .timeline-text { font-weight: 600; color: var( --ink ); }
.timeline-row[data-kind="caption"] .timeline-text { color: var( --ink-quiet ); }
.timeline-row[data-resolved] .timeline-text { color: var( --ink-quiet ); text-decoration: line-through; }

/* Where the player is: an edge, not a fill, so it reads on every theme without a contrast sum. */
.timeline-row[data-current] { box-shadow: inset 3px 0 0 var( --accent ); }
.timeline-row[data-current][data-kind="caption"] .timeline-text { color: var( --ink ); }
.timeline-row[data-highlight] { background: var( --accent-quiet ); }

.timeline-body { min-width: 0; overflow-wrap: anywhere; }
.timeline-badge { margin-left: .4rem; }

.timeline-byline
{
	display: block;
	margin-top: .15rem;
	color: var( --ink-quiet );
	font-size: var( --text-xs );
}

.timeline-replies
{
	list-style: none;
	margin: .4rem 0 0;
	padding: 0 0 0 .75rem;
	border-left: 2px solid var( --rule );
}

.timeline-reply { margin: 0 0 .4rem; }

.timeline-row-controls
{
	display: flex;
	flex-wrap: wrap;
	gap: .15rem .6rem;
	align-items: baseline;
	margin-top: .25rem;
}

.timeline-inline-form { display: inline; margin: 0; }

.timeline-quiet-button
{
	padding: 0;
	border: 0;
	background: transparent;
	color: var( --ink-quiet );
	font: inherit;
	font-size: var( --text-xs );
	text-decoration: underline;
	cursor: pointer;
}

.timeline-quiet-button:hover,
.timeline-quiet-button:focus-visible { color: var( --ink ); }

.timeline-reply-disclosure,
.timeline-edit-disclosure { display: inline-block; }
.timeline-reply-disclosure[open],
.timeline-edit-disclosure[open] { display: block; flex-basis: 100%; }
.timeline-reply-disclosure > summary,
.timeline-edit-disclosure > summary { list-style: none; display: inline; }
.timeline-reply-disclosure > summary::-webkit-details-marker,
.timeline-edit-disclosure > summary::-webkit-details-marker { display: none; }

.timeline-reply-form,
.timeline-edit-form,
.timeline-add-form
{
	display: flex;
	flex-wrap: wrap;
	gap: .4rem;
	align-items: center;
	margin: .35rem 0 0;
}

.timeline-add-at,
.timeline-add-to { display: inline-flex; align-items: center; gap: .35rem; color: var( --ink-quiet ); font-size: var( --text-sm ); }
.timeline-seconds-field,
.timeline-end-field { width: 5.5rem; font-variant-numeric: tabular-nums; }
.timeline-body-field { flex: 1 1 12rem; min-width: 0; }
.timeline-pin-button[aria-pressed="true"] { border-color: var( --accent ); color: var( --accent ); }

/* FULLSCREEN ON THE BOX (D533): the media box, not the element, so the pin layer rides along. The
   video is letterboxed on black at the box's full size; the layer follows by the script's measure. */
.tool-media:fullscreen
{
	display: flex;
	align-items: center;
	justify-content: center;
	background: #000;
}

.tool-media:fullscreen video
{
	width: auto;
	height: auto;
	max-width: 100vw;
	max-height: 100vh;
}

/* PINS. The layer is laid over the video element by the script and lets every click through to the
   player's own controls, except on a pin -- and except while one click is armed for a new pin, when
   it takes the whole picture and says so with the cursor. The PLAYER's media box is made the
   layer's positioning context by the script, inline and on that one box: .tool-media is shared with
   the cropper and the 3D and spatial viewers, and a rule on the class would re-parent their layers. */

.timeline-pins
{
	position: absolute;
	pointer-events: none;
	z-index: 2;
}

.timeline-pins[data-pinning] { pointer-events: auto; cursor: crosshair; outline: 2px dashed var( --accent ); outline-offset: -2px; }

.timeline-pin
{
	position: absolute;
	width: 1.15rem;
	height: 1.15rem;
	margin: -.575rem 0 0 -.575rem;
	padding: 0;
	border: 2px solid #fff;
	border-radius: 50%;
	background: var( --accent );
	box-shadow: 0 0 0 1px rgba( 0, 0, 0, .55 ), 0 1px 6px rgba( 0, 0, 0, .5 );
	pointer-events: auto;
	cursor: pointer;
}

.timeline-pin[data-draft] { background: var( --warning ); cursor: default; }

/* ---------------------------------------------------------------------------------------------
   THE METADATA DUMP (D475) -- what the file says about itself, inside the In-depth block.

   One <details> per group family, all closed, because a real photograph carries 160-odd values and
   the section has to cost a line of height until somebody wants it. The groups are deliberately
   plainer than the tables above them: those are OUR facts about the item, these are the FILE's
   claims about itself, and the flatter treatment is the difference showing.

   Font sizes name a step from the scale (D429) rather than a rem literal, so the phone block that
   bumps the scale carries this with it.
*/

.item-depth-metagroup
{
	border: 1px solid var( --rule );
	border-radius: .5rem;
	margin-bottom: .4rem;
	background: var( --card-background );
	overflow: hidden;
}

.item-depth-metagroup > summary
{
	display: flex;
	gap: .5rem;
	align-items: baseline;
	padding: .45rem .7rem;
	cursor: pointer;
	font-size: var( --text-sm );
	list-style: none;
}

.item-depth-metagroup > summary::-webkit-details-marker { display: none; }

/* The same disclosure triangle the outer block draws, so the two read as one mechanism. */
.item-depth-metagroup > summary::before
{
	content: "▸";
	color: var( --ink-quiet );
	font-size: var( --text-xs );
	transition: transform .12s ease;
}

.item-depth-metagroup[ open ] > summary::before { transform: rotate( 90deg ); }

.item-depth-metagroup > summary:focus-visible
{
	outline: 2px solid var( --accent );
	outline-offset: -2px;
}

.item-depth-metagroup-name
{
	font-weight: 600;
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* The group's own table loses the card treatment -- it is already inside one. */
.item-view .item-depth-metagroup table
{
	border: 0;
	border-top: 1px solid var( --rule );
	border-radius: 0;
	background: transparent;
}

.item-view .item-depth-metagroup th
{
	width: 38%;
	font-weight: 400;
	color: var( --ink-quiet );
	font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: var( --text-xs );
	word-break: break-word;
}

.item-view .item-depth-metagroup td
{
	font-size: var( --text-xs );
	word-break: break-word;
}

/* A row carrying a location or an identity is marked in the table as well as counted at the top:
   the sentence says how many there are, and this says which ones, so a person can find them. */
.item-depth-metaflagged
{
	background: var( --warning-quiet );
}

.item-depth-metaflag
{
	display: inline-block;
	margin-left: .35rem;
	padding: 0 .3rem;
	border: 1px solid var( --warning );
	border-radius: .25rem;
	color: var( --warning );
	font-size: var( --text-xs );
	white-space: nowrap;
}
