/* orco.css - all styling for Orco Slides.
 *
 * The design language is lifted from aibrain.pages.dev, which is Victor's own
 * site: an aurora bloom over a deep gradient, glass cards with a purple-tinted
 * hairline, a rainbow hairline on the top edge of the important surfaces,
 * uppercase eyebrow labels, pills for status, and one warm gradient reserved
 * for the primary action. Tokens are inlined rather than pulled from
 * sBs/design-md, because Orco must open from file:// with nothing beside it
 * but its own folder.
 *
 * THE ONE RULE THAT OUTRANKS THE AESTHETIC. The slide canvas is paper. Orco
 * exports .pptx, so what is on the canvas is what lands in PowerPoint and on a
 * projector: white paper renders white, at the colours the user chose. The
 * chrome is aurora dark glass; the canvas is not tinted, blended, filtered or
 * dimmed. That is what Figma, Keynote and Google Slides all do in dark mode,
 * and for the same reason. The mechanism is the token reset on .stage and
 * .thumb near the foot of this file: everything drawn inside the canvas reads
 * paper values for --ink, --panel and the rest, so a rule written for the
 * chrome cannot leak pale text onto white paper. A previous round shipped that
 * exact bug; the reset is what stops it coming back.
 *
 * Motion: every transition here is 120-220ms on --ease, and the whole sheet,
 * aurora included, goes still under prefers-reduced-motion (bottom of file).
 */

:root {
  /* =====================================================================
     ORCO, BOUND IN NAVY AND GOLD. Re-grounded 2026-09-10.
     =====================================================================
     Direction and reasoning: docs/beta-run/UX-VISUAL-DIRECTION.md

     WHAT CHANGED AND WHY, because this replaces a palette that worked.

     The old "aurora" palette carried FIVE accent hues plus a rainbow
     gradient, on a violet-leaning ground. His slides are navy #050652, cream
     #F2EFE6 and antique gold #E3C878, set in Georgia. So opening the running
     order put his navy and gold work inside a magenta and purple application,
     and the most prominent object on several screens was a rainbow that
     belonged to nothing.

     It was also against his own canon, which fixes Lapis Lazuli and Antique
     Gold as the default, allows gold two or three times per page, and bans
     purple and lilac outright.

     So the frame now uses the picture's own material. One accent, one
     hairline, cream ink, and a serif for the names of his talks.

     THE OLD TOKEN NAMES ARE ALL KEPT AND REMAPPED. Four thousand lines of
     rules were written against them, and each of those rules exists because
     of a defect it was written to fix. Renaming them would re-open every one
     of those defects to save a word. ===================================== */

  /* ---- the ground, and what sits on it ---- */
  --ground:      #0a0f24;
  --ground-2:    #060a1a;
  --surface:     #101838;
  --surface-2:   #16204a;

  /* ---- ink. Cream, never pure white: his slides are set in #F2EFE6 and a
         pure white beside them reads as a different, colder product. ---- */
  --ink:         #f2efe6;
  --ink-2:       #c9d3e2;
  --ink-3:       #8794b5;
  --ink-4:       #5d6786;

  /* ---- THE accent. One hue. ---- */
  --gold:        #e3c878;
  --gold-deep:   #9c7620;
  --gold-lit:    #f0dda2;

  /* ---- the defining feature: the gold hairline, at rest and awake ---- */
  --hair:        rgba(227, 200, 120, 0.16);
  --hair-lit:    rgba(227, 200, 120, 0.42);

  /* ---- semantic state. Three, and used for nothing else. ---- */
  --ok:          #6fce9f;
  --warn:        #e8b765;
  --bad:         #ef8878;
  --ok-bg:       rgba(111, 206, 159, 0.12);
  --warn-bg:     rgba(232, 183, 101, 0.13);
  --bad-bg:      rgba(239, 136, 120, 0.13);

  /* ---- type ---- */
  --font:         -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Inter, Roboto, Helvetica, Arial, sans-serif;
  --font-display: Georgia, "Iowan Old Style", "Times New Roman", serif;
  --font-mono:    ui-monospace, SFMono-Regular, Menlo, monospace;

  /* ---- motion. State moves in 140ms, surfaces in 200ms. ---- */
  --t-state:   140ms;
  --t-surface: 200ms;
  --ease:      cubic-bezier(0.2, 0.8, 0.2, 1);

  /* =====================================================================
     COMPATIBILITY REMAP. Every name the previous palette exported, pointed
     at the new material. Nothing below is new vocabulary; it is the old
     vocabulary, re-aimed. ================================================ */
  --c-blue:   #8fb3ef;
  --c-purple: #e3c878;   /* was the violet accent; the product has one accent now */
  --c-pink:   #e3c878;
  --c-orange: #e8b765;
  --c-green:  #6fce9f;

  /* The primary action is gold on navy, not a rainbow. It is a real gradient
     so the button has a machined highlight along its top edge, but it is one
     hue, and the ink on it is the ground rather than white: #0a0f24 on
     #e3c878 measures about 11.6:1, where white on gold measures 1.7:1 and
     fails at any size. */
  --grad-primary: linear-gradient(180deg, #f0dda2 0%, #e3c878 48%, #cbae5f 100%);
  --grad-rainbow: linear-gradient(90deg, rgba(227,200,120,0), var(--gold), rgba(227,200,120,0));
  /* The scrim existed because white on orange failed contrast. The primary
     button no longer uses white ink, so the scrim is now a no-op kept only so
     that any rule still referencing it resolves. */
  --cta-scrim: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0));

  --bg:      #0a0f24;
  --bg-2:    #060a1a;

  --field:   #060a1a;
  --panel:   rgba(16, 24, 56, 0.62);
  --panel-2: rgba(16, 24, 56, 0.88);
  --hair-2:  rgba(227, 200, 120, 0.42);
  --accent:  #e3c878;
  --accent-2: #f0dda2;

  /* =====================================================================
     RESTORED 2026-09-10, and this was my own defect, so it is written down
     rather than quietly repaired.
     =====================================================================

     Re-grounding the palette meant replacing the :root block wholesale. The
     replacement carried every COLOUR the old one had, and dropped all sixteen
     of its non-colour tokens: the radii, the blur, the timings, the shadows,
     --good and --tip-bg. Those names are used in hundreds of rules across
     4,250 lines, and a var() that resolves to nothing makes the whole
     declaration invalid, so from that moment every .tool button, every field
     and every sheet in the default dark theme had SQUARE CORNERS and no
     shadow. It survived because the light theme defines them separately, and
     because I was looking at Home rather than at a button.

     Found by the seat working on Home, which measured them in the browser
     rather than reading the file. The lesson is the one this project keeps
     relearning: replacing a block is not the same as editing it, and the
     things you did not think about are exactly the things that leave.

     The values are the originals, with the shadows and rings re-aimed from
     the retired magenta onto gold. ==================================== */
  --accent-soft:  rgba(227, 200, 120, 0.14);
  --good:         #6fce9f;
  --tip-bg:       #16204a;
  --tip-ink:      #f2efe6;
  --radius:       14px;
  --radius-sm:    10px;
  --r-card:       24px;
  --shadow-1:     0 2px 10px rgba(0, 0, 0, 0.35);
  --shadow-2:     0 14px 40px rgba(0, 0, 0, 0.45);
  --shadow-glow:  0 0 32px rgba(227, 200, 120, 0.14);
  --shadow-btn:   0 10px 26px -10px rgba(227, 200, 120, 0.55);
  --glow-ring:    0 0 0 3px rgba(227, 200, 120, 0.28);
  --paper-shadow: 0 24px 60px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(242, 239, 230, 0.05);
  --blur:         blur(22px) saturate(1.8);
  --t-fast:       130ms;
  --t-med:        180ms;

  color-scheme: dark;
}

/* THE SAME MATERIAL, INVERTED. Re-grounded 2026-09-10.
 *
 * Light was still the retired aurora: a blue-to-violet primary, a rainbow, and
 * a --gold of #b45309, which is a burnt orange rather than antique gold. So a
 * person switching themes changed products.
 *
 * It is now the same two substances the dark theme uses, the other way up:
 * deep navy INK on warm parchment, with gold as the one accent. The gold has
 * to move: #e3c878 on white measures about 1.6:1 and is unreadable, so light
 * uses his own darker gold #9c7620, which measures about 4.9:1 on parchment
 * and carries the same family resemblance. Same idea, legible at both ends.
 *
 * This is also his own canon: Lapis Lazuli and Antique Gold, light as the
 * default, gold used sparingly. */
html[data-theme="light"] {
  --grad-primary: linear-gradient(180deg, #b8912f 0%, #9c7620 52%, #866219 100%);
  --grad-rainbow: linear-gradient(90deg, rgba(156,118,32,0), #9c7620, rgba(156,118,32,0));
  --cta-scrim: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0));

  --ground:   #f7f4ec;
  --ground-2: #efeade;
  --surface:  #ffffff;
  --surface-2: #f2ede1;

  --bg:   #f7f4ec;
  --bg-2: #efeade;

  /* Ink is his navy, not black. A pure black on parchment reads as a printout;
     navy reads as a bound book. */
  --ink:   #0a0f24;
  --ink-2: #2a3355;
  --ink-3: #5d6786;
  --ink-4: #8794b5;

  --field:   #ffffff;
  --panel:   rgba(255, 255, 255, 0.78);
  --panel-2: rgba(255, 255, 255, 0.94);

  --gold:      #9c7620;
  --gold-deep: #6f5314;
  --gold-lit:  #b8912f;

  --hair:     rgba(156, 118, 32, 0.22);
  --hair-lit: rgba(156, 118, 32, 0.55);
  --hair-2:   rgba(156, 118, 32, 0.55);

  --accent:      #9c7620;
  --accent-2:    #b8912f;
  --accent-soft: rgba(156, 118, 32, 0.10);

  /* Semantic state, darkened so it carries on parchment. */
  --ok:   #1c7a4b;
  --good: #1c7a4b;
  --warn: #8a5a00;
  --bad:  #b42318;
  --ok-bg:   rgba(28, 122, 75, 0.10);
  --warn-bg: rgba(138, 90, 0, 0.10);
  --bad-bg:  rgba(180, 35, 24, 0.09);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  font-family: var(--font);
  color: var(--ink);
  background: linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 100%);
  -webkit-font-smoothing: antialiased;
}

/* The aurora bloom. Four very soft, very low opacity blobs bleeding in from
 * the corners over the gradient: purple upper left, orange upper right, green
 * lower right, pink lower left. It is what makes the page feel lit rather than
 * merely dark, and it costs one fixed pseudo element that never scrolls, never
 * animates and never takes a pointer event. Everything above it sits on
 * .app, which is why .app carries a stacking context of its own. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(70vw 62vh at 2% -12%, rgba(227, 200, 120, 0.16), transparent 64%),
    radial-gradient(62vw 54vh at 99% -10%, rgba(255, 157, 46, 0.26), transparent 62%),
    radial-gradient(68vw 60vh at 96% 110%, rgba(31, 201, 124, 0.24), transparent 64%),
    radial-gradient(56vw 48vh at -4% 108%, rgba(255, 46, 138, 0.22), transparent 62%);
}
html[data-theme="light"] body::before {
  background:
    radial-gradient(70vw 62vh at 2% -12%, rgba(227, 200, 120, 0.10), transparent 64%),
    radial-gradient(62vw 54vh at 99% -10%, rgba(74, 143, 255, 0.28), transparent 62%),
    radial-gradient(68vw 60vh at 96% 110%, rgba(31, 201, 124, 0.16), transparent 64%),
    radial-gradient(56vw 48vh at -4% 108%, rgba(255, 46, 138, 0.14), transparent 62%);
}

button { font-family: inherit; }

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--panel-2);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  color: var(--ink);
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--hair-2);
}
.skip:focus { left: 12px; top: 12px; }

/* The focus ring is the aurora ring, and it is visible on glass, on paper and
 * on the gradient button alike: an outline for the shape and a soft halo
 * behind it, because a 2px line alone disappears against a busy blur. */
:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 2px;
  box-shadow: var(--glow-ring);
}

/* ------------------------------------------------------------------ *
 * Top bar                                                             *
 * ------------------------------------------------------------------ */

.app {
  display: grid;
  grid-template-rows: 60px auto 1fr 30px;
  height: 100%;
  position: relative;
  z-index: 1;
}
/* A grid track takes its minimum size from its content unless told otherwise,
 * so without this the toolbar's natural width pushes the canvas off screen. */
.app > * { min-width: 0; }

.topbar {
  display: flex;
  min-width: 0;
  align-items: center;
  gap: 10px;
  padding: 0 14px;
  background: var(--panel);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  border-bottom: 1px solid var(--hair);
  position: relative;
  z-index: 20;
}
/* The signature detail of the aibrain language: a rainbow hairline on the
 * edge of the surface that matters most. One per surface, low opacity, never
 * more than a hair thick, so it reads as light rather than as a stripe. */
.topbar::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -1px;
  height: 1px;
  background: var(--grad-rainbow);
  opacity: 0.42;
  pointer-events: none;
}

.brand {
  display: flex;
  align-items: center;
  gap: 9px;
  padding-right: 6px;
}
.brand svg { width: auto; }
/* `display` is deliberately NOT set on `.brand svg`: that selector outranks the
   single-class rules below, and pinning display there once put two whales in
   the navbar at the same time. Found by looking at the render. */
/* The defs carry no ink, only the shared path and the gradient. */
.brand-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* The lockup has NO gap. The two halves are adjacent slices of one wordmark,
   so any space between them would open a seam down the middle of the logo. */
/*
 * THE LOCKUP RESERVES ITS FULL WIDTH, ALWAYS.
 *
 * Victor's call, 2026-09-09, and he is right: the reveal used to push the deck
 * name and the whole toolbar sideways, so hovering the logo made every control
 * in the bar move under the pointer. A row that rearranges itself when you are
 * only looking at it is a row you cannot aim at.
 *
 * So the space for the word is spent whether the word is showing or not, and
 * the letters slide out into ground that was already theirs. 108.9px is the
 * mark plus the letters at 38px tall: 38 * 455.7 / 541 for the orca's slice of
 * the viewBox, plus 38 * 1094.3 / 541 for the letters'. Change the height and
 * both numbers change.
 */
.brand-lockup { display: flex; align-items: center; gap: 0; width: 108.9px; }
.brand-mark { display: block; flex: none; }

/*
 * The letters, revealed on hover of the brand.
 *
 * The bar shows the orca alone, because that is the mark: it is what the
 * favicon and the home-screen icon are, and it survives being small. The word
 * pushes out to its right on hover, which is the one gesture that shows the
 * whole lockup without spending a permanent 77px of a row that is already over
 * budget at 1440.
 *
 * It is a width transition on an overflow:hidden wrapper rather than an opacity
 * fade, because the letters should look like they were behind the mark and slid
 * out, not like they materialised. The svg inside is flex: none so it keeps its
 * full width while the wrapper closes over it; without that it would squash.
 *
 * 76.9px is not a guess: the rco half spans 1094.3 user units of a 541-unit-tall
 * viewBox, so at 38px tall it is 38 * 1094.3 / 541 wide. Change the height and
 * this number has to change with it.
 */
.brand-rco {
  display: flex;
  flex: none;
  width: 0;
  overflow: hidden;
  /* The letters follow the dive rather than racing it. 70ms of delay is enough
     for the orca to be visibly turning before the word starts to appear, and
     600ms puts the word's arrival just inside the spin's 820ms so the two land
     together. Measured, not guessed: at 460ms the word was fully out at 380ms
     while the whale still had half a turn to go, and the pair read as two
     separate events. The easing is a gentle ease-in-out for the same reason
     the spin uses one. */
  transition: width 600ms cubic-bezier(0.45, 0.15, 0.15, 1) 70ms;
}
.brand-rco svg { display: block; flex: none; }
.brand:hover .brand-rco { width: 76.9px; }

/*
 * THE DIVE.
 *
 * The orca turns through a full circle about its own centre, the way it would
 * roll going under. It is a TRANSITION between rotate(0) and rotate(360deg)
 * rather than a keyframe animation, and that is the whole trick: a keyframed
 * spin restarts or snaps if the pointer leaves halfway, while a transition
 * simply reverses from wherever it got to, so a quick pass over the logo turns
 * the whale part way and lets it roll back. Nothing can ever be caught mid-jump.
 *
 * The origin is 52% 50%, not 50% 50%. It is the centre of the glyph's own ink
 * (measured: the bbox spans 0.0 to 427.5 of a box running -24 to 431.7), and
 * the difference is visible: rotating about the box centre makes the tail swing
 * wide and the whole mark wobble in the bar.
 *
 * The easing is an ease-in-out, gathered at the start and settled at the end,
 * because that is the shape of a dive: nothing that rolls in water snaps into
 * the turn. A front-loaded curve was tried first and measured wrong: 70% of
 * the rotation was spent in the first fifth of the time and the rest crawled.
 */
.brand-mark {
  transform-origin: 52% 50%;
  transition: transform 820ms cubic-bezier(0.62, 0.03, 0.20, 1);
  will-change: transform;
}
.brand:hover .brand-mark { transform: rotate(360deg); }

/* The same brand on the surfaces that cover the toolbar. It sits above the
   eyebrow rather than beside it, so the three lines read logo, then what this
   surface is, then what is open: the same order the toolbar reads in. */
.brand--surface { margin: 0 0 12px; padding-right: 0; }
.brand--surface .brand-mark { height: 30px; }
.brand--surface .brand-rco svg { height: 30px; }
/* 30px tall instead of 38, so the two halves are 25.3px and 60.7px. */
.brand--surface .brand-lockup { width: 86px; }
.brand--surface:hover .brand-rco { width: 60.7px; }
/* The slide-code header is a row, not a stack. */
.live-head .brand--surface { margin: 0 14px 0 0; }

