/* ═══════════════════════════════════════════════════════════════════
   NORDLYS - COMPONENTS & INTERACTIVE ELEMENTS
   ═══════════════════════════════════════════════════════════════════ */

/* ── Hero Header: Clock · Date · Greeting ─────────────────────── */
#hero {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  transition: opacity var(--nl-transition-control), transform var(--nl-transition-control);
}

/* The clock is the one thing on this page that is read rather than used, so it
   is the typographic subject and gets treated as one.

   It used to be filled with a gradient through background-clip: text and lit
   with a coloured drop-shadow. Both are the kind of thing that looks like craft
   and is the opposite: clipped-gradient text loses subpixel rendering, and a
   coloured halo behind type is the single fastest way to date an interface.
   Ink, weight and size do the work.

   The width is reserved in ch because tabular figures fix the width of a digit,
   not of the clock: turning seconds on adds two digits and a separator, and the
   hero above the search bar jumped every time. */
#clock {
  font-family: var(--font-display);
  font-weight: 200;
  font-size: clamp(62px, 12vh, 120px);
  line-height: 0.95;
  letter-spacing: var(--nl-track-none);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  display: inline-block;
  /* Reserved for the widest state the clock can reach, not for the current one:
     a minimum the content already exceeds reserves nothing. The slack is
     invisible because the digits are centred inside it. */
  /* #hero is shrink-to-fit, so a percentage here resolves against the clock's
     own width and reserves nothing — which is exactly what the previous attempt
     did. Reserve in ch, and stand down on narrow screens where eight characters
     are wider than the window. */
  min-width: 8ch;
  text-align: center;
  user-select: none;
}

#clock .colon {
  opacity: 0.72;
}

.sc, #ss {
  display: none;
  font-size: 0.42em;
  font-weight: 300;
  opacity: 0.85;
}

#clock .ampm {
  display: none;
  font-size: 0.26em;
  font-weight: 700;
  letter-spacing: var(--nl-track-wide);
  text-transform: uppercase;
  vertical-align: super;
  margin-left: 10px;
  opacity: 0.88;
  color: var(--nl-accent-text);
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  padding: 3px 9px;
  border-radius: 10px;
  box-shadow: var(--nl-shadow-1);
  backdrop-filter: blur(8px);
}

body.seconds .sc, body.seconds #ss {
  display: inline;
}

#date {
  font-size: var(--nl-text-xs);
  font-weight: 650;
  letter-spacing: var(--nl-track-wide);
  text-transform: uppercase;
  color: var(--dim);
  user-select: none;
}

#greet {
  font-size: var(--nl-text-lg);
  font-weight: 450;
  color: var(--dim);
  letter-spacing: var(--nl-track-none);
  user-select: none;
}

/* ── Omni-Search Bar ──────────────────────────────────────────── */
#searchwrap {
  position: relative;
  margin-top: 18px;
  width: min(88vw, 640px);
}

#search {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 54px;
  padding: 0 16px;
  border-radius: 18px;
  border: 1px solid var(--frost);
  background: var(--glass);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  box-shadow: var(--nl-shadow-3);
  transition: border-color var(--nl-motion-control), box-shadow var(--nl-motion-control), background var(--nl-motion-control);
}

#search:focus-within {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--card-tint, #141c34) 82%, transparent);
  box-shadow: var(--nl-shadow-3), var(--nl-ring);
}

/* A magnifier, not a button. The mark here used to be the active engine's logo
   and a switch to cycle through ten of them; the page no longer knows which
   engine it is talking to, and there is nothing to press. */
#search-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--nl-target-min);
  height: var(--nl-target-min);
  flex: 0 0 auto;
  color: var(--dim);
}
#search-icon svg {
  width: 19px;
  height: 19px;
  fill: currentColor;
}

#q {
  flex: 1;
  min-width: 0;
  background: none;
  border: 0;
  outline: 0;
  color: var(--ink);
  font-family: inherit;
  font-size: var(--nl-text-lg);
}
#q::placeholder {
  color: var(--nl-text-tertiary);
  opacity: 1;
}

#search kbd {
  flex: none;
  font: 600 11px/1 var(--font-mono);
  color: var(--faint);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 6px;
  padding: 4px 7px;
  /* A hint, and still text: full strength at rest, gone once you are typing. */
  opacity: 1;
  transition: opacity var(--nl-motion-control);
}
#search:focus-within kbd {
  opacity: 0;
}

/* Autocomplete Suggestion Dropdown */
#sugg {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% + 8px);
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid var(--frost);
  background: rgba(9, 15, 30, 0.88);
  backdrop-filter: blur(24px) saturate(1.4);
  -webkit-backdrop-filter: blur(24px) saturate(1.4);
  box-shadow: var(--nl-shadow-3);
  display: none;
  z-index: var(--nl-z-float);
}
#sugg.on {
  display: block;
}
#sugg div {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 18px;
  font-size: var(--nl-text-md);
  color: #c9d6f0;
  cursor: pointer;
  transition: background var(--nl-motion-fast), color var(--nl-motion-fast);
  animation: suggIn var(--nl-transition-control) both;
  /* Cascade, but bounded: eight suggestions at 24ms each would put the last
     one outside the motion budget on its own. */
  animation-delay: min(calc(var(--si, 0) * 24ms), 120ms);
}

@keyframes suggIn {
  from { opacity: 0; transform: translateY(-5px); }
  to { opacity: 1; transform: none; }
}

#sugg .sugg-calc .calc-val {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--nl-accent-text);
}
#sugg .bm-info {
  display: flex;
  align-items: baseline;
  min-width: 0;
  flex: 1;
}
#sugg .bm-name {
  color: var(--ink);
  font-weight: 550;
}
/* The address is quieter than the name by tier, not by opacity: an inline 0.6
   on top of the secondary colour measured 2.8 to one in Solarized Light. */
#sugg .bm-url {
  max-width: 45%;
  margin-left: 6px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--nl-text-xs);
  color: var(--nl-text-secondary);
}
#sugg .hist-text {
  flex: 1;
  color: var(--ink);
}
#sugg svg {
  width: 15px;
  height: 15px;
  flex: none;
  fill: var(--dim);
}
#sugg .sugg-history svg {
  fill: none;
  stroke: var(--dim);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
#sugg .sugg-badge {
  font-size: var(--nl-text-2xs);
  font-weight: 650;
  padding: 2px 8px;
  border-radius: 9999px;
  margin-left: auto;
  flex-shrink: 0;
  text-transform: uppercase;
  letter-spacing: var(--nl-track-label);
}
#sugg .sugg-calc-badge {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  color: var(--nl-accent-text);
}
#sugg .sugg-folder-badge {
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--dim);
}
#sugg .sugg-del-btn {
  border: 0;
  background: transparent;
  color: var(--faint);
  font-size: var(--nl-text-xs);
  padding: 2px 7px;
  border-radius: 6px;
  margin-left: auto;
  cursor: pointer;
  opacity: 0.45;
  transition: color var(--nl-motion-control), background-color var(--nl-motion-control), opacity var(--nl-motion-control), transform var(--nl-motion-control);
}
#sugg .sugg-del-btn:hover {
  opacity: 1;
  color: var(--nl-status-danger);
  background: rgba(239, 68, 68, 0.18);
}
#sugg div.sel, #sugg div:hover {
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  color: var(--ink);
}

/* ── Focus Search Mode ────────────────────────────────────────── */
/* The scrim sits at z-index 3 and <main id="page"> at 5, so it can only darken
   the canvas: it never covers the clock, the board or the dock. Those have to
   step back on their own, which they do by fading — the blur and the scale
   they used to apply as well turned the page into coloured smudges and made
   the whole composition twitch. */
#dim {
  position: fixed;
  inset: 0;
  z-index: var(--nl-z-scrim);
  background: rgba(3, 7, 18, 0.32);
  opacity: 0;
  visibility: hidden;
  backdrop-filter: blur(0px);
  -webkit-backdrop-filter: blur(0px);
  transition: opacity var(--nl-transition-panel), visibility 0s var(--nl-motion-panel);
  pointer-events: none;
}

body.searching #dim {
  opacity: 1;
  visibility: visible;
  background: rgba(3, 7, 18, 0.5);
  transition: opacity var(--nl-transition-panel), visibility 0s;
  pointer-events: auto;
}

#dim.on {
  opacity: 1;
  visibility: visible;
  z-index: var(--nl-z-drawer-scrim);
  pointer-events: auto;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

/* The bar keeps its width. Growing it on focus moved the caret out from under
   the cursor that had just clicked it. */
body.searching #searchwrap {
  z-index: var(--nl-z-float);
}

body.searching #search {
  /* Entering search is a mode change the user asked for, and the bar is the
     thing that changed. Without this it swaps its border and shadow with no
     motion at all, which reads as a repaint rather than a response. */
  animation: pop var(--nl-transition-panel);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--card-tint, #141c34) 94%, transparent);
  box-shadow: var(--nl-shadow-3), var(--nl-ring);
  filter: none !important;
}

/* The scrim covers them now. It used to sit below the page, which is why every
   element had to step back on its own — three rules doing one rule's job, and
   the reason the first attempt at simplifying this failed. */
body.searching #board,
body.searching #hero,
body.searching #hiddenDock {
  pointer-events: none;
}

/* ── Board & Card Containers ──────────────────────────────────── */
/* The board is a stack of rows and a row is one or more lines of folders
   (grid.js flowRows decides where a line breaks). One gap everywhere: between
   rows, between the lines of a row, and between folders on a line — it follows
   the Spacing setting, a step wider than the gap between tiles. */
