/* ---------- The neighborhood ----------
   A cup in the dock that pours out the 88x31 buttons of other people's
   sites, plus a blogroll and jeni's own button for others to take.

   Every axis of the design is a class on <html>, written once at boot by
   js/neighborhood.js from config/settings:

     nb--layout-pour | nb--layout-cork
     nb--art-glossy  | nb--art-pixel
     nb--ent-spring  | nb--ent-drift  | nb--ent-perk
     nb--dress-tape  | nb--dress-pins | nb--dress-washi
     nb--frame-none  | nb--frame-hairline | nb--frame-inset | nb--frame-glow
     nb--chip-ghost  | nb--chip-red | nb--chip-rose | …
     nb--heart       (optional)
     nb-open         (while the thing is open)

   The classes live on the root element rather than on a wrapper because
   the cork overlay must escape the dock: .vertical-dock has a
   backdrop-filter, which makes it a containing block for position:fixed
   children, so the overlay is a sibling of the dock and both need to see
   the same config.

   The three entrances are four custom properties each, and BOTH layouts
   read the same four. That is the whole trick: any entrance works with
   any layout for free, because the pour column animates .nb-b88 and the
   board animates .nb-pin from one set of variables. */

/* ---------- entrance tempos ---------- */
.nb--ent-spring {
  --nb-dur: 420ms;
  --nb-fade: 200ms;
  --nb-ease: cubic-bezier(0.34, 1.56, 0.64, 1);
  --nb-d0: 40ms;
  --nb-step: 70ms;
  --nb-sway: 0px;
}
.nb--ent-drift {
  --nb-dur: 700ms;
  --nb-fade: 340ms;
  --nb-ease: cubic-bezier(0.22, 0.9, 0.3, 1);
  --nb-d0: 60ms;
  --nb-step: 130ms;
  --nb-sway: 3px;
}
.nb--ent-perk {
  --nb-dur: 340ms;
  --nb-fade: 160ms;
  --nb-ease: cubic-bezier(0.2, 1.8, 0.45, 1);
  --nb-d0: 120ms;
  --nb-step: 55ms;
  --nb-sway: 0px;
}

/* ---------- the cup in the dock ----------
   .dock-nb also carries .dock-item, so the 36px box, the hover spring,
   the tilt, the glow ring and the tooltip all come from css/dock.css.
   Only what is genuinely different lives here. */
/* .dock-item was written for anchors, so it never resets the UA button
   styling — and this is a <button>, because it performs an action rather
   than navigating. Without these it renders on the browser's default
   buttonface grey with an outset border. .dock-music-btn resets the same
   three for the same reason. */
.dock-nb {
  border: none;
  background: transparent;
  padding: 0;
  overflow: visible;
}
/* Beats css/dock.css's `.dock-item svg` (0,1,1) on specificity so the cup
   can be taller than the 15px stroke icons without touching that rule. */
.dock-nb .nb-cup {
  width: 19px;
  height: 25px;
  display: block;
  pointer-events: none;
}
/* Pixel art only ever scales cleanly at whole multiples of its source,
   the same reasoning as the clique badge that used to sit here. */
.nb--art-pixel .dock-nb .nb-cup {
  width: 15px;
  height: 20px;
  image-rendering: pixelated;
}
.dock-nb.is-open { color: var(--color-red-bright); background: rgba(179, 0, 0, 0.12); }
.dock-nb.is-open .nb-cup { filter: drop-shadow(0 0 5px rgba(179, 0, 0, 0.5)); }

/* The moka gives one shudder as it starts to percolate. Keyed to the cup
   art, so it costs nothing when another cup is selected. */
.nb-open .nb-cup--moka { animation: nb-gurgle 520ms ease-in-out; }
@keyframes nb-gurgle {
  0%, 100% { transform: translateX(0) rotate(0); }
  20%      { transform: translateX(-0.7px) rotate(-2deg); }
  45%      { transform: translateX(0.7px) rotate(2deg); }
  70%      { transform: translateX(-0.4px) rotate(-1deg); }
}