.deckname {
  font: inherit;
  font-size: 13px;
  color: var(--ink-2);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  padding: 6px 8px;
  min-width: 90px;
  flex: 0 1 230px;
  width: 230px;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.deckname { color: var(--ink-3); }
.deckname:hover { border-color: var(--hair-2); background: var(--panel); }
.deckname:focus { background: var(--panel-2); border-color: var(--accent-2); color: var(--ink); outline: none; box-shadow: var(--glow-ring); }

.sep {
  width: 1px;
  height: 22px;
  background: linear-gradient(180deg, transparent, var(--hair-2), transparent);
  margin: 0 4px;
  flex: none;
}

.spacer { flex: 1; min-width: 4px; }

/* Labels collapse before anything is pushed out of reach. Present and Save
 * keep their words at every width, because they are the two a presenter looks
 * for under pressure. */
.lbl-short { display: none; }
/* 1560 -> 2160, measured 2026-09-09. The full words on the six file buttons
 * cost about 150px, which is what the orco.vdr.me pill now needs. At 1600 and
 * 1700 the bar was over budget by 122 and 22; with the words held back until
 * 2160 it fits at every width from 1401 up. The icons and the tooltips carry
 * the same information, and 1920 was ALREADY over budget by 197 before this
 * pill existed, so this fixes a defect that was there before it too. */
@media (max-width: 2160px) {
  #btn-text .lbl, #btn-shape .lbl, #btn-image .lbl, #btn-slide .lbl { display: none; }
  #btn-import .lbl, #btn-export .lbl, #btn-open-decks .lbl, #btn-show .lbl,
  #btn-library .lbl { display: none; }
  .lbl-short { display: inline; }
  .topbar { gap: 7px; }
  .deckname { flex: 0 1 170px; width: 170px; }
}
/* The save status is the one thing on this bar that must never be cut off,
 * because it is the only thing telling the user whether their work exists.
 * A verifier found it rendering "Saved in this b" at 1024px. Everything else
 * on the row gives up room before this pill does. */
.status { flex: none; }
/* The order things give up room as the window narrows, decided once and
 * written down: the format bar's hint goes first, then the brand word, then
 * the brand mark, then the deck name shrinks. Present, Save and the status
 * never move, because those three are what a person under pressure looks for.
 * Verified by measurement at 1440, 1180, 1024 and 900, not by eye. */
/* Tightening, in the order that costs the least information: spacing first,
 * then the long form of the save status, then the deck name's width. Measured
 * at 1024, 1160, 1200, 1280, 1440 and 1560 with every child's flex-shrink
 * disabled, which is the only way to see that a row is over budget rather than
 * merely looking cramped. */
.status-short { display: none; }
@media (max-width: 1500px) {
  .topbar { gap: 8px; }
  #btn-text, #btn-shape, #btn-image, #btn-slide,
  #btn-undo, #btn-redo, #btn-duplicate, #btn-arrange, #btn-delete { min-width: 34px; padding: 0 9px; }
}
/*
 * THE TWO PILLS GIVE UP THEIR LONG SENTENCES AT 1560, not at 1300.
 *
 * Measured 2026-09-09 with the orco.vdr.me pill on the bar: at 1440 with a
 * .pptx open (so the compatibility chip is there too) the row needed 1485 of
 * 1440. The two long sentences are about 300px of that and their short forms
 * are about 65px cheaper, which is what buys the fit on a 1440 laptop without
 * pushing the whole bar onto a second line.
 *
 * The short forms are not a different vocabulary. "Saved" is the store's own
 * word for the browser copy and "On orco.vdr.me" is a place, so the two can
 * still never be read as one claim, and the whole sentence is one hover or one
 * tap away in both cases.
 */
@media (max-width: 1560px) {
  #status-text, #sync-text { display: none; }
  .status-short { display: inline; }
}
@media (max-width: 1300px) {
  .fmt-hint { display: none; }
  .deckname { flex: 0 1 100px; width: 100px; min-width: 54px; }
  /* Both pills shorten together. One long sentence beside one short one
   * would read as a ranking, and they are two equal facts. */
  #status-text, #sync-text { display: none; }
  .status-short { display: inline; }
}
/* Below 1560 the four file buttons give up their words and keep their icons,
 * which is about 200px back on a bar that was overlapping its own labels at
 * 1024. A verifier found that by looking at a screenshot; every assertion in
 * the suite said Present was on screen, and it was, on top of Save.
 *
 * The threshold moved from 1200 to 1560 when the running order joined this
 * group, which is the same width at which the long labels go. So these four
 * now step straight from their full words to their icons, with no middle
 * state. The bar was measured at exactly its budget at 1280 before the fifth
 * button existed; it needed 1339 of 1280 with the button and 1500 of 1440
 * with the short words still on. Measured at 1024, 1280, 1440, 1560 and 1600,
 * not judged by eye. */
@media (max-width: 2160px) {
  #btn-import .lbl-short, #btn-export .lbl-short, #btn-open-decks .lbl-short,
  #btn-show .lbl-short, #btn-library .lbl-short { display: none; }
  #btn-import, #btn-export, #btn-open-decks, #btn-show, #btn-library { padding: 0 9px; }
}
/* The library button joined this group the night Orco stopped holding one
 * document. It is a sixth button on a bar that was already measured at its
 * budget, so it gives up its word at exactly the widths the other five do
 * rather than being special. Measured at 1024 by the disclosure suite, which
 * is what caught it as an overflow within a minute of it being added. */
/* And the band above that, which nobody had measured because every test in
 * the suite stopped at 1440. The bar needed 1762 of 1600 with four buttons
 * and no fifth: it was already overlapping its own labels between 1561 and
 * about 1780, before the running order existed. These four give up their
 * words there too, so the whole range from 900 to 1780 now fits. */
@media (max-width: 1780px) {
  #btn-import .lbl, #btn-export .lbl, #btn-open-decks .lbl, #btn-show .lbl,
  #btn-library .lbl { display: none; }
  #btn-import, #btn-export, #btn-open-decks, #btn-show, #btn-library { padding: 0 9px; }
}
@media (max-width: 1160px) {
  .brand { display: none; }
  .topbar { gap: 6px; padding: 0 10px; }
  .tool { padding: 0 8px; }
  /* The six icon-only file buttons carry an id-specific 9px from the 1560
   * band, which outranks .tool above and left the row 35px over budget at
   * 1024 once the library made it six. Named here rather than fought with
   * !important. Measured, not eyeballed. */
  #btn-import, #btn-export, #btn-open-decks, #btn-show, #btn-library { padding: 0 5px; min-width: 28px; }
  .sep { margin: 0 1px; }
  .deckname { flex: 0 1 90px; width: 90px; min-width: 40px; }
}
/* The format bar has its own budget and its own order of giving up: the scope
 * label first, because the same fact is in the selection on the slide, then
 * the font name shortens. The font control never shrinks below a width where
 * the name it shows is readable. */
@media (max-width: 1100px) {
  .fmt-scope { display: none; }
}
@media (max-width: 1000px) {
  .topbar { gap: 4px; padding: 0 8px; }
  .tool { padding: 0 6px; gap: 5px; }
  .deckname { display: none; }
}

.tool {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 36px;
  min-width: 36px;
  padding: 0 11px;
  border: 1px solid var(--hair);
  border-radius: var(--radius-sm);
  background: var(--panel);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  color: var(--ink-2);
  font-size: 13px;
  font-weight: 560;
  white-space: nowrap;
  cursor: pointer;
  position: relative;
  transition: transform var(--t-fast) var(--ease),
              background var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              box-shadow var(--t-fast) var(--ease),
              opacity var(--t-fast) var(--ease);
}
.tool:hover:not(:disabled) {
  background: var(--panel-2);
  border-color: var(--hair-2);
  color: var(--ink);
  box-shadow: var(--shadow-1), var(--shadow-glow);
}
.tool:active:not(:disabled) { transform: translateY(1px); }
.tool:disabled { opacity: 0.42; cursor: not-allowed; }
.tool svg { display: block; flex: none; }
.tool .k {
  font-size: 10px;
  color: var(--ink-3);
  border: 1px solid var(--hair-2);
  border-radius: 4px;
  padding: 0 4px;
  line-height: 15px;
}

/* Not every button on this row deserves the same weight. Making a slide is
 * what people came to do; undo, redo, duplicate and delete are corrections,
 * so they sit quietly until they are hovered. */
.tool--quiet {
  border-color: transparent;
  background: transparent;
  color: var(--ink-3);
}
.tool--quiet:hover:not(:disabled) { background: var(--panel-2); color: var(--ink); border-color: var(--hair); }

/* The one primary action on the page, and the only place the warm gradient is
 * used as a fill. See --cta-scrim for why the scrim is there: it is what makes
 * the white label legible at the orange end of the gradient. */
.tool--present {
  border-color: transparent;
  background: var(--cta-scrim), var(--grad-primary);
  color: #ffffff;
  font-weight: 640;
  box-shadow: var(--shadow-btn), inset 0 1px 0 rgba(255, 255, 255, 0.28);
}
.tool--present:hover:not(:disabled) {
  background: var(--cta-scrim), var(--grad-primary);
  border-color: transparent;
  color: #ffffff;
  transform: translateY(-1px);
  box-shadow: 0 16px 40px rgba(255, 46, 138, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.35);
}
.tool--present:active:not(:disabled) { transform: translateY(0); }

/* The provenance badge. It is not decoration: it is the difference between
 * editing a copy and thinking you are editing the original. */
.origin {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 9px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--gold) 45%, var(--hair-2));
  background: color-mix(in srgb, var(--gold) 14%, transparent);
  color: var(--gold);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
  flex: none;
}
/* .origin sets display, which outranks the browser's own [hidden] rule.
 * Without this line the badge claims every blank deck is a copy. */
.origin[hidden] { display: none; }

/* ------------------------------------------------------------------ *
 * Chips: what this document is, in the status bar at the foot.        *
 *                                                                     *
 * They started beside the deck name and pushed the toolbar past its    *
 * width at 1024, where the file buttons began overlapping each other.  *
 * The footer is the right home anyway: this is document status, not an *
 * action, and a count in a status bar is a grammar every editor shares.*
 *                                                                     *
 * The compatibility chip is deliberately the quietest thing that can   *
 * still be noticed. Orco preserving an object it cannot edit is good   *
 * news, so the chip reads as information rather than as a warning, and *
 * only the recovery chip, which is about a real loss the user has yet  *
 * to act on, takes the warning colour.                                 *
 * ------------------------------------------------------------------ */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  height: 22px;
  padding: 0 9px;
  border: 1px solid var(--hair);
  border-radius: 999px;
  background: var(--panel-2);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  color: var(--ink-2);
  font-size: 11.5px;
  font-weight: 550;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.chip:hover { background: var(--accent-soft); border-color: var(--hair-2); color: var(--ink); }
.chip svg { flex: none; opacity: 0.7; }
.chip[hidden] { display: none; }
.chip[data-tone="kept"] { color: var(--c-purple); border-color: color-mix(in srgb, var(--c-purple) 42%, transparent); }
.chip[data-tone="clean"] { color: var(--good); border-color: color-mix(in srgb, var(--good) 38%, transparent); }
.chip--warn {
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 38%, var(--hair));
  background: color-mix(in srgb, var(--warn) 7%, var(--panel));
}
.chip--warn:hover { background: color-mix(in srgb, var(--warn) 13%, var(--panel)); border-color: var(--warn); color: var(--warn); }
@media (max-width: 900px) {
  #compat span, .chip-label { display: none; }
  .chip { padding: 0 6px; }
  #compat[data-count]::after {
    content: attr(data-count);
    font-variant-numeric: tabular-nums;
  }
}

/* The deck picker: a list of his decks, and nothing else. */
.sheet-back {
  position: fixed;
  inset: 0;
  background: rgba(5, 6, 14, 0.62);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  z-index: 250;
  display: grid;
  place-items: center;
  animation: fade var(--t-med) var(--ease);
}
.sheet {
  width: min(520px, calc(100vw - 40px));
  max-height: min(560px, calc(100vh - 80px));
  display: flex;
  flex-direction: column;
  background: var(--panel-2);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  border: 1px solid var(--hair-2);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-2), var(--shadow-glow);
  overflow: hidden;
  position: relative;
  animation: sheetIn var(--t-med) var(--ease);
}
/* The rainbow hairline again, on the top edge of every card that matters. */
.sheet::before, .menu::before, .toast::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 1px;
  background: var(--grad-rainbow);
  opacity: 0.6;
  pointer-events: none;
  z-index: 1;
}
@keyframes sheetIn {
  from { opacity: 0; transform: translateY(8px) scale(0.99); }
  to   { opacity: 1; transform: none; }
}
.sheet-head {
  padding: 14px 16px 10px;
  border-bottom: 1px solid var(--hair);
  display: grid;
  gap: 3px;
}
.sheet-head strong { font-size: 15px; font-weight: 660; color: var(--ink); letter-spacing: -0.01em; }
.sheet-head span { font-size: 12px; color: var(--ink-3); line-height: 1.4; }
.sheet-list { overflow-y: auto; padding: 6px; }
.sheet-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  padding: 8px 10px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.sheet-row:hover, .sheet-row:focus-visible {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent-2) 45%, transparent);
}
.sheet-row .swatch {
  width: 10px; height: 22px; border-radius: 3px; flex: none;
  background: var(--hair-2);
}
.sheet-row .rowname { flex: 1; font-weight: 550; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sheet-row .rowcount { color: var(--ink-3); font-size: 12px; font-variant-numeric: tabular-nums; flex: none; }
.sheet-empty { padding: 18px 14px; color: var(--ink-3); font-size: 12.5px; line-height: 1.5; display: grid; gap: 6px; }
.sheet-empty div:first-child { color: var(--ink-2); font-weight: 600; }
.sheet-empty .tool { justify-self: start; margin-top: 6px; border-color: var(--hair-2); color: var(--ink); }
/* Which of the two lists a row came from, said on the row itself and not only
 * in the heading above it, because a heading scrolls away and the question
 * "is this the one in my browser or the one from the file" is asked at the
 * moment of clicking. */
.sheet-row .rowsrc {
  flex: none;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-4);
  border: 1px solid var(--hair);
  border-radius: 999px;
  padding: 2px 8px;
  white-space: nowrap;
}
.sheet-row[data-origin="file"] .rowsrc { color: var(--accent-2); border-color: color-mix(in srgb, var(--accent-2) 40%, transparent); }
/* A file being dragged over the picker. The rainbow hairline is already on the
 * top edge; this lights the whole rim rather than inventing a second idea. */
.sheet.is-drop {
  border-color: var(--accent-2);
  box-shadow: var(--shadow-2), var(--glow-ring);
}
.sheet.is-drop .sheet-list { opacity: 0.72; }
.sheet-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px;
  border-top: 1px solid var(--hair);
  background: var(--accent-soft);
  font-size: 11.5px;
  color: var(--ink-3);
}
/* The report and the recovery panel reuse the deck picker's sheet wholesale,
 * so a person who has opened one already knows how to leave the others. */
.sheet--wide { width: min(680px, calc(100vw - 40px)); max-height: min(640px, calc(100vh - 72px)); }
.sheet-section {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 12px 10px 5px;
  font-size: 10.5px;
  font-weight: 640;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.sheet-section .count { text-transform: none; letter-spacing: 0; font-weight: 500; font-variant-numeric: tabular-nums; }
/* The same divider as the shortcut headings: a label sitting in a hairline,
 * so a section break reads as one kind of thing everywhere in the product. */
.sheet-section::after {
  content: "";
  flex: 1;
  height: 1px;
  align-self: center;
  background: linear-gradient(90deg, var(--hair-2), transparent);
}
.sheet-row--static { cursor: default; align-items: flex-start; }
.sheet-row--static:hover { background: transparent; border-color: transparent; }
.sheet-row .rowbody { flex: 1; min-width: 0; display: grid; gap: 2px; }
.sheet-row .rowtitle { font-weight: 550; overflow: hidden; text-overflow: ellipsis; }
.sheet-row .rowmeta { font-size: 11.5px; color: var(--ink-3); line-height: 1.45; white-space: normal; }
.sheet-row .rowgo { color: var(--ink-3); font-size: 11.5px; flex: none; }
.sheet-row:hover .rowgo, .sheet-row:focus-visible .rowgo { color: var(--accent-2); }
.sheet-row .kept {
  flex: none;
  align-self: flex-start;
  margin-top: 3px;
  width: 18px; height: 18px;
  border-radius: 4px;
  border: 1px dashed var(--hair-2);
  background: repeating-linear-gradient(45deg, rgba(150,150,150,.10) 0 4px, transparent 4px 8px);
}
.sheet-note {
  margin: 10px;
  padding: 9px 11px;
  border-radius: var(--radius-sm);
  background: var(--field);
  border: 1px solid var(--hair);
  font-size: 12px;
  color: var(--ink-2);
  line-height: 1.5;
}
.sheet-more { margin: 4px 10px 10px; }
.sheet-more > summary {
  cursor: pointer;
  font-size: 12px;
  color: var(--ink-2);
  padding: 6px 2px;
  min-height: 30px;
  display: flex;
  align-items: center;
  gap: 8px;
  list-style: none;
}
/* display:flex eats the browser's own disclosure triangle, and a summary with
 * no marker reads as a sentence rather than as something to press. */
.sheet-more > summary::-webkit-details-marker { display: none; }
.sheet-more > summary::before {
  content: "";
  flex: none;
  width: 5px; height: 5px;
  border-right: 1.4px solid var(--ink-3);
  border-bottom: 1.4px solid var(--ink-3);
  transform: rotate(-45deg);
  transition: transform var(--t-fast) var(--ease);
}
.sheet-more[open] > summary::before { transform: rotate(45deg); }
.sheet-more > summary:hover { color: var(--ink); }
.rawbox {
  margin: 0 10px 10px;
  max-height: 170px;
  overflow: auto;
  padding: 9px 11px;
  border: 1px solid var(--hair);
  border-radius: var(--radius-sm);
  background: var(--field);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
  line-height: 1.5;
  color: var(--ink-2);
  white-space: pre-wrap;
  word-break: break-all;
}
.sheet-foot .foot-acts { display: flex; align-items: center; gap: 8px; }
.tool--danger { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 35%, var(--hair)); }
.tool--danger:hover:not(:disabled) { background: color-mix(in srgb, var(--bad) 10%, var(--panel)); border-color: var(--bad); color: var(--bad); }

/* Every shortcut, in one place, so the footer does not have to be a manual. */
.kbdgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 2px 22px; padding: 4px 12px 12px; }
.kbdgrid h4 {
  grid-column: 1 / -1;
  margin: 14px 0 5px;
  font-size: 10.5px;
  font-weight: 640;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-3);
  display: flex;
  align-items: center;
  gap: 10px;
}
.kbdgrid h4::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, var(--hair-2), transparent);
}
.kbdgrid .krow { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 4px 0; font-size: 12.5px; border-bottom: 1px solid color-mix(in srgb, var(--hair) 60%, transparent); }
.kbdgrid .krow span { color: var(--ink-2); }
.kbdgrid .krow kbd {
  font: inherit;
  font-size: 11px;
  border: 1px solid var(--hair-2);
  border-bottom-width: 2px;
  border-radius: 4px;
  padding: 1px 5px;
  color: var(--ink);
  background: var(--panel-2);
  white-space: nowrap;
}

/* A toast that offers a route forward carries the button that takes it. */
.toast { white-space: pre-line; }
.toast-act {
  display: inline-flex;
  align-items: center;
  min-height: 28px;
  margin-top: 8px;
  padding: 0 10px;
  border: 1px solid var(--hair-2);
  border-radius: var(--radius-sm);
  background: var(--panel-2);
  color: var(--ink);
  font-size: 12px;
  font-weight: 550;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.toast-act:hover { background: var(--accent-soft); border-color: var(--accent-2); }

/* ------------------------------------------------------------------ *
 * The arrange menu: one popup, two doors (the toolbar and a right     *
 * click on the object). Same radii, same hairlines, same motion as    *
 * everything else, so it reads as part of the product rather than as  *
 * something imported.                                                 *
 * ------------------------------------------------------------------ */

.menu {
  position: fixed;
  z-index: 260;
  min-width: 232px;
  padding: 5px;
  background: var(--panel-2);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  border: 1px solid var(--hair-2);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2), var(--shadow-glow);
  overflow: hidden;
  animation: sheetIn var(--t-fast) var(--ease);
}
.menu-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 18px;
  width: 100%;
  min-height: 32px;
  padding: 0 9px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.menu-item:hover:not(:disabled), .menu-item:focus-visible:not(:disabled) { background: var(--accent-soft); }