#board {
  --board-gap: calc(var(--grid-gap, 12px) + 4px);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--board-gap);
  width: min(96vw, 1480px);
  contain: layout style;
  transition: opacity var(--nl-transition-control);
}
.board-row {
  display: flex;
  flex-direction: column;
  gap: var(--board-gap);
}
.board-line {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  gap: var(--board-gap);
}
/* Spans the board and has no height: what grid.js watches for a new width. */
.board-width-probe {
  position: absolute;
  inset: 0 0 auto;
  height: 0;
  visibility: hidden;
  pointer-events: none;
}
/* Measuring: every folder at the width its own columns give it. */
#board.is-measuring .board-line > .card { flex-grow: 0 !important; max-width: none !important; }

/* Fitted: each line runs edge to edge and its folders share one height. The
   room left on a line is shared out by columns, so a four-column folder gains
   twice what a two-column one does and tiles keep one rhythm along the line.
   There is no ceiling on the stretch — an edge that stops short is exactly
   what Fitted is chosen to avoid — so the tiles are what hold back instead:
   past a comfortable spacing they gather in the middle of their folder. */
#board[data-layout="fitted"]:not(.is-measuring) .board-line { align-items: stretch; }
#board[data-layout="fitted"]:not(.is-measuring) .board-line > .card { flex: var(--span, 2) 1 var(--natural-w, auto); }
/* The tiles spread with the folder, each centred in its share of the width,
   and a folder with fewer tiles than columns spreads the tiles it has. Past a
   comfortable spacing they stop spreading and gather in the middle, so one
   small folder alone on a wide line is a banner, not four scattered icons. No
   transition on the tracks: measuring swaps them for a moment, and an eased
   swap is read back half-way through. */
#board[data-layout="fitted"] .grid { transition: none; }
#board[data-layout="fitted"]:not(.is-measuring) .grid:not([data-cols="1"]),
.drag-lift[data-layout="fitted"] .grid:not([data-cols="1"]) {
  grid-template-columns: repeat(var(--cols-used, 2), minmax(var(--tw), calc(var(--tw) * 1.7)));
  justify-content: center;
  justify-items: center;
}

body.minimal #board {
  display: none !important;
}

.card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px 20px 18px;
  border-radius: var(--card-radius);
  border: 1px solid var(--glass-border);
  /* Theme glass tint × user translucency — both actually take effect */
  background: linear-gradient(
    160deg,
    rgb(from var(--card-tint, #141c34) r g b / calc(var(--glass-opacity, 0.7) * 0.62)),
    rgb(from var(--card-tint-deep, #090e1c) r g b / calc(var(--glass-opacity, 0.7) * 0.52))
  );
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate, 190%));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate, 190%));
  /* Depth + accent halo both scale with the Ambient Glow slider;
     specular top line scales with the Border Sheen slider */
  box-shadow:
    0 22px 60px rgba(0, 0, 0, calc(0.18 + 0.35 * var(--card-glow-intensity, 0.4))),
    0 0 34px color-mix(in srgb, var(--accent) calc(var(--card-glow-intensity, 0.4) * 22%), transparent),
    inset 0 1px 0 rgba(255, 255, 255, calc(0.02 + var(--glass-border-sheen, 0.45) * 0.16));
  contain: layout style;
  transition: box-shadow var(--nl-transition-control), border-color var(--nl-transition-control);
  position: relative;
}

/* First-paint choreography: cards rise in sequence, then never re-animate */

.card:hover {
  border-color: color-mix(in srgb, var(--glass-border) 40%, var(--accent) 22%);
  box-shadow: 0 26px 70px rgba(0, 0, 0, 0.36), 0 0 0 1px color-mix(in srgb, var(--accent) 12%, transparent), inset 0 1px 0 rgba(255, 255, 255, 0.08);
}

.tile.tile-updated {
  animation: tileGlow var(--nl-transition-panel) both;
}

@keyframes tileGlow {
  0% { transform: scale(0.97); opacity: 0.72; }
  60% { transform: scale(1.03); }
  100% { transform: scale(1); opacity: 1; }
}

.cat {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: grab;
  user-select: none;
  border-radius: 8px;
  padding: 2px 4px;
  transition: background var(--nl-transition-control);
}
.cat:hover {
  background: rgba(255, 255, 255, 0.03);
}
.cat s {
  /* A dot, not a word. The empty alt text keeps it out of the accessibility
     tree without hiding the element from the paint. */
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: var(--nl-shadow-1);
  text-decoration: none;
}
.cat b {
  font-size: var(--nl-text-2xs);
  font-weight: 650;
  letter-spacing: var(--nl-track-wide);
  color: var(--dim);
  text-transform: uppercase;
  transition: color var(--nl-transition-control);
  cursor: pointer;
}
.cat:hover b {
  color: var(--ink);
}
.cat i {
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, var(--frost), transparent);
}

.groupGrip, .foldBtn, .tileGrip {
  border: 0;
  color: var(--dim);
  background: transparent;
  cursor: pointer;
}
.groupGrip {
  width: var(--nl-target-min);
  height: var(--nl-target-min);
  border-radius: 7px;
  font-size: var(--nl-text-lg);
  line-height: 24px;
  cursor: grab;
  /* A control that is shown has to be seen: at 0.35 the hint read at under
     2:1, below the 3:1 a control needs. 0.7 clears it on every theme. */
  opacity: 0.7;
  transition: color var(--nl-motion-control), background-color var(--nl-motion-control), opacity var(--nl-motion-control), transform var(--nl-motion-control);
}
.cat:hover .groupGrip, .groupGrip:hover {
  opacity: 1;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.08);
}
.groupGrip:active {
  cursor: grabbing;
  transform: scale(0.92);
}

.foldBtn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--nl-target-min);
  height: var(--nl-target-min);
  border-radius: 8px;
  opacity: 0.7;
  /* transform belongs in this list. Without it the two-pixel nudge on hover
     was a teleport in both directions — the displacement restored, the
     motion not. */
  transition: color var(--nl-transition-control), background-color var(--nl-transition-control),
              opacity var(--nl-transition-control), transform var(--nl-transition-control);
}
.foldBtn svg { width: 16px; height: 16px; }
.foldBtn:hover {
  opacity: 1;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.1);
  transform: translateX(2px);
}

/* Arranging chrome only matters while you are arranging. Where there is a
   pointer to hover with, the grip and the hide button wait for the card; where
   there is not — touch, or a keyboard walking the page — they stay put, since
   a control you cannot reveal is a control you do not have. */
@media (hover: hover) and (pointer: fine) {
  .groupGrip,
  .foldBtn {
    opacity: 0;
  }
  .card:hover .groupGrip,
  .card:focus-within .groupGrip,
  .card:hover .foldBtn,
  .card:focus-within .foldBtn {
    opacity: 0.7;
  }
  .card .groupGrip:hover,
  .card .groupGrip:focus-visible,
  .card .foldBtn:hover,
  .card .foldBtn:focus-visible {
    opacity: 1;
  }
}

.grid {
  display: grid;
  gap: var(--grid-gap);
  flex: 1;
  /* Cards in a row stretch to match the tallest. Centring the rows inside that
     stretch left a gap between a short folder's title and its first icon, which
     read as a rendering fault; starting them lines every folder's first row up. */
  align-content: start;
  transition: grid-template-columns var(--nl-transition-panel);
}
.grid[data-cols="1"] {
  grid-template-columns: minmax(180px, 1fr);
  width: 100%;
}
.grid[data-cols="1"] .tile {
  flex-direction: row;
  justify-content: flex-start;
  gap: 12px;
  width: 100%;
  padding: 5px 8px;
  border-radius: calc(var(--tile-radius) * 0.75);
  background: rgba(255, 255, 255, 0.025);
  border: 1px solid rgba(255, 255, 255, 0.04);
  transition: color var(--nl-motion-control), background-color var(--nl-motion-control), border-color var(--nl-motion-control), opacity var(--nl-motion-control), transform var(--nl-motion-control);
}
.grid[data-cols="1"] .tile:hover {
  background: rgba(255, 255, 255, 0.07);
  border-color: rgba(255, 255, 255, 0.12);
  transform: translateX(3px);
}
.grid[data-cols="1"] .tile .box {
  width: calc(var(--tw) * 0.62);
  height: calc(var(--tw) * 0.62);
  flex-shrink: 0;
}
.grid[data-cols="1"] .tile .lbl {
  max-width: none;
  font-size: var(--nl-text-sm);
  font-weight: 500;
  text-align: left;
}
.grid[data-cols="2"] { grid-template-columns: repeat(2, var(--tw)); }
.grid[data-cols="3"] { grid-template-columns: repeat(3, var(--tw)); }
.grid[data-cols="4"] { grid-template-columns: repeat(4, var(--tw)); }
.grid[data-cols="5"] { grid-template-columns: repeat(5, var(--tw)); }
.grid[data-cols="6"] { grid-template-columns: repeat(6, var(--tw)); }
.grid[data-cols="7"] { grid-template-columns: repeat(7, var(--tw)); }
.grid[data-cols="8"] { grid-template-columns: repeat(8, var(--tw)); }

/* ── Moving folders and bookmarks ───────────────────────────── */
/* The copy in hand. It is fixed to the window and follows the pointer; the
   lift itself is a little scale and tilt on the copy, which let go of both
   as it lands. */
body.board-dragging,
body.board-dragging * {
  cursor: grabbing !important;
  user-select: none;
}
.drag-lift {
  position: fixed;
  z-index: var(--nl-z-overlay);
  margin: 0;
  pointer-events: none;
  transform-origin: 0 0;
  will-change: transform;
}
.drag-lift > .card,
.drag-lift > .grid {
  width: 100%;
  height: 100%;
  margin: 0;
}
.drag-lift > .card {
  box-shadow: var(--nl-shadow-3);
  scale: 1.02;
  rotate: -0.6deg;
  transition: scale var(--nl-transition-control), rotate var(--nl-transition-control);
}
.drag-lift > .grid { display: block; }
.drag-lift .tile { width: 100%; }
.drag-lift .tile .box {
  box-shadow: var(--nl-shadow-3);
  scale: 1.08;
  transition: scale var(--nl-transition-control);
}
.drag-lift.is-landing > .card,
.drag-lift.is-landing .tile .box { scale: 1; rotate: 0deg; }