/* ---------- the heart ----------
   Off unless the config says otherwise. Sits on the cup's shoulder and
   beats on the double-thump rhythm of an actual heartbeat rather than a
   single even pulse, which reads as a loading spinner. */
.nb-heart {
  position: absolute;
  top: 3px;
  right: 3px;
  width: 9px;
  height: 9px;
  display: none;
  pointer-events: none;
  color: var(--color-red-bright);
  filter: drop-shadow(0 0 3px rgba(179, 0, 0, 0.6));
}
.nb--heart .nb-heart { display: block; animation: nb-beat 1.6s ease-in-out infinite; }
.nb-heart svg { width: 100%; height: 100%; display: block; }
@keyframes nb-beat {
  0%, 100% { transform: scale(1); }
  14%      { transform: scale(1.32); }
  28%      { transform: scale(1); }
  42%      { transform: scale(1.22); }
  56%      { transform: scale(1); }
}

/* ---------- the buttons themselves ---------- */
.nb-b88 {
  position: relative;
  display: block;
  width: 88px;
  height: 31px;
  flex: 0 0 auto;
  background: var(--color-surface);
  overflow: hidden;
  text-decoration: none;
  /* No border, for two reasons. An 88x31 arrives with its own edge drawn
     into the art, so a CSS border double-frames it. And with the global
     border-box sizing from reset.css, a 1px border would shrink the
     content box to 86x29 and clip the last two pixels off the right and
     bottom of every badge — which on pixel art reads as broken.
     The frame appears on hover instead, as an outline, which is drawn
     outside the box and changes no layout. */
  outline: 1px solid transparent;
  transition: outline-color 200ms ease, box-shadow 200ms ease;
  /* Some of these are <button> (the take-mine one) and some are <a>. The
     UA gives buttons a grey face, a border and padding; reset here rather
     than per-variant so the two are interchangeable. */
  border: none;
  padding: 0;
  font: inherit;
  cursor: pointer;
}
.nb-b88 img {
  width: 88px;
  height: 31px;
  display: block;
  /* 88x31 is a fixed-size format from an era of fixed-size images, and a
     great many of them are pixel art. Never resample them. */
  image-rendering: pixelated;
  /* The `title` lives on the <img> so anything reading the markup still
     finds it — the clique's own snippet is written that way — while the
     hit target stays the anchor, which has no title and so raises no
     native tooltip over the styled one. Same trick the dock badge used. */
  pointer-events: none;
}
.nb-b88:hover {
  outline-color: rgba(179, 0, 0, 0.55);
  box-shadow: 0 0 12px var(--color-red-glow);
  z-index: 2;
}

/* ---------- Bubblelicious, in its original form ----------
   The clique publishes an 88x31, but the badge that used to sit in the
   dock is a two-frame animated sprite, and that is the nicer object. So
   the clique gets a hand-built button: their sprite at 1:1 with their
   name beside it, filling the same 88x31 slot as everyone else, and
   keeping the full "Coffee Jelly Milk Tea w/ White Pearls" title on
   hover. Because this one has no art edge of its own, it keeps a faint
   outline at rest where the others do not. */
