/* ==========================================================================
   folder.css — homepage section 04, "Your canvas, in a folder." (v1, 2026-09-30)
   --------------------------------------------------------------------------
   Loaded by index.html only, after home.css: the canvas tokens (.demo) and
   the block grammar (.cv-note, .cv-task, .cv-md, .cv-link, .cv-image,
   .cv-heading) come from home.css §5 and are reused here unchanged.

   THE FIGURE is one frame with two fields and a hinge:
     the canvas     the app's own field and dots, nine blocks of the Ember
                    Gambit project from section 02
     the hinge      the owner's folder icon, on the seam between the fields
     your computer  flat paper, and the folder those blocks become

   LINES. Every block has one line into the folder; the folder's tree grows
   out of it. Curves on the canvas, right angles in the folder: the same
   material, gathered and then put in order.

   GEOMETRY. From 1240px the figure is a fixed sheet in em (124 × 56), like
   the canvas demos: --x/--y/--w place a block, and the lines are a static
   SVG in the same units (viewBox = em × 10). Each left-hand block's line
   runs through a gap left for it between the right-hand blocks, so no line
   ever passes behind a block. Below 1240px the fields stack: the blocks
   flow in two columns beside one spine that runs down into the icon, and
   the tree stands under it (two columns on tablets, one on phones). Those
   lines are CSS. Nothing here needs scripting to be complete.

   MOTION is folder.js: the lines draw once when the figure comes into view,
   and a block or a file can be followed along its path. This file holds the
   finished state; the script only moves the figure there (--seen,
   --ff-wipe-*, --ff-spine, --ff-tick) and marks what is being followed
   (.has-active, .is-on, .is-landed).

   Contents
     1 section · 2 frame and fields · 3 canvas blocks · 4 lines · 5 hinge
     6 folder tree · 7 stacked layout · 8 what is followed · 9 motion
   ========================================================================== */

/* ---------- 1. The section ---------------------------------------------------- */
.folder .section-heading h2 { flex: none; }
.folder .section-heading p { min-width: 0; max-width: 52ch; }

/* ---------- 2. The frame and its two fields ----------------------------------- */
/* .demo (home.css §5) supplies the canvas palette in both themes, the 1px
   frame and the app's face. The paper field takes the page's own surface. */
.ff {
  --ff-seam: 78;                                   /* em from the sheet's left edge */
  --ff-ink: var(--sw-ink);
  --ff-muted: var(--sw-muted);
  --ff-paper: var(--sw-surface);
  --ff-line: rgb(var(--tint-rgb) / 0.3);           /* the tree's connectors */
  --ff-edge: rgb(var(--tint-rgb) / 0.5);           /* marks: 3 : 1 against the paper */
  --ff-hot: var(--flow-strong);                    /* what is followed: the orange, one step deeper on the light fields (3 : 1 for a thin line) */
  --cv-mono: "Roboto Mono", var(--sw-mono);        /* the app's mono, as in the demos */
  position: relative;
  margin: 0;
  container-type: inline-size;
}
:root[data-theme="dark"] .ff { --ff-hot: var(--sw-accent); }
/* ⚠ Mirror of the rule above, for System without scripting. KEEP IN SYNC. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ff { --ff-hot: var(--sw-accent); }
}
.ff-stage {
  position: relative;
  height: 56em;
  overflow: hidden;
  font-size: 10px;
  font-size: clamp(9.2px, calc(100cqw / 124), 11.8px);
  background-color: var(--pd-page);
  background-image: radial-gradient(circle at 1px 1px, var(--pd-grid) 1px, transparent 0);
  background-size: 20px 20px;
  background-position: 50% 0;
}
/* the paper: from the seam to the frame's right edge, however wide the frame */
.ff-stage::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0; right: 0;
  left: calc(50% + (var(--ff-seam) - 62) * 1em);
  background: var(--ff-paper);
}
/* the seam: one hairline under the middle label, which stops for the folder
   and its name */