/* Where the thing in hand came from, or where it is about to go: the same
   outline for both, dashed, with nothing inside. */
.card.drag-source {
  background: transparent;
  border-style: dashed;
  border-color: color-mix(in srgb, var(--nl-accent) 50%, transparent);
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
.card.drag-source > * { visibility: hidden; }
.card.drag-source.drag-source-home { border-color: var(--nl-accent); }
.tile.drag-source > * { visibility: hidden; }
.tile.drag-source::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  translate: -50% 0;
  width: var(--tw);
  aspect-ratio: 1;
  border-radius: var(--tile-radius);
  border: 1.5px dashed color-mix(in srgb, var(--nl-accent) 65%, transparent);
  background: color-mix(in srgb, var(--nl-accent) 8%, transparent);
}
.grid[data-cols="1"] .tile.drag-source::before {
  inset: 0;
  width: auto;
  aspect-ratio: auto;
  translate: none;
  border-radius: calc(var(--tile-radius) * 0.75);
}
/* The real thing waits, unseen, for its copy to land on it. */
.drag-landing { visibility: hidden; }
/* A folder that follows the browser cannot take a bookmark from the board. */
.card.drop-refused {
  border-style: dashed;
  border-color: var(--nl-status-danger);
}

/* Where a folder will land: a bar in the gap between two folders, or across
   the gap between two rows with the words for it. */
.board-marker {
  position: fixed;
  /* Over the copy in hand: the line runs through the point it will land. */
  z-index: var(--nl-z-menu);
  border-radius: 999px;
  background: var(--nl-accent);
  box-shadow: var(--nl-shadow-1);
  pointer-events: none;
  transition: left var(--nl-transition-fast), top var(--nl-transition-fast), width var(--nl-transition-fast), height var(--nl-transition-fast);
}
.board-marker[hidden] { display: none; }
.board-marker-label { display: none; }
.board-marker.is-row .board-marker-label {
  display: block;
  position: absolute;
  /* Beside the pointer, just above the line: the folder in hand hangs below
     the point it was picked up by, so this is the one place it never covers. */
  left: var(--label-x, 50%);
  bottom: 10px;
  translate: -50% 0;
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--nl-accent);
  color: var(--nl-on-accent);
  font-size: var(--nl-text-xs);
  font-weight: 650;
  line-height: 1.2;
  white-space: nowrap;
  box-shadow: var(--nl-shadow-1);
}

/* Forced colours paint every background the same: the marker and the hole
   keep the system's highlight so the landing is still shown. */
@media (forced-colors: active) {
  .board-marker,
  .board-marker.is-row .board-marker-label { forced-color-adjust: none; background: Highlight; color: HighlightText; }
  .card.drag-source,
  .tile.drag-source::before { border-color: Highlight; }
  body.arranging .board-row { outline-color: CanvasText; }
}

/* ── Arranging ────────────────────────────────────────────────── */
/* The bar: the few choices that shape the board, over a line that says what
   the one under the pointer does. Fixed to the bottom of the window, above the
   board and below every menu and dialog that can open over it. */
.arrange-bar {
  position: fixed;
  left: 50%;
  bottom: max(18px, env(safe-area-inset-bottom));
  translate: -50% 0;
  z-index: var(--nl-z-float);
  display: grid;
  justify-items: center;
  gap: 8px;
  width: max-content;
  max-width: calc(100vw - 32px);
  pointer-events: none;
  animation: arrangeBarIn var(--nl-transition-enter) both;
}
.arrange-bar[hidden] { display: none; }
@keyframes arrangeBarIn {
  from { opacity: 0; translate: -50% 14px; }
  to { opacity: 1; translate: -50% 0; }
}
.arrange-hint {
  margin: 0;
  max-width: 68ch;
  padding: 6px 14px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--nl-surface-card) 86%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  color: var(--nl-text-secondary);
  font-size: var(--nl-text-xs);
  line-height: 1.45;
  text-align: center;
}
.arrange-controls {
  pointer-events: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 7px;
  border-radius: var(--nl-radius-xl);
  border: 1px solid var(--nl-border);
  background: color-mix(in srgb, var(--nl-surface-elevated) 92%, transparent);
  -webkit-backdrop-filter: blur(24px) saturate(140%);
  backdrop-filter: blur(24px) saturate(140%);
  box-shadow: var(--nl-shadow-3);
}
.arrange-title {
  padding-inline: 10px 4px;
  color: var(--nl-text-primary);
  font-size: var(--nl-text-sm);
  font-weight: 650;
  white-space: nowrap;
}
.arrange-layouts {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  border-radius: var(--nl-radius-md);
  background: color-mix(in srgb, var(--nl-text-primary) 7%, transparent);
}
.arrange-layout,
.arrange-action {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: var(--nl-target-min);
  padding: 0 12px;
  border: 0;
  border-radius: var(--nl-radius-sm);
  background: transparent;
  color: var(--nl-text-secondary);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--nl-motion-control), background-color var(--nl-motion-control), box-shadow var(--nl-motion-control);
}
/* On the track the secondary ink loses a little against the tint, so the
   options not chosen sit between secondary and primary. */
.arrange-layout { min-height: var(--nl-target-min); color: color-mix(in oklch, var(--nl-text-secondary) 55%, var(--nl-text-primary)); }
.arrange-layout[aria-checked="true"] {
  background: var(--nl-surface-card);
  color: var(--nl-text-primary);
  box-shadow: var(--nl-shadow-1);
}
.arrange-layout:hover,
.arrange-action:hover:not(:disabled) {
  color: var(--nl-text-primary);
  background-color: color-mix(in srgb, var(--nl-text-primary) 8%, transparent);
}
.arrange-layout[aria-checked="true"]:hover { background-color: var(--nl-surface-card); }
.arrange-action:disabled { opacity: 0.45; cursor: default; }
.arrange-action[hidden] { display: none; }
.layout-glyph { width: 24px; height: 16px; flex: none; fill: currentColor; }
.arrange-action svg { width: 16px; height: 16px; flex: none; }
.arrange-divider { align-self: stretch; width: 1px; margin: 6px 2px; background: var(--nl-border); }
.arrange-done { min-height: var(--nl-target-min); padding-inline: 18px; }

/* The page while arranging: the clock and search step back, the rows show
   their outline, and every folder wears its handle and its edge. */
body.arranging #hero,
body.arranging #searchwrap {
  opacity: 0.28;
  pointer-events: none;
  transition: opacity var(--nl-transition-panel);
}
body.arranging #page { padding-bottom: 150px; }
body.arranging #board { gap: calc(var(--board-gap) * 2 + 10px); }
body.arranging .board-row {
  border-radius: calc(var(--nl-radius-lg) + 9px);
  outline: 1px dashed color-mix(in srgb, var(--nl-text-primary) 24%, transparent);
  outline-offset: 9px;
}
body.arranging #board .card { cursor: grab; }
body.arranging #board .tile { cursor: grab; }
body.arranging #board .card,
body.arranging #board .tile { touch-action: none; }
body.arranging #board .card .groupGrip,
body.arranging #board .card .foldBtn,
body.arranging #board .card-resize-handle { opacity: 1; }
body.arranging #board .card .groupGrip { color: var(--nl-text-primary); }
/* While arranging, the width handle is a pill on the folder's right edge —
   what the hint calls its edge — with the same 40px to aim at. */
body.arranging #board .card-resize-handle {
  top: 50%;
  bottom: auto;
  right: calc(var(--nl-target-min) / -2);
  translate: 0 -50%;
  width: var(--nl-target-min);
  height: 56px;
  border-radius: 0;
  cursor: ew-resize;
}
body.arranging #board .card-resize-handle::after {
  top: 8px;
  bottom: 8px;
  right: auto;
  left: 50%;
  translate: -50% 0;
  width: 10px;
  height: auto;
  border: 1px solid var(--nl-border-strong);
  border-radius: 999px;
  background:
    linear-gradient(90deg, transparent 3px, var(--nl-text-secondary) 3px, var(--nl-text-secondary) 4px, transparent 4px, transparent 5px, var(--nl-text-secondary) 5px, var(--nl-text-secondary) 6px, transparent 6px) center / 100% 45% no-repeat,
    var(--nl-surface-elevated);
  box-shadow: var(--nl-shadow-1);
}
body.arranging #board .card-resize-handle:hover::after,
body.arranging #board .card.is-resizing .card-resize-handle::after { border-color: var(--nl-accent); }

/* ── Folder Resize Handle & Indicator ─────────────────────────── */
.card-resize-handle {
  position: absolute;
  bottom: 0;
  right: 0;
  width: 28px;
  height: 28px;
  cursor: nwse-resize;
  z-index: var(--nl-z-raised);
  opacity: 0;
  transition: opacity var(--nl-transition-control), background var(--nl-transition-control);
  border-bottom-right-radius: var(--card-radius);
}

.card-resize-handle::after {
  content: "";
  position: absolute;
  bottom: 8px;
  right: 8px;
  width: 8px;
  height: 8px;
  border-right: 2.5px solid var(--dim);
  border-bottom: 2.5px solid var(--dim);
  border-bottom-right-radius: 2px;
  transition: border-color var(--nl-motion-control);
}

.card:hover .card-resize-handle,
.card:focus-within .card-resize-handle,
.card.is-resizing .card-resize-handle {
  opacity: 1;
}

/* Resizing works by dragging the corner, and a touch device has no hover to
   reveal it with. It shares the rule the grip and the hide button follow. */
@media not all and (hover: hover) {
  .card-resize-handle { opacity: 0.5; }
}

