/* ==========================================================================
   SIXFINGERS — the operating system
   Copyright (c) 2026 Tristan Ulrich. All rights reserved.

   Windows XP, built properly and then painted wrong.

   Everything structural is the real thing: 30px caption, 4px frame, 20px
   menu bar, 22px status bar, the Luna caption built from four stacked bands,
   the sunken bevel, the blocked progress bar, the Explorer task pane, the
   two-column start menu. Metrics come from Wine's sysparams.c and Microsoft's
   archived Interface Guidelines (recorded in DECISIONS.md).

   Only the palette is invented, and it is invented on purpose. Three schemes
   ship, switched in Display Properties, exactly as XP shipped Blue, Olive
   Green and Silver. None of them is a colour Microsoft would have signed.

   Colour lives in ONE place: the :root blocks below. Nothing further down
   this file names a hue.
   ========================================================================== */

/* --------------------------------------------------------------- the palette
 *
 * ONE palette, and it is not switchable. It is taken from the desktop
 * picture, the other way round from how it started: the SKY gives the chrome
 * its blue, and the SAND gives the start button its orange. Three
 * interchangeable schemes made the site look like a theme demo; a product
 * has a colour.
 *
 * Colour lives here and nowhere else. Nothing below this block names a hue.
 */

:root {
  /* Caption: four bands, top to bottom, plus the outline. The sky, held at a
     depth that can carry white text all day. */
  --cap-1: #6cc0f5;
  --cap-2: #2a92e4;
  --cap-3: #0f6fc4;
  --cap-4: #0a4f95;
  --cap-edge: #063a70;
  --cap-gloss: rgba(255,255,255,.55);
  --cap-ink: #ffffff;
  --cap-shadow: rgba(4,35,70,.7);

  --cap-off-1: #c2cfdb;
  --cap-off-2: #a7b6c4;
  --cap-off-3: #93a3b2;
  --cap-off-4: #8192a1;
  --cap-off-ink: #f2f7fb;

  --frame-1: #2a92e4;
  --frame-2: #0f6fc4;

  /* Faces: a very pale cool white, the colour of the sky at the horizon. */
  --face: #eef5fb;
  --face-hi: #ffffff;
  --face-lo: #bed2e2;
  --face-dk: #7b93a8;
  --ink: #0f1a24;
  --ink-dim: #4d6172;
  --ink-off: #9aacba;

  --field: #ffffff;
  --field-edge: #6d90ad;
  /* Selection is the sky, deeper — the way the original did it. Making it
     sand meant every highlighted row, every chosen icon and every hovered
     menu item came back orange, which is far more of the screen than it
     sounds like. */
  --sel: #2a7fd4;
  --sel-2: #0d55a0;
  --sel-ink: #ffffff;
  --hot: #ffd400;

  /* Taskbar: the sky, deeper. */
  --bar-1: #3f9fe8;
  --bar-2: #1275c8;
  --bar-3: #0a5aa2;
  --bar-4: #06427a;
  --bar-edge: #a8dcff;
  --bar-ink: #ffffff;
  /* Start: green on a blue bar, which is the arrangement the original
     shipped with. The sand is spoken for by the selection. */
  --start-1: #9ee063;
  --start-2: #57ad20;
  --start-3: #3a8a10;
  --start-4: #266b06;
  --start-ink: #ffffff;
  --tray-1: #0d5ea8;
  --tray-2: #084880;

  /* The Explorer task pane. */
  --pane-1: #e6f2fd;
  --pane-2: #b9dbf5;
  --pane-head-1: #f2f9ff;
  --pane-head-2: #c8e3f8;
  --pane-head-ink: #0a4f95;
  --pane-body: #f7fbff;
  --pane-link: #0f6fc4;
}

/* --------------------------------------------------- constants and metrics */

:root {
  /* Rarity never changes with the scheme: it means something. */
  --r0: #8a94a0;
  --r1: #16b07a;
  --r2: #1f7fe0;
  --r3: #8a3fd8;
  --r4: #ff6a00;

  --cap: 34px;          /* XP caption height */
  --frame: 5px;         /* SM_CXFRAME */
  --radius: 9px;        /* Luna top-corner radius */
  --menubar: 26px;
  --toolbar: 30px;
  --addr: 24px;
  --status: 22px;
  --pane-w: 212px;
  --task: 48px;

  --ui: "Trebuchet MS", "Segoe UI", Tahoma, Verdana, sans-serif;
  --sys: Tahoma, "Segoe UI", Verdana, "DejaVu Sans", sans-serif;

  --pop: cubic-bezier(.2,1.3,.4,1);
  --ease: cubic-bezier(.25,.8,.3,1);

  --shadow: 0 12px 34px -8px rgba(0,0,0,.45), 0 3px 10px rgba(0,0,0,.25);
}

*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }

html, body {
  height: 100%; margin: 0; overflow: hidden;
  font: 400 13px/1.45 var(--sys);
  color: var(--ink);
  background: var(--cap-4);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
}
button, input, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; padding: 0; }

/* The pointer is the mark, on EVERYTHING.
 *
 * It is applied to `*` on purpose. Per-element rules kept losing to some
 * other rule further down — a `cursor: pointer` on a button, a `default` on a
 * pane — and the site's own pointer would blink back to the system arrow the
 * moment you moved over anything clickable, which is exactly when it matters.
 *
 * The image is generated from the same bitmap js/icons.js draws, at 1:1, and
 * injected as --mark-cursor at boot. --mark-down is the same hand with the
 * index pressed, swapped in while a mouse button is held.
 */
*, *::before, *::after { cursor: var(--mark-cursor, default) !important; }
body[data-press="1"] *, body[data-press="1"] *::before, body[data-press="1"] *::after {
  cursor: var(--mark-down, var(--mark-cursor, default)) !important;
}
/* Scrollbar parts are shadow elements and do not inherit, so they keep the
   system arrow unless they are named here. The text caret shape over a field
   is dropped too: there is one pointer on this site and it is this one. */
::-webkit-scrollbar, ::-webkit-scrollbar-track, ::-webkit-scrollbar-thumb,
::-webkit-scrollbar-button, ::-webkit-scrollbar-corner {
  cursor: var(--mark-cursor, default) !important;
}
h1,h2,h3,p,ul,figure { margin: 0; }
ul { padding: 0; list-style: none; }
img { display: block; }
canvas { display: block; }
.sr { position:absolute; width:1px; height:1px; margin:-1px; overflow:hidden; clip-path:inset(50%); }

/* The 2001 focus ring is a dotted rectangle, not a glow. */
:focus-visible { outline: 1px dotted currentColor; outline-offset: -3px; }

/* ==========================================================================
   Scrollbars. The era's are 17px wide with bevelled arrow buttons and a
   dithered trough, and they are half of why a window reads as Windows.
   ========================================================================== */

* { scrollbar-color: var(--face-lo) var(--face); }
::-webkit-scrollbar { width: 17px; height: 17px; }
::-webkit-scrollbar-track {
  background:
    repeating-conic-gradient(var(--face-hi) 0 25%, transparent 0 50%) 0 0 / 2px 2px,
    var(--face);
}
::-webkit-scrollbar-thumb {
  background:
    linear-gradient(180deg, var(--face-hi) 0 1px, transparent 1px),
    linear-gradient(90deg, var(--face-hi) 0 1px, transparent 1px),
    linear-gradient(180deg, var(--face) 0 60%, var(--face-lo));
  border: 1px solid var(--face-dk);
  box-shadow: inset -1px -1px 0 var(--face-lo);
}
::-webkit-scrollbar-thumb:hover { filter: brightness(1.05); }
::-webkit-scrollbar-button:single-button {
  background: linear-gradient(180deg, var(--face) 0 60%, var(--face-lo));
  border: 1px solid var(--face-dk);
  box-shadow: inset 1px 1px 0 var(--face-hi);
  height: 17px; width: 17px;
  background-repeat: no-repeat; background-position: center;
}
::-webkit-scrollbar-button:single-button:vertical:decrement {
  background-image: linear-gradient(45deg, transparent 40%, var(--ink) 40% 60%, transparent 60%);
  background-size: 7px 7px;
}
::-webkit-scrollbar-button:single-button:vertical:increment {
  background-image: linear-gradient(225deg, transparent 40%, var(--ink) 40% 60%, transparent 60%);
  background-size: 7px 7px;
}
::-webkit-scrollbar-button:single-button:horizontal:decrement,
::-webkit-scrollbar-button:single-button:horizontal:increment {
  background-image: linear-gradient(135deg, transparent 40%, var(--ink) 40% 60%, transparent 60%);
  background-size: 7px 7px;
}
::-webkit-scrollbar-corner { background: var(--face); }

/* ==========================================================================
   Boot, then the welcome screen. Two separate moments, as they were.
   ========================================================================== */

/* The startup screen, laid out like the one everybody waited in front of:
   black, the mark, the name, a trough with three blocks chasing across it,
   and the copyright at the foot. One typeface for the whole screen — the
   era's own UI face — because four of them is what made it read as a web
   page wearing a costume. */