.menu-item:disabled { opacity: 0.38; cursor: default; }
.menu-item--danger:hover:not(:disabled) { background: color-mix(in srgb, var(--bad) 10%, var(--panel)); color: var(--bad); }
.menu-item kbd {
  font: inherit;
  font-size: 10.5px;
  color: var(--ink-3);
  border: 1px solid var(--hair);
  border-radius: 4px;
  padding: 0 4px;
  white-space: nowrap;
}
.menu-sep { height: 1px; margin: 5px 7px; background: var(--hair); }


/* Tooltips: the only way a disabled button can explain itself. */
[data-tip] { position: relative; }
[data-tip]:hover::after,
[data-tip]:focus-visible::after {
  content: attr(data-tip);
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--tip-bg);
  color: var(--tip-ink);
  border: 1px solid var(--hair);
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.35;
  white-space: pre;
  padding: 6px 9px;
  border-radius: 8px;
  box-shadow: var(--shadow-2);
  z-index: 60;
  pointer-events: none;
  animation: tipIn var(--t-med) var(--ease);
}
/* A tooltip on the status bar has to open upward, or it opens off the bottom
 * of the window and is never seen. */
.hintbar [data-tip]:hover::after,
.hintbar [data-tip]:focus-visible::after {
  top: auto;
  bottom: calc(100% + 8px);
  animation: tipUp var(--t-med) var(--ease);
}
@keyframes tipUp {
  from { opacity: 0; transform: translateX(-50%) translateY(3px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}
@keyframes tipIn {
  from { opacity: 0; transform: translateX(-50%) translateY(-3px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* A locked object draws no handles, so the frame and the padlock have to
 * carry the whole message on their own. */
.selframe--locked { border-style: dashed; }
.lockbadge {
  position: absolute;
  display: grid;
  place-items: center;
  width: 20px; height: 20px;
  border-radius: 999px;
  background: var(--panel);
  border: 1px solid var(--hair-2);
  color: var(--ink-2);
  box-shadow: var(--shadow-1);
  z-index: 12;
}

/* Save status pill ------------------------------------------------- */

.status {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 30px;
  padding: 0 12px 0 10px;
  border-radius: 999px;
  border: 1px solid var(--hair);
  background: var(--panel-2);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  font-size: 12px;
  font-weight: 560;
  color: var(--ink-2);
  white-space: nowrap;
  transition: color var(--t-med) var(--ease), border-color var(--t-med) var(--ease);
}
.status .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ink-3);
  flex: none;
  transition: background var(--t-med) var(--ease);
}
.status[data-state="saved"] { color: var(--good); border-color: color-mix(in srgb, var(--good) 45%, transparent); }
.status[data-state="saved"] .dot { background: var(--good); }
.status[data-state="saving"] .dot { background: var(--accent-2); animation: pulse 900ms ease-in-out infinite; }
.status[data-state="dirty"] .dot { background: var(--warn); }
.status[data-state="dirty"] { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
.status[data-state="error"] { color: var(--bad); border-color: color-mix(in srgb, var(--bad) 50%, transparent); }
.status[data-state="error"] .dot { background: var(--bad); }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

/* ------------------------------------------------------------------ *
 * Body: sorter | stage                                                *
 * ------------------------------------------------------------------ */

.body {
  display: grid;
  grid-template-columns: 190px 1fr;
  min-height: 0;
}

.sorter {
  border-right: 1px solid var(--hair);
  background: var(--panel);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  overflow-y: auto;
  padding: 12px 10px 40px;
  display: flex;
  flex-direction: column;
  gap: 9px;
}

/* Eyebrow label: uppercase, letter-spaced, small, quiet. The same treatment
 * is on .fmt-scope, .sheet-section and the shortcut headings, so a heading
 * anywhere in this product looks like the same kind of thing. */
.sorter-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 10.5px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 640;
  padding: 0 2px 6px;
  border-bottom: 1px solid var(--hair);
}

.thumb {
  position: relative;
  border: 1px solid rgba(227, 200, 120, 0.30);
  border-radius: var(--radius-sm);
  background: #fff;
  color: #101828;
  aspect-ratio: 16 / 9;
  cursor: pointer;
  overflow: hidden;
  flex: none;
  transition: border-color var(--t-fast) var(--ease),
              box-shadow var(--t-fast) var(--ease),
              transform var(--t-fast) var(--ease);
}
.thumb:hover { border-color: var(--c-purple); transform: translateY(-1px); box-shadow: var(--shadow-1); }
.thumb[aria-current="true"] {
  border-color: transparent;
  box-shadow: 0 0 0 2px var(--c-pink), 0 0 22px rgba(255, 46, 138, 0.35);
}
.thumb .num {
  position: absolute;
  left: 5px;
  bottom: 4px;
  font-size: 10px;
  font-weight: 600;
  color: var(--ink-2);
  background: color-mix(in srgb, var(--panel) 78%, transparent);
  border-radius: 4px;
  padding: 0 4px;
  z-index: 2;
}
.thumb .grip {
  position: absolute;
  right: 4px;
  top: 4px;
  opacity: 0;
  color: var(--ink-3);
  z-index: 2;
  transition: opacity var(--t-fast) var(--ease);
  cursor: grab;
}
.thumb:hover .grip { opacity: 1; }
.thumb.dragging { opacity: 0.45; cursor: grabbing; }
.thumb.dropbefore { box-shadow: 0 -3px 0 var(--accent-2); }
.thumb.dropafter { box-shadow: 0 3px 0 var(--accent-2); }

.thumb-inner {
  position: absolute;
  inset: 0;
  transform-origin: 0 0;
}

/* Transparent on purpose: the aurora is behind the canvas and this is the
 * largest surface on the page, so filling it would put the bloom in a 60px
 * strip at the top and nowhere else. The grid stays, one shade quieter. */
.stagewrap {
  position: relative;
  display: grid;
  place-items: center;
  overflow: hidden;
  padding: 22px;
  background:
    linear-gradient(color-mix(in srgb, var(--ink) 4%, transparent) 1px, transparent 1px) 0 0 / 48px 48px,
    linear-gradient(90deg, color-mix(in srgb, var(--ink) 4%, transparent) 1px, transparent 1px) 0 0 / 48px 48px,
    transparent;
}

/* ------------------------------------------------------------------ *
 * PAPER. Read the note at the top of this file before changing a line *
 * of this block.                                                      *
 *                                                                     *
 * The canvas and its thumbnails are the document, not the app. They    *
 * reset the whole token set to paper values, so every rule in this     *
 * sheet that reaches inside the canvas (the empty state, the selection *
 * frame, the handles, the snap readout, the "kept as-is" tag, the      *
 * inline text editor) renders dark ink on white, in both themes, with  *
 * no tint, no filter and no blend. What is on this surface is what     *
 * lands in the .pptx and on a projector.                              *
 * ------------------------------------------------------------------ */
.stage, .thumb {
  --ink: #101828;
  --ink-2: #475467;
  --ink-3: #667085;
  --ink-4: #98A2B3;
  --field: #F2F4F7;
  --panel: #FFFFFF;
  --panel-2: #F7F8FA;
  --hair: #E4E7EC;
  --hair-2: #D0D5DD;
  --accent: #2F5FE0;
  --accent-2: #2F5FE0;
  --accent-soft: rgba(47, 95, 224, 0.12);
  --good: #197A4B;
  --warn: #B54708;
  --bad: #B42318;
  --shadow-1: 0 1px 2px rgba(16, 24, 40, 0.10);
  --shadow-2: 0 8px 24px rgba(16, 24, 40, 0.18);
  --glow-ring: 0 0 0 3px rgba(47, 95, 224, 0.30);
  color-scheme: light;
}

.stage {
  position: relative;
  background: #fff;
  color: #101828;
  box-shadow: var(--paper-shadow);
  border-radius: 4px;
  overflow: hidden;
  cursor: default;
  user-select: none;
}

/* ------------------------------------------------------------------ *
 * Objects on the stage                                                *
 * ------------------------------------------------------------------ */

.obj {
  position: absolute;
  transform-origin: 50% 50%;
}
/* Clicking a row in the compatibility report takes the user to the object and
 * says so on the canvas, so the report is a route to the thing rather than a
 * second description of it. Zero under prefers-reduced-motion, like the rest. */
.obj.pulse::before {
  content: "";
  position: absolute;
  inset: -5px;
  border-radius: 5px;
  border: 2px solid var(--accent-2);
  animation: objPulse 200ms var(--ease) 3;
  pointer-events: none;
}
@keyframes objPulse {
  from { opacity: 1; }
  to   { opacity: 0.15; }
}

.stage .obj { cursor: move; }

/* ---------------------------------------------------------------------
   THE DRIFTING CODE GROUND

   Victor's slides sit on a ground of code symbols that MOVES: one tile
   width every 129.09 seconds. It is his, out of template/reckoning.css:

     .slide::before{background-image:var(--codepat);background-size:932.34px;
       opacity:.06;filter:invert(1);mix-blend-mode:screen;
       animation:cgBgScroll 129.09s linear infinite}
     .slide.light::before{opacity:.028;filter:none;mix-blend-mode:multiply}

   His 27 typed decks already drift here, because Orco renders them through
   his own pageFor() against his own vendored stylesheet. His 44 hand-built
   decks were migrated by photographing each slide, which froze the drift
   into the picture. This is the layer that puts the movement back, drawn by
   js/orco-render.js over the background raster and under every word.

   THE VALUES ARE HIS AND ARE NOT ROUNDED. .06 and .028, invert and screen,
   none and multiply, 129.09s linear infinite. The tile size and the drift
   distance are the only two that cannot be constants, because they scale
   with the stage, so they arrive as --orco-tile from the renderer, which
   computes 932.34px x (stage width / 1920).

   The wrapper carries the opacity, the filter and the blend, exactly as his
   one pseudo-element does, so the two composite the same way. The inner
   strip is one tile wider than the stage and slides left by exactly one
   tile, which loops seamlessly at any scale and moves on the compositor
   instead of repainting a background on every frame of a 197 screen show.
   --------------------------------------------------------------------- */
/* A screen blend blends with whatever is painted beneath it in its stacking
   context. Without this the stage is not one, so on a slide that carries the
   layer the tile would reach past the sheet and lift the editor's paper or
   the black behind a projected slide. It is scoped with :has so a stage that
   holds no texture is left exactly as it was. */
.stage:has(.obj-texture) { isolation: isolate; }

.obj-texture {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  opacity: .06;
  filter: invert(1);
  mix-blend-mode: screen;
}
.obj-texture[data-orco-texture="light"] {
  opacity: .028;
  filter: none;
  mix-blend-mode: multiply;
}
.obj-texture-strip {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  background-repeat: repeat;
  animation: orcoCodeScroll 129.09s linear infinite;
  will-change: transform;
}
@keyframes orcoCodeScroll {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(calc(-1 * var(--orco-tile)), 0, 0); }
}
/* A sorter card is a still. Sixty-eight of them drifting at once is a fan
   during a rehearsal, and nobody is looking for movement in a thumbnail. */
.obj-texture-strip[data-static] { animation: none; }
/* His own opt-out, kept: reckoning.css stops this animation under
   prefers-reduced-motion and so does Orco. The ground stays, the drift goes. */
@media (prefers-reduced-motion: reduce) {
  .obj-texture-strip { animation: none; }
}

.obj-text {
  white-space: pre-wrap;
  overflow: hidden;
  line-height: 1.28;
}
.obj-text p { margin: 0; }

.obj-image img {
  width: 100%;
  height: 100%;
  object-fit: fill;
  display: block;
  pointer-events: none;
}

/* An image whose bytes are a file next to Orco rather than characters inside
   the document. Victor's 47 migrated decks carry 18.6MB of background rasters,
   which is four times what this origin may store, so they are served as static
   files and the object points at one. The colour underneath is the AVERAGE
   COLOUR OF THAT EXACT PICTURE, measured from the picture by
   tools/split-migrated-assets.mjs, and it is there for one reason: half of
   these decks set pale type on a dark photograph, and without it a slide whose
   artwork has not arrived would be his white words on white paper. The real
   image covers it the moment it decodes. */
.obj-image[data-artwork] { background-size: cover; background-position: center; }
.obj-image[data-artwork="pending"] img { opacity: 0; }
.obj-image[data-artwork="here"] img { opacity: 1; transition: opacity .18s ease; }

.artworkTag {
  position: absolute;
  left: 8px;
  bottom: 8px;
  font-size: 11px;
  letter-spacing: .02em;
  padding: 3px 8px;
  border-radius: 6px;
  color: var(--ink-2);
  background: var(--paper);
  border: 1px solid var(--hair-2);
  opacity: .92;
  pointer-events: none;
}

/* Quiet on a thumbnail. A grid of 71 cards each carrying the same sentence is
   noise, and the honest disclosure belongs where he can act on it: the stage. */
.thumb .artworkTag, .rocard-paper .artworkTag { display: none; }

/* Hover reveals what is grabbable, before anything is selected. */
.stage .obj::after {
  content: "";
  position: absolute;
  inset: -3px;
  border: 1px solid transparent;
  border-radius: 3px;
  pointer-events: none;
  transition: border-color var(--t-fast) var(--ease);
}
.stage .obj:hover::after { border-color: color-mix(in srgb, var(--accent-2) 55%, transparent); }
.stage .obj.selected::after { border-color: transparent; }

.empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 6px;
  text-align: center;
  color: var(--ink-3);
  pointer-events: none;
  font-size: 13px;
}
.empty strong { color: var(--ink-2); font-size: 14px; font-weight: 600; }
.empty .keys { font-size: 12px; }
.empty kbd {
  font: inherit;
  font-size: 11px;
  border: 1px solid var(--hair-2);
  border-bottom-width: 2px;
  border-radius: 4px;
  padding: 1px 5px;
  color: var(--ink-2);
}

/* Selection frame and handles -------------------------------------- */

.selframe {
  position: absolute;
  border: 1.5px solid var(--accent-2);
  pointer-events: none;
  z-index: 40;
  animation: selIn var(--t-fast) var(--ease);
}
@keyframes selIn { from { opacity: 0; } to { opacity: 1; } }

.handle {
  position: absolute;
  width: 10px;
  height: 10px;
  background: var(--panel);
  border: 1.5px solid var(--accent-2);
  border-radius: 3px;
  pointer-events: auto;
  z-index: 41;
  transition: transform var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
/* Small to the eye, 44px to the pointer. */
.handle::before {
  content: "";
  position: absolute;
  inset: -17px;
}
.handle:hover { transform: scale(1.3); background: var(--accent-2); }

.handle[data-h="nw"] { cursor: nwse-resize; }
.handle[data-h="ne"] { cursor: nesw-resize; }
.handle[data-h="se"] { cursor: nwse-resize; }
.handle[data-h="sw"] { cursor: nesw-resize; }
.handle[data-h="n"], .handle[data-h="s"] { cursor: ns-resize; }
.handle[data-h="e"], .handle[data-h="w"] { cursor: ew-resize; }

/* A dragging selection hides its handles until the object comes to rest. */
.stage.dragging .handle { display: none; }

/* Snap guides: the gold line the object snapped to. */
.guide {
  position: absolute;
  background: var(--c-pink);
  z-index: 45;
  pointer-events: none;
  box-shadow: 0 0 0 0.5px rgba(255, 46, 138, 0.45), 0 0 8px rgba(255, 46, 138, 0.35);
}
.guide.v { width: 1px; top: 0; bottom: 0; }
.guide.h { height: 1px; left: 0; right: 0; }

/* The readout that follows a drag or resize. */
.readout {
  position: absolute;
  z-index: 50;
  background: var(--ink);
  color: var(--field);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  font-weight: 550;
  padding: 4px 7px;
  border-radius: 5px;
  pointer-events: none;
  white-space: nowrap;
  box-shadow: var(--shadow-2);
}

/* Inline text editing: a textarea, never contenteditable, because the model
 * holds characters and a contenteditable holds markup. */
.textedit {
  position: absolute;
  z-index: 55;
  margin: 0;
  border: 1.5px solid var(--accent-2);
  border-radius: 2px;
  background: color-mix(in srgb, #fff 92%, var(--accent-soft));
  color: inherit;
  font-family: inherit;
  line-height: 1.28;
  padding: 0;
  resize: none;
  overflow: hidden;
  outline: none;
  white-space: pre-wrap;
}

/* ------------------------------------------------------------------ *
 * Hint bar                                                            *
 * ------------------------------------------------------------------ */

.hintbar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 10px 0 14px;
  border-top: 1px solid var(--hair);
  background: var(--panel);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  font-size: 11.5px;
  color: var(--ink-3);
  overflow: hidden;
  white-space: nowrap;
}
.hintbar kbd {
  font: inherit;
  font-size: 10.5px;
  border: 1px solid var(--hair-2);
  border-radius: 4px;
  padding: 0 4px;
  color: var(--ink-2);
}
.hintbar .live { color: var(--ink-2); font-weight: 550; overflow: hidden; text-overflow: ellipsis; }

/* Three hints, for the state the user is in, swapped with a short fade so the
 * change registers without pulling the eye off the slide. */
.hints {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
  overflow: hidden;
  animation: hintIn var(--t-fast) var(--ease);
}
@keyframes hintIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.hints .hint { display: inline-flex; align-items: center; gap: 5px; flex: none; }

.keysbtn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  height: 22px;
  padding: 0 8px 0 6px;
  border: 1px solid var(--hair);
  border-radius: 6px;
  background: var(--panel-2);
  color: var(--ink-2);
  font-size: 11.5px;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.keysbtn:hover { background: var(--accent-soft); border-color: var(--hair-2); color: var(--ink); }
/* The theme control lives here rather than on the top bar, because the top bar
 * is already at its width budget at 1024 and a screenshot caught it overlapping
 * its own labels once. Same pill, same hairline, one glyph. */
.themebtn { padding: 0 7px; }
.themebtn svg { display: block; }
.keysbtn kbd {
  font: inherit;
  font-size: 10.5px;
  border: 1px solid var(--hair-2);
  border-radius: 4px;
  padding: 0 4px;
  color: var(--ink-2);
}
@media (max-width: 900px) {
  .keysbtn-label { display: none; }
}

/* ------------------------------------------------------------------ *
 * Toast: a failure names its cause and offers the next move.          *
 * ------------------------------------------------------------------ */

.toasts {
  position: fixed;
  right: 16px;
  bottom: 44px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 200;
  max-width: 380px;
}
.toast {
  position: relative;
  background: var(--panel-2);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  border: 1px solid var(--hair-2);
  border-left: 3px solid var(--c-purple);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2), var(--shadow-glow);
  padding: 10px 12px;
  font-size: 12.5px;
  color: var(--ink);
  line-height: 1.4;
  animation: toastIn var(--t-med) var(--ease);
}
.toast[data-kind="error"] { border-left-color: var(--c-pink); }
.toast[data-kind="ok"] { border-left-color: var(--good); }
.toast[data-kind="warn"] { border-left-color: var(--warn); }
.toast b { display: block; font-weight: 620; margin-bottom: 2px; }
@keyframes toastIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ------------------------------------------------------------------ *
 * Present mode                                                        *
 * ------------------------------------------------------------------ */

.present {
  position: fixed;
  inset: 0;
  background: radial-gradient(120vw 120vh at 50% 45%, #10121f 0%, #05060c 70%);
  z-index: 300;
  display: grid;
  place-items: center;
  animation: fade var(--t-med) var(--ease);
}
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
.present .stage { box-shadow: 0 30px 90px rgba(0, 0, 0, 0.7); border-radius: 0; }
.present-bar {
  position: fixed;
  bottom: 14px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 10px;
  background: rgba(37, 42, 63, 0.82);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  border: 1px solid rgba(227, 200, 120, 0.38);
  color: #f2f4ff;
  border-radius: 999px;
  padding: 7px 12px;
  font-size: 12px;
  opacity: 0;
  transition: opacity var(--t-med) var(--ease);
}
/*
 * VISIBILITY IS DRIVEN BY AN IDLE TIMER, NOT BY :hover.
 *
 * The overlay covers the whole viewport, so on a laptop :hover was true from
 * the moment the pointer existed and the bar was permanently on. A cold
 * verifier found it parked over Victor's own footer line on his migrated
 * decks. Now js/orco-render.js sets data-chrome on and off, any movement or
 * key wakes it, and it goes quiet after 2.6 seconds of stillness. Focus still
 * pins it open, so it is reachable by keyboard alone.
 */
.present[data-chrome="on"] .present-bar,
.present-bar:focus-within { opacity: 1; }
/* When the slide is letterboxed with a deep enough band, the bar sits IN the
 * band and never fades, because there it covers nothing and a control that
 * covers nothing has no reason to hide. */
.present[data-band="yes"] .present-bar { opacity: 1; }
.present-bar button {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, 0.28);
  color: inherit;
  border-radius: 999px;
  padding: 3px 10px;
  font-size: 12px;
  cursor: pointer;
}
.present-bar button:hover:not(:disabled) { background: rgba(255, 255, 255, 0.12); }
.present-bar button:disabled { opacity: 0.4; cursor: default; }

/* The speaker's own note, in the empty band under a 16:9 slide on a portrait
 * phone. Never over the slide, never when there is no room, never when the
 * slide has no note. */
.present-notes {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  top: 12px;
  width: min(92vw, 620px);
  /* The band above a 16:9 slide on a portrait phone is about a third of the
   * screen, so the note gets that rather than an arbitrary fifth. At 22vh his
   * own sources were cut off mid sentence with nothing to say there was more,
   * which reads as a broken panel rather than as a scroll. */
  max-height: 32vh;
  overflow: auto;
  /* A soft bottom edge, so a note that continues LOOKS like it continues. */
  -webkit-mask-image: linear-gradient(180deg, #000 0, #000 calc(100% - 22px), transparent 100%);
  mask-image: linear-gradient(180deg, #000 0, #000 calc(100% - 22px), transparent 100%);
  color: #d9deff;
  font-size: 14px;
  line-height: 1.5;
  text-align: left;
  white-space: pre-wrap;
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  background: rgba(10, 12, 24, 0.72);
  border: 1px solid rgba(227, 200, 120, 0.26);
}
.present-notes[hidden] { display: none !important; }

/* A finger is not a mouse pointer. Every control a presenter reaches for at a
 * lectern is at least 44px, which is the smallest target Apple's own guidance
 * will stand behind, and the bar keeps clear of the home indicator. */
@media (hover: none), (pointer: coarse) {
  .present-bar {
    gap: 8px;
    padding: 8px 10px;
    bottom: max(18px, env(safe-area-inset-bottom));
    font-size: 14px;
  }
  .present-bar button {
    min-height: 44px;
    min-width: 60px;
    padding: 0 16px;
    font-size: 14px;
  }
  /* "1 of 1" broke over two lines at 390 and made the pill twice as tall as it
   * needed to be. A counter is one line by definition. */
  .present-bar [data-testid="present-counter"] { white-space: nowrap; padding: 0 4px; }
}

/* ------------------------------------------------------------------ *
 * The format bar                                                      *
 *                                                                     *
 * Contextual, not permanent. It exists while something with text is    *
 * selected and it is gone the rest of the time, so the canvas is never *
 * paying rent for a control the user is not using. Same grammar as     *
 * Keynote's inspector and the Google Slides second row: a person who   *
 * has used either one already knows what this is.                      *
 * ------------------------------------------------------------------ */

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

.formatbar {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  padding: 7px 14px;
  background: var(--panel);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  border-bottom: 1px solid var(--hair);
  /* Not overflow:auto. A clipped tooltip is a black stub hanging off a
   * button, which is worse than no tooltip at all, so the bar lets them out
   * and gives up its own labels first at narrow widths instead. */
  overflow: visible;
  position: relative;
  z-index: 15;
  animation: fmtIn var(--t-med) var(--ease);
}
.formatbar[data-active="false"] > *:not(.fmt-scope) { display: none; }
.formatbar[data-active="false"] { animation: none; }
.formatbar[data-active="false"] .fmt-scope { text-transform: none; letter-spacing: 0; font-weight: 500; font-size: 12px; }
@keyframes fmtIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

.fmt-scope {
  font-size: 10.5px;
  font-weight: 640;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
  flex: none;
  font-variant-numeric: tabular-nums;
}
.formatbar[data-scope="range"] .fmt-scope { color: var(--c-pink); }

.fmt-sep {
  width: 1px;
  height: 20px;
  background: var(--hair);
  flex: none;
}

.fmt-group {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--hair);
  border-radius: var(--radius-sm);
  background: var(--panel);
  flex: none;
}

.fmt-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 30px;
  height: 28px;
  padding: 0 7px;
  border: 1px solid transparent;
  border-radius: 5px;
  background: transparent;
  color: var(--ink-2);
  font-size: 13px;
  font-weight: 560;
  cursor: pointer;
  position: relative;
  transition: background var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
/* Small to the eye, 44px to the pointer, same trick as the resize handles. */
.fmt-btn::before { content: ""; position: absolute; inset: -8px -2px; }
.fmt-btn:hover { background: var(--accent-soft); color: var(--ink); }
.fmt-btn[aria-pressed="true"] {
  background: var(--cta-scrim), var(--grad-primary);
  border-color: transparent;
  color: #fff;
  box-shadow: 0 4px 14px rgba(255, 46, 138, 0.35);
}
.fmt-btn[aria-pressed="mixed"] {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent-2) 45%, transparent);
  color: var(--ink);
}
/* Mixed is a real answer, not an unchecked box, so it says so with a mark
 * rather than pretending the selection agrees with itself. */