.card-resize-handle:hover::after,
.card.is-resizing .card-resize-handle::after {
  border-color: var(--accent);
}

.resize-indicator-pill {
  position: absolute;
  bottom: -36px;
  left: 50%;
  /* Centred with translate, which reduced motion's "transform: none" leaves
     in place; the slide in is the transform. */
  translate: -50% 0;
  transform: translateY(10px);
  padding: 6px 16px;
  border-radius: 20px;
  background: rgba(14, 22, 42, 0.85);
  border: 1px solid rgba(255, 255, 255, 0.15);
  color: #ffffff;
  font-size: var(--nl-text-xs);
  font-weight: 600;
  box-shadow: var(--nl-shadow-2);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--nl-transition-control), transform var(--nl-transition-control), visibility 0s var(--nl-motion-control), border-color var(--nl-motion-control), box-shadow var(--nl-motion-control);
  white-space: nowrap;
  z-index: var(--nl-z-float);
}

.resize-indicator-pill.show {
  opacity: 1;
  visibility: visible;
  border-color: var(--accent);
  box-shadow: var(--nl-shadow-2);
  transform: translateY(0);
  transition: opacity var(--nl-transition-control), transform var(--nl-transition-enter), visibility 0s, border-color var(--nl-motion-control), box-shadow var(--nl-motion-control);
}

.card.is-resizing {
  border-color: var(--accent);
  box-shadow: var(--nl-shadow-3), var(--nl-ring);
  z-index: var(--nl-z-float);
}

/* Live column-count pill injected while a folder is being resized */
.card-resize-pill {
  position: absolute;
  bottom: -38px;
  left: 50%;
  translate: -50% 0;
  padding: 6px 16px;
  border-radius: 20px;
  background: rgba(14, 22, 42, 0.9);
  border: 1px solid var(--accent);
  color: #ffffff;
  font-size: var(--nl-text-xs);
  font-weight: 600;
  white-space: nowrap;
  box-shadow: var(--nl-shadow-2);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  pointer-events: none;
  z-index: var(--nl-z-float);
  animation: pillIn var(--nl-transition-pop-fast) both;
}

@keyframes pillIn {
  from { opacity: 0; transform: translateY(8px) scale(0.9); }
  to { opacity: 1; transform: none; }
}

.brk {
  flex-basis: 100%;
  height: 0;
}

/* ── Tile Items & Icons ───────────────────────────────────────── */
.tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  text-decoration: none;
  position: relative;
}

/* ── The board as a command line ──────────────────────────────────
   While a command is being typed the page stays in view behind a lighter
   dim, so a previewed theme or sky can be judged, and the folder or bookmark
   a command would act on is lifted above the rest. */
body.searching.commanding #dim { background: rgba(3, 7, 18, 0.12); }
.light-ui body.searching.commanding #dim { background: rgba(255, 255, 255, 0.1); }
#board.command-preview .card:not(.command-target):not(:has(.command-target)) { opacity: 0.45; }
#board.command-preview .tile:not(.command-target) { opacity: 0.55; }
#board .command-target { outline: 2px solid var(--nl-accent); outline-offset: 4px; border-radius: var(--nl-radius-lg); }
#board .tile.command-target { opacity: 1; border-radius: var(--nl-radius-md); }
.sugg-command .sugg-command-mark { width: 18px; flex: none; color: var(--nl-accent-text); font: 700 var(--nl-text-md) / 1 var(--font-mono); text-align: center; }
.sugg-command .sugg-command-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.sugg-command-help .sugg-command-text strong { font-family: var(--font-mono); font-size: var(--nl-text-sm); font-weight: 600; color: var(--nl-text-primary); }
.sugg-command-help .sugg-command-text small { color: var(--nl-text-secondary); font-size: var(--nl-text-xs); }

/* The first stop for a keyboard: straight past the clock and search to the
   board. Out of sight until it is focused. Moved with `translate`, not
   `transform`: reduced motion drops every transform, which left it on screen. */
.skip-link {
  position: fixed;
  top: 12px;
  left: 12px;
  z-index: var(--nl-z-toast);
  padding: 10px 16px;
  border-radius: var(--nl-radius-sm);
  background: var(--nl-accent);
  color: var(--nl-on-accent);
  font-weight: 650;
  text-decoration: none;
  translate: 0 -160%;
}
.skip-link:focus-visible { translate: none; }

/* Alt held: each of the first nine tiles shows the digit that opens it. */
body.alt-held .tile[data-shortcut]::after {
  content: attr(data-shortcut);
  position: absolute;
  top: -6px;
  left: 50%;
  translate: -50% 0;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  display: grid;
  place-items: center;
  border-radius: var(--nl-radius-sm);
  background: var(--nl-accent);
  color: var(--nl-on-accent);
  font: 700 var(--nl-text-2xs) / 1 var(--font-mono);
  box-shadow: var(--nl-shadow-1);
  pointer-events: none;
}


.box {
  position: relative;
  width: var(--tw);
  aspect-ratio: 1;
  border-radius: var(--tile-radius);
  overflow: hidden;
  display: grid;
  place-items: center;
  border: 1px solid rgba(255, 255, 255, 0.09);
  background:
    radial-gradient(120% 120% at 30% 18%, color-mix(in srgb, var(--c, var(--dim)) 12%, transparent), transparent 62%),
    linear-gradient(150deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.015));
  box-shadow: var(--nl-shadow-2);
  transition: transform var(--nl-transition-panel), box-shadow var(--nl-transition-panel), border-color var(--nl-motion-panel), background var(--nl-motion-panel);
}

.box::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 32%, rgba(255, 255, 255, 0.18) 48%, transparent 62%);
  transform: translateX(-170%);
  transition: transform var(--nl-transition-panel);
}

.box svg, .box img {
  width: 46%;
  height: 46%;
  fill: var(--c, var(--dim));
  color: var(--c, var(--dim));
  /* No shadow. The plate already separates the mark from the board, and the
     rule that makes toned icons legible sets filter: none on exactly these
     elements — so a shadow declared here reached monograms and nothing else,
     leaving two kinds of tile on one board that did not match. */
  transition: transform var(--nl-transition-panel);
  object-fit: contain;
  pointer-events: none;
  user-select: none;
}

.box .mono {
  font-size: calc(var(--tw) * 0.38);
  font-weight: 700;
  color: var(--c, var(--dim));
  line-height: 1;

  pointer-events: none;
  user-select: none;
}

.lbl {
  max-width: calc(var(--tw) + 16px);
  font-size: var(--nl-text-xs);
  font-weight: 450;
  color: var(--dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color var(--nl-motion-control);
  user-select: none;
  pointer-events: none;
}

.tile:hover .box, .tile:focus-visible .box {
  transform: translateY(-5px) scale(1.05);
  border-color: color-mix(in srgb, var(--c, var(--dim)) 55%, rgba(255, 255, 255, 0.12));
  background:
    radial-gradient(120% 120% at 30% 18%, color-mix(in srgb, var(--c, var(--dim)) 28%, transparent), transparent 66%),
    linear-gradient(150deg, rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.02));
  /* The plate and its border already say which tile this is. A halo in
     the bookmark's own colour on top of them is decoration, and it was on
     by default rather than chosen. */
  box-shadow: var(--nl-shadow-2);
}

/* Dynamic Hover Styles */
body.hover-lift .tile:hover .box {
  transform: translateY(-6px) scale(1.04);
}
body.hover-glow .tile:hover .box {
  transform: translateY(-2px);
  border-color: var(--accent);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),
    0 0 26px var(--accent-glow),
    0 12px 30px rgba(0, 0, 0, 0.35);
}
body.hover-scale .tile:hover .box {
  transform: scale(1.10);
}
body.hover-none .tile:hover .box {
  transform: none;
}

.tile:hover .box::after {
  transform: translateX(170%);
}
.tile:hover .box svg, .tile:hover .box img {
  transform: scale(1.12);
}
.tile:hover .lbl {
  color: var(--ink);
}
.tile:active .box {
  transform: translateY(-2px) scale(0.97);
}

.tile {
  cursor: pointer;
}
.tile.dragging,
.tile:active {
  cursor: grabbing;
}

/* Drag states */
.ghost {
  opacity: 0.35;
  filter: saturate(1.2);
  transform: scale(0.94);
}
.tile.drop-before::before, .tile.drop-after::after {
  content: "";
  position: absolute;
  top: 6px;
  bottom: 24px;
  width: 3.5px;
  border-radius: 4px;
  background: var(--accent);
  box-shadow: 0 0 16px 2px var(--accent-glow);
  z-index: var(--nl-z-raised);
  animation: pulse 1.2s ease-in-out infinite;
}
.tile.drop-before::before { left: -7px; }
.tile.drop-after::after { right: -7px; }
.card.group-before, .card.group-after {
  position: relative;
}
.card.group-before::before, .card.group-after::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 4px;
  border-radius: 4px;
  background: var(--accent);
  box-shadow: 0 0 20px 4px var(--accent-glow), 0 0 6px var(--accent);
  z-index: var(--nl-z-raised);
  animation: pulse 1.2s ease-in-out infinite;
}
.card.group-before::before { left: -10px; }
.card.group-after::after { right: -10px; }

.card.dropping {
  border-color: var(--accent);
  box-shadow: var(--nl-shadow-3), var(--nl-ring);
}

/* ── Folded folders ─────────────────────────────────────────────── */
/* A folder somebody folded away is one they chose not to see, so the dock does
   not show them either. At rest it is one quiet mark under the board — a
   folder and how many are folded, a little dimmer than the text, with no glass
   of its own — and a pointer, the keyboard or a tap opens it into chips of the
   same glass as the gear and the fit switch. The chips keep their places while
   it is closed, so opening it moves nothing and the fit measures the same dock
   either way; until it opens, only the middle of it, where the mark is,
   answers the pointer. It closes a moment after it is left. */