.ff-stage::before {
  content: '';
  position: absolute;
  z-index: 1;
  top: 0; bottom: 0;
  left: calc(50% + (var(--ff-seam) - 62) * 1em);
  width: 1px;
  background: linear-gradient(transparent 5.4em, var(--pd-line-2) 5.4em, var(--pd-line-2) 22.2em, transparent 22.2em, transparent 38.9em, var(--pd-line-2) 38.9em);
}
.ff-sheet { position: absolute; z-index: 1; left: 50%; top: 0; width: 124em; height: 56em; margin-left: -62em; }

.sw .ff-label { position: absolute; top: 2.3em; left: 4em; margin: 0; max-width: none; font-size: 10px; line-height: 1.6; color: var(--pd-text-2); }
.sw .ff-folder .ff-label { left: 11.6em; color: var(--ff-muted); }
/* three labels on one line: the canvas, the download between, your computer */
.sw .ff-folder .ff-label--step { left: 0; transform: translateX(-50%); white-space: nowrap; color: var(--ff-ink); }

/* ---------- 3. The canvas and its blocks -------------------------------------- */
/* A picture of the canvas: the blocks answer the pointer, the field does not. */
.ff-canvas { position: absolute; inset: 0; pointer-events: none; }
.ff-col { display: contents; }
.ff-spine { display: none; }
.ff-b {
  position: absolute;
  left: calc(var(--x) * 1em);
  top: calc(var(--y) * 1em);
  width: calc(var(--w) * 1em);
  margin: 0;
  font-family: Inter, var(--sw-sans);
  font-size: 1em;
  letter-spacing: 0;
  color: var(--pd-text);
  text-wrap: pretty;
  pointer-events: auto;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: opacity 220ms var(--sw-ease);
}
/* Markdown: a window onto the file, as the app's block is; longer lines and
   the rest of the file run on under its edges */