.fmt-btn[aria-pressed="mixed"]::after {
  content: "";
  position: absolute;
  left: 5px; right: 5px; bottom: 3px;
  height: 2px;
  border-radius: 2px;
  background: repeating-linear-gradient(90deg, var(--accent-2) 0 3px, transparent 3px 6px);
}
/* A control at the end of its range says so rather than doing nothing. */
.fmt-btn:disabled { opacity: 0.35; cursor: default; }
.fmt-btn:disabled:hover { background: transparent; color: var(--ink-2); }

.fmt-b { font-weight: 800; font-family: Georgia, serif; }
.fmt-i { font-style: italic; font-family: Georgia, serif; }
.fmt-u { text-decoration: underline; font-family: Georgia, serif; }

/* appearance:none, because a select left on the browser's own appearance keeps
 * the browser's own light chrome in dark mode: the computed background says
 * #151A22 and the control still paints white. Caught in a screenshot, not in a
 * computed style. The chevron below replaces the one that is given up. */
.fmt-select {
  min-width: 96px;
  font: inherit;
  font-size: 12.5px;
  height: 30px;
  max-width: 148px;
  padding: 0 22px 0 8px;
  appearance: none;
  -webkit-appearance: none;
  border: 1px solid var(--hair-2);
  border-radius: var(--radius-sm);
  background: var(--panel);
  color: var(--ink);
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease);
}
.fmt-select:hover { border-color: var(--accent-2); }
.fmt-field { position: relative; }
.fmt-field::after {
  content: "";
  position: absolute;
  right: 9px;
  top: 50%;
  width: 6px; height: 6px;
  margin-top: -4px;
  border-right: 1.4px solid var(--ink-3);
  border-bottom: 1.4px solid var(--ink-3);
  transform: rotate(45deg);
  pointer-events: none;
}
.fmt-field { display: inline-flex; align-items: center; flex: none; }

.fmt-stepper {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--hair-2);
  border-radius: var(--radius-sm);
  background: var(--panel);
  height: 30px;
  flex: none;
  overflow: hidden;
}
.fmt-stepper .fmt-btn { height: 28px; border-radius: 0; min-width: 26px; color: var(--ink-2); }
.fmt-size {
  font: inherit;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  width: 42px;
  height: 28px;
  text-align: center;
  border: 0;
  border-left: 1px solid var(--hair);
  border-right: 1px solid var(--hair);
  background: transparent;
  color: var(--ink);
  padding: 0;
}
.fmt-size:focus { outline: none; background: var(--accent-soft); }

/* Colour: a swatch that shows the answer, and a palette rather than a picker,
 * because ten good colours chosen in one click beat sixteen million chosen in
 * four. */
.fmt-colour { position: relative; flex: none; }
.fmt-swatch-btn {
  flex-direction: column;
  gap: 1px;
  border: 1px solid var(--hair-2);
  background: var(--panel);
  height: 30px;
  padding: 0 8px;
}
.fmt-swatch-btn .glyph { font-size: 11px; font-weight: 700; line-height: 11px; }
.fmt-swatch-btn .bar {
  width: 15px; height: 4px; border-radius: 2px;
  background: var(--ink);
  box-shadow: inset 0 0 0 1px rgba(128, 128, 140, 0.45);
}
.fmt-palette {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 80;
  display: grid;
  grid-template-columns: repeat(5, 26px);
  gap: 5px;
  padding: 8px;
  background: var(--panel);
  border: 1px solid var(--hair-2);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
  animation: fmtIn var(--t-fast) var(--ease);
}
.fmt-palette[hidden] { display: none; }
.fmt-chip {
  width: 26px; height: 26px;
  border-radius: 6px;
  border: 1px solid rgba(128, 128, 140, 0.45);
  cursor: pointer;
  padding: 0;
  position: relative;
  transition: transform var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.fmt-chip:hover { transform: scale(1.12); box-shadow: var(--shadow-1); }
.fmt-chip[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--panel), 0 0 0 4px var(--accent-2); }
.fmt-chip.auto {
  grid-column: span 5;
  width: auto;
  font-size: 11.5px;
  color: var(--ink-2);
  background: var(--panel-2);
  font-weight: 550;
}

.fmt-hint {
  font-size: 11.5px;
  color: var(--ink-3);
  white-space: nowrap;
  flex: none;
  padding-right: 2px;
}

/* The characters a format command just acted on stay marked after the
 * textarea closes, so the user SEES what changed and can keep working on the
 * same words without selecting them twice. */
.obj-text .mark {
  background: color-mix(in srgb, var(--accent-2) 24%, transparent);
  border-radius: 2px;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent-2) 28%, transparent);
}

/* Bullets and numbers are drawn by the renderer as their own column, so a
 * wrapped line hangs under the text rather than under the marker. */
.obj-text p.li { display: flex; gap: 0.4em; align-items: baseline; }
.obj-text p.li > .marker { flex: none; opacity: 0.9; font-variant-numeric: tabular-nums; }
.obj-text p.li > .liBody { flex: 0 1 auto; min-width: 0; }

/* ------------------------------------------------------------------ *
 * The empty slide, as an invitation rather than a notice              *
 * ------------------------------------------------------------------ */

.empty-actions {
  display: flex;
  gap: 10px;
  margin-top: 4px;
  pointer-events: auto;
}
.empty-act {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: 104px;
  padding: 13px 8px 11px;
  border: 1px dashed var(--hair-2);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--panel) 70%, transparent);
  color: var(--ink-2);
  font-size: 12.5px;
  font-weight: 550;
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease),
              background var(--t-fast) var(--ease),
              transform var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease);
}
.empty-act:hover {
  border-style: solid;
  border-color: var(--accent-2);
  background: var(--panel);
  color: var(--ink);
  transform: translateY(-2px);
}
.empty-act svg { display: block; color: var(--accent); }
.empty-act kbd {
  font: inherit;
  font-size: 10px;
  border: 1px solid var(--hair-2);
  border-bottom-width: 2px;
  border-radius: 4px;
  padding: 0 4px;
  color: var(--ink-3);
}
/* Corner tickmarks: the slide says where its edges are before anything is on
 * it, which is the one thing a blank rectangle cannot say by itself. */
.corner {
  position: absolute;
  width: 16px; height: 16px;
  pointer-events: none;
  border-color: color-mix(in srgb, var(--accent) 24%, transparent);
  border-style: solid;
  border-width: 0;
}
.corner.nw { left: 12px; top: 12px; border-top-width: 1px; border-left-width: 1px; }
.corner.ne { right: 12px; top: 12px; border-top-width: 1px; border-right-width: 1px; }
.corner.sw { left: 12px; bottom: 12px; border-bottom-width: 1px; border-left-width: 1px; }
.corner.se { right: 12px; bottom: 12px; border-bottom-width: 1px; border-right-width: 1px; }

/* ------------------------------------------------------------------ *
 * Reduced motion: zero, not merely less.                              *
 * ------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0s !important;
    animation-delay: 0s !important;
    transition-duration: 0s !important;
    transition-delay: 0s !important;
  }
}

/* ==================================================================== *
 * THE RUNNING ORDER                                                    *
 * ==================================================================== *
 *
 * The home screen, in the same language as the rest of the chrome: aurora
 * glass, a rainbow hairline on the top edge of the header, uppercase
 * eyebrow labels, pills for state. It is a surface, not a dialog, so it
 * takes the whole viewport and scrolls its own grid.
 *
 * THE ONE RULE THAT OUTRANKS THE AESTHETIC still holds here. A card that
 * holds a real slide holds PAPER: .rocard-paper carries .thumb, so the
 * token reset above applies and dark-chrome ink can never leak onto a
 * white slide. The card around it is glass; the slide inside it is not.
 */

.showview {
  position: fixed;
  inset: 0;
  z-index: 150;
  display: flex;
  flex-direction: column;
  background: linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 100%);
  overflow: hidden;
}
.showview[hidden] { display: none; }

/* The surface itself does NOT fade in. A full screen panel cross-fading over
 * a slide editor shows both at once for the length of the fade, and what that
 * looks like is a bug: caught in a screenshot with the toolbar and the grid
 * superimposed. The ground lands at once, opaque, and only the content rises
 * into it, which reads as a page change rather than a ghost. */
.show-head { animation: showRise 200ms var(--ease); }
.show-grid { animation: showRise 240ms var(--ease); }
@keyframes showRise {
  from { opacity: 0; transform: translateY(7px); }
  to   { opacity: 1; transform: none; }
}

/* The same aurora bloom the app has, so the two surfaces are one place. */
.showview::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(60% 55% at 8% 0%, rgba(227, 200, 120, 0.09), transparent 70%),
    radial-gradient(55% 50% at 96% 4%, rgba(255, 157, 46, 0.11), transparent 72%),
    radial-gradient(70% 60% at 50% 108%, rgba(74, 143, 255, 0.13), transparent 70%);
}
html[data-theme="light"] .showview::before {
  background:
    radial-gradient(60% 55% at 8% 0%, rgba(47, 95, 224, 0.10), transparent 70%),
    radial-gradient(55% 50% at 96% 4%, rgba(47, 95, 224, 0.06), transparent 72%);
}

/* ---- the header --------------------------------------------------- */

.show-head {
  position: relative;
  z-index: 1;
  flex: none;
  display: flex;
  align-items: flex-end;
  gap: 26px;
  padding: 26px 30px 20px;
  background: var(--panel);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  border-bottom: 1px solid var(--hair);
}
/* The rainbow hairline, on the top edge, exactly as the toolbar wears it. */
.show-head::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 2px;
  background: var(--grad-rainbow);
  opacity: 0.85;
}

.show-head-id { min-width: 0; flex: 1; }

.eyebrow {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-4);
}

.show-name {
  margin: 5px 0 0;
  font-size: clamp(22px, 2.6vw, 34px);
  font-weight: 680;
  letter-spacing: -0.022em;
  line-height: 1.08;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.show-sub {
  margin-top: 6px;
  font-size: 12.5px;
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.show-metrics { display: flex; gap: 10px; flex: none; padding-bottom: 3px; flex-wrap: wrap; }
.show-metric {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  padding: 7px 13px 6px;
  border: 1px solid var(--hair);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--panel-2) 60%, transparent);
  min-width: 62px;
}
.show-metric-n {
  font-size: 21px;
  font-weight: 660;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.show-metric-l {
  font-size: 9.5px;
  font-weight: 650;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-4);
}
.show-metric--good { border-color: color-mix(in srgb, var(--good) 42%, transparent); }
.show-metric--good .show-metric-n { color: var(--good); }
.show-metric--warn { border-color: color-mix(in srgb, var(--warn) 42%, transparent); }
.show-metric--warn .show-metric-n { color: var(--warn); }
.show-metric--muted .show-metric-n { color: var(--ink-3); }

.show-acts { display: flex; gap: 8px; flex: none; padding-bottom: 6px; }

/* ---- the grid ----------------------------------------------------- */

.show-grid {
  position: relative;
  z-index: 1;
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(238px, 1fr));
  gap: 20px 18px;
  padding: 24px 30px 40px;
  align-content: start;
  /* max-content, not auto. This grid is a flex item with a definite height,
   * and Chrome compresses auto rows to fit it: measured at 29px per row for
   * a 226px card, which clipped every thumbnail, badge and slug on the page.
   * Caught by looking at the render, not by reading the rules. */
  grid-auto-rows: max-content;
  scrollbar-gutter: stable;
}

.rocard {
  position: relative;
  /* A block, deliberately, not a flex column. The shot gets its height from
   * aspect-ratio, and a flex item resolving aspect-ratio against a container
   * whose own height is still being negotiated collapses to a hairline in
   * Chrome: measured at 1px before this was a block. Verified by looking. */
  display: block;
  border: 1px solid var(--hair);
  border-radius: var(--r-card);
  background: var(--panel);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  overflow: hidden;
  text-align: left;
  /* A pointer drag over text selects it, and a grid of 71 cards half
   * highlighted in browser blue is what that looks like. Seen in a
   * screenshot of a drag in flight. */
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
  transition: transform var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              box-shadow var(--t-fast) var(--ease),
              opacity var(--t-fast) var(--ease);
}
.rocard:focus-visible {
  outline: none;
  border-color: var(--accent-2);
  box-shadow: var(--glow-ring);
}

/* A card that opens says so by moving. A card that does not, does not:
 * a hover lift on something that cannot be clicked is a promise the card
 * has no way to keep. The ones Orco does not hold brighten their hairline
 * and nothing else. */
.rocard[data-typed="true"] { cursor: pointer; }
.rocard[data-typed="true"]:hover {
  transform: translateY(-3px);
  border-color: var(--hair-2);
  box-shadow: var(--shadow-2);
}
.rocard[data-typed="false"] { cursor: default; }
.rocard[data-typed="false"]:hover { border-color: var(--hair-2); }

.rocard[data-off="true"] { opacity: 0.5; }
.rocard[data-off="true"]:hover { opacity: 0.85; }

.rocard-shot {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--field);
  border-bottom: 1px solid var(--hair);
  overflow: hidden;
}