#hiddenDock {
  position: relative;
  display: none;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 14px auto 0;
  padding: 4px;
  border-radius: var(--nl-radius-md);
  animation: fadeIn var(--nl-transition-panel) both;
  z-index: var(--nl-z-chrome);
  clip-path: inset(0 calc(50% - 44px) round var(--nl-radius-md));
  transition: clip-path var(--nl-transition-enter);
  transition-delay: var(--nl-motion-panel);
}
#hiddenDock:is(:hover, :focus-within, .is-open) {
  clip-path: inset(-8px round calc(var(--nl-radius-md) + 8px));
  transition-delay: 0s;
}
#hiddenDock:empty {
  display: none !important;
}

/* The mark: over the middle of the chips, and gone into them when they open. */
.dock-fold {
  position: absolute;
  top: 50%;
  left: 50%;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  translate: -50% -50%;
  color: var(--nl-text-secondary);
  font-size: var(--nl-text-xs);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  opacity: .55;
  transition: opacity var(--nl-transition-control), transform var(--nl-transition-enter);
  transition-delay: var(--nl-motion-panel);
}
.dock-fold svg {
  width: 15px;
  height: 15px;
}
#hiddenDock:is(:hover, :focus-within, .is-open) .dock-fold {
  opacity: 0;
  transform: scale(.8);
  pointer-events: none;
  transition-delay: 0s;
}

.restoreFolder {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0 14px 0 12px;
  border-radius: var(--nl-radius-md);
  border: 1px solid var(--nl-polish-hairline);
  background: color-mix(in srgb, var(--nl-surface-card-deep) 76%, transparent);
  box-shadow: var(--nl-shadow-2);
  color: var(--nl-text-secondary);
  font-family: inherit;
  font-size: var(--nl-text-sm);
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
  user-select: none;
  opacity: 0;
  /* A shift, not a shrink: closed, a chip is still its full 40px target. */
  transform: translateY(4px);
  pointer-events: none;
  transition: opacity var(--nl-transition-control), transform var(--nl-transition-enter), color var(--nl-transition-control), background-color var(--nl-transition-control), border-color var(--nl-transition-control);
  transition-delay: var(--nl-motion-fast);
}
#hiddenDock:is(:hover, :focus-within, .is-open) .restoreFolder {
  opacity: 1;
  transform: none;
  pointer-events: auto;
  backdrop-filter: blur(16px) saturate(125%);
  -webkit-backdrop-filter: blur(16px) saturate(125%);
}
#hiddenDock .restoreFolder:hover {
  color: var(--nl-text-primary);
  border-color: color-mix(in srgb, var(--nl-accent) 44%, var(--nl-border));
  background: var(--nl-polish-surface-solid);
}

.dockFolderIcon {
  width: 15px;
  height: 15px;
  color: var(--nl-text-tertiary);
  flex-shrink: 0;
  transition: color var(--nl-transition-control);
}
.restoreFolder:hover .dockFolderIcon {
  color: var(--accent);
}

.dockFolderName {
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dockFolderCount {
  font-size: var(--nl-text-xs);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--nl-text-tertiary);
  transition: color var(--nl-transition-control);
}
.restoreFolder:hover .dockFolderCount {
  color: var(--nl-text-secondary);
}

@media (prefers-reduced-motion: reduce) {
  #hiddenDock .restoreFolder, #hiddenDock .dock-fold { transform: none; }
}
@media (forced-colors: active) {
  /* With the system's colours there is no mark to find: the chips are there. */
  #hiddenDock { clip-path: none; }
  .dock-fold { display: none; }
  .restoreFolder { opacity: 1; transform: none; pointer-events: auto; border-color: ButtonText; }
}

/* ── Settings Gear Button ─────────────────────────────────────── */
#gear {
  position: fixed;
  right: 18px;
  bottom: 16px;
  z-index: var(--nl-z-chrome);
  width: 44px;
  height: 44px;
  border-radius: 14px;
  display: grid;
  place-items: center;
  cursor: pointer;
  border: 1px solid var(--glass-border);
  background: var(--glass);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  opacity: 0.55;
  transition: opacity var(--nl-motion-control), border-color var(--nl-motion-control), transform var(--nl-motion-control), background var(--nl-motion-control);
}
#gear svg {
  width: 21px;
  height: 21px;
  fill: var(--dim);
  transition: fill var(--nl-motion-control);
}
#gear:hover {
  opacity: 1;
  border-color: var(--accent);
  background: color-mix(in srgb, var(--card-tint, #141c34) 75%, transparent);
  transform: translateY(-2px);
}
/* This was removed once, on the grounds that it drew the eye to a control that
   should stay out of the way. That reasoning does not survive contact with the
   fact that it is a hover state: it fires only once the pointer is already
   there, and nothing can attract attention to what is already being looked at.
   Removing motion from a page that assembles itself on load is a saving.
   Removing it from a response to the user is just a deader interface. */
#gear svg {
  transition: fill var(--nl-transition-control),
              transform var(--nl-transition-panel);
}
#gear:hover svg {
  fill: var(--ink);
  transform: rotate(75deg);
}
/* The shared press rule is :where(), so it carries no specificity and the hover
   lift above beat it — pressing the one control with a hover transform gave no
   press response at all. */
#gear:active {
  transform: translateY(-2px) scale(.97);
}

/* ── One page fit, on the page ────────────────────────────────── */
/* The drawer's switch, one press from the page, with its name on it and a
   small switch that says which way it is. It sits in the band above the
   clock, which is empty at every size. Beside the gear it would lie over the
   last row of a board that runs under the corner. Absolute, not fixed: on the
   ordinary page it scrolls away with the band instead of floating over the
   bookmarks. On a fitted page nothing scrolls, and page-fit.js keeps the
   page clear of it.

   Most people turn the fit on once and never touch it again, so at rest the
   switch is a star: the Nordlys mark, a little dimmer than the text, one more
   star in the sky. A pointer or the keyboard turns it into the switch — the
   glass unfolds leftward out of the star while the star turns and shrinks
   into the knob — and it folds back a moment after it is left. It glints once
   as the page opens, so the eye finds it, and then holds still: an animation
   that ran for ever would keep the page painting at the display's rate, which
   is what the sky's frame budget exists to stop, and a star at rest blurs
   nothing behind it. Lit in the accent while the fit is on. Its box never
   changes, so the room the fit keeps clear is the same either way. */
.fit-toggle {
  position: absolute;
  top: 16px;
  right: 20px;
  z-index: var(--nl-z-chrome);
  display: inline-flex;
  align-items: center;
  gap: 10px;
  box-sizing: border-box;
  min-height: var(--nl-target-min);
  max-width: calc(100% - 40px);
  padding: 0 12px 0 14px;
  border: 1px solid transparent;
  border-radius: var(--nl-radius-md);
  background: transparent;
  box-shadow: none;
  color: var(--nl-text-secondary);
  font: inherit;
  font-size: var(--nl-text-sm);
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
  /* Folded, only the star's corner of the switch is drawn. */
  clip-path: inset(0 0 0 calc(100% - var(--nl-target-min)) round var(--nl-radius-md));
  transition: clip-path var(--nl-transition-enter), color var(--nl-transition-control), background-color var(--nl-transition-control), border-color var(--nl-transition-control), box-shadow var(--nl-transition-control), backdrop-filter var(--nl-transition-control);
  transition-delay: var(--nl-motion-panel);
}
.fit-toggle:is(:hover, :focus-visible, .is-open) {
  /* Past the box on every side, so the focus ring is not cut off. */
  clip-path: inset(-6px round calc(var(--nl-radius-md) + 6px));
  border-color: var(--nl-polish-hairline);
  background: color-mix(in srgb, var(--nl-surface-card-deep) 76%, transparent);
  box-shadow: var(--nl-shadow-2);
  backdrop-filter: blur(16px) saturate(125%);
  -webkit-backdrop-filter: blur(16px) saturate(125%);
  transition-delay: 0s;
}
.fit-toggle:hover {
  color: var(--nl-text-primary);
  border-color: color-mix(in srgb, var(--nl-accent) 44%, var(--nl-border));
  background: var(--nl-polish-surface-solid);
}
.fit-toggle-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  opacity: 0;
  transform: translateX(8px);
  transition: opacity var(--nl-transition-control), transform var(--nl-transition-enter);
  transition-delay: var(--nl-motion-fast);
}
.fit-toggle:is(:hover, :focus-visible, .is-open) .fit-toggle-label {
  opacity: 1;
  transform: none;
}
.fit-toggle-track {
  position: relative;
  flex: none;
  box-sizing: border-box;
  width: 28px;
  height: 16px;
  border: 1.5px solid color-mix(in srgb, currentColor 70%, transparent);
  border-radius: 9999px;
  opacity: 0;
  transform: scale(.5);
  transition: opacity var(--nl-transition-control), transform var(--nl-transition-pop-panel), background-color var(--nl-transition-control), border-color var(--nl-transition-control);
  transition-delay: var(--nl-motion-fast);
}
.fit-toggle:is(:hover, :focus-visible, .is-open) .fit-toggle-track {
  opacity: 1;
  transform: none;
}
/* The star sits on the track, and becomes its knob: towards the knob's left
   end while the fit is off, its right end while it is on. */