.nb-b88--sprite {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 7px;
  /* #a392b4 is the dominant field colour sampled from the clique's own
     88x31, so the hand-built button reads as theirs rather than as ours.
     The text goes dark to keep contrast on it. */
  background: linear-gradient(140deg, #b3a3c3, #a392b4 55%, #94849f);
  outline-color: rgba(0, 0, 0, 0.4);
}
.nb-b88--sprite img {
  width: 15px;
  height: 20px;
  image-rendering: pixelated;
  flex: 0 0 auto;
}
/* Two lines, because "bubblelicious" is thirteen characters and 88px of
   button minus the sprite leaves room for about eleven. Breaking it is
   also just what an 88x31 does. */
.nb-b88--sprite b {
  font-family: var(--font-mono);
  font-size: 7.5px;
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1.15;
  color: #2a2233;
  text-transform: lowercase;
}
.nb-b88--sprite:hover { outline-color: rgba(179, 0, 0, 0.7); }
/* ---------- button frames (the `frame` setting) ----------
   Buttons carry their own edge in their art, so `none` is the default and
   the honest one. The others exist because a wall mixes very pale badges
   with very dark ones, and sometimes you want them to agree. */
.nb--frame-hairline .nb-b88 { outline-color: rgba(255, 255, 255, 0.18); }
.nb--frame-glow .nb-b88 {
  outline-color: rgba(179, 0, 0, 0.35);
  box-shadow: 0 0 10px rgba(139, 0, 0, 0.22);
}
/* Inset is the one that solves pale badges glaring against the dark
   board: it darkens their edge from the inside instead of ringing them. */
.nb--frame-inset .nb-b88 { box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.55); }
/* The sprite button is hand-built and has no art edge of its own, so it
   keeps a faint outline even when nothing else is framed. */
.nb--frame-none .nb-b88--sprite { outline-color: rgba(0, 0, 0, 0.4); }