.rocard-num {
  position: absolute;
  left: 9px; top: 9px;
  z-index: 3;
  min-width: 24px;
  height: 24px;
  padding: 0 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  background: rgba(10, 12, 24, 0.78);
  color: #fff;
  font-size: 12px;
  font-weight: 660;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}

/* The paper. A true miniature, drawn by the same renderer as the stage. */
.rocard-paper {
  position: absolute;
  inset: 0;
  width: 100%;
  background: #fff;
  transform-origin: top left;
  overflow: hidden;
}
.rocard-paper--refused {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 14px;
  text-align: center;
  background: var(--field);
  color: var(--bad);
  font-size: 11.5px;
  font-weight: 550;
}

/* The 44 Orco has not taken in yet. No invented preview: the slug is the
 * one true name Orco has for the deck, so the slug IS the artwork, on a
 * field whose hue comes from the slug and nothing else. */
.rocard-art {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px 14px;
  /* Quiet on purpose. The first cut ran these at 62% saturation and the 44
   * decks Orco does NOT hold ended up the loudest thing on the page, with
   * his 27 real slides outgunned by placeholders. The hue still tells the
   * cards apart; it no longer competes with the content. */
  background:
    linear-gradient(150deg,
      hsl(var(--h) 26% 15%) 0%,
      hsl(calc(var(--h) + 40) 20% 10%) 100%);
}
.rocard-art::after {
  /* A hairline lattice, so the field reads as a surface rather than a fill. */
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
  background-size: 22px 22px;
  mask-image: radial-gradient(70% 70% at 50% 50%, #000, transparent);
  -webkit-mask-image: radial-gradient(70% 70% at 50% 50%, #000, transparent);
}
.rocard-art-slug {
  position: relative;
  z-index: 1;
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1.35;
  text-align: center;
  color: rgba(255, 255, 255, 0.72);
  overflow-wrap: anywhere;
}
.rocard-art-kind {
  position: absolute;
  right: 9px; bottom: 8px;
  z-index: 1;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.16em;
  color: rgba(255, 255, 255, 0.42);
}
html[data-theme="light"] .rocard-art {
  background: linear-gradient(150deg,
      hsl(var(--h) 30% 94%) 0%,
      hsl(calc(var(--h) + 40) 24% 88%) 100%);
}
html[data-theme="light"] .rocard-art::after {
  background-image:
    linear-gradient(rgba(10, 16, 69, 0.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(10, 16, 69, 0.06) 1px, transparent 1px);
}
html[data-theme="light"] .rocard-art-slug { color: rgba(10, 16, 69, 0.82); }
html[data-theme="light"] .rocard-art-kind { color: rgba(10, 16, 69, 0.34); }

/* The affordance, revealed on hover rather than shouted permanently. */
.rocard-go {
  position: absolute;
  right: 9px; top: 9px;
  z-index: 3;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--grad-primary);
  background-image: var(--cta-scrim), var(--grad-primary);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.rocard[data-typed="true"]:hover .rocard-go,
.rocard[data-typed="true"]:focus-visible .rocard-go { opacity: 1; transform: none; }

.rocard-meta { padding: 11px 13px 12px; display: grid; gap: 3px; }
.rocard-title {
  font-size: 13.5px;
  font-weight: 620;
  letter-spacing: -0.012em;
  color: var(--ink);
  line-height: 1.25;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* On a deck Orco does not hold, the SLUG is the identity, so the slug gets
 * the title slot. "Hand-built HTML deck" is the same sentence on 44 cards;
 * repeated in bold it was noise, and it pushed the one unique thing on the
 * card into the grey line underneath. The two have swapped places. */
.rocard[data-typed="false"] .rocard-title {
  color: var(--ink-2);
  font-weight: 500;
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 12px;
  letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}
.rocard[data-typed="false"] .rocard-slug {
  font-family: var(--font);
  font-size: 11px;
  letter-spacing: 0;
}
.rocard-slug {
  font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  font-size: 10.5px;
  color: var(--ink-4);
  letter-spacing: -0.01em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rocard-pills { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }

.ropill {
  display: inline-flex;
  align-items: center;
  padding: 2px 7px;
  border: 1px solid var(--hair);
  border-radius: 999px;
  background: color-mix(in srgb, var(--panel-2) 55%, transparent);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-2);
  white-space: nowrap;
}
.ropill--muted { color: var(--ink-4); border-style: dashed; }
.ropill--warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 45%, transparent); }
.ropill--info { color: var(--accent-2); border-color: color-mix(in srgb, var(--accent-2) 45%, transparent); }

/* ---- reordering --------------------------------------------------- */

.show-grid.is-reordering .rocard { transition: none; }
.rocard.is-dragging { opacity: 0.28; }
.rocard--ghost {
  position: fixed;
  z-index: 160;
  pointer-events: none;
  margin: 0;
  transform: rotate(-1.4deg) scale(1.03);
  box-shadow: var(--shadow-2), var(--shadow-glow);
  border-color: var(--accent-2);
  opacity: 0.96;
}
/* Where it will land, drawn before it lands: a rainbow spine down the edge
 * the card will arrive at. It sits INSIDE the card, not in the gutter beside
 * it, because .rocard clips its own overflow and a marker at left:-11px was
 * invisible in every drag, which a screenshot of a drag in flight showed and
 * no assertion did. */
.rocard.drop-before::before,
.rocard.drop-after::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0;
  width: 4px;
  background: var(--grad-rainbow);
  box-shadow: 0 0 14px rgba(227, 200, 120, 0.45);
  z-index: 6;
}
.rocard.drop-before::before { left: 0; }
.rocard.drop-after::after { right: 0; }
/* The spine sits under the card being dragged for part of the gesture, since
 * the ghost follows the pointer and the pointer is what chose the gap. So the
 * target also brightens its whole outline, which is the half of the signal
 * that stays visible. */
.rocard.drop-before, .rocard.drop-after { border-color: var(--accent-2); }

/* ---- empty -------------------------------------------------------- */

.show-empty {
  grid-column: 1 / -1;
  justify-self: center;
  max-width: 520px;
  margin-top: 9vh;
  padding: 34px 32px;
  border: 1px dashed var(--hair-2);
  border-radius: var(--r-card);
  background: var(--panel);
  text-align: center;
}
.show-empty-mark { font-size: 30px; color: var(--accent); opacity: 0.55; line-height: 1; }
.show-empty strong { display: block; margin-top: 12px; font-size: 16px; font-weight: 640; color: var(--ink); }
.show-empty p { margin: 10px 0 18px; font-size: 13px; line-height: 1.6; color: var(--ink-3); }

/* ---- narrower ----------------------------------------------------- */

@media (max-width: 1100px) {
  .show-head { flex-wrap: wrap; gap: 14px 20px; padding: 20px 22px 16px; }
  .show-head-id { flex: 1 1 100%; }
  .show-grid { padding: 20px 22px 36px; gap: 16px 14px; grid-template-columns: repeat(auto-fill, minmax(212px, 1fr)); }
}
@media (max-width: 640px) {
  .show-metrics { gap: 7px; }
  .show-metric { padding: 5px 9px; min-width: 0; }
  .show-metric-n { font-size: 17px; }
  .show-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
}

/* ==================================================================== *
 * THE LIBRARY                                                          *
 * ==================================================================== *
 *
 * The sibling of the running order, in the same aurora glass, with the
 * same rainbow hairline on the header and the same card grammar, because
 * the two screens are one place: one lists what you are performing, the
 * other lists what you own. What differs is the rail down the side, which
 * is the folder list, and the bar above the grid, which is search and
 * sort. Both are Finder's and Figma's arrangement rather than anything
 * invented here: the substance is new, the grammar is not.
 *
 * THE ONE RULE THAT OUTRANKS THE AESTHETIC still holds. .libcard-paper
 * carries .thumb, so the token reset higher up this file applies and the
 * chrome's pale ink can never leak onto a white slide.
 */

.libraryview {
  position: fixed;
  inset: 0;
  z-index: 150;
  display: flex;
  flex-direction: column;
  background: linear-gradient(180deg, var(--bg) 0%, var(--bg-2) 100%);
  overflow: hidden;
}
.libraryview[hidden] { display: none; }

/* Same as the running order: the ground lands at once and only the content
 * rises into it, so the editor is never visible through a half-faded panel. */
.lib-head { animation: showRise 200ms var(--ease); }
.lib-body { animation: showRise 240ms var(--ease); }

.libraryview::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(60% 55% at 8% 0%, rgba(227, 200, 120, 0.09), transparent 70%),
    radial-gradient(55% 50% at 96% 4%, rgba(255, 157, 46, 0.11), transparent 72%),
    radial-gradient(70% 60% at 50% 108%, rgba(74, 143, 255, 0.13), transparent 70%);
}
html[data-theme="light"] .libraryview::before {
  background:
    radial-gradient(60% 55% at 8% 0%, rgba(47, 95, 224, 0.10), transparent 70%),
    radial-gradient(55% 50% at 96% 4%, rgba(47, 95, 224, 0.06), transparent 72%);
}

/* ---- the body: rail beside grid ----------------------------------- */

.lib-body {
  position: relative;
  z-index: 1;
  flex: 1;
  min-height: 0;
  display: flex;
}

.lib-rail {
  flex: none;
  width: 232px;
  padding: 18px 12px 24px;
  overflow-y: auto;
  border-right: 1px solid var(--hair);
  background: color-mix(in srgb, var(--panel) 55%, transparent);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
}
.lib-rail-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 8px 8px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-4);
}
.lib-rail-add {
  width: 22px; height: 22px;
  display: grid; place-items: center;
  border: 1px solid var(--hair);
  border-radius: 7px;
  background: transparent;
  color: var(--ink-3);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
              background var(--t-fast) var(--ease);
}
.lib-rail-add:hover { border-color: var(--accent-2); color: var(--ink); background: var(--accent-soft); }

.lib-rail-list { display: grid; gap: 2px; }

.lib-rail-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 34px;
  padding: 0 8px 0 calc(9px + var(--depth, 0) * 12px);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink-2);
  font-family: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
.lib-rail-row:hover { background: var(--accent-soft); color: var(--ink); }
.lib-rail-row:focus-visible { outline: none; border-color: var(--accent-2); box-shadow: var(--glow-ring); }
.lib-rail-row.is-on {
  background: var(--panel-2);
  border-color: var(--hair-2);
  color: var(--ink);
  font-weight: 600;
}
.lib-rail-icon { flex: none; opacity: 0.62; }
.lib-rail-row.is-on .lib-rail-icon { opacity: 1; color: var(--accent-2); }
.lib-rail-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lib-rail-count {
  flex: none;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-4);
}
.lib-rail-row--trash { margin-top: 10px; border-top: 1px solid var(--hair); border-radius: 0 0 var(--radius-sm) var(--radius-sm); padding-top: 4px; }

/* The folder menu handle. Invisible until the row is hovered or focused, so
 * the rail stays a list of names, but the hit target is a full 30px square
 * the whole time rather than the glyph it draws. */
.lib-rail-more {
  flex: none;
  width: 22px; height: 22px;
  display: grid; place-items: center;
  border-radius: 6px;
  color: var(--ink-4);
  font-size: 13px;
  line-height: 1;
  opacity: 0;
  cursor: pointer;
  transition: opacity var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.lib-rail-row:hover .lib-rail-more,
.lib-rail-row:focus-within .lib-rail-more { opacity: 1; }
.lib-rail-more:hover { background: var(--accent-soft); color: var(--ink); }

/* ---- the bar ------------------------------------------------------ */

.lib-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.lib-bar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 30px 12px;
  border-bottom: 1px solid var(--hair);
}
.lib-bar .spacer { flex: 1; }

.lib-search {
  display: flex;
  align-items: center;
  gap: 7px;
  width: min(320px, 40vw);
  padding: 0 10px;
  height: 32px;
  border: 1px solid var(--hair);
  border-radius: 999px;
  background: var(--field);
  color: var(--ink-4);
  transition: border-color var(--t-fast) var(--ease);
}
.lib-search:focus-within { border-color: var(--accent-2); box-shadow: var(--glow-ring); color: var(--ink-3); }
.lib-search svg { flex: none; }
.lib-search-input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 13px;
  outline: none;
}
.lib-search-input::placeholder { color: var(--ink-4); }
.lib-search-input::-webkit-search-cancel-button { filter: invert(0.6); }