.fit-toggle-star {
  --knob: -7.5px;
  position: absolute;
  top: 50%;
  right: 18px;
  width: 16px;
  height: 16px;
  margin-top: -8px;
  display: grid;
  place-items: center;
  color: var(--nl-text-secondary);
  opacity: .55;
  pointer-events: none;
  transition: transform var(--nl-transition-enter), opacity var(--nl-transition-control), color var(--nl-transition-control);
  transition-delay: var(--nl-motion-panel);
}
.fit-toggle-star svg {
  width: 14px;
  height: 14px;
  fill: currentColor;
}
.fit-toggle[aria-checked="true"] .fit-toggle-star {
  --knob: 4.5px;
  color: var(--accent);
  opacity: .9;
}
.fit-toggle:is(:hover, :focus-visible, .is-open) .fit-toggle-star {
  opacity: 0;
  transform: translateX(var(--knob)) rotate(135deg) scale(.35);
  transition-delay: 0s;
}
.fit-toggle.glint .fit-toggle-star {
  animation-name: fitStarGlint;
  animation-duration: calc(var(--nl-motion-enter) * 4);
  animation-timing-function: var(--nl-ease-state);
}
@keyframes fitStarGlint {
  40% { opacity: 1; transform: rotate(45deg) scale(1.4); }
  100% { transform: rotate(90deg); }
}
@media (prefers-reduced-motion: reduce) {
  .fit-toggle:is(:hover, :focus-visible, .is-open) .fit-toggle-star { transform: none; }
  .fit-toggle.glint .fit-toggle-star { animation: none; }
}
.fit-toggle-track::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: currentColor;
  transition: transform var(--nl-transition-pop), background-color var(--nl-transition-control);
}
.fit-toggle[aria-checked="true"] {
  color: var(--nl-text-primary);
}
.fit-toggle[aria-checked="true"]:is(:hover, :focus-visible, .is-open) {
  border-color: color-mix(in srgb, var(--nl-accent) 55%, var(--nl-border));
}
.fit-toggle[aria-checked="true"] .fit-toggle-track {
  background: var(--accent);
  border-color: var(--accent);
}
.fit-toggle[aria-checked="true"] .fit-toggle-track::after {
  transform: translateX(12px);
  background: #060c1a;
}
/* Arranging pauses the fit and has a bar of its own. */
body.arranging .fit-toggle { display: none; }
@media (forced-colors: active) {
  /* No star to find: with the system's colours the switch is simply there. */
  .fit-toggle { clip-path: none; border-color: ButtonText; }
  .fit-toggle-label, .fit-toggle-track { opacity: 1; transform: none; }
  .fit-toggle-star { display: none; }
  .fit-toggle-track { border-color: ButtonText; }
  .fit-toggle-track::after { background: ButtonText; }
  .fit-toggle[aria-checked="true"] .fit-toggle-track { background: Highlight; border-color: Highlight; }
  .fit-toggle[aria-checked="true"] .fit-toggle-track::after { background: HighlightText; }
}

/* Below this the reservation would overflow the window, and a scrollbar costs
   more than a clock that shifts when seconds are switched on. */
@media (max-width: 560px) {
  #clock { min-width: 0; }
}

@media (max-width: 480px) {
  #gear { top: 12px; right: 12px; bottom: auto; }
  /* The gear comes up to the top corner here; the switch sits beside it,
     centred on it (polish.css moves both 2px in below 420px). */
  .fit-toggle { top: 14px; right: 64px; max-width: calc(100% - 76px); }
}

/* ── Frosted Glass Bookmark Context Menu ──────────────────────── */
.glass-context-menu {
  position: fixed;
  z-index: var(--nl-z-menu);
  min-width: 190px;
  background: rgba(11, 18, 36, 0.94);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 14px;
  padding: 6px;
  box-shadow: var(--nl-shadow-3);
  backdrop-filter: blur(28px) saturate(1.8);
  -webkit-backdrop-filter: blur(28px) saturate(1.8);
  display: flex;
  flex-direction: column;
  gap: 2px;
  opacity: 0;
  visibility: hidden;
  /* --origin is set from JS so the menu grows out of the cursor */
  transform-origin: var(--origin, top left);
  transform: scale(0.92);
  transition: opacity var(--nl-transition-fast), transform var(--nl-transition-pop-fast), visibility 0s var(--nl-motion-fast);
  user-select: none;
}

.glass-context-menu.open {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity var(--nl-transition-fast), transform var(--nl-transition-enter-fast), visibility 0s;
}

.ctx-header {
  padding: 4px 10px 6px;
  font-size: var(--nl-text-2xs);
  font-weight: 700;
  letter-spacing: var(--nl-track-wide);
  text-transform: uppercase;
  color: var(--nl-accent-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
  margin-bottom: 4px;
  user-select: none;
}

.ctx-item {
  /* The board's items are divs; the settings overflow menu uses real buttons,
     which arrive wearing user-agent chrome. One reset serves both. */
  appearance: none;
  width: 100%;
  border: 0;
  background: transparent;
  font-family: inherit;
  text-align: left;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-radius: 8px;
  color: var(--ink);
  font-size: var(--nl-text-sm);
  font-weight: 500;
  cursor: pointer;
  transition: color var(--nl-motion-fast), background-color var(--nl-motion-fast), opacity var(--nl-motion-fast), transform var(--nl-motion-fast);
}

.ctx-item svg {
  color: var(--dim);
  transition: color var(--nl-transition-fast);
}

.ctx-item:hover {
  background: color-mix(in srgb, var(--accent) 20%, transparent);
  color: #ffffff;
}
.ctx-item:hover svg {
  color: var(--accent);
}

.ctx-item[disabled] {
  opacity: 0.38;
  cursor: default;
}

.ctx-item.danger,
.ctx-item.danger svg {
  color: var(--nl-status-danger);
}
/* A filter field at the top of a long menu. Same height as an item, so the list
   below does not jump when it appears. */
.ctx-filter {
  display: block;
  width: 100%;
  box-sizing: border-box;
  margin: 0 0 4px;
  padding: 8px 10px;
  border: 1px solid var(--nl-border);
  border-radius: var(--nl-radius-sm);
  background: color-mix(in srgb, var(--nl-surface-card-deep) 60%, transparent);
  color: var(--nl-text-primary);
  font: inherit;
  font-size: var(--nl-text-sm);
}
.ctx-filter:focus-visible { outline: none; box-shadow: var(--nl-ring); }

.ctx-item.danger:hover {
  /* This forced white ink on a translucent red, which on a light menu is white
     on pale pink. The tint carries the warning; the ink stays readable. */
  background: color-mix(in srgb, var(--nl-status-danger) 16%, transparent);
  color: var(--nl-status-danger);
}

.ctx-divider {
  height: 1px;
  background: rgba(255, 255, 255, 0.08);
  margin: 3px 0;
}

/* ── Quick Edit Bookmark Modal ────────────────────────────────── */
.quick-modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--nl-z-overlay);
  background: rgba(2, 6, 14, 0.65);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  display: grid;
  place-items: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--nl-motion-panel), transform var(--nl-motion-panel), visibility 0s var(--nl-motion-panel);
  padding: 20px;
}

.quick-modal-backdrop.open {
  opacity: 1;
  visibility: visible;
}

.quick-modal-card {
  width: min(92vw, 420px);
  background: rgba(11, 18, 38, 0.95);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 18px;
  box-shadow: var(--nl-shadow-3);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* In a short window, or at 200% zoom, an editor with its Appearance
     section open ran off both edges; the head stays, the body scrolls. */
  max-height: calc(100dvh - 24px);
}
.quick-modal-card > .quick-modal-body { min-height: 0; overflow-y: auto; overscroll-behavior: contain; }

/* Pop plays on every open, not just once at page load */
.quick-modal-backdrop.open .quick-modal-card {
  animation: modalPop var(--nl-transition-enter) both;
}

.quick-modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.quick-modal-head b {
  font-size: var(--nl-text-md);
  color: var(--ink);
}

.quick-modal-body {
  padding: 16px 18px 18px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.quick-icon-box {
  width: 48px;
  height: 48px;
  border-radius: 12px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.12);
  display: grid;
  place-items: center;
  cursor: pointer;
  flex-shrink: 0;
  transition: color var(--nl-motion-control), background-color var(--nl-motion-control), border-color var(--nl-motion-control), opacity var(--nl-motion-control), transform var(--nl-motion-control);
}
.quick-icon-box:hover {
  background: rgba(255, 255, 255, 0.14);
  border-color: var(--accent);
  transform: scale(1.05);
}

.quick-label {
  display: block;
  font-size: var(--nl-text-xs);
  font-weight: 500;
  color: var(--dim);
  margin-bottom: 4px;
}

/* ── Clock figures (each digit turns over on its own: motion.css) ─ */
#hh, #mm, body.seconds #ss {
  display: inline-block;
}

/* ── Notices ─────────────────────────────────────────────────── */
/* In the bottom left corner, where nothing else is: the gear keeps the right,
   and a notice in the middle came up on top of the folded folders and the
   last row of the board. They slide in from the edge and stack upward, in the
   same glass as the gear and the fit switch. */
#toast-dock {
  position: fixed;
  left: 20px;
  bottom: 20px;
  z-index: var(--nl-z-toast);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  max-width: calc(100vw - 40px);
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: center;
  gap: 10px;
  box-sizing: border-box;
  max-width: min(calc(100vw - 40px), 440px);
  min-height: var(--nl-target-min);
  padding: 6px 8px 6px 14px;
  border-radius: var(--nl-radius-md);
  border: 1px solid var(--nl-polish-hairline);
  background: color-mix(in srgb, var(--nl-surface-card-deep) 88%, transparent);
  box-shadow: var(--nl-shadow-2);
  backdrop-filter: blur(16px) saturate(125%);
  -webkit-backdrop-filter: blur(16px) saturate(125%);
  color: var(--nl-text-primary);
  font-size: var(--nl-text-sm);
  font-weight: 500;
  letter-spacing: var(--nl-track-tight);
  opacity: 0;
  transform: translateX(-12px);
  transition: opacity var(--nl-transition-panel), transform var(--nl-transition-pop-panel);
}
.toast.on {
  opacity: 1;
  transform: none;
}
.toast svg {
  width: 15px;
  height: 15px;
  flex: none;
}
.toast-info svg { color: var(--accent); }
.toast-success svg { color: var(--nl-status-success); }
.toast-danger svg { color: var(--nl-status-danger); }
.toast span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* A notice with nothing to press keeps its right-hand room even. */
.toast:not(:has(.toast-action)) { padding-right: 14px; }