.boot {
  position: fixed; inset: 0; z-index: 300;
  display: grid; place-content: center; justify-items: center;
  background: #000;
  color: #fff;
  font-family: var(--sys);
  transition: opacity .5s var(--ease), visibility .5s;
}
body:not([data-phase="boot"]) .boot { opacity: 0; visibility: hidden; }
.boot-inner { display: grid; justify-items: center; gap: 18px; }
.boot-hand { width: 56px; height: 56px; }
.boot-word {
  font: 700 21px/1 var(--sys); letter-spacing: .42em; text-indent: .42em;
  color: #fff;
}
.boot-word b { font-weight: 400; color: #b9b9c4; }

/* The trough. 1px light rule, black inside, three blocks crossing it. */
.boot-bar {
  width: 132px; height: 13px; padding: 2px;
  border: 1px solid #6f6f7a; border-radius: 7px;
  background: #000; display: flex; gap: 3px; overflow: hidden;
}
.boot-bar i {
  width: 9px; height: 100%; border-radius: 1px; flex: none;
  background: linear-gradient(180deg, var(--cap-1), var(--cap-2) 55%, var(--cap-3));
  box-shadow: 0 0 5px var(--cap-2);
  animation: chase 2.1s infinite linear;
}
.boot-bar i:nth-child(2) { animation-delay: .2s; }
.boot-bar i:nth-child(3) { animation-delay: .4s; }
@keyframes chase {
  0%   { transform: translateX(-16px); opacity: 0; }
  10%  { opacity: 1; }
  78%  { opacity: 1; }
  100% { transform: translateX(132px); opacity: 0; }
}
.boot-note {
  font: 400 11px/1.4 var(--sys); letter-spacing: .04em;
  color: #9a9aa6; min-height: 1.4em;
}
.boot-corp {
  position: absolute; bottom: 30px; left: 0; right: 0; text-align: center;
  font: 400 11px/1.5 var(--sys); color: #6e6e78;
}

/* ---------------------------------------------------------------- welcome */

.welcome {
  position: fixed; inset: 0; z-index: 290;
  display: grid; grid-template-rows: auto 1fr auto;
  background: linear-gradient(180deg, var(--cap-2), var(--cap-3) 60%, var(--cap-4));
  color: #fff;
  font-family: var(--sys);
  animation: fadeIn .4s var(--ease);
}
@keyframes fadeIn { from { opacity: 0; } }
body[data-phase="desk"] .welcome { animation: fadeOut .45s var(--ease) forwards; }
@keyframes fadeOut { to { opacity: 0; visibility: hidden; } }

/* The two horizontal rules with the light streak, top and bottom. */
.welcome-bar { position: relative; height: 92px; }
.welcome-bar::after {
  content: ""; position: absolute; left: 0; right: 0; height: 3px;
  background: linear-gradient(90deg,
    transparent, var(--cap-1) 12%, #fff 50%, var(--cap-1) 88%, transparent);
  box-shadow: 0 0 14px var(--cap-1);
}
.welcome-bar-top::after { bottom: 0; }
.welcome-bar-bottom::after { top: 0; }
.welcome-bar-bottom p {
  padding: 18px 34px; font-size: 13.5px; line-height: 1.6; color: var(--cap-off-ink);
}
.welcome-grid { display: grid; grid-template-columns: 1fr 2px 1fr; align-items: center; }
.welcome-left { padding-right: 46px; text-align: right; }
.welcome-brand { font: 400 34px/1 var(--sys); letter-spacing: .02em; text-shadow: 0 2px 10px var(--cap-shadow); }
.welcome-brand b { font-weight: 700; }
.welcome-hint { margin-top: 12px; font-size: 14px; color: var(--cap-off-ink); }
.welcome-rule { align-self: stretch; margin: 30px 0; background: linear-gradient(180deg, transparent, rgba(255,255,255,.75), transparent); }
.welcome-right { padding-left: 46px; }
.tile {
  display: flex; align-items: center; gap: 14px; padding: 8px 16px 8px 8px;
  border-radius: 4px; color: #fff; text-align: left;
  transition: background-color .15s;
}
.tile:hover { background: rgba(255,255,255,.16); }
.tile-pic {
  width: 52px; height: 52px; padding: 3px; border-radius: 4px;
  background: linear-gradient(135deg, #fff, var(--cap-off-1));
  box-shadow: 0 2px 8px var(--cap-shadow);
}
.tile-pic canvas { width: 100%; height: 100%; }
.tile-txt { display: grid; gap: 2px; }
.tile-txt b { font: 700 17px/1.2 var(--sys); text-shadow: 0 1px 3px var(--cap-shadow); }
.tile-txt i { font-style: normal; font-size: 13px; color: var(--cap-off-ink); }

/* PLUSIEURS COMPTES, DONC UNE LISTE. La colonne portait une tuile unique et
   centrée ; elle en porte autant qu'il y a de gens, plus celle qui en crée un. */
.welcome-right { display: grid; gap: 4px; align-content: center; max-height: 68vh; overflow: auto; }
.welcome-sub { margin-top: 8px; font-size: 13px; color: var(--cap-off-ink); opacity: .85; }
.tile-new { opacity: .92; }
.tile-new .tile-pic { background: linear-gradient(135deg, #fff, #cfe0f0); }
.tile-new:hover { opacity: 1; }

/* LE DIALOGUE « NOUVEAU COMPTE » A SA PROPRE NAPPE. Il doit pouvoir s'ouvrir sur
   l'écran d'accueil, qui couvre déjà tout : une fenêtre du bureau se serait
   ouverte DESSOUS, invisible. 295 le pose juste au-dessus de l'accueil (290) et
   bien en dessous du rituel (400), qui doit rester intouchable. */
.newuser {
  position: fixed; inset: 0; z-index: 295;
  display: grid; place-items: center;
  background: rgba(0,0,0,.42);
  font-family: var(--sys);
}
.newuser-box {
  width: min(440px, calc(100vw - 32px));
  padding: 18px 20px 16px;
  background: var(--face); color: var(--ink);
  border: 1px solid var(--cap-edge); border-radius: var(--radius);
  box-shadow: 0 24px 60px rgba(0,0,0,.55);
}
.newuser-h { font: 700 17px/1.2 var(--sys); margin-bottom: 6px; }
.newuser-p { font-size: 13px; line-height: 1.45; color: var(--ink-dim); }
.newuser-l { display: block; margin-top: 14px; font-size: 13px; font-weight: 700; }
.newuser-l .field { margin-top: 5px; }

/* LE REFUS, DANS LE DIALOGUE. Il n'existe que quand il a quelque chose à dire —
   `display: none` plutôt qu'un blanc réservé, sinon la nappe a un trou permanent
   pour une phrase que presque personne ne verra jamais. La matière est celle des
   bulles d'info du site, comme partout où il faut lire sans qu'on vous commande
   quoi que ce soit. */
.newuser-no { display: none; }
.newuser-no[data-on="1"] {
  display: block; margin-top: 12px;
  font-size: 12.5px; line-height: 1.45; color: #4a3f16;
  background: linear-gradient(180deg, #ffffe1, #fff8bf);
  border: 1px solid #ddcd72; border-radius: 4px; padding: 8px 10px;
}

/* Choisir l'image de son compte : six pastilles, celle qui est prise se voit. */
.pickpic { display: flex; gap: 8px; margin: 10px 0 2px; flex-wrap: wrap; }
.pickpic-b {
  width: 42px; height: 42px; padding: 2px; border-radius: 4px;
  background: var(--face); box-shadow: inset 0 0 0 1px var(--face-lo);
}
.pickpic-b canvas { width: 100%; height: 100%; border-radius: 2px; display: block; }
.pickpic-b[aria-pressed="true"] {
  box-shadow: 0 0 0 2px var(--sel), inset 0 0 0 1px var(--face-hi);
}

/* Une ligne de la fenêtre User Accounts : l'image, qui c'est, et ce qu'on peut
   faire. Les boutons restent à droite et ne se serrent pas. */
.acct-row { align-items: center; gap: 12px; cursor: default; }
.acct-pic {
  width: 48px; height: 48px; flex: none; border-radius: 4px;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.25);
}
.acct-acts { display: flex; gap: 6px; flex: none; margin-left: auto; }
.acct-acts .btn { min-width: 0; padding: 0 12px; }
/* La fiche qu'on colle pour ajouter une machine, et le verdict en dessous. */
.newuser-wide { width: min(620px, calc(100vw - 32px)); }
.src-field {
  /* `height: auto` ne suffit pas : `.field` impose une hauteur, et un textarea
     réduit à une ligne ne montre pas qu'on y colle un objet de vingt lignes. */
  width: 100%; height: auto; min-height: 200px; margin-top: 10px; padding: 8px;
  font: 400 12.5px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  resize: vertical;
}
.src-verdict { min-height: 20px; margin-top: 8px; font-size: 13px; color: var(--ink-dim); }
.src-verdict[data-bad="1"] { color: #a3241b; font-weight: 700; }
.src-help { margin-right: auto; }
.src-row { align-items: center; gap: 12px; cursor: default; }

/* L'écran de consentement : deux colonnes, ce qu'on annonce et ce que ça veut dire. */
.consent { display: grid; grid-template-columns: auto 1fr; gap: 6px 14px; margin: 12px 0 4px; font-size: 13px; }
.consent dt { font-weight: 700; color: var(--ink); white-space: nowrap; }
.consent dd { margin: 0; color: var(--ink-dim); line-height: 1.45; }

/* La fenêtre dit qu'elle attrape ce qu'on lui lâche. */
.view.drop-on { box-shadow: inset 0 0 0 2px var(--sel); background: var(--sel-soft, rgba(49,106,197,.06)); }

/* ------------------------------------------------------------------- bye */

.bye {
  position: fixed; inset: 0; z-index: 400; background: #000;
  display: grid; place-content: center; text-align: center;
  color: var(--cap-1); font: 400 20px/1.7 var(--ui); letter-spacing: .1em;
  animation: fadeIn .8s var(--ease);
}

/* ==========================================================================
   The desktop
   ========================================================================== */

.desk { position: fixed; inset: 0; overflow: hidden; }
.sky { position: absolute; inset: 0; width: 100%; height: 100%; }

/* The rubber band you drag across empty desktop. Dotted, as it was. */
.rubber {
  position: absolute; z-index: 3; pointer-events: none;
  border: 1px dotted #fff;
  background: rgba(255,255,255,.14);
  mix-blend-mode: difference;
}

.icons-corner {
  top: auto !important; left: auto !important; bottom: calc(var(--task) + 10px);
  right: 10px;
}
.icons {
  position: absolute; top: 8px; left: 8px; bottom: calc(var(--task) + 8px);
  display: flex; flex-direction: column; flex-wrap: wrap; align-content: flex-start;
  /* De l'air entre les icônes : leurs étiquettes tiennent maintenant sur deux
     lignes de 13 px, et 2 px de gouttière les faisaient se toucher. */
  gap: 8px 10px; z-index: 2;
}
.icon {
  /* `relative` so the grains of light over a sealed folder can be placed against
     the icon. A dragged icon gets `position: absolute` inline, which is also a
     positioning context, so both cases work. Note this is `.icon`, the item — NOT
     `.icons`, the container, where re-declaring position once sent the corner list
     back to the top left. */
  position: relative;
  width: 94px; padding: 8px 4px 7px;
  display: grid; justify-items: center; gap: 5px;
  border: 1px dotted transparent;
  user-select: none;
}
.icon canvas { width: 40px; height: 40px; filter: drop-shadow(1px 2px 2px rgba(0,0,0,.45)); }
.icon span {
  font-size: 13px; line-height: 1.25; color: #fff; text-align: center;
  text-shadow: 1px 1px 2px rgba(0,0,0,.9), 0 0 5px rgba(0,0,0,.55);
  padding: 1px 3px;
  overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; max-height: 2.6em;
}
.icon[aria-selected="true"] span {
  -webkit-line-clamp: 4; max-height: none;
  background: var(--sel); text-shadow: none;
}
.icon[aria-selected="true"] canvas { filter: drop-shadow(1px 2px 2px rgba(0,0,0,.45)) saturate(.6) brightness(.85); }
.icon[aria-selected="true"] { border-color: rgba(255,255,255,.6); }

/* A pack nobody has opened yet nudges, once every few seconds. */
.icon[data-fresh="1"] canvas { animation: nudge 2.6s var(--ease) infinite; }
@keyframes nudge {
  0%, 74%, 100% { transform: translateY(0) rotate(0); }
  80% { transform: translateY(-4px) rotate(-5deg); }
  87% { transform: translateY(-1px) rotate(4deg); }
}

/* ==========================================================================
   Windows. Frame first, then the caption, then the client furniture.
   ========================================================================== */

.windows { position: absolute; inset: 0; z-index: 10; pointer-events: none; }

.win {
  position: absolute; pointer-events: auto;
  display: flex; flex-direction: column;
  min-width: 260px; min-height: 160px;
  /* The frame IS the caption colour continued down the sides, which is what
     makes a Luna window look moulded rather than outlined. */
  padding: 0 var(--frame) var(--frame);
  background: linear-gradient(180deg, var(--frame-1), var(--frame-2));
  border: 1px solid var(--cap-edge);
  border-radius: var(--radius) var(--radius) 0 0;
  box-shadow: var(--shadow);
  animation: winIn .16s var(--pop);
}
@keyframes winIn { from { opacity: 0; transform: scale(.96) translateY(6px); } }
.win:not(.is-on) { background: linear-gradient(180deg, var(--cap-off-2), var(--cap-off-4)); }
.win.is-min { display: none; }
.win.is-max {
  left: 0 !important; top: 0 !important;
  width: 100% !important; height: calc(100% - var(--task)) !important;
  border-radius: 0; border-left: 0; border-right: 0;
  padding-left: 0; padding-right: 0; padding-bottom: 0;
}
.win.is-max .title { border-radius: 0; }

/* --------------------------------------------------------------- caption */

.title {
  flex: none; height: var(--cap);
  display: flex; align-items: center; gap: 5px;
  margin: 0 calc(-1 * var(--frame));
  padding: 0 3px 0 6px;
  border-radius: var(--radius) var(--radius) 0 0;
  /* Four bands: the bright lip, the body, the shadow, the base. */
  background:
    linear-gradient(180deg,
      var(--cap-gloss) 0 8%,
      transparent 9% 46%,
      rgba(0,0,0,.06) 47% 100%),
    linear-gradient(180deg,
      var(--cap-1) 0%, var(--cap-2) 22%, var(--cap-2) 52%,
      var(--cap-3) 70%, var(--cap-4) 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.55),
    inset 1px 0 0 rgba(255,255,255,.22),
    inset -1px 0 0 rgba(255,255,255,.22);
  color: var(--cap-ink);
  font: 700 13px/1 var(--ui);
  text-shadow: 1px 1px 2px var(--cap-shadow);
  user-select: none; touch-action: none;
}
.win:not(.is-on) .title {
  background:
    linear-gradient(180deg, rgba(255,255,255,.35) 0 8%, transparent 9% 46%, rgba(0,0,0,.05) 47%),
    linear-gradient(180deg, var(--cap-off-1) 0%, var(--cap-off-2) 22%, var(--cap-off-2) 52%,
      var(--cap-off-3) 70%, var(--cap-off-4) 100%);
  color: var(--cap-off-ink);
}
.title-ico { width: 16px; height: 16px; flex: none; margin-left: 1px; }
.title-txt { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The three caption buttons. Close is wider, as it is on the real thing. */
.tbtns { display: flex; gap: 2px; flex: none; }
.tbtn {
  width: 22px; height: 22px; border-radius: 3px;
  border: 1px solid rgba(255,255,255,.75);
  background:
    linear-gradient(180deg, rgba(255,255,255,.62) 0 42%, rgba(255,255,255,.06) 43%, rgba(0,0,0,.18));
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.2), 0 1px 1px rgba(0,0,0,.25);
  display: grid; place-content: center;
  color: var(--cap-ink);
  position: relative;
}
.tbtn:hover { filter: brightness(1.25) saturate(1.1); }
.tbtn:active { filter: brightness(.8); box-shadow: inset 1px 1px 3px rgba(0,0,0,.4); }
.tbtn-x { width: 30px; background-color: rgba(255,80,40,.55); }
.win:not(.is-on) .tbtn { opacity: .82; }

/* Glyphs drawn in CSS so they sit on the pixel grid like bitmaps. */
.tbtn i { display: block; background: currentColor; }
.tbtn-min i { width: 8px; height: 3px; margin-top: 6px; }
.tbtn-max i {
  width: 11px; height: 10px; background: none;
  border: 1px solid currentColor; border-top-width: 3px;
}
.tbtn-restore i {
  width: 9px; height: 8px; background: none;
  border: 1px solid currentColor; border-top-width: 2px;
  box-shadow: 3px -3px 0 -1px var(--cap-ink), 3px -3px 0 0 currentColor;
  margin: 3px 0 0 -3px;
}
.tbtn-x i {
  width: 11px; height: 11px; background: none;
  color: inherit; position: relative;
}
.tbtn-x i::before, .tbtn-x i::after {
  content: ""; position: absolute; left: 0; top: 4px; width: 11px; height: 3px;
  background: currentColor;
}
.tbtn-x i::before { transform: rotate(45deg); }
.tbtn-x i::after { transform: rotate(-45deg); }

/* ----------------------------------------------------------- client area */

.client {
  flex: 1; min-height: 0;
  display: flex; flex-direction: column;
  background: var(--face);
  border: 1px solid var(--cap-edge);
  border-top: 0;
  overflow: hidden;
}
.win.is-max .client { border-left: 0; border-right: 0; border-bottom: 0; }

/* Menu bar --------------------------------------------------------------- */
.menubar {
  flex: none; height: var(--menubar);
  display: flex; align-items: stretch;
  background: linear-gradient(180deg, var(--face-hi), var(--face));
  border-bottom: 1px solid var(--face-lo);
  user-select: none;
}
.menubar > button {
  padding: 0 7px; font-size: 13px; color: var(--ink);
  display: flex; align-items: center;
}
.menubar > button u { text-decoration: none; }
body[data-alt="1"] .menubar > button u { text-decoration: underline; }
.menubar > button:hover,
.menubar > button[aria-expanded="true"] { background: var(--sel); color: var(--sel-ink); }

/* Toolbar ---------------------------------------------------------------- */
.toolbar {
  flex: none; min-height: var(--toolbar);
  display: flex; align-items: center; gap: 1px; padding: 2px 4px;
  background: linear-gradient(180deg, var(--face-hi) 0 40%, var(--face) 100%);
  border-bottom: 1px solid var(--face-lo);
  user-select: none;
  /* The gripper: two vertical rules at the left, as every XP band had. */
  padding-left: 9px; position: relative;
}
.toolbar::before {
  content: ""; position: absolute; left: 3px; top: 4px; bottom: 4px; width: 3px;
  border-left: 1px solid var(--face-hi); border-right: 1px solid var(--face-lo);
}
.tool {
  height: 30px; display: flex; align-items: center; gap: 5px; padding: 0 9px;
  border: 1px solid transparent; border-radius: 3px;
  font-size: 13px; color: var(--ink); white-space: nowrap;
}
.tool canvas { width: 20px; height: 20px; flex: none; }
.tool:hover:not([disabled]) {
  border-color: var(--sel); background: color-mix(in srgb, var(--sel) 18%, var(--face-hi));
}
.tool:active:not([disabled]) { background: color-mix(in srgb, var(--sel) 32%, var(--face)); }
.tool[disabled] { color: var(--ink-off); }
.tool[disabled] canvas { opacity: .4; filter: grayscale(1); }
/* L'ONGLET ENFONCÉ. Explorer marquait ainsi le volet ouvert ; Community s'en sert
   pour dire lequel de ses deux onglets on regarde. */
.tool[aria-pressed="true"] {
  background: linear-gradient(180deg, rgba(0,0,0,.10), rgba(0,0,0,.04));
  border-color: var(--sel-edge, rgba(0,0,0,.28));
  box-shadow: inset 0 1px 2px rgba(0,0,0,.22);
}

/* ==========================================================================
   LE MUR — LE FACEBOOK DE 2007, DANS UNE FENÊTRE DE WINDOWS XP

   La structure est celle de 2007, littéralement : une boîte de publication en
   haut, puis des messages avec la photo à gauche, le nom en gras, et la ligne
   « Like · Comment · Share ». Les couleurs, elles, ne sont PAS celles de Facebook :
   ce sont celles du chrome du site, qui est déjà le bleu de Luna. C'est le geste
   habituel de ce projet — la structure d'une chose réelle, la palette d'une autre.

   Rien ne bouge ici. Pas une animation, pas un badge qui monte, pas un compteur
   qui s'anime. Un fil est exactement l'endroit où on a envie d'en mettre.
   ========================================================================== */
.wall { padding: 0 0 18px; }

/* La boîte de publication. En 2007 c'était « What's on your mind? ». */
.wall-say {
  margin: 0 0 14px;
  padding: 12px 14px 11px;
  background: linear-gradient(180deg, #f2f6fb, #e6edf6);
  border-bottom: 1px solid var(--line, rgba(0, 0, 0, .16));
}
.wall-say-h { display: flex; gap: 10px; align-items: flex-start; }
.wall-say-h canvas { width: 32px; height: 32px; flex: none; }
.wall-say-h b { display: block; font-size: 14px; color: #1c4f86; }
.wall-say-h span { display: block; font-size: 12.5px; color: var(--ink-dim); margin-top: 2px; }
.wall-say-b { display: flex; gap: 8px; margin: 10px 0 0 42px; }
.wall-note {
  margin: 8px 0 0 42px; font-size: 11.5px; line-height: 1.45; color: var(--ink-2);
}

/* Un message. Photo à gauche, tout le reste à droite : c'est la forme de 2007 et
   elle tient encore parce qu'elle se lit en diagonale. */
.post {
  display: grid; grid-template-columns: 40px minmax(0, 1fr); gap: 11px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--line, rgba(0, 0, 0, .10));
}
.post:hover { background: rgba(0, 0, 0, .022); }
.post-pic, .post-c-pic { width: 40px; }
.post-pic img, .post-c-pic img {
  width: 100%; height: auto; display: block;
  border: 1px solid rgba(0, 0, 0, .3);
  /* Carrée et bordée, comme en 2007. Les photos rondes sont venues bien après. */
  border-radius: 1px;
}
.post-c-pic { width: 28px; }

.post-body { min-width: 0; }
.post-head { margin: 0; font-size: 13px; color: var(--ink-dim); }
.post-head b { color: #1c4f86; font-size: 13.5px; }
.post-cat {
  margin-left: 7px; font-size: 11px; color: var(--ink-2);
  border: 1px solid var(--line, rgba(0,0,0,.18)); border-radius: 3px; padding: 1px 5px;
}
.post-title { margin: 4px 0 0; font-size: 13.5px; font-weight: 700; }
.post-text {
  margin: 3px 0 0; font-size: 13px; line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 5; -webkit-box-orient: vertical; overflow: hidden;
}

/* La ligne d'actions. En 2007 c'étaient des liens bleus séparés par des points —
   pas des boutons — et c'est ce qui rend la ligne légère. */
.post-foot { margin: 7px 0 0; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.post-act {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: 12px/1.4 var(--ui); color: #1c4f86; text-decoration: none;
}
.post-act:hover { text-decoration: underline; }
.post-dot { color: var(--ink-2); font-size: 12px; }
.post-when { margin-left: 6px; font-size: 11.5px; color: var(--ink-2); }
.post-report {
  margin-left: auto; background: none; border: 0; padding: 0; cursor: pointer;
  font: 11.5px/1.4 var(--ui); color: var(--ink-2); text-decoration: underline;
}
.post-report:hover { color: #a4302a; }

/* Les commentaires, en retrait et sur un fond légèrement plus froid — exactement
   le traitement de 2007, qui disait « ceci est une réponse » sans un mot. */
.post-comments {
  margin: 9px 0 0; padding: 8px 10px;
  background: rgba(28, 79, 134, .05);
  border-left: 2px solid rgba(28, 79, 134, .22);
  display: grid; gap: 8px;
}
.post-c { display: grid; grid-template-columns: 28px minmax(0, 1fr); gap: 8px; align-items: start; }
.post-c-t { margin: 0; font-size: 12.5px; line-height: 1.45; }
.post-c-t b { color: #1c4f86; }

@media (max-width: 720px) {
  .wall-say-b, .wall-note { margin-left: 0; }
  .post { grid-template-columns: 32px minmax(0, 1fr); }
  .post-pic { width: 32px; }
}

/* ==========================================================================
   LE FEED : UNE LIGNE PAR MACHINE

   C'est une LISTE DE FICHIERS, pas un fil de réseau social — c'est le vocabulaire
   de ce système d'exploitation, et une liste se lit vite. Rien n'y bouge : aucune
   animation, aucun compteur qui s'anime, aucune pastille qui clignote. La règle
   qui a fait planter le Mac deux fois vaut ici plus qu'ailleurs, parce qu'un feed
   est exactement l'endroit où on a envie de mettre des choses qui respirent.
   ========================================================================== */
.feed-row {
  display: grid;
  grid-template-columns: 40px minmax(0, 1fr) auto auto;
  gap: 12px; align-items: start;
  padding: 11px 14px;
  border-bottom: 1px solid var(--line, rgba(0, 0, 0, .12));
}
.feed-row:hover { background: rgba(0, 0, 0, .035); }
.feed-row > canvas { width: 40px; height: 40px; }

.feed-mid { min-width: 0; }
.feed-h { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.feed-h b { font-size: 13.5px; }
/* Le nom d'extension est le petit, le nom du modèle est le grand — décision du
   25 août, la même que dans la rangée de choix de New pack. */
.feed-set { font-size: 11.5px; color: var(--ink-2); }
.feed-by { font-size: 11.5px; color: var(--ink-2); font-style: italic; }
.feed-what {
  margin: 3px 0 0; font-size: 12.5px; line-height: 1.45; color: var(--ink-dim);
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
.feed-state { margin: 5px 0 0; font-size: 11.5px; color: var(--ink-2); }
.feed-state[data-on="yes"] { color: #2c7a3f; font-weight: 700; }
.feed-state[data-on="no"] { color: #a4302a; }

/* Les votes. Deux triangles et un nombre : pas de barre, pas de score, pas de
   médaille — « No rarity » vaut ici aussi. Un compte est un fait sur des gens,
   pas une note sur un objet. */
.feed-votes { display: grid; gap: 2px; justify-items: center; }
.feed-vote {
  display: flex; align-items: center; gap: 5px;
  background: none; border: 1px solid transparent; border-radius: 3px;
  padding: 2px 6px; cursor: pointer;
  font: 11.5px/1 var(--ui); color: var(--ink-2);
}
.feed-vote:hover:not(:disabled) { background: rgba(0, 0, 0, .06); border-color: rgba(0, 0, 0, .14); }
.feed-vote:disabled { cursor: default; opacity: .5; }
.feed-vote i {
  width: 0; height: 0; display: block;
  border-left: 5px solid transparent; border-right: 5px solid transparent;
}
.feed-vote[data-dir="up"] i { border-bottom: 7px solid #2c7a3f; }
.feed-vote[data-dir="down"] i { border-top: 7px solid #a4302a; }

.feed-acts { display: grid; gap: 5px; justify-items: stretch; min-width: 132px; }
.feed-acts .btn { width: 100%; }
.feed-have {
  font: 700 11.5px/1.4 var(--ui); color: #2c7a3f; text-align: center; padding: 4px 0;
}

@media (max-width: 720px) {
  /* Sur un téléphone la ligne se replie : la machine, puis ses actions dessous.
     Quatre colonnes sur 375 px donneraient quatre colonnes illisibles. */
  .feed-row { grid-template-columns: 40px minmax(0, 1fr); }
  .feed-votes { grid-column: 2; grid-auto-flow: column; justify-content: start; gap: 10px; }
  .feed-acts { grid-column: 2; }
}

/* L'écran d'accueil de l'onglet « Everybody's », qui est celui que tout le monde
   verra en premier et longtemps, puisque le catalogue part vide. */
.feed-empty { padding: 18px 22px; max-width: 62ch; }
.feed-empty h3 { margin: 0 0 8px; font-size: 15px; }
.feed-empty p { margin: 0 0 12px; font-size: 13px; line-height: 1.5; color: var(--ink-dim); }
/* Le `dl` du site aligne ses valeurs à DROITE : c'est juste dans un volet étroit
   de 130 px, où la colonne de gauche et la colonne de droite se touchent presque.
   Ici la fenêtre fait 460 px de large et le même style envoie chaque réponse se
   coller au bord opposé de sa question, illisible. Aligné à gauche, et les deux
   colonnes se suivent. */
.feed-empty .dl { margin-bottom: 14px; }
.feed-empty .dl dd { text-align: left; }
.feed-empty .dl dt { color: var(--ink-dim); }

.tool-sep { width: 1px; align-self: stretch; margin: 3px 3px;
  border-left: 1px solid var(--face-lo); border-right: 1px solid var(--face-hi); }
.tool-caret { width: 5px; height: 5px; margin-left: 1px;
  background: currentColor; clip-path: polygon(0 0, 100% 0, 50% 100%); }

/* Address bar ------------------------------------------------------------ */
.addrbar {
  flex: none; display: flex; align-items: center; gap: 5px; padding: 3px 4px;
  background: linear-gradient(180deg, var(--face-hi) 0 40%, var(--face));
  border-bottom: 1px solid var(--face-lo);
  font-size: 13px; color: var(--ink-dim);
  padding-left: 9px; position: relative;
}
.addrbar::before {
  content: ""; position: absolute; left: 3px; top: 4px; bottom: 4px; width: 3px;
  border-left: 1px solid var(--face-hi); border-right: 1px solid var(--face-lo);
}
.addr {
  flex: 1; min-width: 0; height: var(--addr);
  display: flex; align-items: center; gap: 5px; padding: 0 2px 0 3px;
  background: var(--field);
  border: 1px solid var(--field-edge);
  box-shadow: inset 1px 1px 2px rgba(0,0,0,.14);
}
.addr canvas { width: 16px; height: 16px; flex: none; }
.addr span {
  flex: 1; min-width: 0; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Beats `.addr span`, which would otherwise stretch the drop-down arrow
   across the whole bar. */
.addr .addr-drop {
  width: 15px; height: calc(var(--addr) - 4px); flex: none;
  display: grid; place-content: center;
  background: linear-gradient(180deg, var(--face-hi), var(--face-lo));
  border: 1px solid var(--face-dk);
}
.addr-drop::after { content: ""; width: 7px; height: 4px; background: var(--ink);
  clip-path: polygon(0 0, 100% 0, 50% 100%); }

/* Status bar ------------------------------------------------------------- */
.statusbar {
  flex: none; height: var(--status);
  display: flex; align-items: stretch; gap: 2px; padding: 2px;
  background: var(--face);
  border-top: 1px solid var(--face-hi);
  font-size: 13px; color: var(--ink-dim);
  user-select: none;
}
.statusbar > span {
  display: flex; align-items: center; gap: 4px; padding: 0 6px;
  border: 1px solid var(--face-lo);
  border-right-color: var(--face-hi); border-bottom-color: var(--face-hi);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.statusbar > span:first-child { flex: 1; min-width: 0; }
.statusbar canvas { width: 14px; height: 14px; }
/* The ribbed resize corner. */
.status-grip {
  width: 16px; border: 0 !important; padding: 0 !important; flex: none;
  background:
    linear-gradient(135deg, transparent 0 42%, var(--face-lo) 42% 50%, var(--face-hi) 50% 58%, transparent 58%),
    linear-gradient(135deg, transparent 0 62%, var(--face-lo) 62% 70%, var(--face-hi) 70% 78%, transparent 78%),
    linear-gradient(135deg, transparent 0 82%, var(--face-lo) 82% 90%, var(--face-hi) 90% 98%, transparent 98%);
}

/* The body proper -------------------------------------------------------- */
.win-body { flex: 1; min-height: 0; overflow: auto; padding: 10px; background: var(--face); }
.win-body.flush { padding: 0; }

/* The Explorer split: task pane on the left, the view on the right. */
.split { flex: 1; min-height: 0; display: flex; }
.pane {
  flex: none; width: var(--pane-w); overflow: auto; padding: 8px 6px;
  /* `grid-auto-rows: min-content` : sans lui la grille comprimait chaque
     groupe a la hauteur disponible et `overflow: hidden` coupait le texte au
     milieu d'une phrase, sans barre de defilement pour le dire. */
  display: grid; grid-auto-rows: min-content; gap: 8px; align-content: start;
  background: linear-gradient(160deg, var(--pane-1), var(--pane-2));
  border-right: 1px solid var(--face-lo);
}
.view { flex: 1; min-width: 0; overflow: auto; background: var(--field); position: relative; }

/* A task pane group: rounded, header bar, chevron, list of links. */
.grp { border-radius: 4px; overflow: hidden; box-shadow: 0 1px 2px rgba(0,0,0,.12); }
.grp-h {
  width: 100%; display: flex; align-items: center; gap: 6px;
  padding: 6px 8px; text-align: left;
  background: linear-gradient(180deg, var(--pane-head-1), var(--pane-head-2));
  color: var(--pane-head-ink); font: 700 13px/1.3 var(--sys);
}
.grp-h span { flex: 1; }
.grp-h i {
  width: 15px; height: 15px; flex: none; border-radius: 50%;
  background: var(--pane-head-ink);
  position: relative;
}
.grp-h i::after {
  content: ""; position: absolute; left: 4px; top: 6px; width: 7px; height: 4px;
  background: var(--pane-head-1); clip-path: polygon(0 0, 100% 0, 50% 100%);
  transition: transform .15s;
}
.grp[data-open="0"] .grp-h i::after { transform: rotate(180deg); }
.grp[data-open="0"] .grp-b { display: none; }
.grp-b { padding: 6px 8px; background: var(--pane-body); display: grid; gap: 5px; }
.grp-b p { font-size: 13px; color: var(--ink-dim); line-height: 1.45; }
.grp-b b { color: var(--ink); }
.lnk {
  /* Faisait 19px de haut : la plus petite cible du site, et il y en a une
     dizaine dans chaque volet. */
  display: flex; align-items: center; gap: 7px; text-align: left;
  min-height: 26px; padding: 2px 2px;
  font-size: 13px; color: var(--pane-link);
}
.lnk canvas { width: 18px; height: 18px; flex: none; }
.lnk:hover span { text-decoration: underline; }
.lnk[disabled] { color: var(--ink-off); }
.lnk[disabled] canvas { filter: grayscale(1); opacity: .5; }
.dl { display: grid; grid-template-columns: auto 1fr; gap: 2px 8px; font-size: 13px; }
.dl dt { color: var(--ink-dim); }
.dl dd { margin: 0; color: var(--ink); text-align: right; font-variant-numeric: tabular-nums; }

/* ==========================================================================
   Dropdown menus: the menu bar's, and the right-click one. Same construction
   as the real thing — a white sheet with a grey icon gutter down the left.
   ========================================================================== */

.pop, .ctx {
  position: fixed; z-index: 220; min-width: 150px; padding: 2px;
  background: var(--face);
  border: 1px solid var(--face-dk);
  box-shadow: 2px 2px 3px rgba(0,0,0,.35);
  font-size: 13px; user-select: none;
}
.pop::before, .ctx::before {
  content: ""; position: absolute; left: 2px; top: 2px; bottom: 2px; width: 22px;
  background: linear-gradient(90deg, var(--face-lo), var(--face));
  opacity: .5;
}
.pop button, .ctx button {
  position: relative; z-index: 1;
  width: 100%; display: flex; align-items: center; gap: 8px;
  height: 21px; padding: 0 18px 0 6px; text-align: left; color: var(--ink);
}
.pop button:hover:not([disabled]), .ctx button:hover:not([disabled]) {
  background: var(--sel); color: var(--sel-ink);
}
.pop button[disabled], .ctx button[disabled] { color: var(--ink-off); }
.pop button span, .ctx button span { flex: 1; white-space: nowrap; }
.pop button u, .ctx button u { text-decoration: none; }
body[data-alt="1"] .pop button u, body[data-alt="1"] .ctx button u { text-decoration: underline; }
.pop button i, .ctx button i {
  width: 16px; height: 16px; flex: none; font-style: normal;
  display: grid; place-content: center;
}
.pop button canvas, .ctx button canvas { width: 16px; height: 16px; }
.pop button kbd, .ctx button kbd {
  font: inherit; color: var(--ink-dim); padding-left: 14px;
}
.pop button:hover kbd, .ctx button:hover kbd { color: var(--sel-ink); }
.pop hr, .ctx hr {
  position: relative; z-index: 1;
  margin: 3px 2px 3px 26px; border: 0;
  border-top: 1px solid var(--face-lo); border-bottom: 1px solid var(--face-hi);
}
.pop button.on i::after {
  content: ""; width: 9px; height: 5px; margin-top: -3px;
  border: 2px solid currentColor; border-top: 0; border-right: 0;
  transform: rotate(-45deg);
}
.ctx button b { font-weight: 700; }

/* ==========================================================================
   Controls: buttons, fields, group boxes, progress. All bevel, no shadow.
   ========================================================================== */

.btn {
  /* 82 x 23 en 11px, c'est la mesure d'origine de Windows — et c'est
     exactement le bouton que l'audit avait pointé du doigt. Tout le reste a
     grossi d'un cran le 24 août ; celui-ci avait été oublié. */
  min-width: 96px; height: 28px; padding: 0 14px;
  border: 1px solid var(--field-edge); border-radius: 3px;
  background: linear-gradient(180deg, var(--face-hi) 0 46%, var(--face) 47%, var(--face-lo) 100%);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.65);
  font: 400 13px/1 var(--sys); color: var(--ink);
  transition: box-shadow .1s, filter .1s;
}
.btn:hover:not([disabled]) {
  border-color: var(--hot);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.7), 0 0 0 1px var(--hot), 0 0 6px var(--hot);
}
.btn:active:not([disabled]) {
  background: linear-gradient(180deg, var(--face-lo), var(--face));
  box-shadow: inset 1px 1px 3px rgba(0,0,0,.28);
}
.btn[disabled] { color: var(--ink-off); text-shadow: 1px 1px 0 var(--face-hi); }
/* The default button carries the extra ring XP gave it. */
/* The default button borrows the start button's colour, not the caption's:
   on a face this saturated, a pink button on a pink window is unreadable. */
.btn-go {
  font-weight: 700;
  border-color: var(--start-4);
  background: linear-gradient(180deg,
    var(--start-1) 0 46%, var(--start-2) 47%, var(--start-3) 100%);
  color: var(--start-ink);
  text-shadow: 0 1px 0 rgba(255,255,255,.45);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.5);
}
.btn-go:hover:not([disabled]) {
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.6), 0 0 0 1px var(--hot), 0 0 7px var(--hot);
}
.btn-go[disabled] { filter: saturate(.15) brightness(1.12); color: var(--ink-off); text-shadow: none; }

.field {
  width: 100%; height: 26px; padding: 0 6px;
  border: 1px solid var(--field-edge);
  background: var(--field); color: var(--ink);
  box-shadow: inset 1px 1px 2px rgba(0,0,0,.16);
  outline: none; border-radius: 0;
}
select.field { padding: 0 2px; height: 21px; }
.field:focus { box-shadow: inset 1px 1px 2px rgba(0,0,0,.16), 0 0 0 1px var(--sel); }
label.row { display: flex; align-items: center; gap: 6px; font-size: 13px; }

/* The group box: a sunken rule with the legend punched through it. */
.group { border: 1px solid var(--face-lo); box-shadow: inset -1px -1px 0 var(--face-hi);
  padding: 12px 10px 10px; margin-top: 6px; position: relative; }
.group > legend, .group > .legend {
  position: absolute; top: -7px; left: 8px; padding: 0 4px;
  background: var(--face); font-size: 13px; color: var(--ink); font-weight: 700;
}

/* The blocked progress bar, which is the detail everyone remembers. */
.prog {
  height: 17px; padding: 2px;
  border: 1px solid var(--face-dk);
  box-shadow: inset 1px 1px 2px rgba(0,0,0,.2);
  background: var(--field);
  overflow: hidden;
}
.prog i {
  display: block; height: 100%; width: 0;
  background:
    repeating-linear-gradient(90deg,
      color-mix(in srgb, var(--sel) 88%, #fff) 0 9px,
      transparent 9px 11px);
  transition: width .45s var(--ease);
}

/* ==========================================================================
   Taskbar
   ========================================================================== */

.taskbar {
  position: absolute; left: 0; right: 0; bottom: 0; height: var(--task); z-index: 60;
  display: flex; align-items: stretch; gap: 3px;
  background:
    linear-gradient(180deg,
      var(--bar-edge) 0 1px,
      rgba(255,255,255,.5) 1px 2px,
      transparent 3px 100%),
    linear-gradient(180deg, var(--bar-1) 0%, var(--bar-2) 9%, var(--bar-2) 82%, var(--bar-3) 90%, var(--bar-4) 100%);
  box-shadow: 0 -2px 8px rgba(0,0,0,.3);
  padding-bottom: env(safe-area-inset-bottom, 0);
  user-select: none;
}

.start {
  flex: none; height: 100%;
  display: flex; align-items: center; gap: 6px; padding: 0 20px 0 8px;
  border-radius: 0 11px 11px 0;
  background:
    linear-gradient(180deg, rgba(255,255,255,.55) 0 6%, rgba(255,255,255,.12) 7% 44%,
      transparent 45% 100%),
    radial-gradient(120% 80% at 50% 130%, rgba(255,255,255,.4), transparent 60%),
    linear-gradient(180deg, var(--start-1) 0%, var(--start-2) 24%, var(--start-2) 58%,
      var(--start-3) 80%, var(--start-4) 100%);
  color: #fff; font: 700 16px/1 var(--ui); font-style: italic;
  text-shadow: 1px 1px 2px rgba(0,0,0,.55);
  box-shadow: inset -1px 0 0 rgba(0,0,0,.2);
  transition: filter .12s;
}
.start:hover { filter: brightness(1.12); }
.start[aria-expanded="true"] { filter: brightness(.85); box-shadow: inset 2px 2px 5px rgba(0,0,0,.35); }
.start-hand { width: 20px; height: 20px; margin-top: -1px; }

/* Quick launch, with the two grip rules on either side. */
.quick {
  flex: none; display: flex; align-items: center; gap: 2px;
  padding: 0 5px; margin: 4px 0;
  border-left: 1px solid rgba(0,0,0,.25); border-right: 1px solid rgba(0,0,0,.25);
  box-shadow: inset 1px 0 0 rgba(255,255,255,.25), inset -1px 0 0 rgba(255,255,255,.25);
}
.quick button { width: 22px; height: 22px; display: grid; place-content: center; border-radius: 2px; }
.quick button canvas { width: 16px; height: 16px; }
.quick button:hover { background: rgba(255,255,255,.25); box-shadow: inset 0 0 0 1px rgba(255,255,255,.4); }

.tasks { flex: 1; min-width: 0; display: flex; align-items: center; gap: 3px; padding: 0 3px; overflow: hidden; }
.task {
  /* La barre fait 48px depuis le 24 août ; le bouton était resté à 24, soit
     la moitié de vide autour. Sur Windows il en remplit les trois quarts. */
  flex: 0 1 172px; min-width: 44px; height: 34px;
  display: flex; align-items: center; gap: 5px; padding: 0 7px;
  border-radius: 3px;
  background: linear-gradient(180deg, rgba(255,255,255,.34) 0 45%, rgba(255,255,255,.06) 46%, rgba(0,0,0,.14));
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.22);
  color: var(--bar-ink); font-size: 13px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.task span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.task:hover { filter: brightness(1.2); }
.task.is-on {
  background: linear-gradient(180deg, rgba(0,0,0,.3), rgba(0,0,0,.1));
  box-shadow: inset 1px 1px 3px rgba(0,0,0,.45);
  font-weight: 700;
}
.task.blink { animation: blink 1s steps(1) 6; }
@keyframes blink { 50% { background: var(--start-2); color: var(--start-ink); } }
.task canvas { width: 16px; height: 16px; flex: none; }

.tray {
  flex: none; display: flex; align-items: center; gap: 7px;
  padding: 0 9px 0 8px; margin: 3px 2px 3px 0;
  border-radius: 2px;
  background: linear-gradient(180deg, var(--tray-1), var(--tray-2));
  box-shadow: inset 1px 1px 3px rgba(0,0,0,.45), inset -1px -1px 0 rgba(255,255,255,.12);
  color: var(--bar-ink); font-size: 13px;
}
.tray-ico { width: 16px; height: 16px; display: block; }
.tray-ico canvas { width: 16px; height: 16px; }
.tray-clock { font-variant-numeric: tabular-nums; letter-spacing: .02em; }

/* ==========================================================================
   Start menu: two columns, the header, the dark footer. As it shipped.
   ========================================================================== */

.menu {
  position: absolute; left: 2px; bottom: calc(var(--task) - 1px); z-index: 70;
  width: 404px;
  border: 1px solid var(--cap-edge);
  border-radius: 9px 9px 0 0;
  background: var(--field);
  box-shadow: 0 -6px 30px rgba(0,0,0,.5), inset 0 0 0 1px rgba(255,255,255,.5);
  overflow: hidden;
  animation: menuIn .13s var(--pop);
  transform-origin: 0 100%;
  font-size: 13px;
}
@keyframes menuIn { from { opacity: 0; transform: scaleY(.88) translateY(14px); } }

/* The header: the caption gradient again, the user picture in its frame, and
   the two-tone rule the era ran underneath it. */
.menu-head {
  display: flex; align-items: center; gap: 10px; padding: 7px 12px;
  background:
    linear-gradient(180deg, var(--cap-gloss) 0 8%, transparent 9% 46%, rgba(0,0,0,.06) 47%),
    linear-gradient(180deg, var(--cap-1), var(--cap-2) 30%, var(--cap-3) 75%, var(--cap-4));
  color: var(--cap-ink); font: 700 15px/1 var(--ui);
  text-shadow: 1px 1px 2px var(--cap-shadow);
  box-shadow: inset 0 -2px 0 rgba(255,255,255,.35);
  border-bottom: 1px solid var(--cap-edge);
}
.menu-pic {
  width: 46px; height: 46px; padding: 2px; border-radius: 4px;
  background: #fff;
  box-shadow: 0 1px 4px rgba(0,0,0,.45), inset 0 0 0 1px var(--cap-edge);
}
.menu-pic canvas { width: 100%; height: 100%; border-radius: 2px; }

.menu-cols { display: grid; grid-template-columns: 214px 1fr; align-items: stretch; }
.menu-left { padding: 5px 0 0; background: var(--field); display: flex; flex-direction: column; }
.menu-right {
  padding: 5px 5px;
  background: linear-gradient(180deg, var(--pane-1), var(--pane-2));
  border-left: 1px solid var(--face-lo);
  box-shadow: inset 1px 0 0 var(--face-hi);
}
.menu-sep { margin: 5px 9px; border: 0; border-top: 1px solid var(--face-lo); border-bottom: 1px solid var(--face-hi); }
.menu-cap {
  padding: 0 11px 3px; font-size: 12px; color: var(--ink-off);
  text-transform: uppercase; letter-spacing: .06em;
}
.menu-list { padding: 0 5px; }
.menu-item {
  width: 100%; display: flex; align-items: center; gap: 9px;
  padding: 5px 6px; border-radius: 3px; text-align: left; color: var(--ink);
  line-height: 1.25;
}
.menu-item canvas { width: 26px; height: 26px; flex: none; }
.menu-item b { font-weight: 700; }
.menu-item span { flex: 1; min-width: 0; overflow: hidden; }
.menu-item span b, .menu-item span i {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.menu-item i { font-style: normal; font-size: 12px; color: var(--ink-dim); }
.menu-item:hover { background: linear-gradient(180deg, var(--sel), var(--sel-2)); color: var(--sel-ink); }
.menu-item:hover i { color: var(--sel-ink); }
.menu-right .menu-item { padding: 4px 6px; font-weight: 700; color: var(--pane-head-ink); }
.menu-right .menu-item canvas { width: 22px; height: 22px; }
.menu-right .menu-item:hover { color: var(--sel-ink); }
.menu-recent .menu-item b { font-weight: 400; }

/* "All expansions", the bar the era kept at the foot of the left column. */
.menu-all { margin-top: auto; padding: 4px 5px 5px; }
.menu-all::before {
  content: ""; display: block; margin: 0 4px 4px;
  border-top: 1px solid var(--face-lo); border-bottom: 1px solid var(--face-hi);
}
.menu-all-btn {
  width: 100%; display: flex; align-items: center; gap: 7px;
  padding: 5px 6px; border-radius: 3px; font-weight: 700; color: var(--ink);
}
.menu-all-btn span { flex: 1; text-align: left; }
.menu-all-btn i {
  width: 17px; height: 17px; border-radius: 3px; flex: none;
  background: linear-gradient(180deg, var(--start-1), var(--start-3));
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.5);
  position: relative;
}
.menu-all-btn i::after {
  content: ""; position: absolute; left: 6px; top: 5px;
  width: 5px; height: 7px; background: #fff;
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}
.menu-all-btn:hover { background: linear-gradient(180deg, var(--sel), var(--sel-2)); color: var(--sel-ink); }

.menu-foot {
  display: flex; justify-content: flex-end; gap: 6px; padding: 7px 12px;
  background:
    linear-gradient(180deg, rgba(255,255,255,.28) 0 2px, transparent 3px),
    linear-gradient(180deg, var(--cap-2), var(--cap-3) 55%, var(--cap-4));
  border-top: 1px solid var(--cap-edge);
}
.menu-foot-btn {
  display: flex; align-items: center; gap: 7px;
  color: var(--cap-ink); font-size: 13px; text-shadow: 1px 1px 1px var(--cap-shadow);
  padding: 4px 8px; border-radius: 3px;
}
.menu-foot-btn:hover { background: rgba(255,255,255,.22); box-shadow: inset 0 0 0 1px rgba(255,255,255,.3); }
.mfi { width: 22px; height: 22px; display: block; }
.mfi canvas { width: 22px; height: 22px; }

/* ==========================================================================
   The balloon tip. It grows out of the tray, it has a tail, it has an X.
   ========================================================================== */

.balloon {
  position: absolute; right: 8px; bottom: calc(var(--task) + 14px); z-index: 150;
  /* Plus large et plus aérée : elle porte maintenant l'objet dont elle parle, et
     une notification qu'on doit plisser les yeux pour lire n'est pas une
     notification, c'est un bruit. */
  width: 296px; padding: 12px 14px 13px;
  display: grid; grid-template-columns: auto 1fr; gap: 2px 11px; align-items: start;
  background: linear-gradient(180deg, #ffffe1, #fff9c4);
  border: 1px solid var(--face-dk); border-radius: 5px;
  box-shadow: 2px 2px 6px rgba(0,0,0,.35);
  color: #000;
  animation: balloonIn .2s var(--pop);
}
@keyframes balloonIn { from { opacity: 0; transform: translateY(10px) scale(.9); } }
.balloon::after {
  content: ""; position: absolute; right: 26px; bottom: -9px;
  width: 16px; height: 10px;
  background: #fff9c4;
  border-right: 1px solid var(--face-dk); border-bottom: 1px solid var(--face-dk);
  clip-path: polygon(0 0, 100% 0, 100% 100%);
}
.balloon-h { font-weight: 700; font-size: 13px; margin-bottom: 4px; padding-right: 16px; }
.balloon-p { font-size: 13px; line-height: 1.5; }
.balloon-x {
  position: absolute; right: 5px; top: 4px; width: 15px; height: 15px;
  display: grid; place-content: center; font-size: 12px; color: #444;
  border: 1px solid transparent; border-radius: 2px;
}
.balloon-x:hover { border-color: var(--face-dk); background: var(--face); }

/* ==========================================================================
   The copy dialog. A pack is a file, so making one is a file being copied.
   ========================================================================== */

.copy-stage {
  display: grid; grid-template-columns: 1fr 1.4fr 1fr; align-items: center;
  gap: 6px; padding: 4px 0 10px;
}
.copy-node { display: grid; justify-items: center; gap: 4px; }
.copy-node canvas { width: 40px; height: 40px; }
.copy-node span { font-size: 12.5px; color: var(--ink-dim); }
.copy-air { position: relative; height: 44px; }
.paper {
  position: absolute; left: 0; top: 50%; width: 13px; height: 16px;
  background: linear-gradient(150deg, #fff 0 58%, #cfd4dc 58%);
  border: 1px solid #8f97a3;
  animation: fly .7s var(--ease) forwards;
}
@keyframes fly {
  0%   { transform: translate(0, -50%) rotate(0); opacity: 0; }
  12%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { transform: translate(calc(100% + 110px), calc(-50% + var(--dy))) rotate(var(--rot)); opacity: 0; }
}


.copy-foot { display: flex; justify-content: flex-end; margin-top: 12px; }

/* ==========================================================================
   The pack window. Six pictures in a folder, and everything you can do with
   one of them sits in its own corner rather than three menus away.
   ========================================================================== */

.view.thumbs {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: 10px; padding: 12px; align-content: start;
}
.view.tiles {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 8px; padding: 10px; align-content: start;
}
.view.film { display: flex; flex-direction: column; padding: 0; }

/* One picture in the folder. */
.shot { position: relative; }
.shot-frame {
  position: relative; aspect-ratio: 1; overflow: hidden;
  background: var(--face);
  border: 1px solid var(--face-dk);
  box-shadow: inset -1px -1px 0 var(--face-hi), 0 1px 3px rgba(0,0,0,.2);
  padding: 3px;
  transition: transform .16s var(--ease), box-shadow .16s;
}
.shot:hover .shot-frame { transform: translateY(-2px); box-shadow: 0 6px 14px -4px rgba(0,0,0,.45); }
.shot.sel .shot-frame { border-color: var(--sel); box-shadow: 0 0 0 2px var(--sel), 0 6px 14px -4px rgba(0,0,0,.45); }
.shot-frame img { width: 100%; height: 100%; object-fit: cover; display: block; }
.shot-cen { padding: 12px; font-size: 12px; color: var(--ink-dim); text-align: center; }

/* The corner toolbar. It appears on the picture, not beside it. */
.shot-actions {
  position: absolute; right: 6px; top: 6px;
  display: flex; flex-direction: column; gap: 4px;
  opacity: 0; transform: translateX(6px);
  transition: opacity .14s, transform .14s var(--pop);
  pointer-events: none;
}
.shot:hover .shot-actions, .shot.sel .shot-actions { opacity: 1; transform: none; pointer-events: auto; }
.shot-btn {
  width: 26px; height: 26px; display: grid; place-content: center;
  border-radius: 4px;
  background: rgba(16,10,22,.62);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.28), 0 2px 5px rgba(0,0,0,.4);
  backdrop-filter: blur(3px);
  transition: background-color .12s, transform .12s;
}
.shot-btn canvas { width: 16px; height: 16px; }
.shot-btn:hover { background: rgba(16,10,22,.85); transform: scale(1.08); }
.shot-btn:active { transform: scale(.94); }

/* A kept picture wears its heart in the opposite corner, always visible. */
.shot-liked {
  position: absolute; left: 6px; top: 6px; width: 20px; height: 20px;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.6));
  animation: likePop .3s var(--pop);
}
@keyframes likePop { from { transform: scale(.2) rotate(-25deg); } }
.shot-liked canvas { width: 20px; height: 20px; }

.shot-name {
  display: block; margin-top: 4px; padding: 1px 4px; font-size: 12.5px;
  text-align: center; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.shot.sel .shot-name { background: var(--sel); color: var(--sel-ink); }

.view.tiles .shot { display: grid; grid-template-columns: 84px 1fr; gap: 10px; align-items: center; padding: 5px; }
.view.tiles .shot-name { text-align: left; margin: 0; }

/* Filmstrip: one big picture, the rest in a strip beneath. */
.film-main { flex: 1; min-height: 0; display: grid; place-items: center; padding: 12px; background: var(--face); position: relative; }
.film-main img { max-width: 100%; max-height: 100%; object-fit: contain;
  border: 1px solid var(--face-dk); box-shadow: 0 3px 12px rgba(0,0,0,.35); }
.film-strip { flex: none; height: 96px; display: flex; gap: 8px; padding: 8px; overflow-x: auto;
  background: var(--pane-body); border-top: 1px solid var(--face-lo); }
.film-strip .shot { width: 76px; flex: none; }

.open-foot {
  flex: none; display: flex; align-items: center; gap: 10px;
  padding: 6px 8px; background: var(--face); border-top: 1px solid var(--face-lo);
}
.open-info { flex: 1; min-width: 0; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ============================================================ collection */

.coll { padding: 2px; }
.coll-empty { padding: 26px 14px; text-align: center; color: var(--ink-dim); font-size: 13px; }
.coll-row {
  width: 100%; display: flex; align-items: center; gap: 9px;
  padding: 4px 7px; text-align: left;
}
.coll-row:hover { background: var(--sel); color: var(--sel-ink); }
.coll-row:hover .coll-txt span, .coll-row:hover .coll-rank { color: var(--sel-ink) !important; }
.coll-row canvas { width: 32px; height: 32px; flex: none; }
.coll-txt { flex: 1; min-width: 0; display: grid; }
.coll-txt b { font-weight: 700; font-size: 13px; }
.coll-txt span { font-size: 13px; color: var(--ink-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.coll-rank { font-size: 12.5px; flex: none; }
/* The column header strip of a details view. */
.cols {
  position: sticky; top: 0; z-index: 1; display: flex; font-size: 13px;
  background: linear-gradient(180deg, var(--face-hi), var(--face));
  border-bottom: 1px solid var(--face-lo);
}
.cols span { padding: 3px 7px; border-right: 1px solid var(--face-lo); }
.cols span:first-child { flex: 1; }

/* ================================================================= about */

.ab-h { font: 700 11px/1.3 var(--sys); margin: 12px 0 3px; color: var(--pane-link); }
.ab-p { font-size: 13px; line-height: 1.6; }
.ab-sig { margin-top: 16px; font-size: 13px; color: var(--ink-dim); }

/* ===================================================== display properties */

.disp-mon {
  margin: 0 auto 10px; width: 210px; padding: 12px 12px 26px;
  border-radius: 12px 12px 6px 6px;
  background: linear-gradient(180deg, #e8e4d8, #b9b3a3);
  box-shadow: inset 0 1px 0 #fff, 0 2px 5px rgba(0,0,0,.3);
  position: relative;
}
.disp-mon::after {
  content: ""; position: absolute; left: 50%; bottom: 7px; transform: translateX(-50%);
  width: 56px; height: 5px; border-radius: 2px; background: #8f8a7c;
}
.disp-screen { aspect-ratio: 4/3; border: 1px solid #55524a; overflow: hidden; position: relative; }
.disp-screen canvas { width: 100%; height: 100%; }
.disp-screen .mini-bar {
  position: absolute; left: 0; right: 0; bottom: 0; height: 9px;
  background: linear-gradient(180deg, var(--bar-1), var(--bar-2) 40%, var(--bar-4));
  display: flex; align-items: center;
}
.disp-screen .mini-start {
  width: 26px; height: 100%; border-radius: 0 3px 3px 0;
  background: linear-gradient(180deg, var(--start-1), var(--start-3));
}
.disp-screen .mini-win {
  position: absolute; left: 18%; top: 20%; width: 56%; height: 44%;
  background: var(--face); border: 1px solid var(--cap-edge);
}
.disp-screen .mini-cap { height: 7px; background: linear-gradient(180deg, var(--cap-1), var(--cap-3)); }
.themes { display: grid; gap: 4px; }
.theme-row {
  display: flex; align-items: center; gap: 8px; padding: 4px 6px; text-align: left; font-size: 13px;
}
.theme-row:hover { background: var(--sel); color: var(--sel-ink); }
.theme-row[aria-checked="true"] { background: var(--sel); color: var(--sel-ink); }
.swatch { display: flex; flex: none; border: 1px solid rgba(0,0,0,.4); }
.swatch i { width: 12px; height: 12px; }

/* ==================================================================== tabs */

.tabs { display: flex; gap: 2px; padding: 0 4px; margin-bottom: -1px; position: relative; z-index: 1; }
.tab {
  padding: 3px 12px; font-size: 13px;
  border: 1px solid var(--face-lo); border-bottom: 0;
  border-radius: 3px 3px 0 0;
  background: linear-gradient(180deg, var(--face), var(--face-lo));
  color: var(--ink-dim);
}
.tab[aria-selected="true"] {
  background: var(--face); color: var(--ink); font-weight: 700;
  padding-bottom: 4px; margin-top: -2px;
}
.tabpage { border: 1px solid var(--face-lo); box-shadow: inset -1px -1px 0 var(--face-hi); padding: 12px; }

/* ==========================================================================
   Choosing an expansion. The expansions on a shelf, because that is the choice
   being made: not a model string, a set.
   ========================================================================== */



.shelf-pack canvas {
  width: 64px; transition: transform .18s var(--pop), filter .18s;
  filter: drop-shadow(1px 2px 3px rgba(0,0,0,.35));
}
.shelf-pack:hover canvas { transform: translateY(-4px) rotate(-2deg); }
.shelf-pack[aria-pressed="true"] {
  border-color: var(--sel); background: color-mix(in srgb, var(--sel) 22%, #fff);
}
.shelf-pack[aria-pressed="true"] canvas { transform: translateY(-6px); filter: drop-shadow(0 6px 8px rgba(0,0,0,.4)); }
.shelf-pack.out canvas { filter: grayscale(1) opacity(.45); }
.shelf-pack em { font-style: normal; font-size: 11px; color: var(--ink-off); }
.shelf-note { font-size: 13px; line-height: 1.5; min-height: 4.2em; color: var(--ink-dim); }
.shelf-note b { color: var(--ink); }
.shelf-note i { font-style: normal; color: var(--pane-link); }

.dlg-foot { display: flex; gap: 8px; justify-content: flex-end; margin-top: 12px; }

/* The slots in the copy dialog: one fills per finished picture. */
.slots { display: flex; gap: 4px; margin: 0 0 8px; }
.slots i {
  flex: 1; height: 7px; background: var(--field);
  border: 1px solid var(--face-dk); box-shadow: inset 1px 1px 1px rgba(0,0,0,.18);
}
.slots i.on {
  background: var(--sel); border-color: var(--sel-2);
  animation: slotIn .3s var(--pop);
}
@keyframes slotIn { from { transform: scaleY(2.4); } }

/* ------------------------------------------- light getting out of a sealed pack
 *
 * Grains of light that have already escaped the seam, twinkling above it. They are
 * elements rather than paint because they move, and repainting a canvas to animate
 * anything is what crashed this laptop.
 *
 * Two per folder, each with its own delay and duration from an inline --d/--t, so
 * no two folders on the desktop twinkle together. Transform and opacity only.
 */
.seal-spark {
  position: absolute; pointer-events: none;
  width: var(--s, 9px); height: var(--s, 9px);
  left: var(--x, 50%); top: var(--y, 10%);
  margin: calc(var(--s, 9px) / -2) 0 0 calc(var(--s, 9px) / -2);
  opacity: 0;
  background:
    linear-gradient(0deg, transparent 42%, #fffbe8 49%, #fffbe8 51%, transparent 58%),
    linear-gradient(90deg, transparent 46%, rgba(255,251,232,.85) 49.4%,
                    rgba(255,251,232,.85) 50.6%, transparent 54%),
    radial-gradient(closest-side, #fffdf4 0 10%, rgba(255,248,214,.5) 26%, transparent 62%);
  animation: twinkle var(--t, 3.4s) ease-in-out var(--d, 0s) infinite;
}
@keyframes twinkle {
  0%, 62%, 100% { opacity: 0; transform: scale(.35) rotate(0deg); }
  12% { opacity: .95; transform: scale(1) rotate(18deg); }
  30% { opacity: .55; transform: scale(.82) rotate(34deg); }
  46% { opacity: .12; transform: scale(.5) rotate(48deg); }
}
/* When the folder lurches, the light out of it jumps with it. */
.icon[data-restless] .seal-spark { animation-duration: .9s; animation-delay: 0s; }

@media (prefers-reduced-motion: reduce) {
  .seal-spark { animation: none; opacity: .75; transform: scale(.8); }
}

/* ------------------------------------------------- a sealed folder is restless
 *
 * Something is alive in there and it wants out. Four different movements, dealt
 * out at uneven intervals to one folder at a time, so the desktop is never doing
 * the same thing twice and never doing it in unison.
 *
 * INTERMITTENT ON PURPOSE, and not only for the look of it. A permanent animation
 * on every sealed icon is the shape of the thing that crashed this laptop: the
 * class goes on for well under a second and then comes off, so between shivers
 * the desktop is doing nothing at all. It is also more alive that way — something
 * moving inside a box does not hum, it lurches.
 */
.icon[data-restless] canvas { will-change: auto; }
.icon[data-restless="a"] canvas { animation: restlessA .62s cubic-bezier(.3,.9,.3,1); }
.icon[data-restless="b"] canvas { animation: restlessB .78s ease-in-out; }
.icon[data-restless="c"] canvas { animation: restlessC .46s cubic-bezier(.2,1.4,.4,1); }
.icon[data-restless="d"] canvas { animation: restlessD .94s ease-in-out; }

/* a shiver */
@keyframes restlessA {
  0%, 100% { transform: translate(0,0) rotate(0deg); }
  12% { transform: translate(-1.2px,.4px) rotate(-1.4deg); }
  28% { transform: translate(1.1px,-.3px) rotate(1.2deg); }
  44% { transform: translate(-.8px,.5px) rotate(-.9deg); }
  62% { transform: translate(.6px,-.2px) rotate(.6deg); }
  80% { transform: translate(-.3px,.2px) rotate(-.3deg); }
}
/* a slow breath, as if something inside turned over */
@keyframes restlessB {
  0%, 100% { transform: scale(1,1) translateY(0); }
  40% { transform: scale(1.05,.95) translateY(1.5px); }
  70% { transform: scale(.98,1.03) translateY(-1px); }
}
/* one hard kick */
@keyframes restlessC {
  0%, 100% { transform: translate(0,0) rotate(0deg); }
  22% { transform: translate(0,-3.5px) rotate(2.4deg); }
  55% { transform: translate(.8px,1.2px) rotate(-1.2deg); }
}
/* a long lean, like something pressing against the inside of one wall */
@keyframes restlessD {
  0%, 100% { transform: rotate(0deg) translateX(0); }
  35% { transform: rotate(2.2deg) translateX(1.6px); }
  55% { transform: rotate(1.9deg) translateX(1.4px); }
  85% { transform: rotate(-.7deg) translateX(-.4px); }
}

@media (prefers-reduced-motion: reduce) {
  .icon[data-restless] canvas { animation: none; }
}

/* ==========================================================================
   Opening a pack. THE COUNTER-CURRENT.

   Everything above this line is flat, dense, faithful 2001 chrome. Everything
   below it is the opposite, on purpose: the entire screen goes to flat black,
   one folder stands in the middle of it, and the only other thing in the room
   is light. That contrast is the product, not a decoration on it.

   The folder travels in from its own icon, is knocked open by hand, throws a
   fan of light, hands you one card at a time, swallows them all back and then
   goes and files itself. See the long note in js/main.js for the order.

   PERFORMANCE. This site crashed a laptop twice. So the IDLE STATE HOLDS STILL:
   a folder waiting to be knocked is not animating, and a card being looked at
   is not animating. Everything loud is a one-shot element that CSS animates and
   JS then removes, and every animation here moves `transform` or `opacity` and
   nothing else. No masks, no animated filters — both were measured at several
   milliseconds a frame and both are why this ran at 33fps once.
   ========================================================================== */

.rite {
  /* The sizes the whole ceremony is laid out from. EVERY ONE OF THEM IS BOUNDED BY
     THE WIDTH AS WELL AS THE HEIGHT. They were height-only, which is fine on a
     desktop and wrong on a phone held upright: six 74px slots with 10px between
     them need 494px and Tristan's screen is 375 wide, so the row of six ran off
     both sides. The row's own arithmetic is written out here — six slots and five
     gaps inside the screen, less a margin — so it can never overflow again
     whatever the phone. */
  --rgap: 10px;                      /* between two of the six */
  --fs: min(268px, 34vh, 74vw);      /* the folder */
  --cs: min(440px, 58vh, 86vw);      /* the picture, out and being looked at */
  --slot: min(74px, 9vh, calc((100vw - 24px - 5 * var(--rgap)) / 6));
  position: fixed; inset: 0; z-index: 9000;
  display: grid; place-items: center;
  isolation: isolate;
  /* NO BACKGROUND OF ITS OWN. Every bit of the darkness comes from `.veil`, which
     fades. This carried `background: #000` with only the arriving state overriding
     it — so the instant the phase changed the parent went solid black in ONE frame
     and the veil's fade was invisible on the way in; and on the way out the veil
     faded to nothing behind a parent that was still opaque, so the black did not
     lift at all, it vanished when the element was removed.

     Both of Tristan's reports — "the fade does not appear smoothly" and "the folder
     goes to its place before the fade has gone" — were this one declaration. */
  background: transparent;
  overflow: hidden;
}
/* NOTHING ANSWERS, AND NOTHING UNDERNEATH ANSWERS EITHER.
   `pointer-events: none` on the whole overlay let every tap during the three and a
   half seconds of the intro fall THROUGH to the desktop icons behind it — so on a
   phone, where a single tap opens an icon, spamming a folder started a second and a
   third ceremony on top of the first. The overlay keeps eating the taps; only its
   contents stop answering, and the handlers all check the phase anyway. */
.rite[data-phase="arriving"] > * { pointer-events: none; }

/* THE DARK IS A FADE. Everywhere, evenly.
 *
 * It was a circular hole closing in around the folder, which I argued for on the
 * grounds that a flat fade reads as a black rectangle appearing. Tristan looked at
 * it and disagreed: the round shape is the thing you notice, and noticing the
 * mechanism is worse than noticing nothing. So the veil is a plain sheet of black
 * that fades up, and the constant round pool of light around the folder is gone
 * with it.
 */
.veil {
  position: absolute; inset: 0;
  pointer-events: none; z-index: 0;
  background: #000;
  opacity: 0;
  /* Its own compositing layer: fading a promoted layer is the GPU blending two
     textures, where fading an unpromoted one makes the browser repaint everything
     underneath it on every frame. */
  will-change: opacity;
  transition: opacity 1.5s cubic-bezier(.35,.25,.25,1);
}
.rite[data-phase="charge"] .veil, .rite[data-phase="reveal"] .veil,
.rite[data-phase="look"] .veil, .rite[data-phase="done"] .veil,
.rite[data-phase="closing"] .veil { opacity: 1; }
/* And it lifts the same way, evenly. */
/* It leaves more slowly than it came. A room coming back into the light does it
   gradually, and this is the last thing you see of the ceremony. */
.rite[data-phase="lifting"] .veil {
  opacity: 0;
  transition: opacity 1.75s cubic-bezier(.35,.4,.3,1);
}
/* The row and the hint go with it, unhurried. */
/* La rangée part AVANT le reste, pas avec lui. Le noir met 1,75 s à se lever et la
   rangée mettait 1,1 s : elle restait donc suspendue dans une pièce déjà éclairée,
   ce qui la faisait voir comme un oubli. Elle s'en va en 0,4 s, dès la première
   image de la levée. */
.rite[data-phase="lifting"] .rite-row,
.rite[data-phase="lifting"] .rite-hint {
  opacity: 0; transition: opacity .4s ease-in;
}

/* And nothing in the ceremony answers a click once it is on its way out. */
.rite[data-phase="lifting"] { pointer-events: none; }

/* The room used to be a second full-screen layer here (`.rite::before`) with
   three radial gradients, faded in on its own. MEASURED: it cost about 14ms a
   frame while the room was going dark — 10 of that just for fading it. Its glow is
   baked into the veil's own gradient now, so ONE layer does both jobs: the dark
   closing in, and the pool of light the folder stands in. */

/* --------------------------------------------------------------- the type */



.rite-hint {
  position: absolute; left: 0; right: 0;
  bottom: calc(4.5vh + var(--slot) + 22px); text-align: center;
  z-index: 3;
  font-size: 13px; letter-spacing: .18em; text-transform: uppercase;
  color: rgba(255,255,255,.72);
  opacity: 0; transition: opacity .5s;
  pointer-events: none;
}
.rite-hint.on { opacity: 1; }


/* ------------------------------------------------------------- the folder */

/* A real box, centred on the middle of the screen. It was a zero-size point
   with place-items:center, which does nothing: the folder grew down and to the
   right of the point instead of standing on it. */
.rite-mid {
  position: absolute; left: 50%; top: 46%;
  width: var(--fs); height: var(--fs);
  transform: translate(-50%, -50%);
  z-index: 2;                        /* above the veil, with everything in it */
}

.folder {
  position: absolute; inset: 0; z-index: 2;
  cursor: pointer;
  /* --squeeze runs 0 to 1 across the knocks. It squashes down and spreads
     sideways and leans back, which is what "about to give" looks like on
     something made of card with a thing straining inside it. */
  transform:
    scaleX(calc(1 + var(--squeeze, 0) * .14))
    scaleY(calc(1 - var(--squeeze, 0) * .19))
    translateY(calc(var(--squeeze, 0) * 6px));
  transition: transform .26s cubic-bezier(.24,1.5,.36,1);
  /* No drop shadow. On a black room it only ever showed as a grey smear under the
     folder, and on the way in and out it dragged along behind it. */
}
/* And it trembles, harder with every knock. A held object under load is never
   quite still, and this is the whole difference between "it responded" and "it
   is about to give". */
.folder[data-strain="1"] { animation: tremble 1.1s ease-in-out infinite; }
.folder[data-strain="2"] { animation: tremble .52s ease-in-out infinite; }
@keyframes tremble {
  0%, 100% { translate: 0 0; rotate: 0deg; }
  25%  { translate: -.7px .4px; rotate: -.22deg; }
  50%  { translate: .5px -.3px; rotate: .16deg; }
  75%  { translate: .6px .5px; rotate: .2deg; }
}

/* Light leaking out of the seam along the top edge. It is the only clue that
   what is inside is brighter than what is outside, and it grows with the
   squeeze. */
.folder-seam {
  position: absolute; left: 8%; right: 8%; top: 35.5%; height: 3px;
  z-index: 3; pointer-events: none;
  border-radius: 50%;
  background: linear-gradient(90deg,
    transparent, rgba(255,255,255,.5) 18%, #fff 50%, rgba(255,255,255,.5) 82%, transparent);
  box-shadow: 0 0 14px 3px rgba(255,240,190,.75);
  opacity: calc(var(--squeeze, 0) * var(--squeeze, 0) * 1.15);
  transform: scaleX(calc(.5 + var(--squeeze, 0) * .5)) scaleY(calc(.6 + var(--squeeze, 0) * 1.4));
  transition: opacity .26s, transform .26s cubic-bezier(.24,1.5,.36,1);
}
.folder canvas { width: 100%; height: 100%; display: block; position: relative; z-index: 1; }
.folder:focus-visible { outline: none; }

/* There was a contact shadow here, an ellipse under the folder that spread as it
   was squashed. With no floor and no light in the room it read as a grey smudge,
   and Tristan asked for the folder to carry no shadow at either end. */

/* One knock. */
/* A real recoil: it takes the hit, and what comes back up overshoots. The old
   one moved five pixels and read as a nudge. */
.folder.knock { animation: knock .46s cubic-bezier(.18,.9,.28,1); }
@keyframes knock {
  0%   { translate: 0 0; scale: 1 1; }
  16%  { translate: 0 9px; scale: 1.05 .93; }
  44%  { translate: 0 -6px; scale: .97 1.04; }
  72%  { translate: 0 2px; scale: 1.01 .99; }
  100% { translate: 0 0; scale: 1 1; }
}
/* The one where it gives. */
.folder.blow { animation: blow .8s cubic-bezier(.16,.9,.3,1); }
@keyframes blow {
  0%   { translate: 0 0; rotate: 0deg; }
  10%  { translate: 0 12px; rotate: -1.5deg; }
  30%  { translate: 0 -26px; rotate: 2deg; }
  60%  { translate: 0 6px; rotate: -.8deg; }
  100% { translate: 0 0; rotate: 0deg; }
}
/* And the one where it swallows a picture back. */
.folder.gulp { animation: gulp .42s cubic-bezier(.2,1.5,.4,1); }
@keyframes gulp {
  0%   { scale: 1 1; }
  40%  { scale: 1.1 .88; }
  100% { scale: 1 1; }
}

/* ---------------------------------------------------------- the fan of light
 *
 * A rainbow off the top of a dune. It comes UP out of the folder and it goes one
 * way, because that is what the reference shows and what a thing bursting out of
 * a container does.
 *
 * A version of this sprayed light in all 360 degrees, taking the three later
 * references as directions when they were references for the QUALITY of light,
 * and it read as a colour wheel — it went everywhere and meant nothing. What the
 * references actually teach is three things a fan needs: it fades as it travels,
 * it has a blurred bed under the hard streaks, and the source is white enough to
 * lose detail in.
 *
 * THIS BLOCK USES MASKS AND A BLUR, which nothing else on this site may. Masks
 * were measured at 6.4ms a frame, which is why the folder does without them. The
 * difference: this lives for two seconds, once, on a click — a bounded cost on
 * the loudest moment in the product rather than a permanent one while nothing is
 * happening.
 */

.fanwrap {
  position: absolute; left: 50%; top: 42%;
  width: 0; height: 0;
  pointer-events: none; z-index: 1;
  transition: opacity .8s;
}
.fanwrap.dim { opacity: .38; }        /* it settles back behind the card */
.fanwrap.out { opacity: 0; }

/* The fan itself: one bitmap, apex at its own bottom centre, scaled up out of
   the folder. No gradient, no mask, no blur — see js/icons.js fanArt(). */
canvas.fan {
  /* Laid out at roughly the bitmap's own pixel size and then SCALED UP by the
     animation. Shown at 126vmax the browser rasterised it at about 2500 device
     pixels a side — a 25MB texture in one go, which was the 283ms hitch that
     survived moving the fan into a canvas. Scaling a layer that is already
     rasterised costs the compositor nothing. */
  /* The apex is at 80% of the bitmap's height, not on its bottom edge — see
     fanArt() — so the rotation origin and the placement follow it, and the empty
     bottom fifth hangs below the folder's mouth. */
  position: absolute; left: 0; bottom: -13.6vmax;
  width: 68vmax; height: 68vmax;
  margin-left: -34vmax;
  transform-origin: 50% 80%;
  /* IT STARTS BIG AND IT STRETCHES. Growing one bitmap from a thirtieth of its
     size to nearly twice is a zoom, and a zoom is exactly what makes light read as
     a photograph of light: the dust and the glints balloon along with the beams,
     which no real light does. It now begins at something like half size — light
     arrives, it does not grow into the room — and the beams LENGTHEN about twice as
     much as they widen, so the grain travels outward along them instead. Same one
     already-rasterised layer, same cost to the compositor. */
  transform: rotate(var(--tilt, 0deg)) scale(.42, .26);
  opacity: 0;
  display: block;
  animation: fanUp 2.5s cubic-bezier(.16,.62,.2,1) forwards;
}
/* IT SPENDS ITSELF. The light rises out of the folder, keeps travelling, thins
   as it goes and is gone — one continuous movement that ends at nothing. It used
   to stop at 95% opacity and sit there behind the picture until a later
   cross-fade removed it, and a cross-fade reads as a layer being switched off
   rather than as light leaving. */
@keyframes fanUp {
  0%   { transform: rotate(var(--tilt, 0deg)) scale(.42, .26); opacity: 0; }
  8%   { opacity: 1; }
  46%  { opacity: 1; }
  100% { transform: rotate(calc(var(--tilt, 0deg) + 5deg)) scale(1.22, 2.08); opacity: 0; }
}

/* AND A SECOND PATTERN MOVING THROUGH THE FIRST.
   One bitmap, however it is animated, is a still picture in motion — the eye locks
   onto its detail and follows it. Two different fans crossing each other at
   different rates never resolve into one image, which is the whole difference
   between "light" and "a picture of light". It costs nothing to draw: the intro
   already draws six fans and only one is used per burst, so this borrows one of the
   others that is not on screen. Dimmer, wider, slower, and leaning the other way,
   so it reads as depth behind the beams rather than as a duplicate. */
canvas.fan.ghost {
  opacity: 0;
  /* NO BLEND MODE. It was `mix-blend-mode: screen`, which is the honest way to add
     two lights together — and it is also the one thing on this site that forces the
     compositor to keep the backdrop behind the layer available on every frame. The
     burst is already the worst moment in the product at about 28fps, and the room is
     BLACK: over black, screen and ordinary compositing differ by almost nothing,
     because the thing underneath is zero. So the effect is kept and the cost is not.

     Both fans are drawn additively INSIDE their own bitmaps (see fanArt), which is
     where the adding that matters actually happens. */
  animation: fanGhost 2.5s cubic-bezier(.2,.58,.24,1) forwards;
}
@keyframes fanGhost {
  0%   { transform: rotate(calc(var(--tilt, 0deg) * -.6)) scale(.56, .3); opacity: 0; }
  10%  { opacity: .5; }
  42%  { opacity: .42; }
  100% { transform: rotate(calc(var(--tilt, 0deg) * -.6 - 9deg)) scale(1.5, 1.72); opacity: 0; }
}

/* The anamorphic streak, the white core and the four-point glint used to be
   three CSS layers here, each with a radial mask. They are painted into the
   bitmap now — see fanArt() — because a mask cost measurable milliseconds every
   frame and these were duplicating what the bitmap already had.  */

/* THE ROUND HALF OF THE SAME EXPLOSION. Shells of light leaving the same point
   the fan leaves, on the same beat, so the two read as one event rather than as
   two effects that happened to fire together. Soft — a stroked circle reads as a
   diagram — and they die out on their own like the fan does. */
/* A shell of light leaving the mouth. Soft — a stroked circle reads as a diagram —
   with a bright leading edge, and every one of its numbers comes in from JS so no
   two bursts ring the same way. It dies out on its own like the fan does. */
.fan-ring {
  position: absolute; left: 0; bottom: 0;
  width: 22vmax; height: 22vmax; margin-left: -11vmax; margin-bottom: -11vmax;
  border-radius: 50%;
  opacity: 0;
  background: radial-gradient(closest-side,
    transparent 0 calc(var(--thick, 62%) - 16%),
    color-mix(in srgb, var(--c1, #fff) 40%, transparent) calc(var(--thick, 62%) - 4%),
    rgba(255,255,255,.9) var(--thick, 62%),
    color-mix(in srgb, var(--c2, #fff) 45%, transparent) calc(var(--thick, 62%) + 6%),
    transparent 100%);
  animation: fanRing var(--t, 2.2s) cubic-bezier(.12,.68,.2,1) var(--d, 0s) forwards;
}
@keyframes fanRing {
  0%   { transform: scale(.06) scaleY(var(--squash, 1)); opacity: 0; }
  7%   { opacity: var(--lit, .9); }
  100% { transform: scale(var(--to, 7)) scaleY(var(--squash, 1)); opacity: 0; }
}

/* ------------------------------------------------------------- the card out
 *
 * It comes out of the folder FACE DOWN, settles in front of it, and waits. One
 * click turns it over, the next files it away. Nothing is on a timer, so a
 * picture can be looked at for as long as anybody wants.
 */

.rite-card {
  position: absolute; left: 50%; top: 50%;
  width: var(--cs); aspect-ratio: 1;
  margin-left: calc(var(--cs) / -2);
  margin-top: calc(var(--cs) / -2);
  transform: translateY(-7vh);
  perspective: 1000px;
  z-index: 5; cursor: pointer;
}
/* It comes out of the TOP EDGE of the folder, tiny, drifts up to the middle
   still small, and only then opens out to full size. One long smooth curve and
   no overshoot: the card is rising, not springing. */
/* Out of the top edge of the folder, in three movements you can actually watch:
   it JUMPS clear of the mouth, hangs for a moment, then travels to the middle
   and opens out to full size. One long curve, no overshoot, no bounce: the card
   is rising, not springing. */
/* Out of the folder and up to the middle. The jump is a lift rather than a leap,
   the growth is spread across the WHOLE flight instead of being saved for the last
   two thirds, and the easing is gentle at both ends. It used to hang almost still
   at a fifth of its size and then rush outward, which is the opposite of fluid. */
/* OUT OF THE FOLDER: TWO MOVEMENTS ON TWO CURVES, EACH ONE A SINGLE
   INTERPOLATION. The rise belongs to the card, the turn and the growth belong to
   the face inside it, and because the two run on different easings the picture
   does not appear to inflate on rails.

   This was five keyframes of translate-and-scale under one easing. The file
   already records the lesson from the rotation — "stops laid over a curve is
   exactly how a rotation comes out looking stepped" — and the same thing was
   still being done to the position and the size. There is now no stop anywhere in
   either path, so there is nothing left to step. */
.rite-card.out { animation: cardRise 2.35s cubic-bezier(.2,.74,.24,1) both; }
@keyframes cardRise {
  0%   { transform: translateY(calc(var(--fs) * -.16)); opacity: 0; }
  6%   { opacity: 1; }
  100% { transform: translateY(-7vh); opacity: 1; }
}
.rite-card-in {
  position: absolute; inset: 0; transform-style: preserve-3d;
  transform: rotateY(0deg);
}
/* The turn AND the growth, together, on a curve that starts gently: the picture
   is already facing you well before it arrives, so there is nothing left to do
   when it gets there but look at it. */
.rite-card.out .rite-card-in {
  animation: cardTurnIn 2.35s cubic-bezier(.44,.06,.2,1) both;
}
@keyframes cardTurnIn {
  0%   { transform: rotateY(180deg) scale(.055); }
  100% { transform: rotateY(0deg) scale(1); }
}
.rite-card.turning { animation: cardTurn 1.05s cubic-bezier(.28,.7,.2,1); }
@keyframes cardTurn {
  0%   { translate: 0 0; scale: 1; }
  42%  { translate: 0 -26px; scale: 1.08; }
  100% { translate: 0 0; scale: 1; }
}
.rite-card-back, .rite-card-front {
  position: absolute; inset: 0; overflow: hidden; border-radius: 14px;
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
  box-shadow:
    0 26px 44px -14px rgba(0,0,0,.9),
    0 0 40px -6px color-mix(in srgb, var(--c1, #fff) 45%, transparent),
    inset 0 0 0 1px rgba(255,255,255,.3);
}
/* A plain back: dark card with a sheen, and nothing printed on it. It is on
   screen for under a second. */
/* Le dos est l'image, retournée. Il n'y a plus de dos dessiné : voir bigCard(). */
.rite-card-back { transform: rotateY(180deg); background: #0b0810; }
.rite-card-back img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
}
.rite-card-front { background: #0b0810; }
/* THE PICTURE, edge to edge. It used to sit in a drawn frame with a plate under
   it naming the expansion; now the picture is the whole card. */
.rite-card-front img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
}
.rite-cen {
  position: absolute; inset: 0;
  display: grid; place-items: center; padding: 14px;
  font-size: 13px; color: #b6adc2; text-align: center;
}
/* A sheen crosses the picture once, the moment it comes over. */
.rite-card[data-face="up"] .rite-card-front::after {
  content: ""; position: absolute; inset: -40%;
  background: linear-gradient(100deg,
    transparent 38%, rgba(255,255,255,.14) 45%,
    rgba(255,120,220,.45) 49%, rgba(255,255,255,.9) 51%,
    rgba(120,220,255,.45) 53%, rgba(255,255,255,.14) 58%, transparent 65%);
  pointer-events: none;
  animation: sheen .9s ease-out .1s both;
}
@keyframes sheen {
  0%   { transform: translateX(-70%); opacity: 0; }
  20%  { opacity: 1; }
  100% { transform: translateX(70%); opacity: 0; }
}




/* Filing one away: a single arc from the middle of the screen down into its
   slot. --fx/--fy/--fs are measured in JS and handed in, and the -7vh is the
   card's own resting offset, which has to stay in every keyframe or the card
   jumps before it moves. */
/* FILING ONE AWAY: A REAL ARC, MADE OF TWO STRAIGHT INTERPOLATIONS.
   The horizontal travel is the card's, decelerating; the fall and the shrink
   belong to the face inside it, accelerating. Two different easings on two axes
   is what an arc IS — and it needs no intermediate keyframe, which is what was
   making this one feel wrong. It had a wind-up at 22%, a waypoint at 64% whose
   size and position disagreed with each other, and a rotation on top, all under
   one easing: four things pulling at each other inside a single second.

   --fx/--fy/--fs come from JS. The card's resting -7vh stays in both stops of the
   horizontal path or it jumps 7vh before it moves. The scale sits on the INNER
   element so the measured --fy is not divided by it: `translateY() scale()` scales
   first and then travels, so the numbers land exactly on the slot. */
.rite-card.filing {
  animation: fileX 1s cubic-bezier(.36,.04,.5,.72) forwards;
  z-index: 6;
  /* AND THE 3D IS SWITCHED OFF FOR THE FLIGHT. The card carries perspective so it
     can turn over on the way out of the folder; the turn is long finished by now,
     and leaving it on means the inner element's travel is drawn through a
     projection, which lands the picture a few pixels off the slot it was measured
     against. MEASURED, after: the picture ends on the slot's centre to the pixel. */
  perspective: none;
}
@keyframes fileX {
  0%   { transform: translate(0, -7vh); }
  100% { transform: translate(var(--fx), -7vh); }
}
.rite-card.filing .rite-card-in {
  animation: fileY 1s cubic-bezier(.62,.02,.82,.62) forwards;
}
@keyframes fileY {
  0%   { transform: translateY(0) scale(1); }
  100% { transform: translateY(var(--fy)) scale(var(--fs)); }
}

/* --------------------------------------------------------- the row of six */

.rite-row {
  position: absolute; left: 0; right: 0; bottom: 4.5vh;
  display: flex; gap: var(--rgap); justify-content: center;
  flex-wrap: nowrap; padding: 0 12px;
  pointer-events: none;
  z-index: 3;                        /* the six are never under the dark */
  /* And they are not there at all until the first picture is out. Six empty boxes
     under an unopened folder is a diagram of what is about to happen. */
  opacity: 0; transform: translateY(10px);
  transition: opacity .7s, transform .7s cubic-bezier(.25,.7,.3,1);
}
.rite[data-row="1"] .rite-row { opacity: 1; transform: translateY(0); }

.rite-slot {
  position: relative;
  width: var(--slot); aspect-ratio: 1; flex: none; border-radius: 7px;
  background:
    linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.012));
  box-shadow:
    inset 0 0 0 1px rgba(255,255,255,.13),
    inset 0 1px 0 0 rgba(255,255,255,.1),
    0 6px 14px -8px #000;
  overflow: hidden;
  transition: box-shadow .3s, background .3s;
}
/* A waiting place says which one it is, faintly. */
.rite-slot::before {
  content: ""; position: absolute; inset: 26%;
  border-radius: 3px;
  border: 1px dashed rgba(255,255,255,.14);
  transition: opacity .3s;
}
.rite-slot.on::before { opacity: 0; }
.rite-slot.on {
  background: none;
  box-shadow:
    inset 0 0 0 1px rgba(255,255,255,.5),
    0 0 18px -3px color-mix(in srgb, var(--c1, #fff) 70%, transparent);
}
.rite-slot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rite-slot.landing { animation: landHit .5s cubic-bezier(.2,.9,.3,1); }
@keyframes landHit {
  0%   { transform: scale(1.02) translateY(-2px); }
  26%  { transform: scale(.88) translateY(4px); }   /* it takes the weight */
  58%  { transform: scale(1.06) translateY(-2px); }
  100% { transform: scale(1) translateY(0); }
}
.rite-slot.landing::after {
  content: ""; position: absolute; inset: -7px; border-radius: 11px;
  box-shadow: 0 0 18px 4px color-mix(in srgb, var(--c1, #fff) 80%, transparent);
  animation: landRing .42s ease-out forwards;
}
@keyframes landRing { 0% { opacity: 1; } 100% { opacity: 0; } }

/* A picture on its way back into the folder. */
.rite-fly {
  position: fixed; z-index: 6; pointer-events: none;
  border-radius: 6px; overflow: hidden;
  box-shadow:
    0 0 24px -4px color-mix(in srgb, var(--c1, #fff) 75%, transparent),
    inset 0 0 0 1px rgba(255,255,255,.4);
  /* The stages set their own transitions; this is only the resting state. */
}
.rite-fly img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ------------------------------------------------------------ the sparkle */

.sparks { position: absolute; left: 50%; top: 50%; pointer-events: none; z-index: 7; }
.sparks i {
  position: absolute; width: var(--s, 12px); height: var(--s, 12px);
  margin: calc(var(--s, 12px) / -2) 0 0 calc(var(--s, 12px) / -2);
  opacity: 0;
  background:
    linear-gradient(0deg, transparent 46%, #fff 49.5%, #fff 50.5%, transparent 54%),
    linear-gradient(90deg, transparent 46%, #fff 49.5%, #fff 50.5%, transparent 54%),
    radial-gradient(closest-side, #fff 0 8%, transparent 30%);
  animation: sparkOut var(--dur, .9s) ease-out var(--d, 0s) forwards;
}
@keyframes sparkOut {
  0%   { transform: translate(0,0) scale(.2) rotate(0deg); opacity: 0; }
  14%  { opacity: 1; }
  100% { transform: translate(var(--x), var(--y)) scale(1) rotate(var(--r, 90deg)); opacity: 0; }
}

.flare {
  position: absolute; left: 50%; top: 50%; width: 620px; height: 620px;
  margin: -310px 0 0 -310px; pointer-events: none; z-index: 1;
  background:
    radial-gradient(closest-side, rgba(255,255,255,.7), transparent 26%),
    conic-gradient(from 0deg,
      rgba(255,255,255,.8) 0 .6%, transparent 1.6% 24.4%,
      rgba(255,255,255,.8) 25% 25.6%, transparent 26.6% 49.4%,
      rgba(255,255,255,.8) 50% 50.6%, transparent 51.6% 74.4%,
      rgba(255,255,255,.8) 75% 75.6%, transparent 76.6% 100%);
  animation: flareOut 1.1s ease-out forwards;
}
@keyframes flareOut {
  0%   { transform: scale(.2) rotate(-14deg); opacity: 0; }
  16%  { opacity: 1; }
  100% { transform: scale(1.1) rotate(16deg); opacity: 0; }
}

.confetti { position: absolute; left: 50%; top: 50%; pointer-events: none; z-index: 7; }
.confetti i {
  position: absolute; width: 7px; height: 12px; opacity: 0; border-radius: 1px;
  animation: burst 1s var(--ease) forwards; animation-delay: var(--d);
}
@keyframes burst {
  0% { transform: translate(0,0) rotate(0); opacity: 1; }
  100% { transform: translate(var(--x), var(--y)) rotate(var(--r)); opacity: 0; }
}

/* Anyone who has asked their system not to animate things gets the same
   ceremony, without the motion. */
@media (prefers-reduced-motion: reduce) {
  .folder.knock, .folder.blow, .folder.gulp,
  .rite-card.out, .rite-card.turning { animation: none; }
  .fan, .fan-ring { animation: none; opacity: .55; transform: none; }
  .burst, .flash, .flare, .sparks i, .confetti i, .ring { display: none; }
  .rite-slot.landing { animation: none; }
}


/* --------------------------------------------------- one picture, one window
 *
 * The theme is old Windows and old Windows opens a window. This was a dark
 * lightbox with arrows for a while, which was out of the theme entirely: there
 * is no next-and-previous here, each picture is its own window, and two of them
 * open side by side if that is what you want.
 */
/* A flex child of `.client`, which is a column — NOT absolutely positioned. At
   inset:0 it covered the window's own menu bar and status bar, and the picture
   ended up sitting on top of the chrome with a black band where the menu was. */
.pic-view {
  /* The SAME interior as every other window on this site: the field colour from
     the one palette block, the same sunken well, the same 4px inset. It had an
     invented grey-blue gradient for a while, which made one window on the whole
     desktop look like it came from somewhere else. */
  position: relative;
  flex: 1 1 auto; min-height: 0; min-width: 0;
  display: grid; place-items: center;
  padding: 8px;
  background: var(--field);
  overflow: hidden;
}
.pic-view img {
  display: block;
  max-width: 100%; max-height: 100%;
  width: auto; height: auto;
  object-fit: contain;
  image-rendering: auto;          /* scaled UP: a photograph must not go blocky */
  /* The bevelled well a picture sits in, the same one the thumbnails use. */
  background: var(--face);
  padding: 3px;
  box-shadow:
    inset 0 0 0 1px var(--face-dk),
    0 1px 3px rgba(0,0,0,.28);
}
.pic-cen {
  padding: 22px; max-width: 300px; text-align: center;
  font-size: 13px; line-height: 1.5; color: var(--ink);
}


/* ---------------------------------------------------------- the New pack box */

.np {
  display: flex; flex-direction: column; gap: 10px;
  padding: 12px 14px 12px;
}
.np-head { display: flex; align-items: center; gap: 11px; }
.np-head canvas { width: 48px; height: 48px; display: block; flex: none; }
.np-head-txt { display: grid; gap: 2px; min-width: 0; }
.np-head-txt b { font-size: 15px; }
.np-head-txt span { font-size: 13px; color: var(--ink-2); }

.np-label {
  font-size: 13px; color: var(--ink-2);
  margin-bottom: -4px;
}
.np-field { font-size: 15px; padding: 5px 7px; }

/* Six things worth typing. A blank field is the hardest thing to answer, and
   these are suggestions rather than categories. */
/* Une seule ligne : une amorce, l'idée, et le bouton qui en tire une autre. */
.np-tries { display: flex; align-items: center; gap: 7px; flex-wrap: nowrap; min-height: 26px; }
.np-lead { font: 400 12px/1 var(--ui); color: var(--ink-dim); flex: none; }
.np-roll {
  flex: none; display: grid; place-items: center;
  width: 24px; height: 24px; padding: 0;
  background: var(--face-hi); border: 1px solid var(--face-lo);
  border-radius: 50%; cursor: pointer;
}
.np-roll:hover { background: #fff; border-color: var(--sel); }
.np-roll canvas { display: block; }
.np-try {
  font: 400 12.5px/1 var(--ui);
  padding: 6px 10px;
  flex: 1 1 auto; min-width: 0; text-align: left;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--link);
  background: var(--face-hi);
  border: 1px solid var(--face-lo);
  border-radius: 9px;
  cursor: pointer;
}
.np-try:hover { background: #fff; border-color: var(--face-dk); }

.np-who {
  display: grid; gap: 5px;
  padding: 8px 10px;
  background: var(--face-hi);
  border: 1px solid var(--face-lo);
  border-radius: 3px;
}
.np-line { font-size: 13px; line-height: 1.45; }
.np-line[data-off="slow"] b { color: #9a6408; }
.np-line[data-off="1"] b { color: #a4302a; }
.np-what { font-size: 13px; line-height: 1.45; color: var(--ink-2); }

/* LA LIGNE DE LA DÉMONSTRATION, dans New pack et pendant la visite seulement.
   Elle dit lequel des deux est en train de se produire — le paquet livré avec le
   site, ou une vraie génération d'une minute — et elle change en direct quand on
   modifie le prompt. La matière est celle des bulles d'info de Windows, déjà
   utilisée partout ailleurs pour « lis ceci, ce n'est pas une commande ». */
.np-demo {
  font-size: 12.5px; line-height: 1.45; color: #4a3f16;
  background: linear-gradient(180deg, #ffffe1, #fff8bf);
  border: 1px solid #ddcd72; border-radius: 4px;
  padding: 7px 9px; margin: 0;
}
.np-demo b { color: #6b4a00; }
.np-demo[data-on="1"] b { color: #1c4f8a; }

.np-picker { display: flex; gap: 6px; flex-wrap: wrap; }
.np-pick {
  display: grid; justify-items: center; gap: 3px;
  padding: 5px 7px; cursor: pointer;
  font-size: 12px; color: var(--ink);
  background: none; border: 1px solid transparent; border-radius: 3px;
}
.np-pick canvas { width: 32px; height: 32px; display: block; }
.np-pick[aria-pressed="true"] {
  background: var(--sel); color: var(--sel-ink); border-color: var(--sel-edge);
}

/* LE NOM DU MODÈLE EST LE GRAND, CE QUI LE DÉCRIT EST LE PETIT.
   C'était l'inverse — une pastille disait « First Light » et rien d'autre, donc
   quelqu'un qui arrivait ne savait pas ce qu'il choisissait. L'objet est le
   modèle ; le nom d'extension le décrit, et un mot qui décrit se met sous le mot
   qu'il décrit. */
.np-pick-n { font-size: 12px; line-height: 1.15; }
.np-pick-s {
  font-size: 10.5px; line-height: 1; color: var(--ink-2);
  letter-spacing: .02em;
}
.np-pick[aria-pressed="true"] .np-pick-s { color: var(--sel-ink); opacity: .85; }

/* Ce qui décrit un modèle, dans le volet d'un dossier ouvert : le nom
   d'extension, l'année et l'auteur, sous le nom du modèle. */
.pane-sub {
  font-size: 11.5px; line-height: 1.35; color: var(--ink-dim); opacity: .85;
  margin: 2px 0 6px;
}

/* COMBIEN DE GENS LA FONT TOURNER, SOUS CHAQUE PASTILLE.
   Les sept extensions sont en vente et elles ne sont pas égales : douze
   volontaires pour l'une, un seul pour une autre. Ce chiffre est relevé au
   démarrage, jamais deviné, et c'est la seule chose de cette rangée qui change
   d'une heure à l'autre — donc c'est la seule qui a besoin d'être écrite. */
.np-pick-w {
  font-size: 10.5px; line-height: 1; color: var(--ink-2);
  letter-spacing: .02em;
}
.np-pick[aria-pressed="true"] .np-pick-w { color: var(--sel-ink); opacity: .85; }

/* Et celle que personne ne fait tourner reste visible, en retrait, hors d'atteinte.
   La cacher laisserait croire qu'elle n'existe pas ; la laisser cliquable donnerait
   un paquet qui n'arrive jamais. */
.np-pick[data-off="1"] { opacity: .42; cursor: default; }
.np-pick[data-off="1"] .np-pick-w { color: #a4302a; }
.np .dlg-foot { margin-top: auto; }

/* -------------------------------------------------------- the copy dialog */

.cw {
  display: flex; flex-direction: column; gap: 9px;
  padding: 12px 14px;
}

/* The flight: what you typed on the left, your desktop on the right, and pages
   in the air between them. This operating system has exactly one shape for "a
   file is on its way", and this is it. */
.cw-flight {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 8px;
  padding: 10px 6px 8px;
  background: var(--face-hi);
  border: 1px solid var(--face-lo);
  border-radius: 3px;
}
.cw-end { display: grid; justify-items: center; gap: 4px; width: 84px; }
.cw-end canvas { width: 40px; height: 40px; display: block; }
.cw-end span { font-size: 12px; color: var(--ink-2); text-align: center; line-height: 1.25; }

.cw-air { position: relative; height: 40px; overflow: hidden; }
.cw-air::before {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  border-top: 1px dashed var(--face-dk);
}
/* One page: a small white sheet with a folded corner, crossing the gap. */
.cw-page {
  position: absolute; top: 50%; left: 0;
  width: 13px; height: 16px; margin-top: -8px;
  background: linear-gradient(160deg, #ffffff 62%, #dfe7ef 62%);
  border: 1px solid #90a7bb;
  box-shadow: 0 1px 2px rgba(0,0,0,.2);
  opacity: 0;
  animation: cwFly 1.65s linear var(--d, 0s) infinite;
}
.cw-air[data-moving="1"] .cw-page { animation-play-state: running; }
.cw-air:not([data-moving="1"]) .cw-page { animation-play-state: paused; opacity: 0; }
@keyframes cwFly {
  0%   { transform: translateX(-14px) rotate(-6deg); opacity: 0; }
  12%  { opacity: 1; }
  84%  { opacity: 1; }
  100% { transform: translateX(calc(100% + 100px)) rotate(5deg); opacity: 0; }
}

.cw-said { font-size: 13.5px; line-height: 1.45; }
.cw-said q { font-style: italic; color: var(--ink-2); }
.cw-line { font-size: 13px; font-weight: 700; }
/* LE BANDEAU DE LA DÉMONSTRATION. Première chose du corps de la fenêtre, au-dessus
   de tout le reste, et elle ne se replie pas : « rien d'inventé » est une règle du
   site, donc une attente qui n'interroge personne doit le dire avant que la
   personne ait eu le temps de croire le contraire. */
.cw-demo {
  font-size: 12.5px; line-height: 1.45; color: #4a3f16;
  background: linear-gradient(180deg, #ffffe1, #fff8bf);
  border: 1px solid #ddcd72; border-radius: 4px;
  padding: 8px 10px; margin: 0 0 2px;
}
.cw-demo b { color: #6b4a00; }

.cw-sub { font-size: 12.5px; line-height: 1.4; color: var(--ink-2); }
.cw-sub[data-warn="1"] { color: #9a6408; }
.cw-foot { margin-top: auto; }


/* ================================================== the collection binder */

/* The pane's search field, the four states, and the state chip on each row. */
.coll-find { width: 100%; font-size: 13px; padding: 3px 5px; margin-bottom: 4px; }

.coll-mode { display: flex; align-items: center; gap: 6px; }
.coll-mode span { flex: 1; }
.coll-mode em {
  font-style: normal; font-size: 12px; font-variant-numeric: tabular-nums;
  color: var(--ink-2);
  background: var(--face-hi);
  border: 1px solid var(--face-lo);
  border-radius: 7px;
  padding: 0 5px;
}
.coll-mode[aria-pressed="true"] { font-weight: 700; }
.coll-mode[aria-pressed="true"] em {
  background: var(--sel); color: var(--sel-ink); border-color: var(--sel-edge);
}
.coll-mode:disabled { opacity: .45; cursor: default; }

.coll-state {
  flex: none; font-size: 12px; letter-spacing: .04em;
  padding: 2px 7px; border-radius: 9px;
  border: 1px solid var(--face-lo);
  background: var(--face-hi); color: var(--ink-2);
}
.coll-state[data-state="sealed"] {
  border-color: #d8b25a; background: #fff6e0; color: #8a6412; font-weight: 700;
}
.coll-state[data-state="kept"] {
  border-color: #d99ab0; background: #fff0f5; color: #9a3358;
}

.exp-count {
  flex: none; font-size: 12px; padding: 1px 7px; border-radius: 9px;
  background: rgba(0,0,0,.28); color: #fff;
}


.view.coll { padding: 0; }
.exp-head {
  display: flex; align-items: center; gap: 8px; padding: 5px 8px;
  background: linear-gradient(90deg, var(--c1), var(--c2));
  color: #fff; text-shadow: 0 1px 2px rgba(0,0,0,.5);
  position: sticky; top: 0; z-index: 1;
  box-shadow: 0 1px 3px rgba(0,0,0,.25);
}
.exp-txt { flex: 1; min-width: 0; display: grid; }
.exp-txt b { font: 700 12px/1.3 var(--ui); letter-spacing: .04em; }
.exp-txt span { font-size: 12.5px; opacity: .85; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.exp-best {
  flex: none; padding: 2px 7px; border-radius: 3px;
  font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.35);
}

.ab-head { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.ab-word { font: 400 20px/1 var(--ui); letter-spacing: .18em; color: var(--pane-link); }
.ab-word b { font-weight: 700; color: var(--ink); }

/* ==========================================================================
   Choosing an expansion, and being told what it is.
   ========================================================================== */





.shelf-pack canvas {
  transition: transform .2s var(--pop), filter .2s;
  filter: drop-shadow(1px 3px 4px rgba(0,0,0,.4));
  border-radius: 2px;
}
.shelf-pack:hover canvas { transform: translateY(-5px) rotate(-3deg); }
.shelf-pack[aria-pressed="true"] {
  border-color: var(--sel); background: color-mix(in srgb, var(--sel) 20%, #fff);
}
.shelf-pack[aria-pressed="true"] canvas {
  transform: translateY(-7px) scale(1.06);
  filter: drop-shadow(0 8px 10px rgba(0,0,0,.45));
}
.shelf-pack.out canvas { filter: grayscale(1) opacity(.4); }

/* The card that explains the machine you just picked. */


.mc-head b { display: block; font: 700 12.5px/1.2 var(--ui); color: var(--pane-head-ink); }
.mc-head span { font-size: 12.5px; color: var(--ink-dim); }


.mc-good b { color: var(--ink); }

.mc-av[data-off="1"] { color: #b03020; }
.mc-av[data-off="slow"] { color: #a86a10; }
.copy-sub[data-warn="1"] { color: #a86a10; }

/* ============================================================== the wait */


.wait-head canvas { flex: none; border-radius: 2px; box-shadow: 0 2px 6px rgba(0,0,0,.35); }
.wait-txt b { display: block; font: 700 12.5px/1.3 var(--ui); color: var(--pane-head-ink); }
.wait-txt span { font-size: 13px; line-height: 1.5; color: var(--ink-dim); }

/* Six lamps, one per picture. Each shows the state the Horde reports for it
   and nothing else: no lamp moves unless a count moved. */
.lamps { display: flex; gap: 5px; margin-bottom: 8px; }
.lamps i, .lamp-key i {
  flex: 1; height: 9px; border: 1px solid var(--face-dk);
  box-shadow: inset 1px 1px 1px rgba(0,0,0,.2);
  transition: background-color .25s;
}
.lamps i[data-state="wait"] { background: var(--field); }
.lamps i[data-state="busy"] {
  background: repeating-linear-gradient(45deg,
    var(--hot) 0 4px, color-mix(in srgb, var(--hot) 40%, #fff) 4px 8px);
  animation: lampBusy 1s linear infinite;
}
@keyframes lampBusy { to { background-position: 11px 0; } }
.lamps i[data-state="done"] { background: var(--sel); border-color: var(--sel-2); }

.lamp-key { display: flex; gap: 12px; margin-top: 7px; font-size: 12px; color: var(--ink-dim); }
.lamp-key .k { display: flex; align-items: center; gap: 4px; }
.lamp-key i { flex: none; width: 12px; }
.lamp-key i[data-state="wait"] { background: var(--field); }
.lamp-key i[data-state="busy"] { background: var(--hot); }
.lamp-key i[data-state="done"] { background: var(--sel); }

/* The bar carries the same two facts: finished solid, in-progress hatched. */
.prog { position: relative; }
.prog u {
  position: absolute; top: 2px; bottom: 2px; width: 0;
  background: repeating-linear-gradient(45deg,
    color-mix(in srgb, var(--hot) 85%, #fff) 0 5px, transparent 5px 10px);
  transition: left .4s var(--ease), width .4s var(--ease);
}

/* ------------------------------------------------------------- the pane */

.pane-set { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
.pane-set b { font: 700 11.5px/1 var(--ui); color: var(--pane-head-ink); }

.exp-hero { display: flex; gap: 14px; align-items: center; margin-bottom: 8px; }
.exp-hero canvas { flex: none; border-radius: 3px; box-shadow: 0 4px 12px rgba(0,0,0,.4); }

/* ================================================================= mobile */

@media (max-width: 720px) {
  :root { --task: 46px; --pane-w: 0px; }
  .icon { width: 70px; }
  .win {
    left: 0 !important; top: 0 !important;
    width: 100% !important; height: calc(100% - var(--task)) !important;
    border-radius: 0; border-left: 0; border-right: 0; padding: 0;
  }
  .title { margin: 0; border-radius: 0; }
  .client { border-left: 0; border-right: 0; border-bottom: 0; }

  /* EXCEPT A DIALOG, WHICH STAYS A DIALOG.
     "Making six pictures" is a question the machine is asking, not a place you
     have gone: full screen it read as the site having changed into a loading
     page. Anything openWindow() cannot resize is marked [data-dlg] and keeps a
     popup's shape here — centred, capped at 460, tall enough for its content and
     no taller, with the desktop still visible around it.

     Set with left AND right rather than a transform, on purpose: `.win` animates
     `transform` on the way in (winIn), so centring with translate(-50%) fights
     the animation and the box lands off-centre for a sixth of a second. */
  .win[data-dlg] {
    left: max(8px, calc(50% - 230px)) !important;
    right: max(8px, calc(50% - 230px));
    width: auto !important;
    top: 7% !important;
    height: auto !important;
    max-height: calc(93% - var(--task) - 8px);
    min-height: 0;
    border-radius: var(--radius) var(--radius) 0 0;
    border-left: 1px solid var(--cap-edge);
    border-right: 1px solid var(--cap-edge);
    padding: 0 var(--frame) var(--frame);
    box-shadow: 0 20px 54px rgba(0,0,0,.6);
  }
  .win[data-dlg] .title { border-radius: var(--radius) var(--radius) 0 0; }
  .win[data-dlg] .client {
    border: 1px solid var(--cap-edge); border-top: 0;
  }
  .win[data-dlg] .win-body { overflow: auto; -webkit-overflow-scrolling: touch; }
  .pane { display: none; }
  .menu { width: calc(100% - 4px); }
  .menu-cols { grid-template-columns: 1fr; }
  .menu-right { border-left: 0; border-top: 1px solid var(--face-lo); }
  .task { flex-basis: 132px; }
  /* SUR TÉLÉPHONE, LA BARRE DE LANCEMENT RAPIDE DISPARAÎT. Ses deux boutons sont
     les deux icônes du bureau et les deux entrées du menu Démarrer ; sur 375px
     ils prenaient 58 des 375 pixels de la barre, et il ne restait plus assez de
     place aux fenêtres ouvertes : leurs étiquettes tombaient à « M » et « N ».
     Une lettre seule ne dit pas quelle fenêtre on rouvre. */
  .quick { display: none; }
  .addrbar { display: none; }
  .view.thumbs { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
  .welcome-grid { grid-template-columns: 1fr; justify-items: center; gap: 24px; }
  .welcome-left { padding: 0; text-align: center; }
  .welcome-right { padding: 0; }
  .welcome-rule { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

/* ---------------------------------------------------------- moving icons */

/* (The containers are already absolutely positioned, which is what makes
   them the containing block for a dragged icon. Re-declaring position here
   overrode that and sent the corner list back to the top left.) */
.icon.dragging {
  z-index: 40; opacity: .85;
  filter: drop-shadow(0 6px 10px rgba(0,0,0,.5));
}
.icon.drop-over {
  border-color: #fff;
  background: color-mix(in srgb, var(--sel) 45%, transparent);
  box-shadow: 0 0 0 2px var(--sel), 0 0 14px var(--sel);
}

/* ==========================================================================
   LA VISITE GUIDÉE
   Voir js/tour.js. Le principe visuel : la consigne est POSÉE SUR l'objet dont
   elle parle, au lieu d'être une bulle à l'autre bout de l'écran.

   Le trou dans le noir est une ombre portée énorme sur un rectangle, pas un
   masque : une seule couche pour le compositeur, et rien qui tourne à chaque
   image. La règle qui a fait planter le Mac deux fois tient toujours.
   ========================================================================== */

.tour-halo {
  position: fixed; z-index: 9500; pointer-events: none;
  border-radius: 10px;
  /* Le halo emprunte le bleu de sélection du bureau plutôt qu'un blanc générique :
     c'est la couleur qui veut déjà dire « cet objet-là » partout ailleurs. */
  box-shadow: 0 0 0 100vmax rgba(4, 14, 30, .58),
              inset 0 0 0 2px rgba(255, 255, 255, .9),
              inset 0 0 0 4px var(--sel),
              0 0 20px 3px color-mix(in srgb, var(--sel) 70%, transparent);
  opacity: 0;
  transition: opacity .35s ease, left .28s cubic-bezier(.3,.7,.3,1),
              top .28s cubic-bezier(.3,.7,.3,1),
              width .28s cubic-bezier(.3,.7,.3,1),
              height .28s cubic-bezier(.3,.7,.3,1);
}
.tour-halo[data-on="1"] { opacity: 1; }

/* La pastille vient d'agir à votre place : un éclat sur l'objet, pour qu'on voie
   CE QUI a été touché. Une animation d'un tiers de seconde, sur un élément qui
   existe déjà — rien de neuf à l'écran, rien qui tourne après. */
@keyframes tour-poke {
  0%   { box-shadow: 0 0 0 100vmax rgba(4, 14, 30, .58),
                     inset 0 0 0 2px rgba(255, 255, 255, .9),
                     inset 0 0 0 4px var(--sel),
                     0 0 20px 3px color-mix(in srgb, var(--sel) 70%, transparent); }
  35%  { box-shadow: 0 0 0 100vmax rgba(4, 14, 30, .58),
                     inset 0 0 0 3px #fff,
                     inset 0 0 0 6px var(--sel),
                     0 0 34px 10px color-mix(in srgb, var(--sel) 90%, transparent); }
  100% { box-shadow: 0 0 0 100vmax rgba(4, 14, 30, .58),
                     inset 0 0 0 2px rgba(255, 255, 255, .9),
                     inset 0 0 0 4px var(--sel),
                     0 0 20px 3px color-mix(in srgb, var(--sel) 70%, transparent); }
}
.tour-halo[data-poke="1"] { animation: tour-poke .34s ease-out; }

.tour-bubble {
  position: fixed; z-index: 9501;
  /* LE MÊME OBJET QUE LES NOTIFICATIONS DU SITE.
     Elle était blanc-bleu, d'un dessin qui n'existe nulle part ailleurs : elle
     avait l'air d'un bandeau de site web posé sur un bureau de 2001. Elle emprunte
     maintenant exactement la matière de la bulle d'info de Windows — le jaune
     pâle, la bordure, l'ombre — qui est déjà celle des notifications de la zone de
     l'horloge. C'est la même famille d'objets, donc elle ne surprend plus. */
  background: linear-gradient(180deg, #ffffe1, #fff8bf);
  border: 1px solid #d9c96a;
  border-radius: 6px;
  box-shadow: 3px 4px 10px rgba(0, 0, 0, .38);
  padding: 13px 15px 11px;
  opacity: 0; transform: translateY(6px);
  transition: opacity .3s ease, transform .3s cubic-bezier(.25,.8,.3,1);
  pointer-events: auto;
}
.tour-bubble[data-on="1"] { opacity: 1; transform: none; }

/* La pointe, qui désigne. Elle suit la cible même quand la bulle a été recadrée
   contre le bord de l'écran. */
.tour-bubble::after {
  content: ""; position: absolute; left: var(--arrow, 28px);
  width: 14px; height: 14px; margin-left: -7px;
  background: #ffffe1; border: 1px solid #d9c96a;
  transform: rotate(45deg);
}
.tour-bubble[data-side="below"]::after {
  top: -8px; border-right: 0; border-bottom: 0;
}
.tour-bubble[data-side="above"]::after {
  bottom: -8px; border-left: 0; border-top: 0;
}

.tour-h {
  margin: 0 0 4px; font: 700 14px/1.3 var(--ui); color: #1a1400;
}
.tour-p {
  margin: 0 0 10px; font: 13px/1.5 var(--ui); color: #3a3117;
}
.tour-foot {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  border-top: 1px solid rgba(150, 130, 40, .35); padding-top: 8px;
}
.tour-step {
  font: 700 11px/1 var(--ui); letter-spacing: .06em;
  color: #8a7a3c;
}
/* CE QU'IL FAUT FAIRE — ET C'EST UN VRAI BOUTON MAINTENANT.
   Elle était peinte exactement comme les boutons d'action du site et n'était
   qu'un morceau de texte : on cliquait, il ne se passait rien, et c'était le
   premier geste de la première minute de quelqu'un. Elle fait la chose à votre
   place (voir `act` dans js/tour.js), donc elle a le droit d'en avoir l'air —
   avec l'enfoncement et le survol qui vont avec, sans quoi elle mentirait encore
   d'un cran. */
.tour-hint {
  font: 700 12px/1 var(--ui); color: #fff;
  background: linear-gradient(180deg, var(--cap-2), var(--cap-4));
  border: 1px solid var(--cap-edge);
  border-radius: 4px; padding: 5px 10px;
  text-shadow: 0 1px 1px rgba(0, 0, 0, .35);
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .45), 0 1px 2px rgba(0, 0, 0, .25);
  transition: filter .12s ease, transform .06s ease;
}
.tour-hint[data-act="1"]:hover { filter: brightness(1.12); }
.tour-hint[data-act="1"]:active { transform: translateY(1px); filter: brightness(.94); }
.tour-hint:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }

/* ET QUAND IL N'Y A RIEN À FAIRE, CE N'EST PLUS UN BOUTON.
   Une étape comme « regarde-les arriver » n'a pas d'action : la pastille redevient
   une étiquette — pas de fond, pas de bordure, pas de curseur main — parce que la
   moitié de la réparation est là. Rien sur cet écran ne doit ressembler à un
   bouton sans en être un. */
.tour-hint[data-act="0"] {
  background: none; border: 1px solid transparent; box-shadow: none;
  color: #6a5c28; text-shadow: none; cursor: default; padding: 5px 0;
  font: 700 12px/1 var(--ui); letter-spacing: .01em;
}
.tour-skip {
  margin-left: auto; background: none; border: 0; padding: 4px 2px;
  font: 12px/1 var(--ui); color: #6d6136; text-decoration: underline;
  cursor: pointer;
}
.tour-skip:hover { color: #1a1400; }

@media (prefers-reduced-motion: reduce) {
  .tour-halo, .tour-bubble { transition-duration: .01ms; }
}


/* L'image de la notification, et sa réapparition quand deux se suivent. */
.balloon-ico { grid-row: 1 / span 2; display: block; }
.balloon-ico[hidden] { display: none; }
.balloon-ico canvas { display: block; }
.balloon-h { grid-column: 2; }
.balloon-p { grid-column: 2; }
.balloon[data-in="1"] { animation: balloonIn .22s var(--pop); }

/* Les trois gestes en haut de « What is this ». */
.ab-lead { font: 400 14px/1.5 var(--ui); color: var(--ink); margin: 0 0 14px; }
.ab-how {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;
  margin: 0 0 18px; padding: 12px 10px;
  background: linear-gradient(180deg, #f4f9ff, #e8f1fb);
  border: 1px solid var(--face-lo); border-radius: 8px;
}
.ab-step { display: grid; justify-items: center; gap: 3px; text-align: center; }
.ab-step canvas { display: block; }
.ab-step b { font: 700 12.5px/1.25 var(--ui); color: var(--ink); }
.ab-step span { font: 400 11.5px/1.3 var(--ui); color: var(--ink-dim); }


/* La barre d'adresse ne se comporte comme un bouton que lorsqu'elle en est un. */
.addr[data-live="1"] { cursor: pointer; }
.addr[data-live="1"]:hover { background: #fff; border-color: var(--sel); }
.addr[data-live="1"]:focus-visible { outline: 2px solid var(--sel); outline-offset: 1px; }