.lib-sort { display: flex; align-items: center; gap: 4px; }
.lib-sort-label {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-4);
  margin-right: 3px;
}
.lib-sort-btn {
  height: 26px;
  padding: 0 10px;
  border: 1px solid transparent;
  border-radius: 999px;
  background: transparent;
  color: var(--ink-3);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
.lib-sort-btn:hover { color: var(--ink); background: var(--accent-soft); }
.lib-sort-btn:focus-visible { outline: none; border-color: var(--accent-2); box-shadow: var(--glow-ring); }
.lib-sort-btn.is-on { background: var(--panel-2); border-color: var(--hair-2); color: var(--ink); font-weight: 600; }

.lib-count { font-size: 12px; color: var(--ink-4); font-variant-numeric: tabular-nums; }

/* ---- the grid ----------------------------------------------------- */

.lib-grid {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 20px 18px;
  padding: 22px 30px 40px;
  align-content: start;
  /* max-content for the same reason the running order needs it: this grid is
   * a flex item with a definite height and Chrome otherwise compresses auto
   * rows to fit it, which clips every thumbnail on the page. */
  grid-auto-rows: max-content;
  scrollbar-gutter: stable;
}

.libcard {
  position: relative;
  display: block;
  border: 1px solid var(--hair);
  border-radius: var(--r-card);
  background: var(--panel);
  backdrop-filter: var(--blur);
  -webkit-backdrop-filter: var(--blur);
  overflow: hidden;
  text-align: left;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  transition: transform var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              box-shadow var(--t-fast) var(--ease),
              opacity var(--t-fast) var(--ease);
}
.libcard:hover { transform: translateY(-3px); border-color: var(--hair-2); box-shadow: var(--shadow-2); }
.libcard:focus-visible { outline: none; border-color: var(--accent-2); box-shadow: var(--glow-ring); }

/* The document on screen behind this surface wears the accent outline all the
 * time, so a person who opens the library knows where they came from without
 * reading a word. */
.libcard[data-current="true"] { border-color: color-mix(in srgb, var(--accent-2) 60%, var(--hair)); }
.libcard[data-trashed="true"] { opacity: 0.66; }
.libcard[data-trashed="true"]:hover { opacity: 1; }

.libcard-shot {
  position: relative;
  aspect-ratio: 16 / 9;
  background: var(--field);
  border-bottom: 1px solid var(--hair);
  overflow: hidden;
}
.libcard-paper { width: 100%; border: 0; border-radius: 0; }
.libcard-paper--broken {
  display: grid;
  place-items: center;
  height: 100% !important;
  padding: 10px;
  background: repeating-linear-gradient(-45deg,
    color-mix(in srgb, var(--warn) 11%, transparent) 0 7px,
    transparent 7px 14px);
  color: var(--warn);
  font-size: 11.5px;
  font-weight: 600;
  text-align: center;
}
.libcard-go {
  position: absolute;
  right: 9px; bottom: 9px;
  z-index: 3;
}
.libcard-meta { padding: 11px 13px 12px; display: grid; gap: 3px; }
.libcard-title {
  font-size: 13.5px;
  font-weight: 620;
  letter-spacing: -0.012em;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* Room for the handle, so a long name never runs under it. */
  padding-right: 22px;
}
.libcard-sub {
  font-size: 11.5px;
  color: var(--ink-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The handle. 30px of target around a 13px glyph, per the rule that hit areas
 * are bigger than they look, and it appears on hover, on focus inside the
 * card, and permanently under coarse pointers where there is no hover. */
.libcard-more {
  position: absolute;
  right: 6px;
  bottom: 8px;
  width: 30px; height: 30px;
  display: grid; place-items: center;
  border: 1px solid transparent;
  border-radius: 9px;
  background: transparent;
  color: var(--ink-3);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--t-fast) var(--ease), background var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.libcard:hover .libcard-more,
.libcard:focus-within .libcard-more,
.libcard-more:focus-visible { opacity: 1; }
.libcard-more:hover { background: var(--accent-soft); border-color: var(--hair); color: var(--ink); }
.libcard-more:focus-visible { outline: none; border-color: var(--accent-2); box-shadow: var(--glow-ring); }
@media (hover: none) {
  .libcard-more { opacity: 1; }
  /* Without hover there is no other way to reach the folder commands. */
  .lib-rail-more { opacity: 1; }
}

/* The too-many state. A row of its own across the grid, not a card, because
 * it is not a document. */
.lib-more {
  grid-column: 1 / -1;
  justify-self: center;
  margin-top: 4px;
  padding: 10px 20px;
  border: 1px dashed var(--hair-2);
  border-radius: 999px;
  background: transparent;
  color: var(--ink-2);
  font: inherit;
  font-size: 12.5px;
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease);
}
.lib-more:hover { border-style: solid; border-color: var(--accent-2); background: var(--accent-soft); color: var(--ink); }
.lib-more:focus-visible { outline: none; border-color: var(--accent-2); box-shadow: var(--glow-ring); }

/* ---- the one text field the sheets need --------------------------- */

.lib-field { padding: 10px 10px 4px; }
.lib-input {
  width: 100%;
  height: 38px;
  padding: 0 12px;
  border: 1px solid var(--hair-2);
  border-radius: var(--radius-sm);
  background: var(--field);
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  outline: none;
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
.lib-input:focus { border-color: var(--accent-2); box-shadow: var(--glow-ring); }

/* ---- narrower ----------------------------------------------------- */

@media (max-width: 1100px) {
  .lib-rail { width: 190px; padding: 14px 9px 20px; }
  .lib-bar { padding: 12px 22px 10px; }
  .lib-grid { padding: 18px 22px 36px; gap: 16px 14px; grid-template-columns: repeat(auto-fill, minmax(208px, 1fr)); }
}
@media (max-width: 780px) {
  .lib-body { flex-direction: column; }
  .lib-rail {
    width: auto;
    max-height: 132px;
    border-right: 0;
    border-bottom: 1px solid var(--hair);
  }
  .lib-rail-list { grid-auto-flow: column; grid-auto-columns: minmax(130px, max-content); overflow-x: auto; }
  /* In the horizontal strip the Trash is one more item in the row, so the
   * separator that made it the last thing in a column has to go completely,
   * padding included, or it sits four pixels lower than its neighbours. */
  .lib-rail-row--trash { margin-top: 0; border-top: 0; padding-top: 0; border-radius: var(--radius-sm); }
  .lib-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
  .lib-search { width: auto; flex: 1; }
}

/* An empty state with two ways forward needs air between them. The running
 * order only ever had one button here, so nothing asked for this until the
 * library did. */
.show-empty .tool + .tool { margin-left: 10px; }

/* A tooltip under an open menu is clutter, not help: the menu already says
 * what the commands are, and the card's tip repeats the first of them. While
 * a library menu is up, the tips underneath it are off. */
body[data-libmenu="open"] [data-tip]:hover::after,
body[data-libmenu="open"] [data-tip]:focus-visible::after { content: none; }

/* -------------------------------------------------------------------------
   THE ONE ACTION ON THE RUNNING ORDER, 2026-09-08
   -------------------------------------------------------------------------
   It sits beside the number that says how many of his decks Orco does not
   hold yet, because that number is where a person notices the gap. It is
   deliberately not the loudest thing on the header: "Back to the editor" is
   still the primary, and this one earns attention by standing next to the
   count it is about rather than by shouting over it. It disappears when
   there is nothing left to bring in. */
.tool--bring {
  border-color: var(--accent-2);
  color: var(--accent-2);
  font-weight: 620;
}
.tool--bring:hover:not(:disabled) {
  border-color: var(--accent-2);
  color: var(--accent-2);
  transform: translateY(-1px);
}

/* -------------------------------------------------------------------------
   THE SECOND PILL: orco.vdr.me
   -------------------------------------------------------------------------
   It sits beside the save status and it is deliberately a different shape of
   claim. The pill on the left is green when the work is in this browser. This
   one only goes green when the server has echoed back the exact revision it
   stored, and it says a PLACE ("On orco.vdr.me") rather than a state, so the
   two can never be read as the same sentence at a glance.

   The colours carry the same discipline. Grey is "not sent", which is a fact
   and not a fault. Amber is "offline", which is a fact about the network and
   not about the work. Red is reserved for a conflict or a refusal, the two
   cases that genuinely need a person. Nothing here is ever green because a
   local save succeeded. */
.status--sync {
  cursor: pointer;
  font-family: inherit;
  gap: 7px;
}
.status--sync:hover { border-color: var(--hair-2); color: var(--ink); }
.status--sync:focus-visible { outline: none; border-color: var(--accent-2); box-shadow: var(--glow-ring); }
.status[data-state="synced"] { color: var(--good); border-color: color-mix(in srgb, var(--good) 45%, transparent); }
.status[data-state="synced"] .dot { background: var(--good); }
.status[data-state="syncing"] .dot { background: var(--accent-2); animation: pulse 900ms ease-in-out infinite; }
.status[data-state="offline"] { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
.status[data-state="offline"] .dot { background: var(--warn); }
.status[data-state="conflict"], .status[data-state="refused"] {
  color: var(--bad); border-color: color-mix(in srgb, var(--bad) 50%, transparent);
}
.status[data-state="conflict"] .dot, .status[data-state="refused"] .dot { background: var(--bad); }
.status[data-state="idle"] .dot, .status[data-state="local"] .dot { background: var(--ink-3); }

/* The devices sheet ------------------------------------------------- */
.devrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
}
.devrow-label { color: var(--ink-2); }
.devrow-value { color: var(--ink); font-weight: 620; text-align: right; }
.devnote {
  margin: 8px 2px 0;
  color: var(--ink-2);
  font-size: 12.5px;
  line-height: 1.55;
}
.devnote--bad { color: var(--bad); }
.devacts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.devspace { height: 2px; }
/* The code is read out loud across a room, so it is set large, in mono, with
 * generous letter spacing and in groups of four. Every ambiguous character is
 * already out of the alphabet; this is the other half of the same job. */
.devcode { margin-top: 12px; }
/* display:flex and display:block both outrank the browser's own [hidden]
 * rule, so a box that is hidden in the model has to be hidden here too. It
 * showed a code input to a user who had asked for nothing. */
.devcode[hidden], .devclaim[hidden] { display: none !important; }
.devcode-value {
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 26px;
  font-weight: 640;
  letter-spacing: 0.18em;
  color: var(--ink);
  padding: 14px 10px;
  border: 1px solid var(--hair-2);
  border-radius: var(--radius-sm);
  background: var(--panel-2);
  text-align: center;
  word-break: break-word;
}
.devclaim { margin-top: 12px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.devclaim-input {
  flex: 1 1 220px;
  min-width: 0;
  height: 44px;
  padding: 0 12px;
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 16px;             /* 16px or iOS zooms the page on focus */
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  background: var(--panel-2);
  border: 1px solid var(--hair-2);
  border-radius: var(--radius-sm);
}
.devclaim-input:focus-visible { outline: none; border-color: var(--accent-2); box-shadow: var(--glow-ring); }
.devclaim .devnote { flex: 1 1 100%; }

/* Replacing a device key is the one action in the devices sheet that can put
 * work out of reach, so it is drawn as a warning rather than as another
 * neutral button beside three neutral buttons. */
.tool--warn {
  border-color: color-mix(in srgb, var(--warn) 45%, var(--hair));
  color: var(--warn);
}
.tool--warn:hover:not(:disabled) { border-color: var(--warn); color: var(--warn); }


/* -------------------------------------------------------------------------
   WORDS THAT ARE ALSO A PICTURE
   -------------------------------------------------------------------------
   Four of Victor's migrated decks (robot-burgers, agi-feels-like,
   lake-fill-exp, humanitys-last-exam) carry 206 text objects the original
   deck never showed: the headline a viewer reads is painted into the
   background raster, and the live text sits behind it. A cold verifier
   deleted all 39 text objects on robot-burgers and "Same burger, half the
   price" was still on the slide.

   So the editor says so, in the same quiet register as the "kept as-is" tag
   for .pptx objects Orco cannot edit: a dashed amber hairline on the object
   and a chip on the hint bar that counts them and explains once. Scoped to
   #stage on purpose. Present mode and the sorter thumbnails must show the
   slide as the audience will see it, with no editing furniture on it. */
#stage .obj[data-role="text-behind-raster"] {
  outline: 1px dashed color-mix(in srgb, var(--warn) 65%, transparent);
  outline-offset: 1px;
}
#stage .obj[data-role="text-behind-raster"]::before {
  content: "";
  position: absolute;
  right: 2px;
  top: 2px;
  width: 7px;
  height: 7px;
  border-radius: 2px;
  background: var(--warn);
  opacity: 0.75;
  pointer-events: none;
}
.chip--baked {
  border-color: color-mix(in srgb, var(--warn) 38%, var(--hair));
  color: var(--warn);
}
.chip--baked:hover { border-color: var(--warn); color: var(--warn); }


/* =========================================================================
   A PHONE IS NOT A SMALL LAPTOP
   =========================================================================

   WHAT A PHONE IS FOR, decided rather than drifted into. Victor at a lectern,
   on a train, in a corridor: he wants to OPEN the show, FLICK THROUGH it,
   PRESENT it, and fix a word he has just noticed. He does not want to drag a
   text box to a precise EMU position with his thumb, and a design that
   pretends otherwise is a design that fails at all four of the things he does
   want.

   So the phone build keeps four things at full strength and is honest about
   the rest:

     the running order   already the best surface here, two columns of his own
                         thumbnails, and it stays first class
     present             swipe, tap zones, 44px controls, chrome that fades
     the library         two columns, real thumbnails, folders
     text editing        the caret, the keyboard, and a format bar that gets
                         out of the keyboard's way

   AND WHAT IT GIVES UP, out loud rather than quietly: Shape, Image, Arrange,
   Duplicate, Delete, Open .pptx, Export .pptx and Open my decks are NOT on the
   phone bar. Every one of them is a precision or a file operation, every one
   is on the same account on his laptop, and a bar with sixteen 44px targets on
   a 390px screen is a bar where he hits the wrong one. The objects are still
   there, still editable, still exported: it is the toolbar that is smaller,
   not the document.

   Measured at 390x844, 768x1024 and 1024x768 with touch emulation on, not by
   narrowing a desktop window.
   ========================================================================= */

/* ---- every touch device, at any width -------------------------------- */
@media (hover: none), (pointer: coarse) {
  /*
   * A tooltip on a touch device is a trap. There is no hover to end, so the
   * first tap opens a black panel that then sits over the thing it was
   * describing until something else is tapped. Screenshots at 768 and 1024
   * caught exactly that, parked over the toolbar. The tip is a hover
   * affordance, so on a device with no hover it does not exist. Every tip in
   * this product repeats something the label, the sheet or the chip already
   * says.
   */
  [data-tip]:hover::after,
  [data-tip]:hover::before,
  /* Focus counts too. Tapping a button focuses it, so a tip pinned to focus
   * is the same trap wearing a different pseudo class: a screenshot at 390
   * caught one hanging off the running order button and running off the right
   * edge of the screen. */
  [data-tip]:focus-visible::after,
  [data-tip]:focus-visible::before { content: none !important; }
  /* No hover, no reveal. The orca alone is the mark on a touch device, and it
     is the same artwork as the icon on that device's home screen. */
  .brand:hover .brand-rco { width: 0; }

  /* 44px is the smallest target Apple's own guidance will stand behind, and a
   * presenter reaching for Present with one hand is exactly the case it was
   * written for. */
  .tool, .chip, .keysbtn, .fmt-btn, .fmt-step {
    min-height: 44px;
    touch-action: manipulation;
  }
  .fmt-btn, .fmt-step { min-width: 44px; }
  /* Momentum scrolling, and no rubber-band on the page itself. */
  .sorter, #sorter-list, .formatbar, .topbar { -webkit-overflow-scrolling: touch; }
  /* 16px or iOS zooms the whole page the moment a field takes focus, and the
   * layout never comes back on its own. */
  .deckname, .fmt-size, .lib-search input, input[type="text"] { font-size: 16px; }
}

/* ---- a tablet in portrait: the canvas wants the width ------------------ */
@media (max-width: 1100px) and (orientation: portrait) {
  /*
   * A 190px rail down the left of a 768px screen costs a quarter of the width
   * to show one thumbnail, and the slide is 16:9. Turned on its side it costs
   * 96px of height and shows five. Same information, a third of the price.
   */
  .body {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr;
  }
  .sorter {
    border-right: 0;
    border-bottom: 1px solid var(--hair);
    flex-direction: column;
    overflow-y: hidden;
    padding: 8px 10px 10px;
    gap: 6px;
  }
  .sorter-head { border-bottom: 0; padding-bottom: 2px; }
  #sorter-list {
    display: flex;
    flex-direction: row;
    gap: 8px;
    overflow-x: auto;
    padding-bottom: 4px;
  }
  #sorter-list > * { flex: 0 0 auto; width: 128px; }
}

/* ---- the phone --------------------------------------------------------- */
@media (max-width: 760px) {
  .app { grid-template-rows: auto auto 1fr auto; }

  /*
   * The bar wraps into two lines rather than scrolling sideways, because a
   * control that has to be scrolled into view is a control that is not there
   * when it is needed. Line one is identity and state and the one button a
   * presenter reaches for. Line two is the small set of edits a thumb can
   * actually perform.
   */
  .topbar {
    flex-wrap: wrap;
    height: auto;
    padding: 6px 8px;
    gap: 6px;
    row-gap: 6px;
  }
  .brand { display: flex; padding-right: 0; order: 0; }
  .deckname {
    display: block;
    order: 1;
    flex: 1 1 80px;
    width: auto;
    min-width: 60px;
  }
  /*
   * The line break, made explicit rather than left to whatever happens to fit.
   * A zero height flex item spanning the full width at order 5 pushes
   * everything after it onto the next line, so line one is exactly brand, deck
   * name and Present, at every deck name length. Left to wrap on its own the
   * bar reshuffled itself as the title changed, which is the kind of layout
   * that makes a person distrust the whole screen.
   */
  .topbar::before {
    content: "";
    order: 5;
    flex: 0 0 100%;
    height: 0;
    margin: 0;
  }

  /* PRESENT IS ON LINE ONE, ALWAYS. It is the one control he reaches for in
   * front of a room, and a control that wraps to a second line is a control
   * he has to look for. Everything else earns its place below it. */
  #btn-present { order: 2; margin-left: auto; }

  /* Line two: the two facts about where the work is, then the save. */
  #status { order: 10; }
  #sync   { order: 11; }
  #btn-save { order: 12; }

  /* Line three: where to go, and the few edits a thumb can perform. */
  #btn-library { order: 20; }
  #btn-show    { order: 21; }
  #btn-text    { order: 22; }
  #btn-slide   { order: 23; }
  #btn-undo    { order: 24; }
  #btn-redo    { order: 25; }
  .spacer, .sep { display: none; }

  /* Precision and file work: not on a phone. See the note above. */
  #btn-shape, #btn-image, #btn-duplicate, #btn-arrange, #btn-delete,
  #btn-import, #btn-export, #btn-open-decks, #origin { display: none; }

  /* Both pills shrink to their dot plus their short word. The two facts stay
   * two facts: a green dot beside a grey one is still two dots. */
  #status-text, #sync-text { display: none; }
  .status-short { display: inline; }
  .status {
    height: 38px;
    padding: 0 10px 0 9px;
    font-size: 11.5px;
    max-width: 46vw;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  #btn-present { padding: 0 12px; font-weight: 640; }
  .tool { padding: 0 10px; gap: 6px; }
  .lbl-short { display: none; }
  #btn-library .lbl, #btn-show .lbl { display: none; }

  /* The slide sorter, on its side, above the canvas. */
  .body { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
  .sorter {
    border-right: 0;
    border-bottom: 1px solid var(--hair);
    overflow-y: hidden;
    padding: 6px 8px 8px;
    gap: 5px;
  }
  .sorter-head { border-bottom: 0; padding-bottom: 2px; font-size: 10px; }
  #sorter-list {
    display: flex;
    flex-direction: row;
    gap: 7px;
    overflow-x: auto;
    padding-bottom: 3px;
  }
  #sorter-list > * { flex: 0 0 auto; width: 108px; }

  /* The canvas gets what is left, with less air around it than a desk has. */
  .stagewrap { padding: 8px; }

  /*
   * The format bar is what a phone edit actually needs, so it scrolls sideways
   * rather than dropping controls: bold and a font size are worth a swipe.
   * It sits directly above the canvas so it is above the keyboard rather than
   * behind it.
   */
  .formatbar {
    flex-wrap: nowrap;
    overflow-x: auto;
    padding: 6px 8px;
    gap: 6px;
  }
  .formatbar > * { flex: 0 0 auto; }
  .fmt-scope, .fmt-hint { display: none; }

  /* The hint bar teaches keyboard shortcuts. There is no keyboard. What is
   * left is what still means something on a phone: the two disclosure chips,
   * the theme and the shortcut list. */
  .hints { display: none; }
  .hintbar { padding: 0 8px; gap: 6px; }
  .keysbtn-label { display: none; }

  /* Sheets are full width on a phone, with room for a thumb at the bottom. */
  .sheet, .sheet--wide {
    width: min(96vw, 560px);
    max-height: 86vh;
  }
  .devacts .tool { flex: 1 1 100%; justify-content: center; }
  .devcode-value { font-size: 22px; letter-spacing: 0.14em; }

  /* The two full-screen surfaces already grid down well; they only need the
   * header to stop shouting and the cards to stay two across. */
  .show-metrics, .lib-metrics { gap: 6px; }
  .show-acts, .lib-acts { gap: 6px; }
}

/* Nothing on this page may scroll the page itself sideways. Individual
 * containers scroll; the document does not. */
@media (max-width: 760px) {
  html, body { overflow-x: hidden; }
}


/*
 * THE TOOLTIP KILL SWITCH, belt as well as braces.
 *
 * (hover: none) is the correct query and it is what the rules above use, but
 * a device that reports (hover: hover) while still being driven by a finger
 * exists (a Chrome mobile emulation, a Windows tablet with a mouse plugged
 * in), and on those the first tap opens a black panel that never closes.
 * js/orco-editor.js feature-detects a touch device once and stamps the body,
 * so the suppression does not depend on one media query being right.
 */
body[data-touch="yes"] [data-tip]:hover::after,
body[data-touch="yes"] [data-tip]:hover::before,
body[data-touch="yes"] [data-tip]:focus-visible::after,
body[data-touch="yes"] [data-tip]:focus-visible::before { content: none !important; }

/* Same on a device stamped as touch: the tap that "hovers" the brand would
 * leave the letters wedged open with nothing to close them. */
body[data-touch="yes"] .brand:hover .brand-rco { width: 0; }


/* -------------------------------------------------------------------------
   THE TABLET BAND, 761 to 1160
   -------------------------------------------------------------------------
   An iPad in portrait is 768 points wide and the bar was already measured at
   its budget there before tonight. Adding the orco.vdr.me pill pushed Save,
   the two pills and Present off the right edge: a screenshot at 768x1024 with
   touch on caught Present gone completely, which on the one device he might
   actually present from is not a cosmetic problem.

   Two honest answers exist: drop controls, or use a second line. A tablet has
   the height and the controls are all useful there, so it takes the second
   line. The break is explicit for the same reason it is on the phone: a bar
   that reshuffles itself as the deck title grows is a bar nobody trusts.

   Line one: brand, deck name, Present.
   Line two: everything else, in the order it already had.
   ------------------------------------------------------------------------- */
/*
 * MEASURED, 2026-09-09, with the orco.vdr.me pill on the bar.
 *
 * The bar had no slack left. Adding a second status pill costs about 150px and
 * put it over budget at 1161, 1200 and 1366, which e2e-disclosure caught at
 * 1280 ("topbar needs 1324 of 1280"). Two honest ways out: drop a control, or
 * use a second line. Both pills exist because two facts must never be merged
 * into one word, so dropping one of them is the one thing that is not
 * available. It takes the second line, the way Google Slides and PowerPoint
 * Online both do, and the break is explicit so it cannot reshuffle itself.
 */
@media (min-width: 761px) and (max-width: 1600px) {
  .app { grid-template-rows: auto auto 1fr 30px; }
  .topbar {
    flex-wrap: wrap;
    height: auto;
    padding: 6px 10px;
    row-gap: 6px;
  }
  .topbar::before {
    content: "";
    order: 5;
    flex: 0 0 100%;
    height: 0;
    margin: 0;
  }
  .brand { display: flex; order: 0; }
  .deckname { display: block; order: 1; flex: 1 1 160px; width: auto; min-width: 90px; }
  #origin { order: 2; }
  #btn-present { order: 3; margin-left: auto; }
  /* Everything after the break keeps its source order, so the groups a user
   * has learned on a laptop are still in the same relation to each other. */
  .topbar > *:not(.brand):not(.deckname):not(#origin):not(#btn-present) { order: 10; }
  .spacer { display: none; }
  /* With a whole line to itself the status can say its whole sentence again. */
  #status-text, #sync-text { display: inline; }
  .status-short { display: none; }
}

/* ==================================================================== *
 * PRESENT MODE, THE WAY HIS ORCHESTRATOR DOES IT
 * --------------------------------------------------------------------
 * Victor is retiring the orchestrator and presenting his 29 September show
 * from Orco. Every control he reaches for and does not find is a reason to
 * open the old tool instead, so these are copied from it deliberately:
 * the same corners, the same sizes, the same words in the same tooltips.
 * The numbers below are his, converted from the 1920x1080 stage that
 * Reckoning/slides/template/reckoning.css lays out on.
 * ==================================================================== */

/* ---- the deck's own chrome, drawn only where the slide lacks it ----- */
.present .stage .deck-mark {
  position: absolute;
  z-index: 8;
  pointer-events: none;
  object-fit: contain;
}
.present .stage .deck-foot {
  position: absolute;
  left: 50%;
  bottom: 2.6%;
  transform: translateX(-50%);
  z-index: 7;
  white-space: nowrap;
  letter-spacing: 0.24em;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Helvetica, Arial, sans-serif;
  color: rgba(18, 19, 26, 0.28);
  text-shadow: 0 1px 3px rgba(255, 255, 255, 0.55);
  pointer-events: none;
}
.present .stage .deck-foot.on-dark {
  color: rgba(201, 211, 226, 0.30);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
/* The one gold on the slide, and it is four small stars. */
.present .stage .deck-foot b { font-weight: 400; margin: 0 0.55em; color: rgba(156, 118, 32, 0.42); }
.present .stage .deck-foot.on-dark b { color: rgba(227, 200, 120, 0.30); }
/* THE PAGE NUMBER. Removed 2026-09-09 at Victor's word: no slide in
   presentation shows one. What stood here styled the "01" Orco used to draw
   bottom right; js/orco-render.js no longer draws it, and js/orco-reckoning.js
   hides the one his own renderer writes inside the frame. This rule is kept as
   a stop rather than deleted, so a cached script from an older deploy cannot
   put a number back on his slide. */
.present .stage .deck-num { display: none !important; }

/* ---- the back of the card ------------------------------------------ *
 * The turn is two halves of a rotation with the faces swapped at the
 * moment nothing can be seen, exactly as reckoning.css does it, and for
 * the same reason: a real two-sided card would mean wrapping the contents
 * of every slide in a face element.
 */
.present .stage { transition: transform 0.2s ease; }
.present .stage.turning { transform: perspective(2400px) rotateY(90deg); }
/* The front is hidden, not removed, so nothing reflows mid-turn. The mark
 * stays: it is printed on the card, not on one side of it. */
.present .stage.refs-on > *:not(.deck-refs):not(.deck-mark) { visibility: hidden; }
.deck-refs {
  --u: 1;
  position: absolute;
  inset: 0;
  z-index: 8;
  display: none;
  flex-direction: column;
  text-align: left;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: calc(104px * var(--u)) calc(150px * var(--u)) calc(96px * var(--u));
  /* Distinctly darker than any slide of his, so the eye knows at once it is
   * behind the slide rather than on another one. */
  background: linear-gradient(160deg, #04050f 0%, #080a1c 48%, #04050f 100%);
}
.present .stage.refs-on .deck-refs { display: flex; visibility: visible; }
/* THE SAME PANEL, MOUNTED ON THE OVERLAY.
 * A slide that RUNS is his own page in a frame sitting above the stage, so the
 * back of the card is appended to the overlay instead and the stage's refs-on
 * class never reaches it. Pressing R flipped the state, hid the front, and
 * showed nothing: 650 characters of his sources present in the DOM at
 * display:none. Found 2026-09-09 11:50 by reading the computed style rather
 * than the screenshot. The compact layout mounts on the overlay too, so this
 * rule covers both. */
.present[data-refs="on"] > .deck-refs { display: flex; visibility: visible; }
.deck-refs:focus { outline: none; }
.deck-refs h3 {
  flex: none;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Helvetica, Arial, sans-serif;
  font-weight: 700;
  font-size: calc(13px * var(--u));
  letter-spacing: 0.30em;
  text-transform: uppercase;
  color: #E3C878;
  opacity: 0.9;
}
.deck-refs .rule {
  flex: none;
  width: calc(96px * var(--u));
  height: 1px;
  margin: calc(16px * var(--u)) 0 calc(30px * var(--u));
  background: linear-gradient(90deg, rgba(227, 200, 120, 0.75), rgba(227, 200, 120, 0));
}
.deck-refs .refnote {
  flex: none;
  margin: 0 0 calc(30px * var(--u));
  font-family: Georgia, "Iowan Old Style", "Times New Roman", serif;
  font-size: calc(27px * var(--u));
  line-height: 1.5;
  color: rgba(242, 239, 230, 0.9);
  max-width: calc(1320px * var(--u));
}
.deck-refs .refbare {
  flex: none;
  margin: 0;
  font-family: Georgia, "Iowan Old Style", "Times New Roman", serif;
  font-size: calc(24px * var(--u));
  font-style: italic;
  color: rgba(242, 239, 230, 0.42);
}
.deck-refs ol {
  margin: 0;
  padding: 0 0 0 calc(44px * var(--u));
  list-style: decimal;
  font-family: Georgia, "Iowan Old Style", "Times New Roman", serif;
  font-size: calc(22px * var(--u));
  line-height: 1.5;
  color: rgba(242, 239, 230, 0.86);
  max-width: calc(1440px * var(--u));
}
.deck-refs ol li { margin: 0 0 calc(26px * var(--u)); padding-left: calc(10px * var(--u)); }
.deck-refs ol li::marker {
  color: rgba(227, 200, 120, 0.75);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Helvetica, Arial, sans-serif;
  font-size: calc(15px * var(--u));
  font-weight: 700;
}
/* A URL is evidence, so it is shown whole rather than hidden behind a word.
 * Mono, one per line, and it may break anywhere: an unbreakable 200-character
 * DOI would otherwise push the panel sideways. His treatment, kept. */
.deck-refs .url {
  display: block;
  margin-top: calc(8px * var(--u));
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: calc(15px * var(--u));
  letter-spacing: 0.01em;
  color: #9dc0f0;
  word-break: break-all;
  overflow-wrap: anywhere;
}
.deck-refs.compact .url { font-size: 12px; margin-top: 6px; }

.deck-refs::-webkit-scrollbar { width: 8px; }
.deck-refs::-webkit-scrollbar-track { background: transparent; }
.deck-refs::-webkit-scrollbar-thumb { background: rgba(227, 200, 120, 0.28); border-radius: 99px; }
.deck-refs { scrollbar-width: thin; scrollbar-color: rgba(227, 200, 120, 0.35) transparent; }

/* ---- his four corners ---------------------------------------------- */
.present-burger,
.present-refs,
.present-edit {
  position: fixed;
  z-index: 320;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border-radius: 50%;
  -webkit-backdrop-filter: blur(9px);
  backdrop-filter: blur(9px);
  transition: opacity 0.2s ease, transform 0.2s ease, background 0.22s ease, border-color 0.22s ease;
}
.present-burger[hidden], .present-refs[hidden], .present-edit[hidden] { display: none !important; }

.present-burger {
  left: 24px;
  bottom: 24px;
  width: 52px;
  height: 52px;
  background: rgba(12, 15, 30, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.16);
  box-shadow: 0 10px 32px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.18);
  opacity: 0.22;
}
.present-burger:hover, .present-burger:focus-visible { opacity: 1; transform: translateY(-2px); outline: none; }
.present-burger.open { opacity: 1; background: #E3C878; border-color: #E3C878; }
/* Three bars that fold into a cross, which is the whole animation. */
.present-burger i {
  position: relative;
  width: 19px;
  height: 2px;
  border-radius: 2px;
  background: #F2EFE6;
  transition: all 0.32s cubic-bezier(0.16, 1, 0.3, 1);
}
.present-burger i::before, .present-burger i::after {
  content: "";
  position: absolute;
  left: 0;
  width: 19px;
  height: 2px;
  border-radius: 2px;
  background: #F2EFE6;
  transition: inherit;
}
.present-burger i::before { top: -6px; }
.present-burger i::after { top: 6px; }
.present-burger.open i { background: transparent; }
.present-burger.open i::before, .present-burger.open i::after { top: 0; background: #0b1020; }
.present-burger.open i::before { transform: rotate(45deg); }
.present-burger.open i::after { transform: rotate(-45deg); }

/* right 132 and right 24. The gap at right 78 is where his orchestrator
 * keeps "Ask the terminal for a change (G)". It is left empty on purpose. */
.present-refs { right: 132px; bottom: 24px; }
.present-edit { right: 24px; bottom: 24px; }
.present-refs, .present-edit {
  width: 44px;
  height: 44px;
  color: #E3C878;
  border: 1px solid rgba(227, 200, 120, 0.4);
  background: rgba(12, 14, 44, 0.72);
  opacity: 0.5;
}
.present-refs {
  font-family: Georgia, "Iowan Old Style", "Times New Roman", serif;
  font-style: italic;
  font-size: 20px;
}
.present-edit { font-size: 17px; }
.present-refs:hover, .present-refs:focus-visible,
.present-edit:hover, .present-edit:focus-visible {
  opacity: 1;
  transform: translateY(-2px);
  background: #E3C878;
  color: #0b1020;
  border-color: #E3C878;
  outline: none;
}
/* A slide whose back is still blank says so by being quieter, rather than by
 * hiding the button and leaving him wondering where it went. */
.present-refs.bare { opacity: 0.22; }
.present-refs[aria-expanded="true"] { opacity: 0.85; font-style: normal; font-size: 17px; }

/* ---- jump to a deck ------------------------------------------------- */
.present-jump {
  position: fixed;
  left: 24px;
  bottom: 88px;
  z-index: 319;
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: min(348px, calc(100vw - 48px));
  max-height: min(68vh, 620px);
  overflow: auto;
  padding: 9px;
  border-radius: 20px;
  background: rgba(16, 20, 42, 0.7);
  -webkit-backdrop-filter: blur(42px) saturate(180%);
  backdrop-filter: blur(42px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 30px 84px rgba(0, 0, 0, 0.62), inset 0 1px 0 rgba(255, 255, 255, 0.2);
  opacity: 0;
  transform: translateY(12px) scale(0.97);
  transform-origin: 0 100%;
  pointer-events: none;
  transition: opacity 0.26s ease, transform 0.36s cubic-bezier(0.16, 1, 0.3, 1);
}
.present-jump.open { opacity: 1; transform: none; pointer-events: auto; }
.present-jump .lab {
  padding: 8px 13px 10px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: rgba(227, 200, 120, 0.8);
}
.present-jump .row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  padding: 9px 13px;
  border: 0;
  border-radius: 13px;
  background: transparent;
  color: #E7EBF6;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.present-jump .row:hover, .present-jump .row:focus-visible { background: rgba(255, 255, 255, 0.09); outline: none; }
.present-jump .row.here { background: rgba(227, 200, 120, 0.16); color: #F6EBCB; }
.present-jump .row .n {
  flex: none;
  min-width: 22px;
  font-size: 11px;
  letter-spacing: 0.14em;
  color: rgba(227, 200, 120, 0.75);
}
.present-jump .row .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.present-jump .row.all {
  margin-top: 4px;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 0 0 13px 13px;
  color: rgba(227, 200, 120, 0.9);
  justify-content: center;
}

/* A finger is not a pointer, and a lectern is not a desk. */
@media (hover: none), (pointer: coarse) {
  .present-burger { left: 14px; bottom: max(14px, env(safe-area-inset-bottom)); width: 50px; height: 50px; opacity: 0.55; }
  .present-refs, .present-edit { width: 48px; height: 48px; opacity: 0.72; bottom: max(14px, env(safe-area-inset-bottom)); }
  .present-refs { right: 76px; }
  .present-edit { right: 16px; }
  .present-jump { left: 12px; right: 12px; width: auto; bottom: 78px; max-height: 56vh; }
  .present-jump .row { padding: 13px; font-size: 15px; }
  /* The control pill moves up out of the corners' row so nothing overlaps at
   * 390px, where three round buttons and a pill do not fit on one line. */
  .present-bar { bottom: 78px; }
}

/* Orco's own focus halo is lapis-purple, which is the right colour on the
 * editor's chrome and the wrong one on a slide: it reads as a defect in the
 * projection. Inside the show the ring is his gold, and only the ring. */
.present-burger:focus-visible,
.present-refs:focus-visible,
.present-edit:focus-visible,
.present-jump .row:focus-visible,
.deck-refs:focus-visible {
  outline: 2px solid rgba(227, 200, 120, 0.85);
  outline-offset: 2px;
  box-shadow: none;
}
.present-jump .row:focus-visible { outline-offset: -2px; }

/* The mark is printed on the card, not on one side of it, so it stays while
 * the card is turned over. On his migrated decks it lives inside the
 * background raster, which the turn hides, so a copy is drawn on the back. */
.present .stage .deck-mark.on-back { display: none; }
.present .stage.refs-on .deck-mark.on-back { display: block; visibility: visible; }

/* The band above the slide carries his note. The citations live on the back
 * of the card, and this is the one line that says where they went. */
.present-notes-more {
  display: block;
  margin-top: 8px;
  font-size: 11px;
  letter-spacing: 0.06em;
  color: rgba(227, 200, 120, 0.72);
}
@media (hover: none), (pointer: coarse) {
  /* On a phone there is no R key, so the sentence names the thing he taps. */
  .present-notes-more { font-size: 12px; }
}

/* ---- the back of the card, at reading distance ---------------------- *
 * Below 760px of stage the panel is not a miniature of a slide. His 22px
 * source line would land at four and a half pixels on a phone, which is
 * why his orchestrator hides this button there entirely. The words are the
 * point, so the panel becomes a reading view over the whole screen instead,
 * with type sized for a hand.
 */
.deck-refs.compact {
  position: fixed;
  inset: 0;
  z-index: 310;
  padding: 26px 20px calc(96px + env(safe-area-inset-bottom));
  overflow-y: auto;
}
.present[data-refs="on"] .deck-refs.compact { display: flex; }
.deck-refs.compact h3 { font-size: 11px; letter-spacing: 0.26em; }
.deck-refs.compact .rule { width: 72px; margin: 12px 0 20px; }
.deck-refs.compact .refnote { font-size: 18px; line-height: 1.55; margin: 0 0 22px; max-width: none; }
.deck-refs.compact .refbare { font-size: 17px; }
.deck-refs.compact ol { font-size: 15px; line-height: 1.55; padding-left: 26px; max-width: none; }
.deck-refs.compact ol li { margin: 0 0 18px; padding-left: 6px; }
.deck-refs.compact ol li::marker { font-size: 12px; }

/* ====================================================================== *
 * THE PUBLISH SHEET                                                      *
 * ====================================================================== *
 *
 * The one screen in Orco where a mistake cannot be undone, so the design puts
 * the consequence in front of the person rather than behind a button: the URL
 * is the largest thing on the sheet, it is real text they can select, and it
 * updates on every keystroke. Everything above it is the means of composing
 * that one line.
 */

.pub-legend {
  margin: 14px 0 8px;
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink-4);
}
.pub-legend:first-child { margin-top: 2px; }

/* ---- the address bar ----
 *
 * It is the link, not a description of one. Four rounds of a segmented builder
 * failed here, and the reason is structural rather than cosmetic: a segmented
 * control is right for data with fixed arity (a date, a card number) and wrong
 * for a path, which has neither fixed arity nor a fixed grammar. So there is
 * one field, styled as the address bar it is, and the only other control is
 * the host, which is a closed set of seven and the one part with a routing
 * consequence.
 */
.pub-bar {
  display: flex; align-items: center; gap: 2px;
  padding: 4px 6px 4px 14px;
  border: 1px solid var(--hair); border-radius: var(--radius);
  background: var(--field);
  font: 500 14px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
/* The whole bar takes the focus ring, not the field inside it, so it reads as
   one object rather than a box inside a box. */
.pub-bar:focus-within { border-color: var(--accent-2); box-shadow: var(--glow-ring); }

.pub-scheme { color: var(--ink-4); flex: none; user-select: none; }
.pub-slash { color: var(--ink-4); flex: none; user-select: none; padding: 0 1px; }

.pub-bar .pub-sel {
  font: inherit; font-weight: 650; color: var(--ink);
  background: transparent; border: 0; padding: 6px 20px 6px 4px;
  flex: none; cursor: pointer; min-width: 0;
  -webkit-appearance: none; appearance: none;
  /* Drawn rather than inherited, so the bar reads as one control on macOS,
     Windows and Android instead of three different widgets. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%238da2c8' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6.5l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 4px center; background-size: 13px 13px;
  field-sizing: content;
}
.pub-bar .pub-sel:focus-visible { outline: none; border-radius: 6px; background-color: var(--accent-soft); }
.pub-bar .pub-sel option { color: var(--ink); background: #171b30; }

/* The address itself: the widest thing on the sheet, because it is the thing
   being decided. */
.pub-path {
  flex: 1 1 auto; min-width: 0;
  font: inherit; color: var(--ink);
  background: transparent; border: 0; outline: none;
  padding: 8px 4px;
}
.pub-path::placeholder { color: var(--ink-4); font-style: italic; }
/* The BAR owns the focus ring, so the field must not draw its own or the
   result is a box inside a box wearing two different colours. A general
   input:focus rule further up the sheet was doing exactly that. */
.pub-path:focus, .pub-path:focus-visible {
  outline: none; box-shadow: none; border-color: transparent; background: transparent;
}

.pub-copy {
  flex: none; font: inherit; font-size: 12px; font-weight: 600;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Inter, sans-serif;
  padding: 6px 12px; border-radius: 8px; cursor: pointer;
  border: 1px solid var(--hair); background: transparent; color: var(--ink-2);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease);
}
.pub-copy:hover { border-color: var(--accent-2); color: var(--ink); background: var(--accent-soft); }
.pub-copy:focus-visible { outline: none; box-shadow: var(--glow-ring); }
.pub-copy.is-done { border-color: var(--good); color: var(--good); }

/* ---- the starting points ----
   One tap, not a control. They teach the shapes without asking him to operate
   anything, and they only appear where there is a choice to make. */
.pub-tries { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 10px; }
.pub-tries:empty { margin: 0; }
.pub-tries-l { font-size: 11px; font-weight: 600; color: var(--ink-4); }
.pub-try {
  font: 500 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  padding: 5px 9px; border-radius: 7px; cursor: pointer;
  border: 1px solid var(--hair); background: transparent; color: var(--ink-3);
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease),
              background var(--t-fast) var(--ease);
}
.pub-try:hover { border-color: var(--accent-2); color: var(--accent-2); background: var(--accent-soft); }
.pub-try:focus-visible { outline: none; box-shadow: var(--glow-ring); }

.pub-status { margin: 0 0 10px; font-size: 12px; line-height: 1.45; min-height: 1.2em; color: var(--ink-4); }
.pub-status.is-good { color: var(--good); }
.pub-status.is-warn { color: var(--warn); }
.pub-status.is-bad  { color: var(--bad); }
.pub-status.is-wait { color: var(--ink-4); }

@media (max-width: 560px) {
  /* 16px or iOS zooms the whole sheet the moment the field takes focus. */
  .pub-bar { font-size: 16px; flex-wrap: wrap; }
  .pub-path { flex: 1 1 100%; }
  .pub-copy { margin-left: auto; }
}

/* ====================================================================== *
 * HOME                                                                   *
 * ====================================================================== *
 *
 * The start of Orco: folders down the left, presentations as a matrix on the
 * right, one filter that narrows both, and a toggle between the matrix and a
 * list. Reached by clicking the logo.
 *
 * It borrows the running order's header rather than inventing a second one, so
 * the two full surfaces are recognisably the same product.
 */

.homeview {
  position: fixed; inset: 0; z-index: 40;
  display: flex; flex-direction: column;
  background: var(--bg);
  animation: showRise 200ms var(--ease);
}
.homeview[hidden] { display: none; }
/* The same aurora bloom the rest of the app has, so this is one place. */
.homeview::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(60% 55% at 8% 0%, rgba(227, 200, 120, 0.09), transparent 70%),
    radial-gradient(55% 50% at 96% 4%, rgba(255, 157, 46, 0.11), transparent 72%),
    radial-gradient(70% 60% at 50% 108%, rgba(74, 143, 255, 0.13), transparent 70%);
}
html[data-theme="light"] .homeview::before {
  background:
    radial-gradient(60% 55% at 8% 0%, rgba(47, 95, 224, 0.10), transparent 70%),
    radial-gradient(55% 50% at 96% 4%, rgba(47, 95, 224, 0.06), transparent 72%);
}

.home-head { align-items: flex-start; gap: 20px; }
.home-id { min-width: 0; flex: 1; }
.home-brand { margin-bottom: 12px; }
.home-h { margin: 0; font-size: clamp(20px, 2.3vw, 28px); font-weight: 680; letter-spacing: -0.02em; color: var(--ink); }
.home-sub { margin: 5px 0 0; font-size: 12.5px; color: var(--ink-3); }
.home-acts { position: relative; z-index: 1; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* The filter. It narrows the folders' counts as well as the matrix, because a
   folder that would show nothing is not a place to click. */
.home-find {
  font: inherit; font-size: 13px; width: 15rem; max-width: 42vw;
  color: var(--ink); background: var(--field);
  border: 1px solid var(--hair); border-radius: var(--radius-sm);
  padding: 8px 11px;
}
.home-find::placeholder { color: var(--ink-4); }
.home-find:focus-visible { outline: none; border-color: var(--accent-2); box-shadow: var(--glow-ring); }

.home-seg { display: inline-flex; gap: 2px; padding: 2px; border: 1px solid var(--hair); border-radius: var(--radius-sm); }
.home-segb {
  font: inherit; font-size: 12px; font-weight: 550; padding: 6px 12px;
  border: 0; border-radius: 7px; background: transparent; color: var(--ink-3); cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.home-segb:hover { background: var(--accent-soft); color: var(--ink); }
.home-segb:focus-visible { outline: none; box-shadow: var(--glow-ring); }
.home-segb.is-on { background: var(--accent-soft); color: var(--accent-2); }

/* ---- the body ---- */
.home-body {
  position: relative; z-index: 1;
  flex: 1; min-height: 0; display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  gap: 0;
}
.home-rail {
  border-right: 1px solid var(--hair);
  padding: 18px 12px; overflow-y: auto;
  display: grid; gap: 2px; align-content: start;
}
.home-rail-head {
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink-4); padding: 0 8px 8px;
}
.home-rail-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 10px; border: 1px solid transparent; border-radius: var(--radius-sm);
  background: transparent; color: var(--ink-3); font: inherit; font-size: 13px;
  cursor: pointer; text-align: left;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.home-rail-row:hover { background: var(--accent-soft); color: var(--ink); }
.home-rail-row:focus-visible { outline: none; border-color: var(--accent-2); box-shadow: var(--glow-ring); }
.home-rail-row.is-on { background: var(--accent-soft); color: var(--ink); border-color: var(--hair-2); }
.home-rail-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home-rail-count { flex: none; font-size: 11.5px; color: var(--ink-4); font-variant-numeric: tabular-nums; }
.home-rail-row.is-on .home-rail-count { color: var(--accent-2); }
.home-rail-add {
  margin-top: 8px; padding: 9px 10px; border: 1px dashed var(--hair); border-radius: var(--radius-sm);
  background: transparent; color: var(--ink-4); font: inherit; font-size: 12.5px; cursor: pointer;
  text-align: left;
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.home-rail-add:hover { border-color: var(--accent-2); color: var(--accent-2); }
.home-rail-add:focus-visible { outline: none; box-shadow: var(--glow-ring); }

/* ---- the matrix ---- */
.home-grid { overflow-y: auto; padding: 20px 22px 40px; animation: showRise 240ms var(--ease); }
.home-grid.is-matrix {
  display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  /* A grid row stretches its items to the tallest track by default, and with
     one row in a tall surface that made every card the height of the window.
     Both properties are needed: content places the rows at the top, items
     stops each card growing to fill its own row. */
  align-content: start; align-items: start;
}
.home-grid.is-list {
  display: grid; gap: 8px; grid-template-columns: minmax(0, 1fr);
  align-content: start; align-items: start;
}

.home-card {
  display: flex; flex-direction: column; gap: 12px; text-align: left;
  padding: 14px; border: 1px solid var(--hair); border-radius: var(--radius);
  background: var(--panel-2, rgba(255, 255, 255, 0.02)); color: var(--ink-2);
  font: inherit; cursor: pointer;
  transition: transform var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
              box-shadow var(--t-fast) var(--ease);
}
.home-card:hover { transform: translateY(-2px); border-color: var(--accent-2); box-shadow: var(--shadow-2); }
.home-card:focus-visible { outline: none; border-color: var(--accent-2); box-shadow: var(--glow-ring); }

/* The face is a stack of hairline sheets, not a thumbnail: this surface has
   not rendered the presentation and must not pretend it has. */
.home-face {
  position: relative; display: block; aspect-ratio: 16 / 10;
  border-radius: var(--radius-sm); overflow: hidden;
  background: linear-gradient(160deg, rgba(227, 200, 120, 0.14), rgba(74, 143, 255, 0.10));
  border: 1px solid var(--hair);
}
.home-sheet {
  position: absolute; left: 16%; right: 16%; top: 18%; bottom: 18%;
  border-radius: 4px; background: var(--field); border: 1px solid var(--hair);
  transition: transform var(--t-med) var(--ease);
}
.home-sheet.s1 { transform: translate(5px, -5px) rotate(1.6deg); opacity: .75; }
.home-sheet.s2 { transform: translate(10px, -10px) rotate(3.2deg); opacity: .5; }
.home-card:hover .home-sheet.s1 { transform: translate(8px, -8px) rotate(2.4deg); }
.home-card:hover .home-sheet.s2 { transform: translate(16px, -16px) rotate(4.8deg); }
.home-ext {
  position: absolute; right: 9px; bottom: 8px;
  font: 600 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .06em; color: var(--ink-4);
  background: var(--bg); padding: 3px 6px; border-radius: 5px; border: 1px solid var(--hair);
}

.home-meta { display: grid; gap: 4px; min-width: 0; }
.home-name { font-size: 13.5px; font-weight: 620; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home-line { font-size: 11.5px; color: var(--ink-4); font-variant-numeric: tabular-nums; }
.home-live { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--good);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home-dot { flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--good); }

/* The list is the same card laid on its side, so nothing has to be learned
   twice and the toggle changes shape rather than meaning. */
.home-grid.is-list .home-card { flex-direction: row; align-items: center; gap: 14px; padding: 10px 14px; }
.home-grid.is-list .home-face { flex: none; width: 78px; aspect-ratio: 16 / 10; }
.home-grid.is-list .home-ext { display: none; }
.home-grid.is-list .home-meta { flex: 1; }

.home-empty { padding: 40px 20px; color: var(--ink-4); display: grid; gap: 10px; justify-items: start; }
.home-empty p { margin: 0; }
.home-empty .q { font-size: 13px; opacity: .85; }

/* The logo is a button now, and must not look like one. */
.brand-home {
  display: block; padding: 0; border: 0; background: transparent; cursor: pointer;
  border-radius: 10px; line-height: 0;
}
.brand-home:focus-visible { outline: none; box-shadow: var(--glow-ring); }

@media (max-width: 860px) {
  .home-body { grid-template-columns: 1fr; }
  /* The folders become a scrolling strip above the matrix rather than a rail
     that would eat half a phone. */
  .home-rail {
    border-right: 0; border-bottom: 1px solid var(--hair);
    display: flex; gap: 6px; overflow-x: auto; padding: 12px;
  }
  .home-rail-head, .home-rail-add { display: none; }
  .home-rail-row { flex: none; }
  .home-find { width: 100%; max-width: none; }
  .home-acts { width: 100%; }
}

/* ==================================================================== *
 * THE LIFECYCLE OF AN ORC                                              *
 * ==================================================================== *
 *
 * Added 2026-09-09 (seat R1): the card menu, the one-field sheet behind
 * Rename and New folder, the Move to list, and the drop target for a .orc
 * that arrived by email.
 *
 * Nothing here invents a grammar. The three dots, the menu that drops from
 * them, Open / Save a copy / Rename / Duplicate / Move to / Delete, and a
 * list of folders with the current one marked are all the shapes Finder,
 * Drive and Keynote already taught everybody. Motion is 140-200ms ease-out
 * and the global prefers-reduced-motion block above zeroes all of it.
 */

/* A live region that is announced and never seen. */
.sr-only {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* The cell holds the card and its menu button side by side. The card stays a
   single button with a single meaning; the menu is its own tab stop. */
.home-cell { position: relative; display: flex; min-width: 0; }
.home-cell > .home-card { flex: 1; min-width: 0; }

.home-more {
  position: absolute; top: 8px; right: 8px;
  width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid transparent; border-radius: 8px;
  background: var(--panel); color: var(--ink-3);
  font: 600 15px/1 system-ui, -apple-system, sans-serif; cursor: pointer;
  opacity: .72;
  transition: opacity var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease);
}
.home-more:hover { opacity: 1; color: var(--ink); border-color: var(--hair); }
.home-more:focus-visible { outline: none; opacity: 1; border-color: var(--accent-2); box-shadow: var(--glow-ring); }
.home-grid.is-list .home-more { top: 50%; transform: translateY(-50%); }

.home-menu {
  position: absolute; top: 38px; right: 8px; z-index: 40;
  min-width: 194px; padding: 6px;
  display: grid; gap: 1px;
  border: 1px solid var(--hair); border-radius: var(--radius-sm);
  background: var(--panel); backdrop-filter: var(--blur); -webkit-backdrop-filter: var(--blur);
  box-shadow: var(--shadow-2);
  animation: homeMenuIn 150ms var(--ease);
}
@keyframes homeMenuIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
.home-grid.is-list .home-menu { top: auto; bottom: 8px; }

.home-mi {
  display: block; width: 100%; text-align: left;
  padding: 7px 10px; border: 0; border-radius: 7px;
  background: transparent; color: var(--ink-2);
  font: inherit; font-size: 13px; cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.home-mi:hover { background: var(--accent-soft); color: var(--ink); }
.home-mi:focus-visible { outline: none; background: var(--accent-soft); color: var(--ink); box-shadow: var(--glow-ring); }
.home-mi--warn { color: var(--bad, #ff6b6b); }
.home-mi-rule { height: 1px; margin: 4px 6px; background: var(--hair); }

/* The one-field sheet. A native prompt cannot say what the old name was, and
   on a locked-down profile it is suppressed outright, which turns Rename into
   a control that silently does nothing. */
.home-ask {
  position: absolute; inset: 0; z-index: 60;
  display: grid; place-items: center; padding: 20px;
  background: rgba(0, 0, 0, 0.42);
  animation: homeAskIn 160ms var(--ease);
}
@keyframes homeAskIn { from { opacity: 0; } to { opacity: 1; } }
.home-ask-box {
  width: min(420px, 100%); display: grid; gap: 10px;
  padding: 18px; border: 1px solid var(--hair); border-radius: var(--radius);
  background: var(--bg); box-shadow: var(--paper-shadow);
}
/* A grid item's automatic minimum size is its content, so a long presentation
   name pushed the heading straight out through both sides of the sheet. Found
   by looking at the screenshot; every selector-level assertion was green. */
.home-ask-box > * { min-width: 0; }
.home-ask-h { margin: 0; font-size: 15px; font-weight: 640; color: var(--ink);
  overflow-wrap: anywhere; }
.home-ask-note { margin: 0; font-size: 12.5px; color: var(--ink-4); }
.home-ask-in {
  height: 38px; padding: 0 11px; font: inherit; font-size: 14px;
  color: var(--ink); background: var(--field);
  border: 1px solid var(--hair); border-radius: var(--radius-sm);
}
.home-ask-in:focus-visible { outline: none; border-color: var(--accent-2); box-shadow: var(--glow-ring); }
.home-ask-row { display: flex; gap: 8px; justify-content: flex-end; margin-top: 2px; }
.tool--primary { border-color: var(--accent-2); color: var(--ink); }

.home-move-list { display: grid; gap: 2px; max-height: 42vh; overflow-y: auto; margin: 2px 0; }
.home-move-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  width: 100%; text-align: left; padding: 8px 10px;
  border: 1px solid transparent; border-radius: 8px;
  background: transparent; color: var(--ink-2); font: inherit; font-size: 13px; cursor: pointer;
  transition: background var(--t-fast) var(--ease);
}
.home-move-row:hover { background: var(--accent-soft); color: var(--ink); }
.home-move-row:focus-visible { outline: none; border-color: var(--accent-2); box-shadow: var(--glow-ring); }
.home-move-row.is-on { border-color: var(--hair); }
.home-move-here { font-size: 11px; color: var(--ink-4); white-space: nowrap; }

/* The drop target. Shown only while a file is actually over the surface, so
   it is a confirmation rather than a permanent instruction nobody reads. */
.home-drop {
  position: absolute; inset: 10px; z-index: 70;
  display: grid; place-items: center; pointer-events: none;
  border: 2px dashed var(--accent-2); border-radius: var(--radius);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  animation: homeAskIn 140ms var(--ease);
}
.home-drop-in { font-size: 14px; font-weight: 600; color: var(--ink); }
/* A class rule beats the user-agent's [hidden] { display: none }, so the
   overlay above needs to say so itself. Found by looking at the screenshot:
   the drop target was painted over the whole of Home at all times, dimming it
   and swallowing the card menu, and every selector-level assertion still
   passed because the elements were all present and "visible". */
.home-drop[hidden] { display: none; }

/* Which orcs use this slide set. Shown where the question is asked: on the
   library row the person is about to edit or throw away. */
.lib-usedby { display: grid; gap: 4px; margin-top: 8px; }
.lib-usedby-h {
  font: 600 10.5px/1 system-ui, -apple-system, sans-serif;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-4);
}
.lib-usedby-list { display: flex; flex-wrap: wrap; gap: 5px; }
.lib-usedby-pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 8px; border: 1px solid var(--hair); border-radius: 999px;
  background: var(--field); color: var(--ink-2); font: inherit; font-size: 11.5px;
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.lib-usedby-pill:hover { border-color: var(--accent-2); color: var(--ink); }
.lib-usedby-pill:focus-visible { outline: none; border-color: var(--accent-2); box-shadow: var(--glow-ring); }
.lib-usedby-none { font-size: 11.5px; color: var(--ink-4); }
.lib-usedby-more { font-size: 11.5px; color: var(--ink-4); align-self: center; }

/* ---------------------------------------------------------------------
   Generated presentation covers, 2026-09-10.
   The hue pair is set inline per card from a hash of the presentation's own
   name, so it is stable across reloads and differs between presentations.
   The sheets stay: they are what says "this is a presentation". What changes
   is that the card is now recognisably THIS presentation and not just A
   presentation. --------------------------------------------------------- */
.home-face[data-cover="generated"] {
  background: linear-gradient(150deg, var(--cover-a, #1e2739), var(--cover-b, #0d1220));
}
.home-initials {
  position: absolute;
  top: 10px;
  right: 12px;
  font: 700 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: .06em;
  color: var(--gold);
  opacity: .78;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .55);
  pointer-events: none;
}

/* =====================================================================
   THE MATERIAL. Appended 2026-09-10 with the visual direction.
   docs/beta-run/UX-VISUAL-DIRECTION.md
   =====================================================================

   Everything here is a primitive that other surfaces inherit, so the finish
   is the same in every corner of the product rather than being re-decided per
   screen. Four things, and they are the whole system:

     1. The gold hairline, at rest and awake.
     2. Gold focus, everywhere, always.
     3. Georgia for the names of his talks, so a title on a card and the same
        title on the slide are one voice.
     4. A primary action that is gold, machined, and readable.

   Gold appears about three times per screen. That is the discipline, and it
   is what stops an accent becoming a theme. ========================== */

/* ---- 1. THE GOLD HAIRLINE -------------------------------------------
   A 1px rule with a light-catching top edge. Used under the primary surface
   of a screen, and as the border of anything raised. */
.rule-gold,
.topbar,
.home-head,
.show-head {
  border-bottom: 1px solid var(--hair);
  box-shadow: inset 0 1px 0 rgba(242, 239, 230, 0.04);
}

/* The corner tick. The only ornament in the product, at two opposing corners
   so a card reads as machined rather than drawn. Pointer-events none: an
   ornament must never eat a click. */
.tick-corners { position: relative; }
.tick-corners::before,
.tick-corners::after {
  content: "";
  position: absolute;
  width: 10px; height: 10px;
  border: 1px solid var(--hair-lit);
  opacity: 0;
  transition: opacity var(--t-state, 140ms) var(--ease);
  pointer-events: none;
}
.tick-corners::before { top: 7px;    left: 7px;  border-right: 0; border-bottom: 0; }
.tick-corners::after  { bottom: 7px; right: 7px; border-left: 0;  border-top: 0; }
.tick-corners:hover::before,
.tick-corners:hover::after,
.tick-corners:focus-visible::before,
.tick-corners:focus-visible::after { opacity: 1; }

/* ---- 2. GOLD FOCUS, EVERYWHERE --------------------------------------
   Keyboard focus is one colour in this product and it is the accent, so a
   person tabbing never has to work out which convention a given screen uses. */
:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
  border-radius: 3px;
}

/* ---- 3. HIS VOICE, ON HIS NAMES -------------------------------------
   Georgia is the face his slides are set in. Using it for the names of his
   talks is what makes the application feel like it belongs to the work rather
   than around it. Interface labels stay in the system stack, which should be
   invisible and fast. */
.home-name,
.rocard-title,
.show-title,
.home-h1,
.homeview h1,
.showview h1,
.home-ask-h {
  font-family: var(--font-display);
  letter-spacing: -0.015em;
}

/* Anything a person compares character by character gets a monospace so they
   can: addresses, counts, timestamps. */
.home-line,
.rocard-slug,
.mono { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0; }

/* ---- 4. THE PRIMARY ACTION ------------------------------------------
   Gold, with a machined highlight along the top edge, and the GROUND as its
   ink. #0a0f24 on #e3c878 measures about 11.6:1. White on the same gold is
   1.7:1 and fails at any size, which is why the previous palette needed a
   scrim over its gradient to be legible at all. One hue needs no scrim. */
.tool--primary,
button.tool--primary {
  background: var(--grad-primary);
  border-color: var(--gold-deep);
  color: #0a0f24;
  font-weight: 680;
  box-shadow: 0 1px 0 rgba(242, 239, 230, 0.35) inset,
              0 6px 18px -8px rgba(227, 200, 120, 0.55);
}
.tool--primary:hover:not(:disabled),
button.tool--primary:hover:not(:disabled) {
  background: linear-gradient(180deg, #f6e6b6 0%, #ecd693 48%, #d8bc72 100%);
  border-color: var(--gold);
  color: #0a0f24;
  box-shadow: 0 1px 0 rgba(242, 239, 230, 0.45) inset,
              0 10px 26px -10px rgba(227, 200, 120, 0.7);
}
.tool--primary:active:not(:disabled) {
  background: linear-gradient(180deg, #cbae5f 0%, #e3c878 100%);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35) inset;
}

/* A disabled control must not look like a dead end. It says why on hover and
   it does not pretend to be a button that might work. */
.tool:disabled { opacity: 0.42; cursor: not-allowed; }

@media (prefers-reduced-motion: reduce) {
  .tick-corners::before,
  .tick-corners::after { transition-duration: 0s; }
}