/* The undo control. It had no style at all — a user-agent button inside a
   designed pill — and, because the dock takes no pointer events so a notice
   never blocks the board behind it, it could not be clicked either: the one
   button in the product that takes a deletion back was unreachable by mouse.
   It re-enables pointer events for itself alone, and it never shrinks, so a
   long bookmark name ellipsises the sentence and not the way out of it. It is
   the accent's own quiet button, the one thing in the notice to press. */
.toast-action {
  flex: none;
  margin-left: 2px;
  padding: 6px 12px;
  border-radius: var(--nl-radius-sm);
  border: 1px solid transparent;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  color: var(--nl-accent-text);
  font: inherit;
  font-weight: 650;
  letter-spacing: var(--nl-track-tight);
  white-space: nowrap;
  cursor: pointer;
  pointer-events: auto;
  transition: background var(--nl-transition-control), border-color var(--nl-transition-control);
}
.toast-action:hover {
  background: color-mix(in srgb, var(--accent) 24%, transparent);
  border-color: color-mix(in srgb, var(--accent) 50%, transparent);
}
.toast-action:active { transform: translateY(1px); }

@media (max-width: 480px) {
  #toast-dock { left: 12px; right: 12px; bottom: 12px; max-width: none; }
  .toast { max-width: 100%; }
}

/* ── Glass Confirm Dialog ─────────────────────────────────────── */
.confirm-backdrop {
  z-index: var(--nl-z-modal);
}
.confirm-card {
  width: min(92vw, 360px);
  align-items: center;
  text-align: center;
  padding: 26px 24px 22px;
  gap: 6px;
}
.confirm-icon {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--nl-status-danger);
  background: color-mix(in srgb, var(--nl-status-danger) 14%, transparent);
  border: 1px solid rgba(239, 68, 68, 0.3);
  margin-bottom: 8px;
  box-shadow: 0 0 0 5px rgba(239, 68, 68, 0.12);
}
.confirm-title {
  font-size: var(--nl-text-lg);
  color: var(--ink);
}
.confirm-message {
  font-size: var(--nl-text-sm);
  line-height: 1.5;
  color: var(--dim);
  margin: 2px 0 14px;
  overflow-wrap: anywhere;
}
.confirm-actions {
  display: flex;
  gap: 10px;
  width: 100%;
}
.confirm-actions .glass-btn {
  flex: 1;
}
/* White on red needs a deep red: on #ef4444 it was 3.8 to one. */
.confirm-ok.danger {
  background: #dc2626;
  border-color: #dc2626;
  color: #ffffff;
  font-weight: 600;
}
.confirm-ok.danger:hover {
  background: #b91c1c;
  border-color: #b91c1c;
}

/* ── Accessibility & Selection Polish ─────────────────────────── */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}
.tile:focus-visible {
  outline: 2px solid var(--nl-focus, var(--accent));
  outline-offset: 4px;
  box-shadow: 0 0 0 6px var(--nl-focus-outer, rgba(3, 7, 18, .86));
  border-radius: var(--tile-radius);
}
::selection {
  background: color-mix(in srgb, var(--accent) 80%, #ffffff);
  color: #0a0f1e;
}

/* Nordlys Refined: stable tiles and source-aware artwork. */
.box {
  border: 1px solid var(--nl-border);
  background: color-mix(in srgb, var(--nl-surface-card) 78%, transparent);
  box-shadow: var(--nl-shadow-1);
  transition: transform var(--nl-transition-control);
}
.box::after { display: none; }
.box .nl-icon { display: grid; place-items: center; transform: scale(var(--icon-optical-scale, 1)); color: var(--icon-accent); }
.box .nl-icon[data-icon-kind="builtin"] { width: 64%; height: 64%; }
.box .nl-icon[data-icon-kind="favicon"], .box .nl-icon[data-icon-kind="raster"] { width: 72%; height: 72%; }
.box .nl-icon[data-icon-kind="monogram"] { width: 68%; height: 68%; }
.box .nl-icon > svg, .box .nl-icon > img { width: 100%; height: 100%; object-fit: contain; filter: none; transform: none; fill: var(--icon-accent); }
.lbl { font-size: clamp(12px, .82vw, 13px); color: var(--nl-text-secondary); }
.lbl { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tile:hover .box, .tile:focus-visible .box, body.hover-lift .tile:hover .box, body.hover-scale .tile:hover .box, body.hover-glow .tile:hover .box {
  transform: translateY(-2px) scale(1.02);
  background: color-mix(in srgb, var(--nl-surface-card) 84%, transparent);
  border-color: color-mix(in srgb, var(--nl-accent) 34%, var(--nl-border));
  box-shadow: var(--nl-shadow-2);
}
.tile:hover .box svg, .tile:hover .box img { transform: none; }
.tile:active .box { transform: translateY(0) scale(.97); }

@media (max-width: 480px) {
  #page { padding-inline: 10px; align-items: stretch; }
  #hero, #searchwrap, #board { align-self: center; }
  #board { width: 100%; --board-gap: 12px; }
  .card { width: 100%; padding-inline: 12px; }
  .grid[data-cols] { grid-template-columns: repeat(auto-fit, minmax(56px, 1fr)); justify-items: center; }
}



/* Contrast tone. Plates stay identical on every tile; what moves is the icon,
   and only for marks that carry no colour of their own — a single-colour glyph
   is meant to flip with its background, which is what its own brand guidance
   says. Coloured logos are never re-toned, so the red stays YouTube's red.
   Marked important because the renderer sets --icon-accent inline. */
.box .nl-icon[data-icon-tone="light"],
.nl-icon[data-icon-tone="light"] { --icon-accent: #f5f7fb !important; }
.box .nl-icon[data-icon-tone="dark"],
.nl-icon[data-icon-tone="dark"] { --icon-accent: #10151f !important; }
/* Raster art has no fill to set; for a single-colour image an inversion is the
   same operation. */
.nl-icon[data-icon-tone] > img {
  /* Important for the same reason the plates were: light themes paint a
     decorative drop-shadow on icons through a higher-specificity selector, and
     decoration must not win over being visible at all. */
  filter: invert(1) !important;
}

/* ── Themed select ──────────────────────────────────────────────────
   Replaces the operating system's dropdown. The native element stays in the
   DOM as the value source but never renders, so nothing here has to fight
   platform chrome. */
.nl-select {
  display: inline-flex; align-items: center; justify-content: space-between; gap: 10px;
  min-height: var(--nl-target-min); min-width: 140px; max-width: 100%;
  padding: 0 12px; border-radius: var(--nl-radius-sm);
  border: 1px solid var(--nl-border);
  background: color-mix(in srgb, var(--nl-surface-elevated) 80%, transparent);
  color: var(--nl-text-primary);
  font: inherit; font-size: var(--nl-text-sm); font-weight: 550;
  cursor: pointer; text-align: left;
  transition: border-color var(--nl-motion-control), background-color var(--nl-motion-control);
}
.nl-select:hover { border-color: var(--nl-border-strong); }
.nl-select[aria-expanded="true"] { border-color: var(--nl-accent); }
.nl-select-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nl-select-caret {
  flex: none; width: 8px; height: 8px; margin-left: auto;
  border-right: 1.6px solid currentColor; border-bottom: 1.6px solid currentColor;
  transform: translateY(-2px) rotate(45deg); opacity: .55;
  transition: transform var(--nl-transition-enter);
}
.nl-select[aria-expanded="true"] .nl-select-caret { transform: translateY(1px) rotate(-135deg); opacity: .9; }

.nl-select-list {
  position: fixed; z-index: var(--nl-z-menu);
  max-height: min(320px, 60vh); overflow-y: auto; overscroll-behavior: contain;
  padding: 6px; border-radius: var(--nl-radius-md);
  border: 1px solid var(--nl-border-strong);
  background: color-mix(in srgb, var(--nl-surface-card-deep) 96%, transparent);
  backdrop-filter: blur(24px) saturate(1.5);
  -webkit-backdrop-filter: blur(24px) saturate(1.5);
  box-shadow: var(--nl-shadow-3);
  opacity: 0; transform: translateY(-4px) scale(.985);
  transition: opacity var(--nl-transition-control), transform var(--nl-transition-enter);
}
.nl-select-list.open { opacity: 1; transform: none; }
.nl-select-group {
  padding: 8px 10px 4px; font-size: var(--nl-text-2xs); font-weight: 700;
  letter-spacing: var(--nl-track-wide); text-transform: uppercase; color: var(--nl-text-tertiary);
}
.nl-select-option {
  display: flex; align-items: center; min-height: 36px; padding: 0 10px;
  border-radius: var(--nl-radius-sm); color: var(--nl-text-secondary);
  font-size: var(--nl-text-sm); cursor: pointer; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
  transition: background-color var(--nl-motion-fast), color var(--nl-motion-fast);
}
.nl-select-option:hover { background: color-mix(in srgb, var(--nl-text-primary) 8%, transparent); color: var(--nl-text-primary); }
.nl-select-option[aria-selected="true"] {
  color: var(--nl-text-primary); font-weight: 600;
  background: color-mix(in srgb, var(--nl-accent) 16%, transparent);
}

/* ── Hit areas ──────────────────────────────────────────────────────
   The 40px contract, applied where an audit of every state found it broken.
   Visuals are unchanged; only the reachable area grows. */
.card-resize-handle { width: var(--nl-target-min); height: var(--nl-target-min); }
.ctx-item { min-height: var(--nl-target-min); }
.quick-modal-card .xbtn { min-width: var(--nl-target-min); min-height: var(--nl-target-min); }
.quick-modal-body :where(input, select, .nl-select, .glass-input) { min-height: var(--nl-target-min); }
.quick-modal-body .glass-btn, .quick-modal-foot .glass-btn { min-height: var(--nl-target-min); }
.confirm-backdrop .glass-btn { min-height: var(--nl-target-min); }
/* The search field filled a 19px strip inside a 54px bar, so most of the bar
   looked clickable and was not. */
#search #q { align-self: stretch; }

/* ── The empty board ────────────────────────────────────────────────
   This is what a new install opens on, so it is the first thing the product
   ever says. It is not an error and it is not a drop zone: the dashed outline
   it used to wear is what an upload target looks like, and a new user reading
   it as "something failed to load" is the opposite of the intent.

   So the atmosphere behind it stays the subject and this is only enough
   surface to hold a line of type and two ways in — the same glass, radius and
   border as a folder card, at a lower opacity, with the display face doing the
   talking. It has to read the same after the last folder is deleted, which is
   the other way to arrive here. */
.board-empty {
  display: grid;
  justify-items: center;
  gap: var(--nl-space-3);
  max-width: min(46ch, 100%);
  padding: var(--nl-space-10) var(--nl-space-6);
  margin: 0 auto;
  text-align: center;
  border-radius: var(--nl-radius-xl);
  border: 1px solid var(--nl-border);
  background: color-mix(in srgb, var(--nl-surface-card) 26%, transparent);
  backdrop-filter: blur(14px);
}
.board-empty-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--nl-text-2xl);
  font-weight: 600;
  letter-spacing: var(--nl-track-tight);
  color: var(--nl-text-primary);
}
.board-empty-text {
  margin: 0;
  max-width: 40ch;
  color: var(--nl-text-secondary);
  font-size: var(--nl-text-md);
  line-height: 1.55;
}
/* Wrapping rather than shrinking: at 320px two buttons stack, and each keeps
   the 40px the rest of the product keeps. */
.board-empty-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--nl-space-2);
  margin-top: var(--nl-space-1);
}
.board-empty-actions .glass-btn { padding-inline: var(--nl-space-5); }