.ff .cv-md pre {
  height: 7em;
  padding: 1.1em 1.3em 0;
  font-size: 1.02em;
  line-height: 1.7;
  white-space: pre;
  -webkit-mask-image: linear-gradient(90deg, #000 84%, transparent), linear-gradient(#000 60%, transparent);
  -webkit-mask-composite: source-in;
  mask-image: linear-gradient(90deg, #000 84%, transparent), linear-gradient(#000 60%, transparent);
  mask-composite: intersect;
}
.ff .cv-link__text b { overflow-wrap: anywhere; }

/* a saved conversation (the prompt panel's thread): its title, your turn
   with its label, the answer */
.ff-chat { padding: 1.05em 1.3em 1.3em; border: 1px solid var(--pd-line); border-radius: 1.4em; background: var(--pd-surface); box-shadow: var(--pd-shadow-soft); }
.ff-chat header { display: flex; align-items: baseline; justify-content: space-between; gap: 1em; font-size: 1.14em; font-weight: 500; letter-spacing: -0.01em; color: var(--pd-ink); }
.ff-chat header small { font-family: var(--cv-mono); font-size: 0.82em; font-weight: 400; letter-spacing: 0.08em; text-transform: uppercase; color: var(--pd-text-3); }
.sw .ff-chat p { margin: 0; max-width: none; font-size: 1.22em; line-height: 1.5; }
.sw .ff-chat__you { margin-top: 0.6em; text-align: right; }
.ff-chat__you span { display: inline-block; max-width: 94%; padding: 0.45em 0.8em; border-radius: 0.9em 0.9em 0.3em 0.9em; background: var(--pd-well); box-shadow: inset 0 0 0 1px var(--pd-line); font-weight: 500; text-align: left; color: var(--pd-ink); }
.sw .ff-chat__ai { margin-top: 0.7em; color: var(--pd-text); }

/* ---------- 4. The lines -------------------------------------------------------- */
/* Flow curves in the canvas's own connection colour. pathLength is 1 on
   every path, so a dash of 1 is the whole line and folder.js draws it by
   moving the offset. Butt caps: a round cap would leave a dot at offset 1. */
.ff-wires { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; fill: none; }
.ff-wire { stroke: var(--pd-wire); stroke-width: 1.25; stroke-dasharray: 1; stroke-dashoffset: 0; transition: opacity 220ms var(--sw-ease); }
.ff-wires circle { fill: var(--pd-page); stroke: var(--pd-wire-tip); stroke-width: 1.25; transition: opacity 220ms var(--sw-ease); }
.ff-trunk { stroke: var(--ff-line); }
.ff-token { fill: var(--ff-hot); }

/* ---------- 5. The hinge: the folder icon on the seam --------------------------- */
/* The icon is the owner's artwork at its own proportions: a 256px square
   whose folder is optically centred 120.5px down, so that point sits on the
   sheet's 28em line. It is never recoloured; on the dark page a hairline of
   light keeps its outline, as the app does in its header. The button plays
   the path again. */
.ff-hinge {
  position: absolute;
  z-index: 3;
  left: calc((var(--ff-seam) - 5.5) * 1em);
  top: calc(28em - 11em * 120.5 / 256);
  width: 11em;
  height: 11em;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.sw .ff-hinge:focus-visible { outline-offset: 2px; }
.ff-icon { display: block; width: 100%; height: 100%; max-width: none; }
:root[data-theme="dark"] .ff-icon { filter: drop-shadow(0 0 0.6px rgb(255 255 255 / 0.42)); }
/* ⚠ Mirror of the rule above, for System without scripting. KEEP IN SYNC. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ff-icon { filter: drop-shadow(0 0 0.6px rgb(255 255 255 / 0.42)); }
}

/* ---------- 6. Your computer: the folder ---------------------------------------- */
/* The folder's name stands under its icon, as on a desktop. Below it, two
   lists make one tree. Folders are set in the page's sans at full ink, files
   in mono one step quieter; a row is 2.6em. Connectors are hairlines with
   one rounded elbow per file, after the app's own file list. */
.ff-folder { position: absolute; left: calc(var(--ff-seam) * 1em); top: 0; right: 0; bottom: 0; color: var(--ff-ink); font-family: var(--sw-sans); }
.sw .ff-name { position: absolute; left: -9em; top: 33.3em; width: 18em; margin: 0; max-width: none; font-size: 1em; line-height: 1.2; text-align: center; }
.ff-name b { display: block; font-size: 1.62em; font-weight: 400; letter-spacing: -0.035em; color: var(--ff-ink); }
.ff-name span { display: block; margin-top: 0.55em; font-family: var(--sw-mono); font-size: 0.95em; line-height: 1.6; letter-spacing: 0.06em; text-transform: uppercase; white-space: nowrap; color: var(--ff-muted); }

.ff-tree { position: absolute; left: 9.1em; top: 6.42em; width: 33.9em; }
.ff-list, .ff-list ul { position: relative; margin: 0; padding: 0; list-style: none; }

/* the spine, from the first row to the last folder, across both lists. Each
   entry draws its own length of it, so it holds whatever height a row takes. */
.ff-dir { position: relative; }
.ff-row--guide::after, .ff-dir::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 1px; background: var(--ff-line); }
.ff-row--guide::after { top: 50%; }
.ff-list:last-child > .ff-dir:last-child::before { bottom: auto; height: 1.3em; }

.ff-row {
  position: relative;
  display: flex;
  align-items: center;
  height: 2.6em;
  padding-left: 2.5em;
  opacity: calc(var(--seen, 1) * var(--dim, 1));
  transform: translateX(calc((var(--seen, 1) - 1) * 0.9em));
  transition: opacity 220ms var(--sw-ease);
}
/* from the spine to a folder (or to START HERE) */
.ff-row--guide::before, .ff-dname::before { content: ''; position: absolute; left: 0; top: 50%; width: 1.7em; height: 1px; background: var(--ff-line); }
/* from a folder down to its files: a branch, and an elbow into each file */
.ff-dir ul .ff-row { padding-left: 4.9em; }
.ff-dir ul .ff-row::before { content: ''; position: absolute; left: 3em; top: 0; width: 1.2em; height: 50%; border-left: 1px solid var(--ff-line); border-bottom: 1px solid var(--ff-line); border-bottom-left-radius: 0.55em; }
/* the branch carries on to the next file from where the elbow leaves it */
.ff-dir ul .ff-row:not(:last-child)::after { content: ''; position: absolute; left: 3em; top: calc(50% - 0.55em); bottom: 0; width: 1px; background: var(--ff-line); }

.ff-dname { cursor: pointer; -webkit-tap-highlight-color: transparent; }
.ff-dname b { font-size: 1.46em; font-weight: 400; letter-spacing: -0.03em; white-space: nowrap; }
.ff-dname b::after { content: '/'; margin-left: 0.14em; color: var(--ff-muted); }

.ff-file { display: inline-flex; align-items: center; gap: 0.8em; min-width: 0; height: 100%; margin: 0; padding: 0; border: 0; border-radius: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.sw .ff-file:focus-visible { outline-offset: 3px; }
.ff-fname { font-family: var(--sw-mono); font-size: 1.13em; line-height: 1.3; color: var(--ff-muted); white-space: nowrap; transition: color 160ms var(--sw-ease); }
.ff-mark { flex: none; width: 0.66em; height: 0.66em; background: var(--ff-paper); box-shadow: inset 0 0 0 1px var(--ff-edge); transition: background-color 160ms var(--sw-ease), box-shadow 160ms var(--sw-ease); }
.ff-aside { margin-left: auto; padding-left: 1em; font-family: var(--sw-mono); font-size: 1.05em; white-space: nowrap; color: var(--ff-muted); }

/* START HERE.md: the first file, and the one orange square at rest */
.ff-row--guide .ff-fname { font-size: 1.22em; color: var(--ff-ink); }
.ff-row--guide .ff-mark { background: var(--ff-hot); box-shadow: none; }
.ff-row--guide .ff-aside { font-size: 0.95em; letter-spacing: 0.06em; text-transform: uppercase; }

/* tasks keep their state: the app's three rings, and the checkbox the file
   holds. On paper the rings take the deeper green and amber the demos keep
   for small status text (--pd-green-ink, --pd-amber-ink), so each state
   holds 3 : 1 in both themes. */
.ff-mark--ring { width: 1.3em; height: 1.3em; margin: 0 -0.32em; background: none; box-shadow: none; fill: none; stroke: var(--ff-edge); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
[data-state='done'] .ff-mark--ring { stroke: var(--pd-green-ink); }
[data-state='doing'] .ff-mark--ring circle { stroke: color-mix(in srgb, var(--pd-amber-ink) 34%, transparent); }
[data-state='doing'] .ff-mark--ring path { stroke: var(--pd-amber-ink); }

/* ---------- 7. Stacked: below the wide sheet ------------------------------------ */
/* The canvas stands over your computer and the icon keeps the seam. Blocks
   flow in two columns beside one spine, each joined to it by a short tick;
   the spine runs into the icon; under it a drop and a bar carry on to the
   tree. Content keeps a 76em measure inside fields that still run from
   edge to edge of the frame. */
@media (max-width: 1239px) {
  .ff { --ff-pad: 1.6em; }
  @supports (width: 1cqw) { .ff { --ff-pad: max(1.6em, calc(50cqw - 38em)); } }
  .ff-stage { height: auto; font-size: 10.4px; background: none; }
  .ff-stage::before, .ff-stage::after { display: none; }
  .ff-sheet { position: static; width: auto; height: auto; margin: 0; }

  .ff-canvas {
    position: relative;
    inset: auto;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 3.2em;
    align-items: start;
    padding: 2.2em var(--ff-pad) 7.2em;
    pointer-events: auto;
    background-color: var(--pd-page);
    background-image: radial-gradient(circle at 1px 1px, var(--pd-grid) 1px, transparent 0);
    background-size: 20px 20px;
    background-position: 50% 0;
  }
  .sw .ff-label { position: static; }
  .ff-canvas .ff-label { grid-column: 1 / -1; margin-bottom: 1.6em; }
  .ff-wires { display: none; }
  .ff-col { display: flex; flex-direction: column; gap: 1.5em; min-width: 0; }
  .ff-b { position: relative; left: auto; top: auto; width: auto; }
  .ff .cv-heading { white-space: normal; }
  .ff .cv-image { aspect-ratio: 2 / 1; }
  /* a tick leaves its block, so these two stop clipping and round their own insides */
  .ff .cv-image, .ff .cv-md { overflow: visible; }
  .ff .cv-image img { border-radius: calc(1em - 1.5px); }
  .ff .cv-md header { border-radius: calc(1.1em - 1px) calc(1.1em - 1px) 0 0; }

  /* the spine, from the first tick to the icon, and a tick from every block */
  .ff-spine { display: block; position: absolute; left: 50%; top: 6.7em; bottom: 0; width: 1px; margin-left: -0.5px; background: var(--pd-wire); transform: scaleY(var(--ff-spine, 1)); transform-origin: 50% 0; }
  .ff-b::after { content: ''; position: absolute; top: 50%; width: 1.6em; height: 1px; background: var(--pd-wire); opacity: var(--ff-tick, 1); }
  .ff-col:first-of-type .ff-b::after { left: 100%; }
  .ff-col:last-of-type .ff-b::after { right: 100%; }

  /* the icon: on the seam, taking no room of its own */
  .ff-hinge { position: relative; left: auto; top: auto; display: block; width: 9em; height: 9em; margin: calc(-9em * 120.5 / 256) auto calc(-9em * 135.5 / 256); }

  .ff-folder { position: relative; left: auto; right: auto; bottom: auto; padding: 7.6em var(--ff-pad) 3em; border-top: 1px solid var(--pd-line-2); background: var(--ff-paper); }
  /* from the icon down, then across to the spine */
  .ff-folder::before { content: ''; position: absolute; left: 50%; top: 3.3em; width: 1px; height: 2.9em; margin-left: -0.5px; background: var(--ff-line); }
  .ff-folder::after { content: ''; position: absolute; left: var(--ff-pad); top: 6.2em; width: calc(50% - var(--ff-pad)); height: 1px; background: var(--ff-line); }
  .sw .ff-name { position: relative; left: auto; top: auto; width: auto; margin: 0 0 1.5em 2.5em; text-align: left; }
  /* the spine passes the folder's name on its way to the tree */
  .ff-name::before { content: ''; position: absolute; left: -2.5em; top: -1.4em; bottom: -1.5em; width: 1px; background: var(--ff-line); }
  .ff-name b { font-size: 1.75em; }
  /* the step stands over the seam, the place under it */
  .sw .ff-folder .ff-label { position: absolute; left: auto; right: var(--ff-pad); top: 1.5em; }
  .sw .ff-folder .ff-label--step { top: -3.1em; transform: none; }

  .ff-tree { position: relative; left: auto; top: auto; width: auto; }
  /* a long file name takes a second line rather than the page's width; the
     file's button fills its row, so the whole row height is the target */
  .ff-row { height: auto; min-height: 3em; align-items: stretch; }
  .ff-dname b, .ff-aside { align-self: center; }
  .ff-file { height: auto; padding-block: 0.45em; }
  .ff-fname { white-space: normal; overflow-wrap: anywhere; text-wrap: balance; }
  /* the spine arrives from above the tree, past the folder's name */
  .ff-row--guide::after { top: 0; }
  .ff-list:last-child > .ff-dir:last-child::before { height: 1.5em; }
}

/* tablets: the tree in two columns, each on its own spine under the one bar */
@media (max-width: 1239px) and (min-width: 700px) {
  .ff-stage { font-size: 10.8px; }
  .ff-tree { display: grid; grid-template-columns: minmax(0, 4fr) minmax(0, 6fr); column-gap: 3.2em; align-items: start; }
  .ff-list:first-child > .ff-dir:last-child::before { bottom: auto; height: 1.5em; }
  .ff-list:last-child > .ff-dir:first-child::before { top: -7.2em; }
}

/* phones: one rail. The blocks hang from it in one column, it runs into the
   icon, and the tree's spine leaves the icon on the same line. */
@media (max-width: 699px) {
  .ff { --ff-rail: calc(var(--ff-pad) + 2.7em); }
  .ff-canvas { display: flex; flex-direction: column; align-items: stretch; gap: 1.2em; padding: 2.2em var(--ff-pad) 5.6em calc(var(--ff-rail) + 1.9em); }
  /* a little less of the two tallest blocks: the column is long enough */
  .ff .cv-image { aspect-ratio: 2.3 / 1; }
  .ff .cv-md pre { height: 5.4em; }
  .ff-canvas .ff-label { margin: 0 0 0.2em -4.6em; }
  .ff-col { display: contents; }
  /* one reading order for the two columns */
  .ff-b[data-k='text'] { order: 1; }
  .ff-b[data-k='image'] { order: 2; }
  .ff-b[data-k='note'] { order: 3; }
  .ff-b[data-k='doc'] { order: 4; }
  .ff-b[data-k='link'] { order: 5; }
  .ff-b[data-k='done'] { order: 6; }
  .ff-b[data-k='doing'] { order: 7; }
  .ff-b[data-k='todo'] { order: 8; }
  .ff-b[data-k='chat'] { order: 9; }
  .ff-spine { left: var(--ff-rail); top: 6.9em; }
  .ff-col:first-of-type .ff-b::after, .ff-col:last-of-type .ff-b::after { left: auto; right: 100%; width: 1.9em; }

  .ff-hinge { width: 7em; height: 7em; margin: calc(-7em * 120.5 / 256) 0 calc(-7em * 135.5 / 256) calc(var(--ff-rail) - 3.5em); }
  .ff-folder { padding: 4.7em var(--ff-pad) 2.6em var(--ff-rail); }
  .ff-folder::before, .ff-folder::after { display: none; }
  .ff-name::before { top: -5em; }
}
@media (max-width: 379px) {
  .ff-row--guide .ff-aside { display: none; }
}
/* the narrowest screens: a task block gives up its clock and some padding,
   so its title keeps a readable measure */
@media (max-width: 359px) {
  .ff .cv-task { gap: 0.75em; padding: 1.2em 1.1em; }
  .ff .cv-task__clock { display: none; }
}
/* under 320px the checkbox column goes too; the rings still show each state */
@media (max-width: 319px) {
  .ff-dir .ff-aside { display: none; }
}
/* a fingertip needs a taller row than a pointer does */
@media (pointer: coarse) and (max-width: 1239px) {
  .ff-dir ul .ff-row, .ff-row--guide { min-height: 4.25em; }
}

/* ---------- 8. What is being followed -------------------------------------------- */
/* folder.js marks the block, the line and the file that belong together
   (.is-on) on a figure that has one (.has-active); everything else steps
   back. The path itself is drawn in the overlay (.ff-hot), and a file takes
   the accent when its square arrives (.is-landed). */
.ff.has-active .ff-b:not(.is-on) { opacity: 0.42; }
.ff.has-active .ff-wires g:not(.is-on) > * { opacity: 0.3; }
.ff.has-active .ff-row:not(.is-on) { --dim: 0.46; }
.ff-row.is-on .ff-fname { color: var(--ff-ink); }
.ff-row.is-landed .ff-mark:not(.ff-mark--ring) { background: var(--ff-hot); box-shadow: none; }
@media (hover: hover) {
  .ff-file:hover .ff-fname { color: var(--ff-ink); }
}

.ff-hot { position: absolute; z-index: 2; left: 0; top: 0; overflow: visible; fill: none; pointer-events: none; }
.ff-hot path { stroke: var(--ff-hot); stroke-width: 1.75px; stroke-linejoin: round; }
.ff-hot rect { fill: var(--ff-hot); }

/* ---------- 9. Motion -------------------------------------------------------------- */
/* folder.js holds the clock. While the figure waits for its entrance the
   lines are undrawn and the tree is wiped shut; the script moves both to
   the finished state written above and then removes its own properties.
   No scripting, or reduced motion: the finished state, at once. */
.ff-tree { clip-path: inset(var(--ff-wipe-t, -14em) -3em var(--ff-wipe-b, -1em) -3em); }

@media (prefers-reduced-motion: reduce) {
  .ff-b, .ff-wire, .ff-wires circle, .ff-row, .ff-fname, .ff-mark { transition: none; }
}