/* ---------- text-link colour (the `chip` setting) ---------- */
.nb--chip-ghost  { --nb-chip: var(--color-text-secondary); --nb-chip-line: var(--color-border); }
.nb--chip-red    { --nb-chip: #e08a8a; --nb-chip-line: rgba(179, 0, 0, 0.5); }
.nb--chip-rose   { --nb-chip: #f0b8cd; --nb-chip-line: rgba(217, 140, 168, 0.5); }
.nb--chip-mint   { --nb-chip: #a9dcb4; --nb-chip-line: rgba(127, 192, 138, 0.5); }
.nb--chip-violet { --nb-chip: #c3b6f0; --nb-chip-line: rgba(157, 140, 217, 0.5); }
.nb--chip-amber  { --nb-chip: #ecc98a; --nb-chip-line: rgba(217, 168, 76, 0.5); }

/* Text links from people who never made a button, sized to sit in the
   same grid as the buttons do. */
.nb-chip {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 88px;
  height: 31px;
  padding: 0 0.35rem;
  border: 1px dashed var(--nb-chip-line, var(--color-border));
  font-family: var(--font-mono);
  font-size: 0.55rem;
  letter-spacing: 0.04em;
  color: var(--nb-chip, var(--color-text-secondary));
  text-decoration: none;
  text-align: center;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.nb-chip:hover { color: var(--color-text); border-color: var(--nb-chip, var(--color-text-muted)); }

/* =====================================================================
   LAYOUT A: the pour column
   ===================================================================== */
.nb-pour {
  position: absolute;
  left: calc(100% + 10px);
  bottom: 12px;
  width: 88px;
  height: 0;
  z-index: 7;
  visibility: hidden;
}
.nb-open.nb--layout-pour .nb-pour { visibility: visible; }
.nb--layout-cork .nb-pour { display: none; }

.nb-pour .nb-item {
  position: absolute;
  left: 0;
  bottom: 0;
  opacity: 0;
  transform: translateY(14px) scale(0.82);
  transition:
    opacity var(--nb-fade) ease,
    transform var(--nb-dur) var(--nb-ease);
}
/* --i is the stack position and --dir alternates the tilt and the drift.
   Both are plain custom properties rather than a mod() on --i: mod() is
   too new to depend on, and one unsupported function invalidates the
   whole transform and drops every badge on the floor. */
.nb-pour .nb-item:nth-child(1) { --i: 0; --dir: -1; }
.nb-pour .nb-item:nth-child(2) { --i: 1; --dir: 1; }
.nb-pour .nb-item:nth-child(3) { --i: 2; --dir: -1; }
.nb-pour .nb-item:nth-child(4) { --i: 3; --dir: 1; }
.nb-pour .nb-item:nth-child(5) { --i: 4; --dir: -1; }
.nb-pour .nb-item:nth-child(6) { --i: 5; --dir: 1; }
.nb-pour .nb-item:nth-child(7) { --i: 6; --dir: -1; }
.nb-pour .nb-item:nth-child(8) { --i: 7; --dir: 1; }
.nb-pour .nb-item:nth-child(n+9) { --i: 8; --dir: 1; }

.nb-open .nb-pour .nb-item {
  opacity: 1;
  transition-delay: calc(var(--nb-d0) + var(--i) * var(--nb-step));
  transform:
    translateY(calc(-4px - var(--i) * 38px))
    translateX(calc(var(--nb-sway) * var(--dir)))
    rotate(calc(var(--dir) * 2.2deg));
}

/* In the column the blogroll and the take-mine row are just more items in
   the stack, so they arrive on the same timer as everything else. */
.nb--layout-pour .nb-board__title,
.nb--layout-pour .nb-board__sub,
.nb--layout-pour .nb-roll__label { display: none; }

/* =====================================================================
   LAYOUT B: the corkboard panel
   ===================================================================== */
.nb-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.25rem;
  background: rgba(0, 0, 0, 0.65);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--transition-base), visibility var(--transition-base);
}
.nb-open.nb--layout-cork .nb-overlay { opacity: 1; visibility: visible; }
.nb--layout-pour .nb-overlay { display: none; }

.nb-board {
  position: relative;
  width: min(380px, 100%);
  max-height: calc(100vh - 2.5rem);
  overflow-y: auto;
  padding: 1.25rem;
  transform: scale(0.96);
  transition: transform var(--transition-base);
  /* Cork: two offset dot layers over a warm brown, which reads as grain
     without shipping a texture image. */
  background:
    radial-gradient(circle at 20% 30%, rgba(255, 255, 255, 0.022) 0 1px, transparent 1px),
    radial-gradient(circle at 70% 65%, rgba(255, 255, 255, 0.02) 0 1px, transparent 1px),
    #2a1f18;
  background-size: 9px 9px, 13px 13px, auto;
  border: 1px solid #3b2c21;
  border-radius: 4px;
  box-shadow: inset 0 0 40px rgba(0, 0, 0, 0.45), 0 20px 50px rgba(0, 0, 0, 0.5);
}
.nb-open .nb-board { transform: scale(1); }

.nb-board__title {
  font-family: var(--font-display);
  font-size: 0.95rem;
  color: #f0e6d8;
  margin: 0 2rem 0.15rem 0;
}
.nb-board__sub {
  font-family: var(--font-mono);
  font-size: 0.45rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #a08a72;
  margin: 0 0 1rem;
}
.nb-close {
  position: absolute;
  top: 0.9rem;
  right: 0.9rem;
  background: none;
  border: none;
  cursor: pointer;
  color: #a08a72;
  font-size: 1.1rem;
  line-height: 1;
  padding: 0.2rem;
}
.nb-close:hover { color: #f0e6d8; }
.nb--layout-pour .nb-close { display: none; }

/* =====================================================================
   The list. One DOM node; js/neighborhood.js moves it into the column or
   the board at boot, so the markup (and the clique link inside it) exists
   exactly once and is in the page source either way.
   ===================================================================== */
.nb--layout-cork .nb-wall {
  display: grid;
  grid-template-columns: repeat(3, 88px);
  gap: 18px 10px;
  justify-content: center;
}
.nb--layout-cork .nb-roll {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.6rem;
  margin: 0;
}
.nb--layout-cork .nb-roll .nb-chip {
  width: auto;
  height: auto;
  padding: 0;
  border: none;
  /* On cork the chips are plain text, but they still honour the colour
     setting — with a warm default that suits the board rather than the
     grey that suits the column. */
  color: var(--nb-chip, #e2d3c0);
}
.nb--layout-cork .nb-roll .nb-chip:hover { color: #ff9a9a; }
.nb-roll__label {
  font-family: var(--font-mono);
  font-size: 0.45rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #b39c82;
  margin: 1.1rem 0 0.4rem;
}
.nb-roll:empty, .nb-roll:empty + .nb-roll__label { display: none; }

/* Each wall item is wrapped so the tilt can live on the wrapper and the
   button keeps its own hover treatment without the two fighting. */
.nb-pin { position: relative; display: block; }
.nb--layout-cork .nb-pin {
  opacity: 0;
  transform: translateY(12px) scale(0.94) rotate(var(--tilt, 0deg));
  transition:
    opacity var(--nb-fade) ease,
    transform var(--nb-dur) var(--nb-ease);
}
.nb--layout-cork .nb-pin:nth-child(1) { --i: 0; }
.nb--layout-cork .nb-pin:nth-child(2) { --i: 1; }
.nb--layout-cork .nb-pin:nth-child(3) { --i: 2; }
.nb--layout-cork .nb-pin:nth-child(4) { --i: 3; }
.nb--layout-cork .nb-pin:nth-child(5) { --i: 4; }
.nb--layout-cork .nb-pin:nth-child(6) { --i: 5; }
.nb--layout-cork .nb-pin:nth-child(7) { --i: 6; }
.nb--layout-cork .nb-pin:nth-child(8) { --i: 7; }
.nb--layout-cork .nb-pin:nth-child(n+9) { --i: 8; }
.nb-open.nb--layout-cork .nb-pin {
  opacity: 1;
  transform: translateY(0) scale(1) rotate(var(--tilt, 0deg));
  transition-delay: calc(var(--nb-d0) + var(--i) * var(--nb-step));
}

/* ---------- dressing (cork only) ---------- */
.nb--layout-cork.nb--dress-tape .nb-pin::after {
  content: '';
  position: absolute;
  top: -7px;
  left: 50%;
  width: 28px;
  height: 11px;
  margin-left: -14px;
  z-index: 3;
  background: rgba(236, 226, 198, 0.28);
  border-left: 1px solid rgba(255, 255, 255, 0.12);
  border-right: 1px solid rgba(255, 255, 255, 0.12);
  transform: rotate(var(--tape, -3deg));
}
.nb--layout-cork.nb--dress-pins .nb-board { background-color: #241a14; }
.nb--layout-cork.nb--dress-pins .nb-wall { gap: 20px 10px; }
.nb--layout-cork.nb--dress-pins .nb-pin::after {
  content: '';
  position: absolute;
  top: -6px;
  left: 50%;
  width: 9px;
  height: 9px;
  margin-left: -4.5px;
  z-index: 3;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 32%, #ff9d9d 0 22%, var(--pin, #b30000) 60%);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.65), inset 0 -1px 1px rgba(0, 0, 0, 0.35);
}
.nb--layout-cork.nb--dress-pins .nb-b88 { box-shadow: 0 4px 9px rgba(0, 0, 0, 0.55); }
.nb--layout-cork.nb--dress-washi .nb-wall { gap: 26px 10px; }
.nb--layout-cork.nb--dress-washi .nb-pin::after {
  content: '';
  position: absolute;
  top: -6px;
  left: -7px;
  width: 30px;
  height: 13px;
  z-index: 3;
  background:
    repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.34) 0 3px, rgba(255, 255, 255, 0.14) 3px 6px),
    var(--washi, #d98ca8);
  opacity: 0.72;
  transform: rotate(var(--washitape, -24deg));
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
}
.nb-pin__name { display: none; }
.nb--layout-cork.nb--dress-washi .nb-pin__name {
  display: block;
  font-family: var(--font-display);
  font-size: 0.5rem;
  line-height: 1.2;
  color: #e0cdb4;
  text-align: center;
  margin-top: 0.25rem;
}

/* ---------- tooltips ----------
   The browser's own tooltip is slow, unstyleable and looks like the OS.
   This is the same object as .dock-tooltip and .tooltip__content: mono,
   uppercase, surface fill, hairline border, scaling in from the edge it
   is anchored to. It hangs off .nb-pin rather than the button because
   .nb-b88 clips its overflow to keep badge art inside its 88x31. */
.nb-tip {
  position: absolute;
  z-index: 20;
  max-width: 190px;
  padding: 0.2rem 0.4rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 3px;
  color: var(--color-text);
  font-family: var(--font-mono);
  font-size: 0.5rem;
  line-height: 1.45;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0;
  pointer-events: none;
  transition: all var(--transition-fast);
}
/* In the column it slides out to the right, exactly as the dock's own
   tooltips do — there is no room above, the next button is 7px away. */
.nb--layout-pour .nb-tip {
  left: calc(100% + 10px);
  top: 50%;
  transform: translateY(-50%) scale(0.85);
  transform-origin: left center;
  white-space: nowrap;
  max-width: none;
}
.nb--layout-pour .nb-pin:hover .nb-tip,
.nb--layout-pour .nb-pin:focus-within .nb-tip {
  opacity: 1;
  transform: translateY(-50%) scale(1);
}
/* On the board it sits above, like the generic tooltip, and wraps rather
   than running off the panel — some of these names are long. */
.nb--layout-cork .nb-tip {
  left: 50%;
  bottom: calc(100% + 7px);
  transform: translateX(-50%) scale(0.85);
  transform-origin: center bottom;
  text-align: center;
  /* Without this it shrink-to-fits against the 44px left of the 88px pin
     and wraps into a thin four-line column. max-content lets it take the
     width it wants, up to the max-width above. */
  width: max-content;
}
.nb--layout-cork .nb-pin:hover .nb-tip,
.nb--layout-cork .nb-pin:focus-within .nb-tip {
  opacity: 1;
  transform: translateX(-50%) scale(1);
}

/* ---------- take mine ---------- */
.nb-mine {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.9rem;
  padding-top: 0.7rem;
  border-top: 1px solid rgba(255, 255, 255, 0.09);
}
/* In the column the row form does not fit, so the group stacks. It also
   needs its own separator: it is one item in a stack of neighbours, and
   without a rule under it "copy code" reads as belonging to whichever
   button happens to sit below. */
.nb--layout-pour .nb-mine {
  flex-direction: column;
  align-items: flex-start;
  gap: 0.25rem;
  width: 88px;
  margin-top: 0;
  padding-top: 0;
  padding-bottom: 0.4rem;
  border-top: none;
  /* Solid, not dashed: text-link chips are dashed, and a dashed rule
     directly above a dashed chip reads as one broken box. */
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}
/* What clicking does, said on the button rather than next to it. Shown on
   focus too, so it is not a mouse-only affordance, and held visible while
   the "copied" state is up. */
.nb-mine__copy {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(8, 6, 9, 0.84);
  color: var(--color-text);
  font-family: var(--font-mono);
  font-size: 7.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0;
  pointer-events: none;
  transition: opacity 160ms ease;
}
.nb-b88--mine:hover .nb-mine__copy,
.nb-b88--mine:focus-visible .nb-mine__copy,
.nb-b88--mine.is-copied .nb-mine__copy { opacity: 1; }
.nb-b88--mine.is-copied .nb-mine__copy { background: rgba(139, 0, 0, 0.88); }
.nb-mine__label {
  font-family: var(--font-mono);
  font-size: 0.42rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}
/* The label leads in the column, so the group reads as "take mine → the
   button" top to bottom. */
.nb--layout-pour .nb-mine__label {
  order: -1;
  color: var(--color-text-label);
}

/* =====================================================================
   Particles. Independent elements that either exist or do not, so every
   combination works without a line of code.
   ===================================================================== */
.nb-fx {
  position: absolute;
  left: calc(100% + 54px);
  bottom: 12px;
  width: 0;
  height: 0;
  z-index: 6;
  opacity: 0;
  pointer-events: none;
}
.nb-open .nb-fx { opacity: 1; }
.nb--layout-cork .nb-fx { left: calc(100% + 20px); bottom: 26px; }
.nb-p-ice, .nb-p-steam, .nb-p-bub { position: absolute; opacity: 0; }

.nb-p-ice {
  width: 6px;
  height: 6px;
  margin-left: -3px;
  border-radius: 1.5px;
  background: linear-gradient(140deg, rgba(233, 247, 255, 0.75), rgba(150, 200, 230, 0.45));
  border: 1px solid rgba(240, 250, 255, 0.6);
}
.nb-open .nb-p-ice { animation: nb-ice 1.9s ease-out infinite; }
.nb-open .nb-p-ice:nth-of-type(2) { animation-delay: 0.55s; margin-left: 4px; }
.nb-open .nb-p-ice:nth-of-type(3) { animation-delay: 1.1s; margin-left: -10px; }
@keyframes nb-ice {
  0%   { opacity: 0; transform: translateY(0) rotate(0) scale(0.7); }
  20%  { opacity: 0.9; }
  100% { opacity: 0; transform: translateY(-150px) rotate(150deg) scale(1); }
}

.nb-p-steam {
  width: 2px;
  height: 14px;
  margin-left: -1px;
  border-radius: 2px;
  background: linear-gradient(to top, rgba(236, 232, 224, 0.45), rgba(236, 232, 224, 0));
}
.nb-open .nb-p-steam { animation: nb-steam 2.6s ease-out infinite; }
.nb-open .nb-p-steam:nth-of-type(2) { animation-delay: 0.85s; margin-left: 6px; }
.nb-open .nb-p-steam:nth-of-type(3) { animation-delay: 1.7s; margin-left: -8px; }
@keyframes nb-steam {
  0%   { opacity: 0; transform: translateY(0) translateX(0) scaleY(0.6); }
  25%  { opacity: 0.7; }
  100% { opacity: 0; transform: translateY(-110px) translateX(10px) scaleY(1.5); }
}

.nb-p-bub {
  width: 5px;
  height: 5px;
  margin-left: -2.5px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 32%, rgba(214, 160, 104, 0.9), rgba(122, 74, 36, 0.75));
}
.nb-open .nb-p-bub { animation: nb-bub 1.15s ease-out infinite; }
.nb-open .nb-p-bub:nth-of-type(2) { animation-delay: 0.28s; margin-left: 6px; }
.nb-open .nb-p-bub:nth-of-type(3) { animation-delay: 0.62s; margin-left: -9px; }
@keyframes nb-bub {
  0%   { opacity: 0; transform: translateY(0) scale(0.5); }
  18%  { opacity: 0.95; }
  100% { opacity: 0; transform: translateY(-125px) scale(1.15); }
}

/* ---------- mobile ----------
   The dock pivots to a horizontal bar along the bottom edge (see the
   media query in css/dock.css), so the column has nowhere to go to the
   right. Send it straight up instead, centred on the dock. */
@media (max-width: 768px) {
  .nb-pour {
    left: 50%;
    margin-left: -44px;
    bottom: calc(100% + 14px);
  }
  .nb-fx {
    left: 50%;
    bottom: calc(100% + 14px);
  }
  .nb--layout-cork .nb-fx { left: 50%; bottom: calc(100% + 14px); }
  .nb-board { max-height: calc(100vh - 7rem); }
  /* Clear the mobile dock, the same 5.5rem the footer reserves. */
  .nb-overlay { padding-bottom: 5.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .nb-pour .nb-item,
  .nb--layout-cork .nb-pin { transition-duration: 1ms; transition-delay: 0ms; }
  .nb-open .nb-cup--moka,
  .nb--heart .nb-heart,
  .nb-open .nb-p-ice,
  .nb-open .nb-p-steam,
  .nb-open .nb-p-bub { animation: none; }
  /* Particles are pure decoration; with motion off they simply do not
     appear, rather than sitting frozen at their start position. */
  .nb-open .nb-fx { opacity: 0; }
}