/* A folder title may widen its card — that is how "GAMING & SIM" fits above two
   columns — but only so far. Without a ceiling a 90-character name stretched the
   card across the whole window and left its tiles adrift in it. */
.card { min-width: 0; }
.cat { min-width: 0; }
.cat b {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 20ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cat i { flex: 1 1 0; min-width: 0; }

/* Reachable sizes for the two places the sweep found short: the chip a folded
   folder leaves in the dock, and the button an empty board offers. */
#hiddenDock > * { min-height: var(--nl-target-min); }
.glass-btn { min-height: var(--nl-target-min); }

/* ── Press response ─────────────────────────────────────────────────
   Every control answers a press the same way. Without it a click on a button
   registered only after the work finished, which reads as a dead interface;
   with it the surface acknowledges the finger immediately. Transform only, and
   short enough that it never delays the action itself. */
:where(.glass-btn, .ctx-item, .nl-select, .icon-chip, .icon-tab-btn, .scene-card,
       .bookmark-compact-action, .theme-card, .mode-btn, .xbtn, #gear):active {
  transform: scale(.97);
}
:where(.glass-btn, .ctx-item, .nl-select, .icon-chip, .icon-tab-btn, .scene-card,
       .bookmark-compact-action, .theme-card, .mode-btn, .xbtn, #gear) {
  transition-property: color, background-color, border-color, box-shadow, opacity, transform;
  transition-duration: var(--nl-motion-control);
  transition-timing-function: var(--nl-ease-emphasized);
}
@media (prefers-reduced-motion: reduce) {
  :where(.glass-btn, .ctx-item, .nl-select, .icon-chip, .icon-tab-btn, .scene-card,
         .bookmark-compact-action, .theme-card, .mode-btn, .xbtn, #gear):active {
    transform: none;
  }
}

/* A colour swatch is a control like any other; it was 22px wide. */
.quick-modal-body input[type="color"], .link-color-ipt {
  min-width: var(--nl-target-min);
  min-height: var(--nl-target-min);
}

/* The bar already lights up as a whole through :focus-within, so the field inside
   it must not draw a second rectangle of its own. */
#search #q:focus-visible { outline: none; box-shadow: none; }

/* How much of the header to keep. Hiding the greeting or the whole block used
   to need Custom CSS, which is a strange price for wanting less on screen. */
body[data-header="compact"] #greet { display: none; }
body[data-header="hidden"] #hero { display: none; }

/* Names under bookmarks, turned off: the words leave the picture, never the
   link, so a screen reader still hears each bookmark by name. */
body.tile-names-hidden #board .tile .lbl {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Size and spacing, over the bar it opens from. The board is its preview, so
   the panel is narrow, holds five measures, and gets out of the way. */
.arrange-size {
  pointer-events: auto;
  display: grid;
  gap: 10px;
  width: min(460px, calc(100vw - 32px));
  padding: 12px 14px 10px;
  border-radius: var(--nl-radius-xl);
  border: 1px solid var(--nl-border);
  background: color-mix(in srgb, var(--nl-surface-elevated) 94%, transparent);
  -webkit-backdrop-filter: blur(24px) saturate(140%);
  backdrop-filter: blur(24px) saturate(140%);
  box-shadow: var(--nl-shadow-3);
}
.arrange-size[hidden] { display: none; }
/* In a short window — or a normal one at 200% zoom — the panel would rise off
   the top of the screen, and a fixed panel cannot be scrolled to. It takes
   what room there is above the bar and scrolls inside itself. */
.arrange-size {
  max-height: calc(100dvh - var(--arrange-controls-h, 72px) - max(18px, env(safe-area-inset-bottom)) - 28px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.arrange-bar:has(.arrange-size:not([hidden])) .arrange-hint { display: none; }
.arrange-size-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 2px; }
.arrange-size-title { color: var(--nl-text-primary); font-size: var(--nl-text-sm); font-weight: 650; }
.arrange-size-reset {
  min-height: var(--nl-target-min); padding: 0 10px; border: 0; border-radius: var(--nl-radius-sm);
  background: none; color: var(--nl-text-secondary); font: inherit; font-size: var(--nl-text-xs); font-weight: 600; cursor: pointer;
}
.arrange-size-reset:hover:not(:disabled) { color: var(--nl-text-primary); background: color-mix(in srgb, var(--nl-text-primary) 8%, transparent); }
.arrange-size-reset:disabled { opacity: .45; cursor: default; }
.arrange-size-row { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; }
.arrange-size-row + .arrange-size-row { padding-top: 10px; border-top: 1px solid color-mix(in srgb, var(--nl-border) 60%, transparent); }
/* Whatever the panel covers can be scrolled out from under it. */
body.arranging:has(.arrange-size:not([hidden])) #page { padding-bottom: 560px; }
.arrange-size-label { flex: 1 0 100%; color: var(--nl-text-primary); font-size: var(--nl-text-xs); font-weight: 600; }
.arrange-size-row:has(.tg) .arrange-size-label { flex: 1 1 auto; }
.arrange-size-presets .arrange-layout { padding-inline: 10px; font-size: var(--nl-text-xs); }
.arrange-size-slider { flex: 1 1 120px; min-width: 0; accent-color: var(--nl-accent); }
.arrange-size-value { min-width: 3ch; color: var(--nl-text-primary); font-size: var(--nl-text-xs); font-variant-numeric: tabular-nums; text-align: end; }
.arrange-size-end { color: var(--nl-text-secondary); font-size: var(--nl-text-2xs); }
#arrange-size[aria-expanded="true"] { color: var(--nl-text-primary); background: color-mix(in srgb, var(--nl-text-primary) 10%, transparent); }
.arrange-size-note { flex: 1 0 100%; margin: 0; color: var(--nl-text-secondary); font-size: var(--nl-text-2xs); }
.arrange-size-note[hidden] { display: none; }

/* ── Forced colours: what is chosen still shows ──────────────────────
   The system paints every background the same, and most choices here are
   shown by a background: the tab, the theme, the scene, the segment, the
   switch. Each one takes the system's selected colours instead, the sliding
   thumbs that stood for them step aside, and a switch shows its state by its
   fill and the side its knob is on. */
@media (forced-colors: active) {
  :is(.ctab.active, .mode-btn.active, .icon-tab-btn.active, .icon-chip.active,
    .arrange-layout[aria-checked="true"], .board-layout-option[aria-checked="true"],
    .palette-chip[aria-checked="true"], .nl-select-option[aria-selected="true"]) {
    forced-color-adjust: none;
    /* Above the sliding-thumb rules, which make a chosen segment transparent
       so the thumb can show through: here there is no thumb. */
    background: SelectedItem !important;
    color: SelectedItemText !important;
  }
  :is(.ctab.active, .mode-btn.active, .icon-tab-btn.active, .icon-chip.active, .arrange-layout[aria-checked="true"], .board-layout-option[aria-checked="true"]) svg { color: SelectedItemText; fill: currentColor; }
  :is(.theme-card.active, .theme-card[aria-pressed="true"], .scene-card[aria-checked="true"]) {
    outline: 3px solid Highlight;
    outline-offset: 2px;
  }
  .icon-url-version.is-current .icon-url-thumb { outline: 3px solid Highlight; }
  [data-thumb]::before,
  .ctabs.has-indicator::after { display: none; }
  .tg i { forced-color-adjust: none; background: Canvas !important; border: 1px solid CanvasText !important; }
  .tg i::after { background: CanvasText !important; box-shadow: none !important; }
  .tg input:checked + i { background: Highlight !important; border-color: Highlight !important; }
  .tg input:checked + i::after { background: HighlightText !important; }
}
/* Where the bar wraps, the divider would start the second line on its own. */
@media (max-width: 720px) {
  .arrange-divider { display: none; }
}
