/* ==========================================================================
   FORA — design system
   Values below are taken directly from the design system spec. Where the spec
   gives an exact number (button heights, radii, type scale, shadows, opacity
   steps) that number is used verbatim; nothing here is improvised.
   ========================================================================== */

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

html { color-scheme: dark; }

:root {
  /* ---------------------------------------------------------------- colour */
  /* primary */
  --primary:            #FA4616;
  --primary-hover:      #D93B12;

  /* accents */
  --accent-blue:        #6CB4EE;
  --accent-orange:      #FF9F43;
  --accent-cyan:        #48D1CC;
  --accent-purple:      #A78BFA;
  --accent-teal:        #2DD4BF;
  --accent-sky:         #38BDF8;
  --accent-yellow:      #FBBF24;

  /* neutral scale */
  --text-primary:       #FFFFFF;
  --text-secondary:     #FFFFFF;
  --text-tertiary:      rgba(255, 255, 255, 0.68);
  --text-disabled:      rgba(255, 255, 255, 0.38);
  --white:              #FFFFFF;
  --gray-light:         #0D0D0D;
  --gray-medium:        #1A1A1A;
  --warm-white:         #050505;

  /* surface & border */
  --surface:            #010101;   /* the page itself */
  --surface-light:      #0B0B0B;   /* cards and panels lifted off the page */
  --border-subtle:      #1F1F1F;
  --border-medium:      #2E2E2E;
  /* The hover edge. utilities.css already reaches for this token with a literal
     fallback; naming it here means the two cannot drift apart. */
  --border-strong:      rgba(255, 255, 255, 0.25);

  /* ---- the edge of something you can press ----
     Separate from the three above, and the separation is the point. A hairline
     between two rows of a card is decoration: it divides, it is not meant to be
     seen so much as felt, and #1F1F1F against #010101 -- 1.27:1 -- is right for
     that. The edge of a *control* is doing a different job. It is the only thing
     telling a reader that the words inside it can be pressed, and at 1.27:1 it
     was not telling them: an outline button on this background is very close to
     invisible, which is why the quiet actions across the app read as labels
     rather than buttons.

     WCAG 1.4.11 asks for 3:1 on the boundary of a user interface component.
     #5A5A5A is 3.03:1 here. Cards keep their hairline; anything you can press
     gets this. */
  --border-control:       #5A5A5A;
  --border-control-hover: #8A8A8A;

  /* semantic */
  --danger:             #FA4616;
  --danger-dark:        #FF7B6B;
  --danger-alt:         #F91F31;

  /* opacity steps — the only values permitted by the system */
  --o-608:              0.608;
  --o-50:               0.5;
  --o-15:               0.15;
  --o-10:               0.1;
  --o-06:               0.06;

  /* ---------------------------------------------------------------- spacing */
  --s-4:   4px;   --s-8:   8px;   --s-12: 12px;  --s-16: 16px;
  --s-24: 24px;   --s-28: 28px;   --s-32: 32px;  --s-40: 40px;
  --s-56: 56px;   --s-64: 64px;   --s-80: 80px;  --s-96: 96px;

  /* ----------------------------------------------------------------- radius */
  --r-4:    4px;    /* small utility elements */
  --r-8:    8px;    /* secondary buttons, inputs */
  --r-10:  10px;    /* primary buttons */
  --r-14:  14px;    /* icon buttons */
  --r-16:  16px;    /* rows inside a card -- .pick-row and its kin */
  --r-24:  24px;    /* cards, elevated surfaces */
  --r-pill: 9999px; /* fully rounded */

  /* ------------------------------------------------------------- elevation */
  --shadow-raised:  rgba(0, 0, 0, 0.6) 0px 4px 6px -1px, rgba(0, 0, 0, 0.6) 0px 2px 4px -2px;
  --shadow-pressed: rgb(182, 38, 0) 0px 3px 0px 0px;
  --shadow-lifted:  rgb(38, 38, 38) 0px 3px 0px 0px;

  /* --------------------------------------------------------------- layering */
  --z-base:      1;
  --z-dropdown: 10;
  --z-sticky:   50;
  --z-modal:  9999;

  /* ------------------------------------------------------------ typography */
  --font-display: 'Acid Grotesk', 'Inter', sans-serif;
  --font-body:    'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono:    'Geist Mono', 'Monaco', 'Courier New', monospace;

  /* ------------------------------------------------------------------ misc */
  --container: 1200px;
  --gutter:    var(--s-16);
  --rail-h:    72px;

  /* ---- the line the three columns begin on ----
     The reading column's own content starts below the top bar, the page's top
     padding and the filter bar that sits over it. The sidebar's nav and the
     rail's first card are set to begin on that same line, so the three columns
     start together instead of each starting wherever its own chrome ends. The
     logo is deliberately not part of this -- it keeps the top of the sidebar. */
  --side-pad-top: var(--s-16);   /* the sidebar's own top padding */
  --side-top-h:   52px;          /* the logo row, which does not move */
  --filters-h:    52px;          /* the filter bar over the reading column */
  --read-top: calc(var(--rail-h) + var(--s-40) + var(--filters-h) + var(--s-16));
  --dock-h:    64px;
  /* The sidebar frame is wider than the column of links inside it. The extra
     width is empty space on the left, so the nav sits away from the edge of
     the screen and reads as a column standing beside the page rather than a
     panel bolted to the window. --side-inset is that empty space, and the
     frame is simply that space plus the column, so the two never drift apart. */
  --side-col:  288px;
  /* 96, not 160. The inset is empty space to the left of the nav, and at 160 it
     was the single largest thing on a wide screen doing nothing -- a band wider
     than a phone, held open beside every page in the app. It also stepped down
     to 96 below 1440, which meant the widest screens were the ones given the
     least room for content. One value across every desktop width instead: the
     nav still stands away from the edge, and the width goes to the page. */
  --side-inset: 96px;
  --side-w:    calc(var(--side-inset) + var(--side-col));

  /* ---- the two widths the page is built out of ----
     Every page in the app is a reading column, sometimes with a rail beside it.
     Those two widths were written into each layout separately -- 920 here, 350
     there, 1600 somewhere else -- so a wide screen ended up with a column that
     had stopped growing and a band of empty page to the right of it: 218px of
     nothing at 1920, 858px at 2560.

     They are tokens now, and both are fluid. The column grows with the window
     up to a measure a paragraph is still readable at; the rail grows with it
     too, at its own smaller rate. Past the ceiling the page has genuinely run
     out of things to widen, and what is left is margin rather than a gap in the
     middle of the layout. */
  --col-max:  1100px;
  --rail-w:   clamp(320px, 22vw, 420px);
}

html { -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 400;
  line-height: 24px;
  background: var(--surface);
  color: var(--text-primary);
  min-height: 100vh;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }
img { display: block; max-width: 100%; }
ul, ol { list-style: none; }

::selection { background: var(--primary); color: var(--white); }
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--border-subtle); border-radius: var(--r-pill); border: 4px solid var(--surface);
}

/* Accessibility: every interactive element keeps a visible focus ring. */
:focus-visible {
  outline: 2px solid var(--accent-sky);
  outline-offset: 2px;
  border-radius: var(--r-4);
}

[x-cloak] { display: none !important; }

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

/* --------------------------------------------------------------------------
   Typography — sizes, weights and line heights exactly as specified
   -------------------------------------------------------------------------- */

.t-display {
  font-family: var(--font-display);
  font-size: 56px; font-weight: 700; line-height: 56px; letter-spacing: 0;
}
.t-h1 {
  font-family: var(--font-display);
  font-size: 56px; font-weight: 700; line-height: 56px; letter-spacing: 0;
}
/* An in-page section heading. It carries the sidebar's nav step -- 20px -- so a
   heading in the page and a link in the rail are the same size, set apart only
   by weight. */
.t-h2 {
  font-family: var(--font-body);
  font-size: 20px; font-weight: 700; line-height: 28px; letter-spacing: -.01em;
}
/* The name step: the same size and weight the account card in the sidebar
   gives a person's name, so a title in a card and a name in the rail read as
   one voice. */
.t-h3 {
  font-family: var(--font-body);
  font-size: 15px; font-weight: 700; line-height: 20px; letter-spacing: 0;
}
.t-body {
  font-family: var(--font-body);
  font-size: 17px; font-weight: 400; line-height: 26px; letter-spacing: 0;
  color: var(--text-primary);
}
.t-emphasis {
  font-family: var(--font-body);
  font-size: 17px; font-weight: 600; line-height: 26px; letter-spacing: 0;
}
.t-link {
  font-family: var(--font-body);
  font-size: 15px; font-weight: 400; line-height: 22px; letter-spacing: 0;
}
/* The handle step, from the sidebar's account card: secondary text sitting
   under a name. --t-micro stays at 14 as the floor for dense strips. */
.t-small {
  font-family: var(--font-body);
  font-size: 15px; font-weight: 400; line-height: 22px; letter-spacing: 0;
  color: var(--text-tertiary);
}
/* The system floor for readable text is 14px; nothing renders below it. */
.t-micro {
  font-family: var(--font-body);
  font-size: 14px; font-weight: 400; line-height: 20px; letter-spacing: 0;
  color: var(--text-tertiary);
}
.t-eyebrow {
  font-family: var(--font-body);
  font-size: 14px; font-weight: 600; line-height: 21px; letter-spacing: 0;
  color: var(--text-tertiary);
}
.t-code {
  font-family: var(--font-mono);
  font-size: 14px; font-weight: 500; line-height: 23.8px;
}
.t-code-display {
  font-family: var(--font-mono);
  font-size: 16px; font-weight: 600; line-height: 24px;
}
.t-num { font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1; }

.wordmark {
  font-family: var(--font-display);
  font-size: 20px; font-weight: 700; line-height: 24px; letter-spacing: 0;
  color: #FFFFFF;
}
.wordmark i { font-style: normal; color: var(--primary); }

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--s-8);
  font-family: var(--font-body);
  cursor: pointer; white-space: nowrap; text-align: center;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
.btn:disabled { cursor: not-allowed; }
.btn svg { width: 16px; height: 16px; flex: none; }

/* Primary — one per screen, and the same object as the Post pill in the
   sidebar: fully rounded, set in bold, wide enough that the label has air
   around it. Everything else on the page steps down from this. */
.btn-primary {
  background-color: var(--primary);
  color: var(--white);
  height: 44px;
  padding: 0 var(--s-24);
  font-size: 16px; font-weight: 700; line-height: 24px;
  border-radius: var(--r-pill);
  border: 0;
  box-shadow: none;
}
.btn-primary:hover:not(:disabled) { background-color: var(--primary-hover); color: var(--white); }
.btn-primary:active:not(:disabled) { background-color: var(--primary-hover); box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1); }
.btn-primary:disabled { background-color: rgba(255, 255, 255, 0.1); color: var(--text-disabled); }

/* Secondary — filled, low emphasis. The fill is a real step off the page now,
   not the 1.06:1 that --surface-light gave it: a button the same colour as the
   background behind it is relying entirely on its border to exist. */
.btn-secondary, .btn-solid {
  background-color: rgba(255, 255, 255, 0.09);
  color: var(--text-primary);
  height: 38px;
  padding: 0 var(--s-16);
  font-size: 15px; font-weight: 600; line-height: 22px;
  border-radius: var(--r-pill);
  border: 1px solid var(--border-control);
  box-shadow: none;
}
.btn-secondary:hover:not(:disabled), .btn-solid:hover:not(:disabled) {
  background-color: rgba(255, 255, 255, 0.16); color: var(--text-primary);
  border-color: var(--border-control-hover);
}
.btn-secondary:active:not(:disabled), .btn-solid:active:not(:disabled) { background-color: rgba(255, 255, 255, 0.22); }
.btn-secondary:disabled, .btn-solid:disabled { opacity: var(--o-50); }

/* Ghost — transparent, but with an edge you can actually see. This is the step
   most of the app's secondary actions are written in, so it is the one that
   decided whether they read as buttons at all. */
.btn-ghost, .btn-outline {
  background-color: rgba(0, 0, 0, 0);
  color: var(--text-primary);
  height: 38px;
  padding: 0 var(--s-16);
  font-size: 15px; font-weight: 600; line-height: 22px;
  border-radius: var(--r-pill);
  border: 1px solid var(--border-control);
  box-shadow: none;
}
.btn-ghost:hover:not(:disabled), .btn-outline:hover:not(:disabled) {
  background-color: rgba(255, 255, 255, 0.1); color: var(--text-primary);
  border-color: var(--border-control-hover);
}
.btn-ghost:active:not(:disabled), .btn-outline:active:not(:disabled) { background-color: rgba(255, 255, 255, 0.16); }
.btn-ghost:disabled, .btn-outline:disabled { opacity: var(--o-50); }

/* Danger — semantic, uses the pressed solid shadow from the elevation scale */
.btn-danger {
  background-color: rgba(0, 0, 0, 0);
  color: var(--danger-dark);
  height: 38px;
  padding: 0 var(--s-16);
  font-size: 15px; font-weight: 600; line-height: 22px;
  border-radius: var(--r-pill);
  border: 1px solid var(--danger-dark);
}
.btn-danger:hover:not(:disabled) { background-color: rgba(250, 70, 22, 0.06); border-color: var(--danger); color: var(--danger); }

.btn-sm { height: 32px; padding: 0 var(--s-16); font-size: 14px; line-height: 20px; border-radius: var(--r-pill); }
.btn-sm svg { width: 14px; height: 14px; }
/* The large step is the Post pill exactly: 52px, 17/700. */
.btn-lg { height: 52px; padding: 0 var(--s-32); font-size: 17px; font-weight: 700; line-height: 24px; border-radius: var(--r-pill); }
.btn-block { width: 100%; }

/* Icon button — 48×48, meets the touch-target minimum on every device.
   flex: none is what makes that true. The bar these sit in is a flex row with a
   search field set to flex: 1 1 680px, and on a phone the field's basis is far
   wider than the bar: the shortfall came out of the buttons, which shrank to
   36px wide. A 48px square that is only 48px when there is room to spare is not
   a touch target, so the square is fixed and the field gives instead. */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none;
  background-color: rgba(0, 0, 0, 0);
  color: var(--text-tertiary);
  width: 48px; height: 48px;
  padding: 0 9px;
  font-size: 18px; font-weight: 500; line-height: 26px;
  border-radius: var(--r-pill);
  border: 0;
  box-shadow: none;
  cursor: pointer;
  transition: background-color .15s ease, color .15s ease;
}
.icon-btn:hover { background-color: rgba(255, 255, 255, 0.08); color: var(--text-primary); }
.icon-btn svg { width: 18px; height: 18px; }

/* --------------------------------------------------------------------------
   Navigation
   -------------------------------------------------------------------------- */

.rail {
  position: sticky; top: 0; z-index: var(--z-sticky);
  height: var(--rail-h);
  display: flex; align-items: center; gap: var(--s-16);
  padding: 0 var(--s-24);
  background: rgba(1, 1, 1, 0.85);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  backdrop-filter: saturate(180%) blur(16px);
  border-bottom: none;
}
.rail-left, .rail-right { display: flex; align-items: center; gap: var(--s-8); }
.rail-left { flex: 1; min-width: 0; }
.rail-right { flex: 1; justify-content: flex-end; }
.rail-centre { display: flex; justify-content: center; }

.nav-link {
  color: var(--text-primary);
  font-size: 16px; font-weight: 400; line-height: 24px;
  text-decoration: none;
}
.nav-link:hover { color: var(--accent-blue); text-decoration: underline; }
.nav-link.on { color: var(--primary); font-weight: 600; }

/* Segmented switcher — the app's primary navigation */
.switcher { display: flex; align-items: center; gap: var(--s-4); }
.switcher a {
  display: inline-flex; align-items: center; gap: var(--s-8);
  height: 40px; padding: 0 var(--s-16);
  border-radius: var(--r-10);
  color: var(--text-tertiary);
  font-size: 16px; font-weight: 400; line-height: 24px;
  transition: background-color .15s ease, color .15s ease;
}
.switcher a svg { width: 18px; height: 18px; flex: none; }
.switcher a:hover { background-color: rgba(255, 255, 255, 0.06); color: var(--text-primary); }
.switcher a.on { background-color: var(--surface-light); color: var(--text-primary); font-weight: 600; }

.rail-badge {
  position: absolute; top: 4px; right: 4px;
  min-width: 20px; height: 20px; padding: 0 var(--s-4);
  border-radius: var(--r-pill);
  background-color: var(--primary); color: var(--white);
  font-size: 14px; font-weight: 600; line-height: 20px; text-align: center;
  border: 2px solid var(--surface);
}

.avatar {
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-pill);
  background-color: var(--primary); color: var(--white);
  font-weight: 600; overflow: hidden; flex: none; user-select: none;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar-28 { width: 32px; height: 32px; font-size: 14px; line-height: 20px; }
.avatar-32 { width: 32px; height: 32px; font-size: 14px; line-height: 20px; }
.avatar-38 { width: 40px; height: 40px; font-size: 14px; line-height: 20px; }
.avatar-48 { width: 48px; height: 48px; font-size: 16px; line-height: 24px; }
.avatar-96 { width: 96px; height: 96px; font-size: 32px; line-height: 40px; border-radius: var(--r-24); }

/* Dropdown panels */
.pop {
  position: absolute; right: 0; top: calc(100% + var(--s-8));
  min-width: 240px;
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-24);
  box-shadow: var(--shadow-raised);
  overflow: hidden;
  z-index: var(--z-dropdown);
}
.pop-head { padding: var(--s-16); border-bottom: 1px solid var(--border-subtle); }
.pop-item {
  display: flex; align-items: center; gap: var(--s-12);
  padding: var(--s-12) var(--s-16);
  color: var(--text-tertiary);
  font-size: 14px; font-weight: 400; line-height: 20px;
  transition: background-color .15s ease, color .15s ease;
}
.pop-item:hover { background-color: rgba(255, 255, 255, 0.06); color: var(--text-primary); }
.pop-item:active { background-color: rgba(255, 255, 255, 0.1); }
.pop-item svg { width: 16px; height: 16px; color: var(--text-disabled); }

/* --------------------------------------------------------------------------
   Sidebar shell
   -------------------------------------------------------------------------- */

.side {
  position: fixed; left: 0; top: 0; bottom: 0; width: var(--side-w);
  z-index: var(--z-sticky);
  display: flex; flex-direction: column;
  padding: var(--side-pad-top) var(--s-16) var(--s-24) var(--side-inset);
  background: var(--surface);
  border-right: none;
  border-left: none;
  overflow-y: auto;
}
/* The nav column is a stack of pills that hug their own contents rather than
   the full width of the sidebar: the hover tint follows the label, so a short
   item reads as a short target. Everything below aligns to the same left edge
   as the icons, which is what holds the column together without any rules. */
.side-top {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 var(--s-16); min-height: var(--side-top-h);
  /* Everything in the column starts directly under the logo row. */
  margin-bottom: var(--s-16);
}
/* Bigger here than anywhere else: in the sidebar the wordmark is the mark at
   the head of the column, not a label in a bar. */
.side-top .wordmark { font-size: 30px; line-height: 34px; }
.side-close { display: none; }
.side-cta { width: 100%; margin-bottom: var(--s-24); }

.side-nav {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: 2px; flex: 0 0 auto; margin-top: 0;
}
.side-label {
  margin: var(--s-16) 0 var(--s-4); padding: 0 var(--s-16);
  font-size: 13px; font-weight: 600; line-height: 20px;
  letter-spacing: 0.02em; text-transform: uppercase;
  color: #FFFFFF;
  opacity: 0.45;
}
.side-label:first-child { margin-top: 0; }

/* Icon then label, both large. The active item is not a filled block -- it is
   simply the one set in bold, which is enough to find at a glance and leaves
   the tint free to mean hover. */
.side-link {
  display: flex; align-items: center; gap: var(--s-16);
  min-height: 50px; padding: var(--s-12) var(--s-16);
  border-radius: var(--r-pill);
  color: #FFFFFF;
  font-size: 20px; font-weight: 400; line-height: 24px;
  transition: background-color .15s ease, color .15s ease;
}
.side-link svg { width: 26px; height: 26px; stroke-width: 1.7; flex: none; }
.side-link:hover { background-color: rgba(255, 255, 255, 0.08); color: #FFFFFF; }
.side-link.on { background-color: transparent; color: #FFFFFF; font-weight: 700; }
.side-link.on svg { stroke-width: 2.1; }
.side-link.on:hover { background-color: rgba(255, 255, 255, 0.08); }

/* A second group under the six destinations -- the sections of the page you are
   on. They are anchors, not places, so they sit a step quieter than the links
   above them. */
.side-nav-sub { margin-top: var(--s-8); }
.side-nav-sub .side-link { min-height: 44px; font-size: 17px; }
.side-nav-sub .side-link svg { width: 22px; height: 22px; }

/* The single action the sidebar carries, on the brand colour so it is the one
   thing in the column that is not a place to go. */
/* flex: none, because the sidebar is a flex column that can run out of height.
   The dashboard's nav is the long one, and there the pill was the item that
   gave: a 52px call to action rendered 24px tall, squeezed to make room for the
   list above it. The column scrolls -- that is what the overflow is for -- so
   nothing in it needs to shrink, least of all the one thing in the sidebar that
   is an action rather than a destination. */
.side-post {
  display: flex; align-items: center; justify-content: center;
  flex: none;
  width: 100%; height: 52px;
  margin: var(--s-16) 0 var(--s-8);
  border-radius: var(--r-pill);
  background-color: var(--primary); color: var(--white);
  font-size: 17px; font-weight: 700; line-height: 24px;
  transition: background-color .15s ease;
}
.side-post:hover { background-color: var(--primary-hover); }

/* Count riding on a nav item — how many things are waiting in that section. */
.side-n {
  margin-left: auto; flex: none;
  min-width: 22px; height: 22px; padding: 0 6px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-pill);
  background-color: var(--primary); color: var(--white);
  font-size: 13px; font-weight: 600; line-height: 22px;
}

/* The way out of a workspace back to the board. Sits above the nav, reads as a
   door rather than a destination. */
.side-back {
  display: inline-flex; align-self: flex-start; align-items: center; gap: var(--s-12);
  margin-bottom: var(--s-8); padding: var(--s-8) var(--s-16);
  border-radius: var(--r-pill);
  border: 1px solid var(--border-subtle);
  color: #FFFFFF; opacity: 0.7;
  font-size: 15px; font-weight: 500; line-height: 22px;
  transition: background-color .15s ease, opacity .15s ease;
}
.side-back svg { width: 18px; height: 18px; flex: none; }
.side-back:hover { background-color: rgba(255, 255, 255, 0.08); opacity: 1; }

/* No box around it. It is the foot of the sidebar, not a card sitting in one --
   the avatar, the name and the handle are enough to mark it, and the frame only
   competed with the nav links above. The hover tint is the same one those links
   use, so it still reads as somewhere to click. */
.side-me-wrap { position: relative; padding-top: var(--s-24); }
/* The foot as a signed-out visitor meets it: the same place in the column, the
   two ways in stacked where the account card would be. */
.side-out {
  margin-top: auto; padding-top: var(--s-24);
  display: grid; gap: var(--s-8);
}
.side-me {
  display: flex; align-items: center; gap: var(--s-12);
  width: 100%; padding: var(--s-12); text-align: left;
  border: 0; background: none; cursor: pointer;
  border-radius: var(--r-pill);
  transition: background-color .15s ease;
}
.side-me:hover { background-color: rgba(255, 255, 255, 0.08); }
.side-me-link { color: inherit; text-decoration: none; }
.side-me-id { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.side-me-name {
  display: block;
  font-size: 15px; font-weight: 700; line-height: 20px; color: #FFFFFF;
}
.side-me-at {
  display: block;
  font-size: 15px; font-weight: 400; line-height: 20px; color: var(--text-tertiary);
}
.side-me-more { width: 18px; height: 18px; flex: none; color: #FFFFFF; }

/* The account menu opens upward out of the foot -- there is nothing below it
   to open into. */
.side-pop {
  right: auto; left: 0; top: auto; bottom: calc(100% - var(--s-8));
  min-width: 260px;
}

.side-scrim {
  position: fixed; inset: 0; z-index: 9990;
  background-color: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(2px);
  display: none;
}

.shell { margin-left: var(--side-w); min-height: 100vh; }

.topbar {
  position: sticky; top: 0; z-index: var(--z-dropdown);
  height: var(--rail-h);
  display: flex; align-items: center; gap: var(--s-16);
  padding: 0 var(--s-24);
  background: rgba(1, 1, 1, 0.85);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  backdrop-filter: saturate(180%) blur(16px);
  border-bottom: none;
}
.nav-toggle { display: none; }
/* The field takes the middle of the bar and gives width back when what sits
   beside it needs more -- a signed-out visitor is met by two buttons there,
   which a fixed 680px field pushed off the end of the bar. */
.topbar-search { position: relative; flex: 1 1 680px; max-width: 680px; min-width: 0; margin: 0 auto; }
.topbar-search .prefix {
  position: absolute; left: var(--s-16); top: 50%; transform: translateY(-50%);
  color: var(--text-disabled); pointer-events: none;
}
.topbar-search .input { padding-left: var(--s-40); border-radius: var(--r-pill); height: 40px; }
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: var(--s-8); }

/* Mobile dock */
.dock {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-sticky);
  height: calc(var(--dock-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
  display: none; align-items: center; justify-content: space-around;
  background: rgba(1, 1, 1, 0.94);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  border-top: 2px solid var(--primary);
}
.dock a, .dock button {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-4); flex: 1; min-height: 48px; padding: var(--s-8) 0;
  background: none; border: 0; cursor: pointer;
  color: var(--text-tertiary);
  font-family: var(--font-body);
  font-size: 14px; font-weight: 400; line-height: 20px;
}
.dock a svg, .dock button svg { width: 24px; height: 24px; }
.dock a.on { color: var(--primary); font-weight: 600; }
.dock .dock-create span.pin {
  display: flex; align-items: center; justify-content: center;
  width: 48px; height: 48px; border-radius: var(--r-14);
  background-color: var(--primary); color: var(--white);
}
.dock .dock-create span.pin svg { width: 24px; height: 24px; }

/* --------------------------------------------------------------------------
   Layout
   -------------------------------------------------------------------------- */

.page { padding: var(--s-40) var(--gutter) var(--s-96); }
.page-narrow { max-width: 640px;           margin: 0 auto; }
.page-mid    { max-width: 900px;           margin: 0 auto; }
/* Anchored, not centred, so a page's first column begins at the same place on
   every screen in the app -- the board, the workspace and the console all line
   up against the sidebar instead of each floating in its own box.
   The ceiling is high enough to be off the end of the widest ordinary desktop:
   these pages are grids of cards, and a grid has no reading measure to protect,
   so stopping one at 1600 left a quarter of a 2560 screen empty for no reason
   the page could point at. What keeps the cards a sane size is the column count
   below, not a ceiling on the page. */
.page-wide   { max-width: 2200px; margin: 0 auto 0 0; }
/* The board pages fill the width the sidebar leaves them. No box around the
   layout and no centring: the two columns start at the sidebar and run to the
   far edge, and the centre column takes whatever the rail does not. */
.page-board  { max-width: none; margin: 0; }

.page-head { margin-bottom: var(--s-32); }
.page-head .t-eyebrow { margin-bottom: var(--s-8); }
.page-head p { margin-top: var(--s-8); max-width: 60ch; }
/* The page's own actions, kept together on one line so the primary keeps its
   place at the end of the row however many sit beside it. On a narrow screen
   they wrap as a group rather than one peeling off on its own. */
.head-actions {
  display: flex; align-items: center; flex: none; flex-wrap: wrap;
  gap: var(--s-12);
}

/* A count riding on a button: the same pill the tab bar uses, sized to sit
   inside a control rather than beside a label. */
.btn .count {
  flex: none;
  min-width: 24px; padding: 0 var(--s-8);
  border-radius: var(--r-pill);
  background-color: rgba(255, 255, 255, 0.1);
  font-size: 13px; font-weight: 700; line-height: 20px;
  text-align: center;
}

.section-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-16); margin-bottom: var(--s-16);
}

.hairline { height: 1px; background-color: var(--border-subtle); border: 0; }

/* --------------------------------------------------------------------------
   Cards & containers
   -------------------------------------------------------------------------- */

.panel {
  background-color: var(--surface);
  color: var(--text-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-24);
}
.panel-pad { padding: var(--s-24); }

.tile {
  background-color: var(--surface);
  color: var(--text-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-24);
  padding: var(--s-24);
}
.tile .t-eyebrow { margin-bottom: var(--s-8); }
.tile .val {
  font-family: var(--font-display);
  font-size: 32px; font-weight: 700; line-height: 40px;
  color: var(--text-primary);
}

/* --------------------------------------------------------------------------
   The flier card
   -------------------------------------------------------------------------- */

.flier {
  position: relative; display: block;
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-24);
  overflow: hidden;
  transition: box-shadow .15s ease, border-color .15s ease;
}
.flier:hover { box-shadow: var(--shadow-raised); border-color: rgba(255, 255, 255, 0.25); }

.flier-art {
  position: relative; width: 100%; overflow: hidden;
  background-color: var(--surface);
}
.flier-art.ratio-poster { aspect-ratio: 4 / 5; }
.flier-art.ratio-wide   { aspect-ratio: 16 / 10; }
.flier-art img { width: 100%; height: 100%; object-fit: cover; }

.flier-art .blank {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-8); padding: var(--s-24);
  color: var(--text-tertiary);
}
.flier-art .blank svg { width: 32px; height: 32px; }
/* With no poster the title becomes the artwork, so it is set like one: large,
   centred, and cut off after four lines rather than pushing the frame open. */
.flier-art .blank .cat { color: var(--text-tertiary); }
.flier-art .blank .stand-in {
  font-size: 19px; font-weight: 700; line-height: 1.2; letter-spacing: -.02em;
  color: var(--text-primary); text-align: center;
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
}

.flier-body { padding: var(--s-24); }
/* A meta line with nothing to say yet, and the credit at the foot: both are
   quieter than the line they sit in. */
.flier-meta .unset { color: var(--text-disabled); }
.flier-foot .who { color: var(--text-tertiary); }
.flier-foot .count svg { width: 13px; height: 13px; }
.flier-title {
  font-family: var(--font-body);
  font-size: 18px; font-weight: 500; line-height: 26px;
  color: var(--text-primary);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.flier-meta {
  margin-top: var(--s-8);
  font-size: 14px; font-weight: 400; line-height: 20px;
  color: var(--text-tertiary);
  display: flex; flex-wrap: wrap; gap: var(--s-4) var(--s-12);
}
.flier-foot {
  margin-top: var(--s-16); padding-top: var(--s-16);
  border-top: 1px solid var(--border-subtle);
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-12);
}

/* The artwork is its own link inside the art box, so the pin tags can stay
   outside it and remain readable to a screen reader. */
.flier-shot { display: block; width: 100%; height: 100%; }
.flier-title a { color: inherit; }
.flier-title a:hover { color: var(--primary); }
.flier-foot > a:hover .t-micro { color: var(--text-primary); }

/* ---- the tool strip every card carries ----
   Interest writes, Discussions and Observations read, Identify says whose and
   which. Equal cells so the strip reads as one control rather than a row of
   buttons competing for the press, split by hairlines that continue the card's
   own. The counts live here and nowhere else on the card. */
.flier-tools {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  margin: var(--s-12) calc(var(--s-24) * -1) calc(var(--s-24) * -1);
  border-top: 1px solid var(--border-subtle);
}
.ftool {
  display: flex; align-items: center; justify-content: center; gap: var(--s-4);
  min-height: 44px; padding: var(--s-8) var(--s-4);
  background: none; border: 0; border-left: 1px solid var(--border-subtle);
  color: var(--text-tertiary); cursor: pointer;
  font-family: var(--font-body);
  font-size: 13px; font-weight: 500; line-height: 18px;
  transition: background-color .15s ease, color .15s ease;
}
.flier-tools > *:first-child .ftool,
.flier-tools > .ftool:first-child { border-left: 0; }
.ftool svg { width: 15px; height: 15px; flex: none; }
.ftool .ftool-caret { width: 12px; height: 12px; opacity: .6; }
.ftool-n { color: var(--text-primary); font-weight: 600; }
/* The word goes when the cell is too narrow to hold it; the icon and the
   number are what the strip is for. */
.ftool-k { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
button.ftool:hover, a.ftool:hover { background-color: rgba(255, 255, 255, 0.08); color: var(--text-primary); }
button.ftool.on { color: var(--primary); }
button.ftool.on .ftool-n { color: var(--primary); }
span.ftool { cursor: default; }

/* The menu opens downward. The strip is the bottom edge of a card that clips
   its own corners, so the panel cannot hang below it in flow -- ftoolMenu()
   places it against the viewport instead, from the button's own position. */
.ftool-menu { position: relative; display: flex; }
.ftool-menu .ftool { width: 100%; }
/* Shown by a class rather than x-show: the panel is placed by writing top and
   left onto it, and x-show writes to the same inline style -- the two fought
   over it and the menu would open but not close. A class keeps the two apart. */
.ftool-pop {
  display: none;
  position: fixed;
  min-width: 210px; z-index: var(--z-modal);
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-14);
  box-shadow: var(--shadow-raised);
  overflow: hidden;
}
.ftool-pop.on { display: block; }
.ftool-pop .pop-item { padding: var(--s-12); }

/* On the feed card the strip is already a full-width child of the card, so it
   keeps its own edges instead of bleeding out of a padded body. */
/* On a post card the strip sits directly under what the card says, with no
   rules boxing the four controls in: the card is one thing, and the lines were
   drawing three cells around what reads as a single row of actions. A flier
   card keeps its rules -- there the strip is the bottom edge of a small tile
   and the divisions do work. */
.post-tools { margin: 0; border-top: 0; }
.post-tools .ftool { border-left: 0; }

@media (max-width: 479px) {
  .ftool-k { display: none; }
}

/* Tag pinned to the artwork */
.pin-tag {
  position: absolute; top: var(--s-12); left: var(--s-12); z-index: var(--z-base);
  display: inline-flex; align-items: center; gap: var(--s-4);
  height: 28px; padding: 0 var(--s-12);
  border-radius: var(--r-8);
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  color: var(--text-primary);
  font-size: 14px; font-weight: 400; line-height: 20px;
}
.pin-tag.right { left: auto; right: var(--s-12); }
.pin-tag svg { width: 14px; height: 14px; }
/* Accent orange carries badge/notification status per the accent roles. The
   label always states the status in words too — colour never carries it alone. */
.pin-tag.warn { background-color: var(--accent-orange); border-color: var(--accent-orange); color: var(--surface); }
.pin-tag.past { background-color: var(--surface-light); color: var(--text-tertiary); }
/* Icon tile used at the top of confirmation and choice cards */
.mark-tile {
  display: inline-flex; align-items: center; justify-content: center;
  width: 56px; height: 56px; flex: none;
  border-radius: var(--r-14);
  border: 1px solid transparent;
}
.mark-tile svg { width: 24px; height: 24px; }
.mark-tile.brand { background-color: var(--primary);     border-color: var(--primary);     color: var(--white); }
.mark-tile.good  { background-color: var(--accent-blue); border-color: var(--accent-blue); color: var(--surface); }
.mark-tile.bad   { background-color: rgba(250, 70, 22, 0.06); border-color: var(--danger); color: var(--danger-dark); }
.mark-tile.mute  { background-color: var(--surface-light); border-color: var(--border-subtle); color: var(--text-tertiary); }

/* Date block */
.datestamp {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  width: 56px; height: 56px; flex: none;
  border-radius: var(--r-14);
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
}
.datestamp .d {
  font-family: var(--font-display);
  font-size: 18px; font-weight: 700; line-height: 20px; color: var(--text-primary);
}
.datestamp .m {
  font-size: 14px; font-weight: 600; line-height: 20px; color: var(--primary);
}

/* The rail's date block: the same object at the size a rail row can hold. */
.datestamp-sm { width: 44px; height: 44px; }
.datestamp-sm .d { font-size: 15px; }
.datestamp-sm .m { font-size: 13px; }

.year-head {
  display: flex; align-items: baseline; gap: var(--s-16);
  padding-bottom: var(--s-12); margin-bottom: var(--s-24);
  border-bottom: 1px solid var(--border-subtle);
}

/* --------------------------------------------------------------------------
   Analytics
   -------------------------------------------------------------------------- */

.bar-row { padding: var(--s-12) 0; border-bottom: 1px solid var(--border-subtle); }
.bar-row:last-of-type { border-bottom: 0; }
.bar {
  display: block; height: 8px; margin-top: var(--s-8);
  border-radius: var(--r-pill); background-color: var(--gray-light); overflow: hidden;
}
.bar i { display: block; height: 100%; background-color: var(--primary); border-radius: var(--r-pill); }

.stat-list { display: grid; gap: var(--s-16); }
.stat-list > div {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-16);
  padding-bottom: var(--s-16); border-bottom: 1px solid var(--border-subtle);
}
.stat-list > div:last-child { border-bottom: 0; padding-bottom: 0; }

.rank-row {
  display: flex; align-items: center; gap: var(--s-16);
  padding: var(--s-16);
  border-bottom: 1px solid var(--border-subtle);
  transition: background-color .15s ease;
}
.rank-row:last-child { border-bottom: 0; }
.rank-row:hover { background-color: rgba(255, 255, 255, 0.06); }

/* A person in a list, with the action that list exists to enable. The name and
   the button are two separate targets, so the row is a container rather than
   one big link with a button smuggled inside it. */
.person-row {
  display: flex; align-items: center; gap: var(--s-12);
  padding: var(--s-8) var(--s-12);
  border-radius: var(--r-14);
  transition: background-color .15s ease;
}
.person-row:hover { background-color: rgba(255, 255, 255, 0.06); }
.person-main {
  display: flex; align-items: center; gap: var(--s-12);
  min-width: 0; flex: 1;
}
.person-act { flex: none; }
.person-row .t-micro.person-act { color: var(--text-disabled); padding-right: var(--s-8); }
.rank {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex: none;
  border-radius: var(--r-8);
  background-color: var(--surface-light);
  font-size: 14px; font-weight: 600; line-height: 20px;
  color: var(--text-tertiary);
}

/* --------------------------------------------------------------------------
   Dashboard console — stacked sections rather than a rail, because each one is
   a different question and they are read in order, not scanned side by side.
   -------------------------------------------------------------------------- */

.dash-section {
  margin-bottom: var(--s-40);
  scroll-margin-top: calc(var(--rail-h) + var(--s-24));
}
.dash-section:last-child { margin-bottom: 0; }

/* Waiting on you. One row per thing the console is asking for, each carrying
   the action that clears it -- a list of problems without the way to fix them
   is just a scolding. Urgent rows are marked on the left edge rather than
   coloured throughout, so the eye finds them without the panel turning loud. */
.attn-row {
  display: flex; align-items: center; gap: var(--s-16);
  padding: var(--s-16);
  border-bottom: 1px solid var(--border-subtle);
  border-left: 3px solid transparent;
  transition: background-color .15s ease;
}
.attn-row:last-child { border-bottom: 0; }
.attn-row:hover { background-color: rgba(255, 255, 255, 0.06); }
.attn-row.urgent { border-left-color: var(--primary); }

.attn-mark {
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex: none;
  border-radius: var(--r-8);
  background-color: var(--surface-light);
  color: var(--text-tertiary);
}
.attn-mark svg { width: 18px; height: 18px; }
.attn-row.urgent .attn-mark { background-color: var(--primary); color: var(--white); }

.attn-text { flex: 1; min-width: 0; }
.attn-action { flex: none; }

/* Nothing waiting still renders. The sidebar links to this section
   unconditionally, and an anchor that scrolls to nothing reads as a broken
   page rather than as an empty queue. */
.attn-clear { display: flex; align-items: center; gap: var(--s-16); }
.attn-clear .attn-mark { background-color: var(--surface-light); color: var(--primary); }

/* On a narrow screen the action drops under the text rather than squeezing it:
   giving the text a basis of the whole first line (less the mark and its gap)
   is what forces the wrap -- flex: 1 alone lets the text shrink to nothing and
   clip instead. The action then lines up under the text, not the mark. */
@media (max-width: 639px) {
  .attn-row { flex-wrap: wrap; }
  .attn-text { flex: 1 1 calc(100% - 32px - var(--s-16)); }
  .attn-action { margin-left: calc(32px + var(--s-16)); }
}

.panel-head {
  padding: var(--s-16) var(--s-24);
  border-bottom: 1px solid var(--border-subtle);
}

/* A single figure with its name and a line saying what it actually counts. The
   help line is the point: a bare number on a dashboard teaches nobody. */
.gauge-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-12); }
.gauge {
  display: flex; flex-direction: column; gap: var(--s-4);
  padding: var(--s-16);
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-14);
  transition: border-color .15s ease;
}
a.gauge:hover { border-color: var(--border-medium); }
.gauge-n {
  font-family: var(--font-display);
  font-size: 30px; font-weight: 700; line-height: 36px; color: var(--text-primary);
}
.gauge-k { font-size: 14px; font-weight: 600; line-height: 20px; color: var(--text-primary); }
.gauge-h { font-size: 13px; line-height: 18px; color: var(--text-tertiary); }
.gauge-live .gauge-n { color: var(--primary); }

@media (max-width: 639px) {
  .gauge-row { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   Feed layout — centre column with an optional right rail. The rail drops away
   below 1280 and the centre column keeps its readable measure rather than
   stretching.
   -------------------------------------------------------------------------- */

.feed-layout {
  display: grid;
  /* One column by default, and it takes the width the page gives it. The rail
     is added back below, at the width where there is honestly room for both. */
  grid-template-columns: minmax(0, 1fr);
  /* The rail is a sidecar, not a second column of the read: it needs enough air
     that the eye stops at the end of a post rather than running on into it. */
  gap: var(--s-32);
  /* Anchored to the left, not centred. The sidebar is the edge this column is
     read against, so any spare width belongs on the far side of the page --
     centring it opened a gap between the nav and the first card that read as a
     seam down the middle of the app. */
  justify-content: start;
  align-items: start;
  /* The column fills the page but stops at a measure a paragraph survives. */
  max-width: var(--col-max);
}

/* ---- where the rail comes back ----
   It used to arrive at 1280, and arriving there made the page worse: the column
   went from 664px with the rail hidden to 470px with it showing, so widening
   the window narrowed the thing being read. 1440 is the first width that can
   carry a column and a rail without either being the loser. */
@media (min-width: 1440px) {
  .feed-layout {
    grid-template-columns: minmax(0, 1fr) var(--rail-w);
    max-width: calc(var(--col-max) + var(--rail-w) + var(--s-32));
  }
}
/* The single column is declared rather than left implicit: an auto column takes
   the max-content width of whatever is inside it, so one wide card sets the
   width of the track and everything else in the column inherits the overflow.
   The rail below was given this treatment; the reading column was not, and on
   the dashboard -- where a three-up row of gauges has more max-content than most
   pages carry -- a 359px column was laying out a 503px track and putting a
   horizontal scrollbar under the whole page on a phone. */
.feed-col {
  min-width: 0; display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-16);
}
.feed-rail {
  display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-16);
  position: sticky; top: calc(var(--rail-h) + var(--s-16));
}
/* Where the column carries a filter bar, the rail clears it and its first card
   begins on the same line as the post line -- and everything under that card,
   Similar Interests included, comes down with it. A page whose column has no
   filter bar has nothing to clear, so the rail stays level with the column. */
.feed-layout:has(.feed-filters) .feed-rail { margin-top: calc(var(--filters-h) + var(--s-16)); }

@media (max-width: 1439px) {
  .feed-rail { display: none; }
}

/* ---- board bar: what you're looking at, and what you can pin ----
   Replaces the avatar-and-prompt composer. Filters sit left, the two pin
   actions right; on a narrow column the actions wrap under and go full width. */
.board-bar {
  display: flex; align-items: center; gap: var(--s-16);
  /* a grid item defaults to min-width:auto, which would let the chips push the
     bar past the column and under the rail */
  min-width: 0;
  padding-bottom: var(--s-12);
  border-bottom: 1px solid var(--border-subtle);
}
.board-bar .chip-row { flex: 1 1 0; min-width: 0; padding-bottom: 0; }
.board-bar .chip-row .chip { flex: 1; justify-content: center; }
.board-pin { display: flex; align-items: center; gap: var(--s-8); flex: none; }

/* --------------------------------------------------------------------------
   The board column — the head of a feed: the views it can be read in, and the
   one thing you can add to it.

   Nothing here is separated by a rule. The column is held together by the
   space between its parts and by the weight of the type, which is why a tab
   marks itself with a short bar under its own label rather than sitting in a
   bordered strip.
   -------------------------------------------------------------------------- */

/* The head of the column is one sticky row carrying two independent controls:
   the category picker (which posts) and the view tabs (how they are ordered).
   The row sticks, not the tabs alone, so the two never come apart while the
   column scrolls under them. */
.feed-filters {
  position: sticky; top: var(--rail-h); z-index: var(--z-sticky);
  display: flex; align-items: center; gap: var(--s-8);
  min-height: var(--filters-h);
  min-width: 0;
  background-color: rgba(1, 1, 1, 0.92);
  backdrop-filter: blur(12px);
}
/* Two pickers and the tab strip do not fit a phone's width in one line. The
   pickers keep their row and the strip drops below them, rather than the strip
   being squeezed to nothing -- it is the control that is used most. */
@media (max-width: 720px) {
  .feed-filters { flex-wrap: wrap; }
  .feed-filters .tabbar { flex-basis: 100%; }
}

.tabbar {
  display: flex; align-items: stretch; min-width: 0; flex: 1;
  overflow-x: auto; scrollbar-width: none;
}
.tabbar::-webkit-scrollbar { display: none; }

/* ---------------------------------------------------------------- category
   The picker sits outside the tab strip rather than in it. The strip scrolls
   sideways and would clip a panel opening out of it, and the two controls
   answer different questions -- putting them in one row of cells would say
   they were alternatives to each other, which they are not.
   -------------------------------------------------------------------------- */
.cat-picker { position: relative; flex: none; display: flex; align-items: center; }

.cat-btn {
  display: inline-flex; align-items: center; gap: var(--s-8);
  height: 38px; padding: 0 var(--s-12);
  border-radius: var(--r-pill);
  border: 1px solid var(--border-control);
  background-color: rgba(0, 0, 0, 0);
  color: var(--text-tertiary);
  font-size: 15px; font-weight: 500; line-height: 22px;
  white-space: nowrap; cursor: pointer;
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
.cat-btn:hover { background-color: rgba(255, 255, 255, 0.08); color: var(--text-primary); }
/* A picked category is the button's own state, so the button carries it
   rather than leaving the fact somewhere else on the page. */
.cat-btn.on {
  background-color: var(--primary); border-color: var(--primary);
  color: var(--white); font-weight: 700;
}
.cat-btn svg { width: 16px; height: 16px; flex: none; }
.cat-btn .caret { width: 14px; height: 14px; transition: transform .15s ease; }
.cat-btn .caret.up { transform: rotate(180deg); }

/* The panel hangs from the left edge of its button, not the right, because the
   button is on the left of the row. */
.cat-pop {
  left: 0; right: auto;
  width: 280px; max-height: 380px;
  display: flex; flex-direction: column;
}
/* The filter box wears the head of the panel rather than sitting as a field
   inside it -- it belongs to the panel, not to the list. */
.cat-search { display: flex; align-items: center; gap: var(--s-8); }
.cat-search svg { width: 16px; height: 16px; flex: none; color: var(--text-disabled); }
.cat-q {
  flex: 1; min-width: 0;
  background: none; border: 0; outline: none;
  color: var(--text-primary);
  font-size: 14px; font-weight: 400; line-height: 20px;
}
.cat-q::placeholder { color: var(--text-disabled); }

.cat-list { overflow-y: auto; padding: var(--s-8) 0; }
.cat-none { padding: var(--s-16); color: var(--text-tertiary); text-align: center; }

/* A searchable list standing in for a <select> whose options have outgrown it.
   The search sits on top, the rows scroll under it, and one row is chosen --
   so it reads as a field, not as a panel that opens over the page. */
.picker {
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-8);
  overflow: hidden;
  background-color: var(--surface);
}
.picker .cat-search { padding: var(--s-12) var(--s-16); border-bottom: 1px solid var(--border-subtle); }
.picker-list { max-height: 260px; }
.picker-opt {
  width: 100%; background: none; border: 0; text-align: left;
  align-items: flex-start;
}
.picker-opt .lbl { display: grid; gap: 2px; }
.picker-title {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 14px; line-height: 20px;
}
.picker-when {
  display: block;
  font-size: 13px; line-height: 18px; color: var(--text-disabled);
  font-variant-numeric: tabular-nums;
}
.picker-opt.on .picker-when { color: var(--text-tertiary); }
.picker-tick { width: 14px; height: 14px; flex: none; margin-top: 3px; color: var(--primary); }

/* A row is a checkbox now: several subjects can be on at once, so each one
   carries its own state rather than the list carrying one winner. The tick is
   drawn rather than native so it can sit in the panel's own palette; the real
   input stays in the accessibility tree, just out of sight. */
.cat-opt {
  display: flex; align-items: center; gap: var(--s-12);
  padding: var(--s-12) var(--s-16);
  color: var(--text-tertiary);
  font-size: 14px; font-weight: 400; line-height: 20px;
  cursor: pointer; user-select: none;
  transition: background-color .15s ease, color .15s ease;
}
.cat-opt:hover { background-color: rgba(255, 255, 255, 0.06); color: var(--text-primary); }
.cat-opt.on { color: var(--text-primary); font-weight: 700; }
.cat-opt .lbl { min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.cat-opt input[type="checkbox"] {
  position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none;
}
.cat-opt .tick {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; flex: none;
  border: 1px solid var(--border-medium); border-radius: var(--r-4);
  color: rgba(0, 0, 0, 0);
  transition: background-color .12s ease, border-color .12s ease, color .12s ease;
}
.cat-opt .tick svg { width: 12px; height: 12px; }
.cat-opt input:checked ~ .tick {
  background-color: var(--primary); border-color: var(--primary); color: var(--white);
}
.cat-opt input:focus-visible ~ .tick { outline: 2px solid var(--accent-sky); outline-offset: 2px; }

/* ------------------------------------------------------------------- place
   The second picker in the bar. It wears the same button and the same panel as
   the category one -- they are two questions of equal standing, and a control
   that looked different would read as a control that worked differently -- but
   its panel holds three fields rather than a list, because "where" narrows in
   three steps and a flat list cannot say which step a row belongs to.
   -------------------------------------------------------------------------- */
/* Three lines and a foot. The rows are the panel -- no boxes drawn inside a
   box, no labels repeating the placeholder underneath them -- so what the
   panel looks like is what it is: where, asked three times, narrowing. */
.place-pop { width: 260px; }
.place-row {
  /* Flex for the same reason .cat-search is: the input is the row, and a
     block parent would leave it at its own idea of a width. */
  display: flex; align-items: center;
  padding: var(--s-12) var(--s-16);
  border-bottom: 1px solid var(--border-subtle);
  cursor: text;
}
/* The three carry the same weight, so nothing in the stack reads as the one
   that matters and the hairline under the last of them would be a line under
   nothing -- the foot draws its own. */
.place-row:last-of-type { border-bottom: 0; }

/* Ticking is not applying: the panel keeps a foot so a handful of subjects can
   be chosen in one go and read in one reload. */
.cat-foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-8);
  padding: var(--s-12) var(--s-16);
  border-top: 1px solid var(--border-subtle);
}

/* The filter, restated under the bar with its own way out. */
.filter-note {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-12);
  padding: var(--s-12) var(--s-16);
  border-radius: var(--r-pill);
  background-color: var(--surface-light);
  border: 1px solid var(--border-subtle);
  color: var(--text-tertiary);
}
.filter-note strong { color: var(--text-primary); font-weight: 700; }
.filter-clear {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--text-tertiary);
  font-size: 14px; font-weight: 500; line-height: 20px;
  cursor: pointer;
  transition: color .15s ease;
}
.filter-clear:hover { color: var(--text-primary); }
.filter-clear svg { width: 14px; height: 14px; }

.tab {
  position: relative; flex: 1 0 auto;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-8);
  min-height: 52px; padding: 0 var(--s-16);
  color: var(--text-tertiary);
  font-size: 15px; font-weight: 500; line-height: 22px;
  white-space: nowrap; cursor: pointer;
  transition: background-color .15s ease, color .15s ease;
}
.tab:hover { background-color: rgba(255, 255, 255, 0.08); color: var(--text-primary); }
.tab.on { color: var(--text-primary); font-weight: 700; }
/* The mark sits under the label, not under the cell, so it reads as belonging
   to the word rather than to the box. */
.tab.on::after {
  content: ''; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%);
  width: calc(100% - var(--s-24)); max-width: 96px; height: 4px;
  border-radius: var(--r-pill);
  background-color: var(--primary);
}

/* The one way to add to the column. It is a link wearing the shape of a field:
   the writing happens on the create page, so there is nothing here to type
   into and nothing to submit. */
.composer { display: flex; align-items: center; gap: var(--s-12); padding: var(--s-16) 0; }
.composer-field {
  flex: 1; min-width: 0;
  display: flex; align-items: center;
  min-height: 44px; padding: 0 var(--s-16);
  border-radius: var(--r-pill);
  background-color: var(--surface-light);
  color: var(--text-tertiary);
  font-size: 17px; font-weight: 400; line-height: 24px;
  transition: background-color .15s ease, color .15s ease;
}
.composer-field:hover { background-color: rgba(255, 255, 255, 0.08); color: var(--text-primary); }


@media (max-width: 639px) {
  .board-bar { flex-wrap: wrap; }
  .board-pin { width: 100%; }
  .board-pin .btn { flex: 1; }
}

/* --------------------------------------------------------------------------
   A post in the feed — a thing pinned to a board, not a status update.

   Three deliberate departures from the social-feed card:
   1. The notice leads, not the person. A category tag and the title come first;
      the byline sits at the foot with the actions, where a poster credits its
      printer.
   2. Media is inset with its own radius, so the eye reads card, then picture --
      rather than a photo bleeding to the card's own edges.
   3. The foot is asymmetric: quiet counts on the left, one real action on the
      right. No row of equal-weight buttons.
   -------------------------------------------------------------------------- */

.post {
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-24);
  overflow: hidden;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.post:hover { border-color: var(--border-medium); }

/* A card standing in for a feed that has nothing in it yet. Everything it needs
   was being written inline on the element; it lives here now. */
.post-blank { padding: var(--s-40) var(--s-24); text-align: center; }
.post-blank .mark-tile { margin: 0 auto var(--s-24); }
.post-blank p { margin: var(--s-8) auto 0; max-width: 46ch; }
.post-blank .actions {
  display: flex; justify-content: center; flex-wrap: wrap;
  gap: var(--s-12); margin-top: var(--s-24);
}

.post-frame { padding: var(--s-16); }

/* the pinned tags that open a card */
.post-tags {
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--s-8);
  margin-bottom: var(--s-12);
}
.tag {
  display: inline-flex; align-items: center; gap: var(--s-4);
  padding: 3px var(--s-8);
  border-radius: var(--r-4);
  border: 1px solid var(--border-subtle);
  background-color: var(--surface-light);
  color: var(--text-tertiary);
  font-size: 13px; font-weight: 600; line-height: 18px;
  letter-spacing: .04em; text-transform: uppercase;
}
.tag svg { width: 12px; height: 12px; }
.tag-cat  { border-color: var(--primary); color: var(--primary); background-color: rgba(250, 70, 22, 0.06); }
.tag-when { border-color: var(--border-medium); color: var(--text-primary); background-color: var(--surface); }
.post-age { margin-left: auto; font-size: 13px; line-height: 18px; color: var(--text-disabled); }

.post-title {
  font-family: var(--font-body);
  font-size: 20px; font-weight: 700; line-height: 26px; letter-spacing: -.01em;
  color: var(--text-primary);
}
.post-title a { color: inherit; }
.post-title a:hover { color: var(--primary); }
.post-text {
  margin-top: var(--s-8);
  font-size: 17px; font-weight: 400; line-height: 26px; color: var(--text-secondary);
  white-space: pre-wrap; word-break: break-word;
}

/* inset media — the fix for square corners inside a rounded card */
/* The frame reserves its own height before the picture inside it has one.
   Without that the box is as tall as an image that has not loaded -- which is
   nothing -- so a card in the line renders two pixels tall and then jumps to
   full height when the file lands, shoving every post below it down the page.
   An aspect ratio holds the space from the first paint; the fill underneath is
   what the reader looks at in the meantime, and it is released the moment the
   image can size itself. */
.post-media {
  display: block; margin-top: var(--s-16);
  border-radius: var(--r-14);
  border: 1px solid var(--border-subtle);
  overflow: hidden;
  background-color: var(--surface-light);
  aspect-ratio: 16 / 10;
}
/* Once the image has its own dimensions it decides the height, up to the cap.
   :has keeps the ratio for as long as the box is empty and drops it as soon as
   there is something in it with a size of its own. */
.post-media:has(img, video) { aspect-ratio: auto; min-height: 120px; }
.post-media img, .post-media video {
  width: 100%; display: block; max-height: 520px; object-fit: cover;
  background-color: var(--surface-light);
}

/* the foot: what you can do with the card, kept to the right */
.post-foot {
  display: flex; align-items: center; gap: var(--s-12);
  padding: var(--s-12) var(--s-16);
  border-top: 1px solid var(--border-subtle);
  background-color: var(--warm-white);
}
.post-by {
  display: inline-flex; align-items: center; gap: var(--s-8);
  flex: 0 1 auto; min-width: 0;
  font-size: 13px; line-height: 18px; color: var(--text-tertiary);
}
.post-by strong { font-weight: 600; color: var(--text-secondary); }
.post-by:hover strong { color: var(--primary); }
.post-by-top {
  display: inline-flex; align-items: center; gap: var(--s-8);
  font-size: 15px; line-height: 20px; font-weight: 700; color: var(--text-primary);
  text-decoration: none;
}
.post-by-top:hover { color: var(--primary); }
.post-do { margin-left: auto; display: inline-flex; align-items: center; gap: var(--s-8); flex: none; }
.post-mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; flex: none;
  background: none; border: 0; cursor: pointer;
  border-radius: var(--r-pill);
  color: var(--text-tertiary);
  transition: background-color .15s ease, color .15s ease;
}
.post-mark svg { width: 17px; height: 17px; }
.post-mark:hover { background-color: rgba(255, 255, 255, 0.08); color: var(--text-primary); }
.post-mark.on { color: var(--primary); }

@media (max-width: 639px) {
  .post-foot { flex-wrap: wrap; row-gap: var(--s-8); }
}

/* ---- the event card: a post with a structured summary attached ---- */
.event-card { border-color: var(--border-medium); }
.event-strip {
  display: flex; align-items: center; gap: var(--s-16);
  margin-top: var(--s-16); padding: var(--s-12);
  border-radius: var(--r-14);
  border: 1px solid var(--border-subtle);
  background-color: var(--surface-light);
  transition: border-color .15s ease;
}
.event-strip:hover { border-color: var(--border-medium); }
.event-strip .when {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  width: 52px; height: 52px; flex: none;
  border-radius: var(--r-10);
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
}
.event-strip .when .d { font-family: var(--font-display); font-size: 18px; font-weight: 700; line-height: 20px; }
.event-strip .when .m { font-size: 13px; font-weight: 600; line-height: 18px; color: var(--primary); }
/* The text half of the strip: the date, then where and what it costs, each on
   its own line and each cut off rather than wrapping. */
.event-strip .strip-text { flex: 1; min-width: 0; }
.event-strip .strip-text > span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.event-strip .strip-text > span + span { margin-top: var(--s-4); }


/* ---- right rail cards ---- */
.rail-card {
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-24);
  padding: var(--s-24);
}
.rail-card h3 { margin-bottom: var(--s-16); }
/* A card's footer link -- "All events" and the like. The gap belonged in the
   template as an inline margin; it belongs here. */
.rail-card .btn-block { margin-top: var(--s-12); }

/* The search at the head of the rail, where the column starts. */
.rail-search { position: relative; }
.rail-search .prefix {
  position: absolute; left: var(--s-16); top: 50%; transform: translateY(-50%);
  color: var(--text-disabled); pointer-events: none;
}
.rail-search .input {
  height: 44px; padding-left: var(--s-40);
  border-radius: var(--r-pill);
  background-color: var(--surface-light);
}
.rail-search .input:focus { background-color: var(--surface); }
.rail-item {
  display: flex; align-items: center; gap: var(--s-12);
  padding: var(--s-12); border-radius: var(--r-pill);
  transition: background-color .15s ease;
}
.rail-item:hover { background-color: rgba(255, 255, 255, 0.08); }
/* The text half of a rail row: it takes the space the date block or the avatar
   leaves, and each line cuts off rather than wrapping the row open. Written
   here rather than assembled from utility classes on the element, so the row
   is described in one place. */
.rail-item .rail-text { flex: 1; min-width: 0; }
.rail-item .rail-text > span {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Event profile — the structured page a visitor should not have to leave
   -------------------------------------------------------------------------- */

.event-hero { position: relative; background-color: var(--surface); }
.event-hero img, .event-hero video { width: 100%; display: block; max-height: 420px; object-fit: cover; }
.event-hero .placeholder {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-8); min-height: 240px; padding: var(--s-32);
  color: var(--text-tertiary); text-align: center;
}

/* --------------------------------------------------------------------------
   The top band: the flier, poster or video with the map standing beside it.
   What it looks like, and where it is, read as one pair.

   Without a mapped venue .has-map is absent and this collapses to the single
   full-width column the hero has always been.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   The event page in two columns: where it is, and what it is.

   The map is a column of the page, not a panel inside the flier band, so it
   stays beside the reader through the About, the speakers and the discussion.
   Source order still puts the poster first -- it is the headline, and it is
   what a phone and a screen reader should meet first -- and the map is placed
   into the left column only once there is room for one.
   -------------------------------------------------------------------------- */

.event-layout.has-map {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  grid-template-rows: auto 1fr;
  gap: var(--s-24);
  align-items: start;
}
/* The poster and the body share the left column, one above the other; the map
   takes the right column and spans both rows so it follows the whole page. */
.event-layout.has-map .event-headline { grid-column: 1; grid-row: 1; min-width: 0; }
.event-layout.has-map .event-main     { grid-column: 1; grid-row: 2; min-width: 0; }
.event-layout.has-map .event-getting  { grid-column: 2; grid-row: 1 / span 2; }

.event-headline { margin-bottom: var(--s-24); }
.event-layout.has-map .event-headline { margin-bottom: 0; }

.event-getting {
  display: flex; flex-direction: column; min-width: 0;
  padding: var(--s-16);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-24);
  background-color: var(--warm-white);
  /* Follows the reader down the page. The rail height keeps it clear of the
     top bar; the max-height stops a tall column outrunning the viewport. */
  position: sticky;
  top: calc(var(--rail-h) + var(--s-16));
  max-height: calc(100vh - var(--rail-h) - var(--s-32));
  overflow-y: auto;
}
.getting-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-8); margin-bottom: var(--s-8);
}

/* ---- the drawn map ----
   An inline SVG street plan. No tiles, no key, no network. */
.map-frame {
  position: relative;
  height: 220px; flex: none;
  margin-bottom: var(--s-12);
  border-radius: var(--r-14);
  border: 1px solid var(--border-subtle);
  background-color: var(--surface-light);
  overflow: hidden;
}
.map-frame svg { display: block; width: 100%; height: 100%; }

.map-key {
  position: absolute; left: var(--s-8); bottom: var(--s-8);
  display: flex; flex-direction: column; gap: 2px;
  padding: 6px var(--s-8);
  border-radius: var(--r-8);
  background-color: rgba(1, 1, 1, 0.92);
  border: 1px solid var(--border-subtle);
}
.map-key-row {
  display: flex; align-items: center; gap: 6px;
  font-size: 13px; font-weight: 600; line-height: 18px; color: var(--text-secondary);
}
.map-key-row i { width: 8px; height: 8px; border-radius: var(--r-pill); flex: none; }
.map-key-row .dot-you   { background-color: var(--accent-sky); }
.map-key-row .dot-venue { background-color: var(--primary); }

.map-note { font-size: 13px; line-height: 18px; color: var(--text-tertiary); margin-bottom: var(--s-12); }
.map-foot { margin-top: var(--s-12); color: var(--text-disabled); }

.travel-list { display: grid; gap: var(--s-4); }
.travel-row {
  display: flex; align-items: center; gap: var(--s-12);
  padding: var(--s-8) var(--s-12);
  border-radius: var(--r-10);
  border: 1px solid var(--border-subtle);
  background-color: var(--surface);
}
.travel-icon { display: inline-flex; flex: none; color: var(--text-tertiary); }
.travel-icon svg { width: 20px; height: 20px; }
.travel-what { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.travel-label { font-size: 14px; font-weight: 600; line-height: 20px; color: var(--text-primary); }
.travel-far { font-size: 13px; line-height: 18px; color: var(--text-tertiary); }
.travel-time {
  font-size: 14px; font-weight: 600; line-height: 20px;
  color: var(--text-primary); flex: none;
}

/* Three columns will not fit inside a 1200px container, so the register rail
   only splits off once the page is wide enough to give the content a readable
   measure alongside both. Below that it stacks under the content -- the
   Register button in the poster block still jumps straight to it. */
.event-body { display: grid; gap: var(--s-32); align-items: start; }
@media (min-width: 1024px) {
  .event-body { grid-template-columns: minmax(0, 1fr) 340px; }
}
@media (max-width: 1599px) {
  .event-layout.has-map .event-body { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 1023px) {
  /* Stacked in source order -- poster, map, body. The explicit placement has to
     be released or the columns would survive into the single-column layout. */
  .event-layout.has-map { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; }
  .event-layout.has-map .event-headline,
  .event-layout.has-map .event-main,
  .event-layout.has-map .event-getting { grid-column: auto; grid-row: auto; }
  .event-layout.has-map .event-headline { margin-bottom: 0; }
  .event-getting { position: static; max-height: none; }
}

.fact-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0;
}
.fact {
  display: flex; gap: var(--s-16); padding: var(--s-24);
  border-right: 1px solid var(--border-subtle);
  border-bottom: 1px solid var(--border-subtle);
}
.fact:last-child { border-right: 0; }
.fact svg { width: 20px; height: 20px; flex: none; color: var(--primary); }

.speaker {
  display: flex; align-items: center; gap: var(--s-12);
  padding: var(--s-12);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-14);
  background-color: var(--surface);
}

.reg-step {
  display: flex; gap: var(--s-16); padding: var(--s-16) 0;
  border-bottom: 1px solid var(--border-subtle);
}
.reg-step:last-child { border-bottom: 0; }
.reg-step .n {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex: none;
  border-radius: var(--r-pill);
  background-color: var(--primary); color: var(--white);
  font-size: 14px; font-weight: 600; line-height: 20px;
}
.reg-step.done .n { background-color: var(--accent-blue); color: var(--surface); }

/* --------------------------------------------------------------------------
   Studio pipeline row
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   Post Feed — three cards to a row.

   The card keeps the proportions it always had: an 80x100 thumbnail with the
   text beside it, 16px of padding, roughly 200px tall. Only the actions moved.
   They used to be a bar of four buttons alongside the text, which at a third of
   the width wrapped onto three lines; now they are one primary button plus an
   overflow, on their own full-width line underneath where there is room.
   -------------------------------------------------------------------------- */

.studio-grid {
  display: grid; gap: var(--s-16);
  grid-template-columns: minmax(0, 1fr);
  margin-bottom: var(--s-40);
}
@media (min-width: 640px)  { .studio-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .studio-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* Past three, the page grows by adding cards to the row rather than by
   stretching the three it has. At 2560 the three-column grid was handing each
   card 512px -- an 80px thumbnail with half a metre of empty card beside it. */
@media (min-width: 1600px) { .studio-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (min-width: 2100px) { .studio-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); } }

.studio-card {
  display: flex; flex-direction: column; min-width: 0;
  padding: var(--s-16);
  background-color: var(--surface-light);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-24);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.studio-card:hover { border-color: var(--border-medium); box-shadow: var(--shadow-raised); }

.studio-main { display: flex; gap: var(--s-16); min-width: 0; }

.studio-thumb {
  position: relative;
  width: 80px; height: 100px; flex: none;
  border-radius: var(--r-14); overflow: hidden;
  background-color: var(--surface);
  display: flex; align-items: center; justify-content: center;
}
.studio-thumb img { width: 100%; height: 100%; object-fit: cover; }
.studio-thumb .blank { color: var(--text-disabled); }
.studio-thumb .blank svg { width: 24px; height: 24px; }

.studio-body { flex: 1; min-width: 0; }
/* Two lines for the title, one for everything else: the card has to come out
   the height it always was, and at 169px of body width unclamped text runs to
   three lines and adds 70px. */
.studio-title {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.studio-body > .badge + .studio-title { margin-top: var(--s-8); }
.studio-when { margin-top: var(--s-4); }
.studio-missing {
  margin-top: var(--s-4); color: var(--danger-dark);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* the kind, sitting on the thumbnail */
.studio-flag {
  position: absolute; left: var(--s-4); top: var(--s-4);
  padding: 1px 5px; border-radius: var(--r-4);
  background-color: rgba(255, 255, 255, 0.92);
  border: 1px solid var(--border-subtle);
  font-size: 13px; font-weight: 600; line-height: 18px; color: var(--surface);
}

/* margin-top:auto so cards in a row end level whatever the title wraps to */
.studio-card-foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-8);
  margin-top: auto; padding-top: var(--s-12);
}

/* the overflow behind the primary action */
.card-menu { position: relative; flex: none; }
.card-menu-btn { width: 32px; height: 32px; }
.card-menu-btn svg { width: 16px; height: 16px; }
.card-menu-pop {
  position: absolute; right: 0; bottom: calc(100% + var(--s-4));
  z-index: var(--z-sticky);
  min-width: 168px; padding: var(--s-4);
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-10);
  box-shadow: var(--shadow-raised);
}
.card-menu-pop .pop-item {
  display: block; width: 100%; text-align: left;
  padding: var(--s-8) var(--s-12);
  background: none; border: 0; cursor: pointer;
  border-radius: var(--r-8);
  color: var(--text-primary);
  font-family: var(--font-body); font-size: 14px; line-height: 20px;
}
.card-menu-pop .pop-item:hover { background-color: rgba(0, 0, 0, 0.05); }
.card-menu-pop .pop-item.danger { color: var(--danger-dark); }

/* --------------------------------------------------------------------------
   Post Line — the board's record, read down the page
   -------------------------------------------------------------------------- */

/* min-width:0 because the Post Line is a grid item: without it a wide card
   inside would size the whole column and push the feed past its measure. */
.postline { position: relative; min-width: 0; }
.postline::before {
  content: ''; position: absolute; left: 11px; top: var(--s-8); bottom: var(--s-8);
  width: 1px; background-color: var(--border-subtle);
}
.postline-item { position: relative; padding-left: var(--s-40); padding-bottom: var(--s-40); }
.postline-item::before {
  content: ''; position: absolute; left: 4px; top: var(--s-8);
  width: 16px; height: 16px; border-radius: var(--r-pill);
  background-color: var(--primary);
  border: 4px solid var(--surface);
}
.postline-item.quiet::before { background-color: var(--gray-medium); }

/* --------------------------------------------------------------------------
   Chips, badges, tabs, category nav
   -------------------------------------------------------------------------- */

.chip {
  display: inline-flex; align-items: center; gap: var(--s-8);
  height: 38px; padding: 0 var(--s-16);
  border-radius: var(--r-pill);
  background-color: rgba(0, 0, 0, 0);
  border: 1px solid var(--border-control);
  color: var(--text-primary);
  font-size: 15px; font-weight: 500; line-height: 22px;
  white-space: nowrap; cursor: pointer;
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
.chip:hover { background-color: rgba(255, 255, 255, 0.08); color: var(--text-primary); }
.chip.on {
  background-color: var(--text-primary); border-color: var(--text-primary);
  color: var(--surface); font-weight: 700;
}
.chip svg { width: 14px; height: 14px; }

.chip-row { display: flex; gap: var(--s-8); overflow-x: auto; padding-bottom: var(--s-4); scrollbar-width: none; }
.chip-row::-webkit-scrollbar { display: none; }
/* Chips that wrap instead of scrolling. A row of four or five belongs on as
   many lines as it needs -- sideways scrolling is for the long ones, where a
   wrap would be six deep. */
.chip-wrap { display: flex; flex-wrap: wrap; gap: var(--s-8); }

/* ---------------------------------------------------------------- search
   The results page's narrowing controls. What used to live here as well was a
   whole landing state -- a full-height centred hero, its own oversized search
   bar, a row of quick-search links and a note about where the filters were.
   All of it belonged to a page that had its own search box; the box is gone,
   the search happens in the top bar on every screen, and the page is results.
   The styles for that state went with it.
   -------------------------------------------------------------------------- */

/* The narrowing controls, laid out rather than stacked. Where and When are
   short enough to share a line; the category list is not, and takes the width
   to itself. */
.facets { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-24) var(--s-32); }
.facet { min-width: 0; }
.facet .t-eyebrow { margin-bottom: var(--s-12); }
.facet-wide { grid-column: 1 / -1; }
@media (max-width: 640px) {
  .facets { grid-template-columns: 1fr; }
}

/* Category navigation on the events listing */
.cat-nav {
  display: flex; flex-wrap: wrap; gap: var(--s-8);
  padding-bottom: var(--s-24);
  border-bottom: 1px solid var(--border-subtle);
}
.cat-link {
  display: inline-flex; align-items: center; gap: var(--s-8);
  height: 40px; padding: 0 var(--s-16);
  border-radius: var(--r-10);
  background-color: rgba(0, 0, 0, 0);
  border: 1px solid var(--border-subtle);
  color: var(--text-tertiary);
  font-size: 16px; font-weight: 400; line-height: 24px;
  white-space: nowrap;
  transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
.cat-link:hover { background-color: rgba(255, 255, 255, 0.06); color: var(--text-primary); }
.cat-link.on {
  background-color: var(--primary); border-color: var(--primary);
  color: var(--white); font-weight: 600;
}
.cat-link .n {
  display: inline-block; min-width: 24px; padding: 0 var(--s-8);
  border-radius: var(--r-pill);
  background-color: var(--surface-light);
  font-size: 14px; font-weight: 600; line-height: 20px; text-align: center;
}
.cat-link.on .n { background-color: rgba(255, 255, 255, 0.15); color: var(--white); }

.badge {
  display: inline-flex; align-items: center; gap: var(--s-4);
  height: 28px; padding: 0 var(--s-12);
  border-radius: var(--r-8);
  border: 1px solid var(--border-subtle);
  background-color: var(--surface-light);
  color: var(--text-tertiary);
  font-size: 14px; font-weight: 400; line-height: 20px;
}
.badge-brand { background-color: var(--primary); border-color: var(--primary); color: var(--white); }
.badge-good  { background-color: var(--accent-blue); border-color: var(--accent-blue); color: var(--surface); }
.badge-warn  { background-color: var(--accent-orange); border-color: var(--accent-orange); color: var(--surface); }
.badge-bad   { background-color: var(--danger); border-color: var(--danger); color: var(--white); }
.badge-mute  { background-color: var(--surface-light); border-color: var(--border-subtle); color: var(--text-tertiary); }

.tabs {
  display: flex; gap: var(--s-32);
  border-bottom: 1px solid var(--border-subtle);
  overflow-x: auto; scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs a, .tabs button {
  position: relative; padding: 0 0 var(--s-16);
  background: none; border: 0; cursor: pointer; white-space: nowrap;
  color: var(--text-tertiary);
  font-size: 16px; font-weight: 400; line-height: 24px;
  transition: color .15s ease;
}
.tabs a:hover, .tabs button:hover { color: var(--text-primary); }
.tabs a.on, .tabs button.on { color: var(--primary); font-weight: 600; }
.tabs a.on::after, .tabs button.on::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -1px;
  height: 2px; background-color: var(--primary);
}
/* The Post Feed history opens on a press. The whole heading is the control,
   so it is a button that lays out like the block of text it replaces -- left
   aligned, no button chrome -- and the chevron is the only part that moves. */
.history-toggle {
  display: flex; align-items: flex-start; gap: var(--s-12);
  flex: 1; min-width: 0;
  padding: 0; border: 0; background: none;
  text-align: left; cursor: pointer;
  color: inherit; font: inherit;
}
.history-toggle:hover .t-h2 { color: var(--primary); }
.history-chev {
  display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; flex: none;
  margin-top: 2px;
  color: var(--text-tertiary);
  transition: transform .18s ease;
}
.history-chev svg { width: 18px; height: 18px; }
.history-chev.open { transform: rotate(90deg); color: var(--primary); }
.history-toggle .count { flex: none; align-self: center; }

.tabs .count {
  display: inline-block; margin-left: var(--s-8); padding: 0 var(--s-8);
  border-radius: var(--r-pill);
  background-color: var(--surface-light);
  font-size: 14px; font-weight: 600; line-height: 20px;
  color: var(--text-tertiary);
}

/* --------------------------------------------------------------------------
   Forms
   -------------------------------------------------------------------------- */

/* A floating label is carried inside its control rather than stacked above it,
   so fields sit closer together than they did when each one owned two boxes. */
.field { display: block; margin-bottom: var(--s-16); }
.field > label, .label {
  display: block; margin-bottom: var(--s-8);
  color: var(--text-primary);
  font-size: 14px; font-weight: 600; line-height: 21px;
}
.label .opt, .field > label .opt { font-weight: 400; color: var(--text-tertiary); margin-left: var(--s-4); }
.hint {
  margin-top: var(--s-4);
  font-size: 13px; font-weight: 400; line-height: 18px;
  color: var(--text-tertiary);
}
.hint.bad  { color: var(--danger-dark); }
.hint.good { color: var(--text-primary); }

/* A field's edge is the whole of its affordance -- there is nothing else on an
   empty input to say "type here". At the hairline it was 1.27:1 against the page
   and the form read as a stack of labels with gaps under them. */
.input, .select, .textarea {
  width: 100%;
  background-color: var(--surface);
  color: var(--text-primary);
  padding: var(--s-12) var(--s-16);
  font-family: var(--font-body);
  font-size: 16px; font-weight: 400; line-height: 24px;
  border-radius: var(--r-8);
  border: 1px solid var(--border-control);
  box-shadow: none;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.input:hover:not(:focus), .select:hover:not(:focus), .textarea:hover:not(:focus) {
  border-color: var(--border-control-hover);
}
.input::placeholder, .textarea::placeholder { color: var(--text-disabled); }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--accent-blue);
  box-shadow: 0px 0px 0px 3px rgba(165, 214, 255, 0.1);
}
.input:disabled, .select:disabled, .textarea:disabled {
  background-color: var(--gray-light); color: var(--text-disabled);
}
.textarea { resize: vertical; min-height: 96px; }
.select {
  appearance: none; cursor: pointer; padding-right: var(--s-40);
  /* The caret is drawn light: the field behind it is --surface, and the near
     black this used to be was invisible on it. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23FFFFFF' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--s-16) center; background-size: 16px;
}
input[type="date"], input[type="time"], input[type="datetime-local"] { color-scheme: light; }

.input-group { position: relative; }
.input-group .prefix {
  position: absolute; left: var(--s-16); top: 50%; transform: translateY(-50%);
  color: var(--text-disabled); font-size: 16px; line-height: 24px; pointer-events: none;
}
.input-group .prefix ~ .input { padding-left: var(--s-32); }
/* The reveal inside a password field. It was 34x40 -- the icon plus its padding
   and nothing more -- which is a small target for the one control on the sign-in
   page a person reaches for when something has already gone wrong. Sized like
   the icon buttons everywhere else instead. */
.input-group .suffix-btn {
  position: absolute; right: var(--s-4); top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  background: none; border: 0; cursor: pointer;
  color: var(--text-tertiary); padding: 0;
  border-radius: var(--r-8);
}
.input-group .suffix-btn:hover { color: var(--text-primary); background-color: rgba(255, 255, 255, 0.06); }
.input-group .suffix-btn svg { width: 18px; height: 18px; }

/* --------------------------------------------------------------------------
   Floating labels

   Written once, here, and inherited by every form in the project. A field is
   given the treatment by being what it already is -- a .field holding a real
   <label> and a real control -- so no template opts in and none can drift.

   The label rests on the control's own text line and rises into the top of the
   box as soon as the control is focused or holds a value. A field then costs
   one box of height instead of a label stacked above a box: about 23px saved
   per field, which is what keeps a five-field form on one screen.

   Left alone deliberately:
     - <span class="label">, which names a group of buttons and has no control
       to float over;
     - label.sr-only, which exists for screen readers and must stay invisible;
     - a .field holding no control at all (a heading above a list of rows);
     - file inputs, which draw their own button and have no text line.

   Emptiness is read from :placeholder-shown, so a control that floats needs a
   placeholder attribute -- placeholder=" " where there is nothing worth
   suggesting. Controls that cannot show a placeholder at all (a select, a
   date) therefore read as filled, which is the right answer: they always have
   something on display.

   The guard below is repeated verbatim on each rule rather than nested, so
   every rule can be read on its own. Read it as: a field with a real label and
   a real control.
   -------------------------------------------------------------------------- */

.field:has(> label:not(.sr-only)):has(> .input:not([type="file"]), > .select, > .textarea, > .input-group) {
  position: relative;
  /* Where the label sits before it lifts: the middle of a 50px control. */
  --fl-rest: 13px;
}

/* A textarea's first line is at its top, not its middle, so the label rests
   against the top padding instead of the centre. The guard above is repeated
   in full and narrowed: written short, this selector is less specific than the
   rule it is meant to override, and loses. */
.field:has(> label:not(.sr-only)):has(> .input:not([type="file"]), > .select, > .textarea, > .input-group):has(> .textarea) {
  --fl-rest: 25px;
}

.field:has(> label:not(.sr-only)):has(> .input:not([type="file"]), > .select, > .textarea, > .input-group) > label {
  position: absolute; z-index: 2;
  left: var(--s-16); top: var(--fl-rest);
  display: block; margin: 0;
  max-width: calc(100% - var(--s-32));
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  pointer-events: none;
  transform-origin: left top;
  font-size: 16px; font-weight: 400; line-height: 24px;
  color: var(--text-tertiary);
  transition: transform .13s ease, color .13s ease;
}

/* Room above the text for the lifted label. Only the field's own control:
   a descendant combinator here would also reach into any row of inputs that
   happened to sit inside the field, and give each one a label's worth of empty
   space it has no label to put there. */
.field:has(> label:not(.sr-only)):has(> .input:not([type="file"]), > .select, > .textarea, > .input-group)
  > :is(.input:not([type="file"]), .select),
.field:has(> label:not(.sr-only)):has(> .input:not([type="file"]), > .select, > .textarea, > .input-group)
  > .input-group > :is(.input, .select) {
  height: 50px;
  padding-top: 20px; padding-bottom: 4px;
}
.field:has(> label:not(.sr-only)):has(> .textarea) > .textarea {
  height: auto; min-height: 104px;
  padding-top: 25px; padding-bottom: var(--s-12);
}

/* The placeholder is the label's understudy: it would sit in the same place,
   so it waits until the label has moved out of the way. */
.field:has(> label:not(.sr-only)) > :is(.input, .textarea)::placeholder,
.field:has(> label:not(.sr-only)) > .input-group > .input::placeholder {
  opacity: 0; transition: opacity .13s ease;
}
.field:has(> label:not(.sr-only)) > :is(.input, .textarea):focus::placeholder,
.field:has(> label:not(.sr-only)) > .input-group > .input:focus::placeholder {
  opacity: 1;
}

/* Lifted: focused, or holding something. A select or a date never reports its
   placeholder as shown, so both settle here permanently -- correctly. */
.field:has(> label:not(.sr-only)):has(:is(.input, .select, .textarea):is(:focus, :not(:placeholder-shown))) > label {
  transform: translateY(calc(3px - var(--fl-rest))) scale(.75);
  color: var(--text-tertiary);
}
.field:has(> label:not(.sr-only)):has(:is(.input, .select, .textarea):focus) > label {
  color: var(--accent-blue);
}

/* A prefix sits on the text line, and stays out of sight until the label has
   left that line -- otherwise the two are printed on top of each other. */
.field:has(> label:not(.sr-only)) .input-group .prefix {
  top: 20px; transform: none;
  opacity: 0; transition: opacity .13s ease;
}
.field:has(> label:not(.sr-only)):has(:is(.input, .select):is(:focus, :not(:placeholder-shown)))
  .input-group .prefix {
  opacity: 1;
}

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 var(--s-16); }

/* Switch */
.switch { position: relative; display: inline-block; width: 48px; height: 28px; flex: none; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch .track {
  position: absolute; inset: 0; border-radius: var(--r-pill);
  background-color: var(--gray-medium);
  transition: background-color .15s ease;
}
.switch .track::after {
  content: ''; position: absolute; top: 3px; left: 3px;
  width: 22px; height: 22px; border-radius: var(--r-pill);
  background-color: var(--surface);
  box-shadow: var(--shadow-raised);
  transition: transform .15s ease;
}
.switch input:checked + .track { background-color: var(--primary); }
.switch input:checked + .track::after { transform: translateX(20px); }
.switch input:focus-visible + .track { outline: 2px solid var(--accent-sky); outline-offset: 2px; }

/* Selectable card */
.pick {
  position: relative; display: block; cursor: pointer;
  background-color: var(--surface-light);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-24);
  padding: var(--s-24);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.pick:hover { box-shadow: var(--shadow-raised); }
.pick input { position: absolute; opacity: 0; }
.pick.on { border-color: var(--primary); box-shadow: var(--shadow-raised); }
.pick .tick {
  position: absolute; top: var(--s-16); right: var(--s-16);
  width: 24px; height: 24px; border-radius: var(--r-pill);
  border: 1px solid var(--border-subtle);
  background-color: var(--surface);
  display: flex; align-items: center; justify-content: center;
  transition: background-color .15s ease, border-color .15s ease;
}
.pick .tick svg { width: 14px; height: 14px; color: var(--white); opacity: 0; transition: opacity .15s ease; }
.pick.on .tick { background-color: var(--primary); border-color: var(--primary); }
.pick.on .tick svg { opacity: 1; }

/* One-line variant. The card version stacks an icon, a name and a sentence of
   explanation; where the name alone says it, the same card lies flat and the
   tick moves off the corner to the end of the row. */
.pick-row {
  display: flex; align-items: center; gap: var(--s-12);
  padding: var(--s-12) var(--s-16);
  border-radius: var(--r-16);
}
.pick-row > .mark-tile { flex: none; }
.pick-row > .t-h3 { flex: 1; min-width: 0; }
.pick-row > .tick {
  position: static; flex: none;
  width: 20px; height: 20px;
}
.pick-row > .tick svg { width: 12px; height: 12px; }

/* --------------------------------------------------------------------------
   Folded-away options — the composer's "More options"
   -------------------------------------------------------------------------- */
/* Whatever is behind the fold covers several unrelated subjects. Each one gets
   a name and a hairline above it, so the panel opens onto short labelled
   groups rather than one long run of fields. */
/* Every group carries the rule, the first one included: which group leads
   depends on the kind being posted, and a rule under the header reads as the
   fold opening rather than as a missing edge. */
/* The row that opens the fold. It reads as a heading, so it carries no button
   chrome -- but it is the control for everything behind it, and a control needs
   a target with height and a state a screen reader can hear. The markup carries
   aria-expanded; this gives it the room. */
.fold-toggle {
  min-height: 44px;
  background: none; border: 0; cursor: pointer;
  color: inherit; font: inherit;
  border-radius: var(--r-8);
  transition: color .15s ease;
}
.fold-toggle:hover { color: var(--text-primary); }

.fold { margin-top: var(--s-16); }
.fold-group {
  margin-top: var(--s-24); padding-top: var(--s-24);
  border-top: 1px solid var(--border-subtle);
}
.fold-group > .t-eyebrow { display: block; margin-bottom: var(--s-12); }

/* The chevron on a fold's header. Sized here rather than inline so that
   toggling the turn cannot take the size with it. */
.chev {
  width: 20px; height: 20px; flex: none;
  color: var(--text-tertiary);
  transition: transform .15s ease;
}
.chev.on { transform: rotate(180deg); }

/* --------------------------------------------------------------------------
   Segmented control — two or three exclusive modes
   -------------------------------------------------------------------------- */

.seg {
  display: inline-flex; gap: var(--s-4);
  padding: var(--s-4);
  background-color: var(--surface-light);
  border: 1px solid var(--border-control);
  border-radius: var(--r-10);
}
.seg button {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-8);
  height: 32px; padding: 0 var(--s-12);
  background: none; border: 0; cursor: pointer;
  border-radius: var(--r-8);
  color: var(--text-tertiary);
  font-family: var(--font-body);
  font-size: 14px; font-weight: 600; line-height: 20px;
  white-space: nowrap;
  transition: background-color .15s ease, color .15s ease;
}
.seg button svg { width: 16px; height: 16px; }
.seg button:hover { color: var(--text-primary); }
/* The selected segment fills with white, so its label has to be the dark of the
   page -- it was --text-primary, which on this theme is also #FFFFFF. White on
   white: the chosen tab in the composer ("Upload artwork", "Background") was
   rendering at 1:1 and could not be read at all. The fill is what marks the
   selection; the label has to survive it. */
.seg button.on { background-color: var(--white); color: var(--surface); box-shadow: var(--shadow-raised); }
.seg button.on:hover { color: var(--surface); }
.seg-sm button { height: 28px; font-size: 14px; }

/* --------------------------------------------------------------------------
   Flier designer — make a poster without leaving the composer
   -------------------------------------------------------------------------- */

.designer { display: grid; grid-template-columns: 260px minmax(0, 1fr); gap: var(--s-24); align-items: start; }

.designer-stage {
  position: sticky; top: calc(var(--rail-h) + var(--s-16));
  padding: var(--s-12);
  background-color: var(--surface-light);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-24);
}
/* No aspect-ratio here: the canvas buffer sets its own shape, so switching
   between portrait, square, story and wide needs no extra CSS. */
.designer-canvas {
  display: block; width: 100%; height: auto;
  border-radius: var(--r-14);
  box-shadow: var(--shadow-raised);
}
.designer-actions {
  display: flex; flex-wrap: wrap; gap: var(--s-8);
  margin-top: var(--s-12);
}
.designer-actions .btn { flex: 1; min-width: 96px; }

.designer-tabs { display: flex; width: 100%; margin-bottom: var(--s-24); }
.designer-tabs button { flex: 1; }
.designer-panel { animation: fade .15s ease both; }

/* colour pickers */
.colour-row { display: flex; align-items: flex-end; gap: var(--s-12); }
.colour-well {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-4);
  cursor: pointer;
}
.colour-well input[type="color"] {
  width: 48px; height: 40px; padding: 0; cursor: pointer;
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-8);
  background: none;
}
.colour-well input[type="color"]::-webkit-color-swatch-wrapper { padding: 3px; }
.colour-well input[type="color"]::-webkit-color-swatch { border: 0; border-radius: var(--r-4); }

/* sliders */
.range {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 4px; padding: 0;
  border-radius: var(--r-pill);
  background: var(--gray-medium);
  cursor: pointer;
}
.range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 20px; height: 20px; border-radius: var(--r-pill);
  background: var(--primary);
  border: 2px solid var(--white);
  box-shadow: var(--shadow-raised);
}
.range::-moz-range-thumb {
  width: 20px; height: 20px; border-radius: var(--r-pill);
  background: var(--primary);
  border: 2px solid var(--white);
  box-shadow: var(--shadow-raised);
}
.range:focus-visible { outline: 2px solid var(--accent-sky); outline-offset: 4px; }

/* a colour dot inside a chip */
.chip .dot {
  width: 12px; height: 12px; flex: none;
  border-radius: var(--r-pill);
  outline: 1px solid var(--border-subtle); outline-offset: -1px;
}

/* aspect-ratio picker */
.ratio-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: var(--s-12); }
.ratio-pick {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-4);
  padding: var(--s-16) var(--s-12);
  background-color: var(--surface-light);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-14);
  cursor: pointer;
  transition: border-color .15s ease, background-color .15s ease;
}
.ratio-pick:hover { background-color: rgba(0, 0, 0, 0.05); }
.ratio-pick.on { border-color: var(--primary); background-color: rgba(255, 123, 114, 0.1); }
.ratio-box {
  width: 40px; margin-bottom: var(--s-4);
  background-color: var(--text-disabled);
  border-radius: var(--r-4);
}
.ratio-pick.on .ratio-box { background-color: var(--primary); }

.designer-controls { min-width: 0; }

.swatch-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(56px, 1fr)); gap: var(--s-8); }
.swatch {
  aspect-ratio: 4 / 5; width: 100%;
  border-radius: var(--r-8);
  border: 2px solid transparent;
  outline: 1px solid var(--border-subtle); outline-offset: -1px;
  cursor: pointer; padding: 0;
  transition: border-color .15s ease, transform .15s ease;
}
.swatch:hover { transform: translateY(-2px); }
.swatch.on { border-color: var(--primary); outline-color: var(--primary); }

.font-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: var(--s-8); }
.font-pick {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-4); padding: var(--s-8);
  min-height: 72px;
  background-color: var(--surface-light);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-8);
  cursor: pointer;
  transition: border-color .15s ease, background-color .15s ease;
}
.font-pick:hover { background-color: rgba(0, 0, 0, 0.05); }
.font-pick.on { border-color: var(--primary); background-color: rgba(255, 123, 114, 0.1); }
.font-pick .sample { font-size: 26px; line-height: 1.1; color: var(--text-primary); }

@media (max-width: 900px) {
  .designer { grid-template-columns: minmax(0, 1fr); }
  .designer-stage { position: static; max-width: 300px; margin: 0 auto var(--s-16); }
}

/* File drop */
.dropzone {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-12); min-height: 192px; padding: var(--s-32);
  text-align: center; cursor: pointer;
  background-color: var(--surface);
  border: 1px dashed var(--border-subtle);
  border-radius: var(--r-24);
  transition: border-color .15s ease, background-color .15s ease;
}
.dropzone:hover, .dropzone.dragging { border-color: var(--accent-blue); background-color: rgba(165, 214, 255, 0.1); }
.dropzone input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.dropzone svg { width: 24px; height: 24px; color: var(--text-tertiary); }

/* --------------------------------------------------------------------------
   Artwork picker — the Post Feed shown as tiles inside a sheet.
   -------------------------------------------------------------------------- */

.pick-grid {
  display: grid; gap: var(--s-12);
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
}
.pick-tile {
  display: flex; flex-direction: column; gap: var(--s-8);
  padding: var(--s-8);
  background: none; border: 1px solid var(--border-subtle); border-radius: var(--r-14);
  cursor: pointer; text-align: left;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.pick-tile:hover:not(:disabled) { border-color: var(--primary); box-shadow: var(--shadow-raised); }
.pick-tile:disabled { cursor: not-allowed; opacity: 0.55; }

.pick-art {
  position: relative; display: block;
  aspect-ratio: 4 / 5; overflow: hidden;
  border-radius: var(--r-10);
  background-color: var(--surface-light);
}
.pick-art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pick-none {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--text-disabled);
}
.pick-kind {
  position: absolute; left: var(--s-4); top: var(--s-4);
  padding: 2px 6px; border-radius: var(--r-4);
  background-color: rgba(255, 255, 255, 0.92);
  border: 1px solid var(--border-subtle);
  font-size: 13px; font-weight: 600; line-height: 18px; color: var(--surface);
}
.pick-meta { min-width: 0; padding: 0 var(--s-4) var(--s-4); }

/* Password strength */
.meter { display: flex; gap: var(--s-4); margin-top: var(--s-8); }
.meter i { flex: 1; height: 4px; border-radius: var(--r-pill); background-color: var(--gray-medium); transition: background-color .15s ease; }
.meter[data-level="1"] i:nth-child(-n+1) { background-color: var(--danger); }
.meter[data-level="2"] i:nth-child(-n+2) { background-color: var(--accent-orange); }
.meter[data-level="3"] i:nth-child(-n+3) { background-color: var(--accent-sky); }
.meter[data-level="4"] i { background-color: var(--accent-blue); }

/* One-time code */
.otp { display: flex; gap: var(--s-8); }
.otp input {
  width: 48px; height: 56px; text-align: center;
  font-family: var(--font-mono);
  font-size: 16px; font-weight: 600; line-height: 24px;
  background-color: var(--surface);
  color: var(--text-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-8);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.otp input:focus {
  outline: none; border-color: var(--accent-blue);
  box-shadow: 0px 0px 0px 3px rgba(165, 214, 255, 0.1);
}
.otp input.filled { border-color: var(--text-primary); }

/* --------------------------------------------------------------------------
   Auth shell
   -------------------------------------------------------------------------- */

/* The shell is exactly one viewport tall and the column scrolls inside it, so
   the bar stays put and a form never runs off the bottom of the page.
   100dvh rather than 100vh: on a phone the address bar is part of the viewport
   until it isn't, and vh does not notice the difference. */
.auth { height: 100dvh; min-height: 100dvh; display: flex; flex-direction: column; background-color: var(--surface); }
.auth-bar {
  height: var(--rail-h); flex: none;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 var(--s-24);
}
/* Centred by `margin-block: auto` on the column rather than by `align-items`.
   Both centre a column that fits; only this one leaves the top reachable when
   the column is taller than the space, instead of cutting it off above the
   scroll. */
.auth-body {
  flex: 1; min-height: 0;
  display: flex; justify-content: center;
  overflow-y: auto; overscroll-behavior: contain;
  padding: var(--s-24) var(--gutter) var(--s-32);
}
.auth-card, .auth-col { margin-block: auto; }
.auth-card { width: 100%; max-width: 480px; }
.auth-card.wide { max-width: 800px; }
.auth-head { text-align: center; margin-bottom: var(--s-32); }
.auth-head h1 {
  font-family: var(--font-display);
  font-size: 56px; font-weight: 700; line-height: 56px;
  color: var(--text-primary);
}
.auth-head p {
  margin-top: var(--s-16);
  font-size: 16px; font-weight: 400; line-height: 24px;
  color: var(--text-tertiary);
}

.steps { display: flex; align-items: center; justify-content: center; gap: var(--s-8); margin-bottom: var(--s-32); }
.steps i { width: 32px; height: 4px; border-radius: var(--r-pill); background-color: var(--gray-medium); }
.steps i.on { background-color: var(--primary); }
.steps i.done { background-color: var(--text-primary); }

/* One-off welcome screen after verification */
.welcome {
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  padding: var(--s-24);
  background: radial-gradient(circle at 50% 0%, rgba(255, 123, 114, 0.1) 0%, var(--surface) 60%);
}
.welcome-card { width: 100%; max-width: 560px; text-align: center; }
.welcome-mark { margin-bottom: var(--s-24); }
.welcome-card .t-eyebrow { margin-bottom: var(--s-8); }
.welcome-lead { margin: var(--s-16) auto 0; max-width: 46ch; color: var(--text-tertiary); }
.welcome-next { margin-top: var(--s-40); }
.welcome-bar {
  display: block; width: 160px; height: 4px; margin: var(--s-12) auto 0;
  border-radius: var(--r-pill); background-color: var(--gray-medium); overflow: hidden;
}
.welcome-bar i { display: block; height: 100%; background-color: var(--primary); transition: width .06s linear; }
.welcome-skip { margin-top: var(--s-24); }

.divider { display: flex; align-items: center; gap: var(--s-16); margin: var(--s-24) 0; }
.divider::before, .divider::after { content: ''; flex: 1; height: 1px; background-color: var(--border-subtle); }
.divider span { font-size: 14px; font-weight: 400; line-height: 20px; color: var(--text-tertiary); }

/* --------------------------------------------------------------------------
   Signup details — the one screen kept on the newer, plainer treatment

   The rest of the flow uses the .auth-card / .auth-head / .panel arrangement
   above. This step alone reads as a single narrow column: a small step marker,
   a heading, and the fields in one card. It shares the .auth shell, so only the
   pieces that differ are described here.
   -------------------------------------------------------------------------- */

.auth-col { width: 100%; max-width: 480px; }

.auth-step {
  font-size: 13px; font-weight: 400; line-height: 18px;
  font-variant-numeric: tabular-nums;
  color: var(--text-disabled);
  margin-bottom: var(--s-8);
}

.auth-title {
  font-family: var(--font-display);
  font-size: 28px; font-weight: 700; line-height: 34px; letter-spacing: -.02em;
  color: var(--text-primary);
}
.auth-sub {
  margin-top: var(--s-4);
  font-size: 15px; font-weight: 400; line-height: 22px;
  color: var(--text-tertiary);
}

/* The card the fields sit in, on the board's card treatment. */
.auth-panel {
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-24);
  padding: 20px;
  margin-top: var(--s-12);
}

.auth-form { margin-top: var(--s-32); }
.auth-panel .auth-form { margin-top: 0; }
.auth-form .field { margin-bottom: 10px; }
.auth-form .meter { margin-top: var(--s-4); }
.auth-form .btn-lg { margin-top: var(--s-16); }

/* A single line of red rather than a boxed notice. */
.auth-error {
  margin-bottom: var(--s-24);
  padding-left: var(--s-12);
  border-left: 2px solid var(--danger);
  font-size: 14px; font-weight: 400; line-height: 20px;
  color: var(--danger-dark);
}

.auth-foot {
  margin-top: var(--s-16);
  text-align: center;
  font-size: 13px; font-weight: 400; line-height: 18px;
  color: var(--text-tertiary);
}
.auth-foot a { color: var(--text-primary); font-weight: 600; }
.auth-foot a:hover { color: var(--primary); }
.auth-foot + .auth-foot { margin-top: var(--s-8); }

/* One line under the button, saying where the rest of the profile is filled
   in. It answers the question the short form raises without putting the fields
   themselves back on the screen. */
.auth-note {
  margin-top: var(--s-12);
  text-align: center;
  font-size: 13px; font-weight: 400; line-height: 18px;
  color: var(--text-disabled);
}

@media (max-width: 479px) {
  .auth-title { font-size: 28px; line-height: 34px; }
  .auth-panel { padding: var(--s-16); }
}

/* --------------------------------------------------------------------------
   Messages, empty states, overlays
   -------------------------------------------------------------------------- */

.note {
  display: flex; align-items: flex-start; gap: var(--s-12);
  padding: var(--s-16);
  border-radius: var(--r-8);
  border: 1px solid var(--border-subtle);
  background-color: var(--surface-light);
  color: var(--text-primary);
  font-size: 14px; font-weight: 400; line-height: 20px;
}
.note svg { width: 16px; height: 16px; flex: none; margin-top: 2px; }
.note-bad   { border-color: var(--danger); background-color: rgba(250, 70, 22, 0.06); color: var(--danger-dark); }
.note-good  { border-color: var(--accent-blue); background-color: rgba(165, 214, 255, 0.1); color: var(--text-primary); }
.note-info  { border-color: var(--border-subtle); background-color: var(--surface-light); color: var(--text-tertiary); }
.note-brand { border-color: var(--primary); background-color: rgba(250, 70, 22, 0.1); color: var(--text-primary); }

.empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; padding: var(--s-64) var(--s-32);
  background-color: var(--surface);
  border: 1px dashed var(--border-subtle);
  border-radius: var(--r-24);
}
.empty .mark {
  display: flex; align-items: center; justify-content: center;
  width: 64px; height: 64px; margin-bottom: var(--s-24);
  border-radius: var(--r-14);
  background-color: var(--primary); color: var(--white);
}
.empty .mark svg { width: 24px; height: 24px; }
.empty h3 {
  font-family: var(--font-body);
  font-size: 20px; font-weight: 700; line-height: 28px; letter-spacing: -.01em;
  color: var(--text-primary);
}
.empty p {
  margin-top: var(--s-8); max-width: 48ch;
  font-size: 17px; font-weight: 400; line-height: 26px; color: var(--text-tertiary);
}
.empty .actions { margin-top: var(--s-24); display: flex; gap: var(--s-12); flex-wrap: wrap; justify-content: center; }

/* A list of people offered inside an empty state. The empty state centres its
   text; these rows are read as a list, so they align left inside it. */
.empty-people {
  width: 100%; max-width: 340px; margin-top: var(--s-24);
  display: grid; gap: var(--s-4); text-align: left;
}
.empty-people .rail-item { background-color: var(--surface-light); }
.empty-people .rail-item:hover { background-color: var(--gray-medium); }

.scrim {
  position: fixed; inset: 0; z-index: var(--z-modal);
  display: flex; align-items: center; justify-content: center; padding: var(--s-24);
  background-color: rgba(0, 0, 0, 0.5);
}
.sheet {
  position: relative; width: 100%; max-width: 560px; max-height: 88vh; overflow-y: auto;
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-24);
  box-shadow: var(--shadow-raised);
}
.sheet-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-16);
  padding: var(--s-24); border-bottom: 1px solid var(--border-subtle);
}
.sheet-body { padding: var(--s-24); }
.sheet-foot {
  display: flex; justify-content: flex-end; gap: var(--s-12);
  padding: var(--s-24); border-top: 1px solid var(--border-subtle);
}

/* A picker is a browsing surface, not a confirmation: it wants room for rows
   of tiles. The head and the foot hold still and only the grid scrolls, so the
   tabs and the upload button stay reachable however long the feed is. */
.sheet-lg {
  max-width: min(1120px, 94vw);
  max-height: 90vh;
  display: flex; flex-direction: column;
  overflow: hidden;
}
.sheet-lg .sheet-head,
.sheet-lg .sheet-foot { flex: none; }
.sheet-lg .sheet-body { flex: 1; min-height: 0; overflow-y: auto; }


.toast-stack {
  position: fixed; bottom: var(--s-24); left: 50%; transform: translateX(-50%);
  z-index: var(--z-modal);
  display: flex; flex-direction: column; gap: var(--s-8);
}
.toast {
  display: flex; align-items: center; gap: var(--s-8);
  padding: var(--s-12) var(--s-24);
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-10);
  box-shadow: var(--shadow-raised);
  font-size: 14px; font-weight: 400; line-height: 20px;
  color: var(--text-primary);
}
.toast.good { border-color: var(--accent-blue); }
.toast.bad  { border-color: var(--danger); }

/* --------------------------------------------------------------------------
   Comments & memory
   -------------------------------------------------------------------------- */

.comment { display: flex; gap: var(--s-16); padding: var(--s-16) 0; border-bottom: 1px solid var(--border-subtle); }
.comment:last-child { border-bottom: 0; }
.comment .who { display: flex; align-items: center; gap: var(--s-8); flex-wrap: wrap; }
.comment .who b { font-size: 14px; font-weight: 600; line-height: 21px; color: var(--text-primary); }
.comment .who time { font-size: 14px; font-weight: 400; line-height: 20px; color: var(--text-tertiary); }
.comment p {
  margin-top: var(--s-4);
  font-size: 16px; font-weight: 400; line-height: 24px; color: var(--text-primary);
  white-space: pre-wrap; word-break: break-word;
}

.stars { display: inline-flex; gap: 2px; }
.stars button, .stars span {
  background: none; border: 0; cursor: pointer; padding: 0;
  font-size: 16px; line-height: 24px; color: var(--text-disabled);
}
.stars .lit { color: var(--accent-orange); }

.memory-wall { display: grid; grid-template-columns: repeat(auto-fill, minmax(176px, 1fr)); gap: var(--s-16); }
.memory {
  position: relative; overflow: hidden; aspect-ratio: 1;
  background-color: var(--surface-light);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-24);
}
.memory img { width: 100%; height: 100%; object-fit: cover; }
.memory .cap {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: var(--s-32) var(--s-12) var(--s-12);
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.6));
  color: var(--white);
  font-size: 14px; font-weight: 400; line-height: 20px;
}
.memory.text-only { display: flex; align-items: center; padding: var(--s-24); aspect-ratio: auto; min-height: 144px; }
.memory.text-only p { font-size: 16px; font-weight: 400; line-height: 24px; color: var(--text-primary); }

/* --------------------------------------------------------------------------
   Motion — respects prefers-reduced-motion
   -------------------------------------------------------------------------- */

@keyframes rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes spin { to { transform: rotate(360deg); } }
.rise { animation: rise .2s ease both; }
.fade { animation: fade .2s ease both; }
.spin { animation: spin .8s linear infinite; }

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

/* --------------------------------------------------------------------------
   Responsive — breakpoints and spacing scale down per the system
   -------------------------------------------------------------------------- */


/* Tablet (LG) 1024–1279 — the rail has dropped away by here; the sidebar keeps
   the narrower frame it took above. */
@media (max-width: 1279px) {
  :root { --gutter: var(--s-16); --side-inset: var(--s-40); }
}

/* Tablet (MD) 640–1023 — the sidebar becomes a drawer */
@media (max-width: 1023px) {
  /* A drawer slides in from the edge, so there is no edge to stand away from:
     the inset goes back to ordinary padding. */
  :root { --gutter: var(--s-12); --side-inset: var(--s-16); }
  .rail-centre { display: none; }
  .rail-left, .rail-right { flex: 0 1 auto; }
  .rail { justify-content: space-between; }
  .dock { display: flex; }

  .side {
    transform: translateX(-100%);
    transition: transform .2s ease;
    box-shadow: var(--shadow-raised);
    width: 288px;
    z-index: 9995;
  }
  .side.open { transform: none; }
  .side-close { display: inline-flex; }
  /* A drawer has no reading column beside it to line up with, so the logo row
     goes back to an ordinary gap. */
  .side-top { margin-bottom: var(--s-8); }
  .side-scrim { display: block; z-index: 9990; }
  .shell { margin-left: 0; }
  .topbar { padding: 0 var(--s-12); }
  .topbar-search { width: 100%; max-width: 680px; }

  .page { padding: var(--s-32) var(--gutter) calc(var(--dock-h) + var(--s-40)); }
  .t-display, .t-h1, .auth-head h1 { font-size: 48px; line-height: 48px; }
  .grid-2 { grid-template-columns: 1fr; }
  .panel-pad, .tile, .pick:not(.pick-row) { padding: var(--s-24); }
  .cat-nav { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .cat-nav::-webkit-scrollbar { display: none; }
  .cat-link { flex: none; }
}

/* Mobile — touch targets grow to the 48px minimum */
@media (max-width: 639px) {
  :root { --gutter: var(--s-8); }
  .t-display, .t-h1, .auth-head h1 { font-size: 36px; line-height: 40px; }
  .page { padding: var(--s-24) var(--gutter) calc(var(--dock-h) + var(--s-32)); }
  .panel-pad, .tile, .pick:not(.pick-row) { padding: var(--s-16); }
  .empty { padding: var(--s-40) var(--s-24); }
  .btn-primary, .btn-secondary, .btn-solid, .btn-ghost, .btn-outline, .btn-danger,
  .btn-sm, .btn-lg, .cat-link { height: 48px; }
  /* The overflow beside a primary button is the same control at the same
     moment, so it takes the same touch target rather than staying 32px. */
  .card-menu-btn { width: 48px; height: 48px; }
  .input, .select, .textarea { min-height: 48px; }
  .otp { gap: var(--s-4); }
  .otp input { width: 48px; height: 56px; }
  .postline-item { padding-left: var(--s-32); }
  .memory-wall { grid-template-columns: repeat(auto-fill, minmax(144px, 1fr)); }
}


/* --------------------------------------------------------------------------
   Calendar — a grid and a day.

   The restraint is the design: the grid is hairlines and numerals, and the only
   colour on it is the two marks. A cell is a link and nothing more, so the page
   has one interaction to learn. Today is stated with a ring, the open day with
   a fill; nothing else is emphasised, which is what makes those two readable.
   -------------------------------------------------------------------------- */

.cal { margin-bottom: var(--s-40); }

.cal-bar {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: var(--s-16);
  margin-bottom: var(--s-16);
}
.cal-month { display: flex; align-items: center; gap: var(--s-8); min-width: 0; }
.cal-month .t-h2 { min-width: 8ch; }
.cal-bar-end { display: flex; align-items: center; gap: var(--s-8); flex: none; }

/* The steppers are quiet until reached for: this is a page you read more often
   than you drive. */
.cal-step {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex: none;
  border-radius: var(--r-8);
  color: var(--text-tertiary);
  transition: background-color .15s ease, color .15s ease;
}
.cal-step:hover { background-color: var(--surface-light); color: var(--text-primary); }
.cal-step svg { width: 18px; height: 18px; }

/* ------------------------------------------------------------------- the key */
.cal-key {
  display: flex; flex-wrap: wrap; gap: var(--s-16);
  margin-bottom: var(--s-12);
  font-size: 13px; line-height: 18px; color: var(--text-tertiary);
}
.cal-key-item { display: inline-flex; align-items: center; gap: var(--s-8); }

/* The two marks. Everything the calendar has to say about what a thing IS, it
   says with these -- so they are the only saturated colour on the grid. */
.cal-dot { display: inline-block; width: 6px; height: 6px; flex: none; border-radius: var(--r-pill); }
.cal-dot.out  { background-color: var(--primary); }
.cal-dot.mine { background-color: var(--accent-purple); }
.cal-dot.to   { background-color: var(--accent-blue); }

/* ------------------------------------------------------------------ the grid */
.cal-grid {
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-24);
  overflow: hidden;
}
.cal-head, .cal-week { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.cal-head {
  border-bottom: 1px solid var(--border-subtle);
  background-color: var(--surface-light);
}
.cal-head span {
  padding: var(--s-12) 0; text-align: center;
  font-size: 13px; font-weight: 600; line-height: 18px;
  color: var(--text-tertiary);
}
.cal-week + .cal-week { border-top: 1px solid var(--border-subtle); }

.cal-day {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: var(--s-4);
  min-height: 84px; padding: var(--s-8) var(--s-4);
  color: var(--text-primary);
  transition: background-color .15s ease;
}
.cal-day + .cal-day { border-left: 1px solid var(--border-subtle); }
.cal-day:hover { background-color: var(--surface-light); }

/* Days from the neighbouring months stay legible but recede -- removing them
   would leave holes in the grid, and dimming them says "not this month"
   without breaking the shape of the weeks. */
.cal-day.muted .cal-n { color: var(--text-disabled); }

.cal-n {
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex: none;
  border-radius: var(--r-pill);
  font-size: 14px; line-height: 20px;
}

/* Today is a ring, the open day is a fill. One says where you are in time, the
   other where you are on the page, and they have to be able to coexist. */
.cal-day.today .cal-n { box-shadow: inset 0 0 0 1.5px var(--text-tertiary); font-weight: 600; }

/* A day with something on it, said in the brand colour so the month can be
   read at a glance rather than by counting dots. This is the number's own
   colour, which leaves the ring (today) and the fill (the open day) free to
   keep meaning what they mean -- all three can land on one date at once. */
.cal-day.has-events .cal-n { color: var(--primary); font-weight: 700; }
.cal-day.has-events:hover .cal-n { background-color: rgba(250, 70, 22, .12); }

.cal-day.on { background-color: var(--surface-light); }
/* The open day is a solid fill, so it overrides the brand-coloured text above
   -- on that one date the fill is the thing that has to read. */
.cal-day.on .cal-n { background-color: var(--primary); color: var(--white); font-weight: 700; box-shadow: none; }

/* The whole cell is a link when it has something on it, so it says so. */
.cal-day.has-events { cursor: pointer; }

.cal-marks { display: flex; align-items: center; justify-content: center; gap: 3px; flex-wrap: wrap; max-width: 100%; }

/* ------------------------------------------------------- the day, on hover */
/* Reading a day without opening it. Positioned from JS against the day under
   the pointer, so everything here is look rather than place. */
.cal-card {
  position: fixed; z-index: var(--z-modal);
  max-height: 260px; overflow-y: auto;
  padding: var(--s-8);
  background-color: var(--surface);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-14);
  box-shadow: var(--shadow-raised);
}
.cal-card-day {
  padding: var(--s-8) var(--s-8) var(--s-4);
  color: var(--text-tertiary);
}
.cal-card-row {
  display: flex; align-items: flex-start; gap: var(--s-8);
  padding: var(--s-8);
  border-radius: var(--r-10);
  color: var(--text-primary);
  transition: background-color .12s ease;
}
.cal-card-row:hover { background-color: var(--surface-light); }
/* The dot sits on the first line of the title rather than the top of the row,
   so it still reads as a bullet when the title wraps. */
.cal-card-row .cal-dot { margin-top: 7px; }

/* The card is a pointer affordance. Without a pointer the day itself is the
   way in, and it already opens the agenda underneath the grid. */
@media (hover: none) {
  .cal-card { display: none !important; }
}

/* ------------------------------------------------------------------- the day */
.cal-day-panel { margin-bottom: var(--s-40); }
.cal-rows { overflow: hidden; }

.cal-row {
  display: flex; align-items: center; gap: var(--s-16);
  padding: var(--s-16);
  border-bottom: 1px solid var(--border-subtle);
}
.cal-row:last-child { border-bottom: 0; }

/* The same two colours as the grid, as an edge rather than a dot: at row size a
   dot is too small to carry the meaning on its own. */
.cal-row-mark { width: 3px; align-self: stretch; flex: none; border-radius: var(--r-pill); }
.cal-row-mark.out  { background-color: var(--primary); }
.cal-row-mark.mine { background-color: var(--accent-purple); }
.cal-row-mark.to   { background-color: var(--accent-blue); }

.cal-row-text { flex: 1; min-width: 0; }
.cal-nudge { color: var(--accent-blue); }
.cal-row-acts { display: flex; align-items: center; gap: var(--s-8); flex: none; }
.cal-cancel:hover:not(:disabled) { color: var(--danger); }

/* ------------------------------------------------------- the composer's end */
/* The composer ends in one control rather than a card of options, so the bar
   holding it gets the room a section used to and nothing else. */
.create-bar {
  display: flex; align-items: center; justify-content: flex-end;
  gap: var(--s-12); flex-wrap: wrap;
  margin-top: var(--s-32);
}
.create-bar .btn { flex: none; }

@media (max-width: 640px) {
  .create-bar { flex-direction: column-reverse; align-items: stretch; }
  .create-bar .btn { width: 100%; justify-content: center; }
}

/* ---------------------------------------------------------------- the nudge */
/* The panel that works ahead of the grid. It reuses .cal-row wholesale, so the
   only thing described here is the distance marker on the end of each row. */
.cal-ahead { margin-bottom: var(--s-32); }

.cal-lead {
  flex: none;
  padding: 3px var(--s-8);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-pill);
  font-size: 13px; font-weight: 600; line-height: 18px;
  color: var(--text-tertiary);
  white-space: nowrap;
}

/* Today and tomorrow are the only two that need to interrupt you. */
.cal-lead.soon {
  background-color: var(--primary);
  border-color: var(--primary);
  color: var(--white);
}

.cal-blank { display: flex; align-items: center; justify-content: space-between; gap: var(--s-16); flex-wrap: wrap; }
.cal-note { margin-top: var(--s-12); color: var(--text-tertiary); }

/* On a phone the cells lose their padding before they lose their squareness,
   and the day's rows stack so the buttons keep a full-width target. */
@media (max-width: 639px) {
  .cal-day { min-height: 60px; padding: var(--s-4) 2px; }
  .cal-n { width: 26px; height: 26px; font-size: 13px; }
  .cal-row { flex-wrap: wrap; }
  .cal-row-text { flex: 1 1 calc(100% - 3px - var(--s-16)); }
  .cal-row-acts { margin-left: calc(3px + var(--s-16)); }
}


/* --------------------------------------------------------------------------
   Interest tiles — a video grid, not a feed.

   The Interest page is scanned rather than read, so the still does the finding
   and everything else is set small underneath it. The grid is auto-fill rather
   than a fixed column count: the tiles keep their size and the row simply holds
   as many as the width allows, which is why it needs no breakpoints of its own
   between the phone and the widest screen.
   -------------------------------------------------------------------------- */

.etile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--s-24) var(--s-16);
}

.etile { min-width: 0; }

/* 16:9, because that is the shape a still is recognised in. */
.etile-art {
  position: relative; display: block;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-14);
  overflow: hidden;
  background-color: var(--surface-light);
}
.etile-art img { width: 100%; height: 100%; object-fit: cover; }

.etile-blank {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  padding: var(--s-16);
}
.etile-stand-in {
  font-size: 16px; font-weight: 700; line-height: 1.25; letter-spacing: -.02em;
  color: var(--text-primary); text-align: center;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}

/* Where a video grid puts the running time. Same corner, same weight -- it is
   the one fact you want off the still without reading the card. */
.etile-stamp {
  position: absolute; right: var(--s-8); bottom: var(--s-8);
  padding: 2px var(--s-8);
  border-radius: var(--r-4);
  background-color: rgba(0, 0, 0, 0.8);
  font-size: 13px; font-weight: 600; line-height: 18px;
  color: var(--white);
}
.etile-stamp.past { background-color: rgba(0, 0, 0, 0.8); color: var(--text-tertiary); }

/* ---------------------------------------------------------------- the footer */
.etile-foot {
  display: flex; align-items: flex-start; gap: var(--s-12);
  margin-top: var(--s-12);
}

.etile-avatar {
  display: flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; flex: none;
  border-radius: var(--r-pill);
  overflow: hidden;
  background-color: var(--surface-light);
  font-size: 13px; font-weight: 600; color: var(--text-tertiary);
}
.etile-avatar img { width: 100%; height: 100%; object-fit: cover; }

.etile-text { flex: 1; min-width: 0; }

/* Two lines, then cut. A grid only reads as a grid while the tiles stay the
   same height, and the title is the only part long enough to break that. */
.etile-title {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  font-size: 15px; font-weight: 600; line-height: 21px;
  color: var(--text-primary);
}
.etile-title:hover { color: var(--primary); }

.etile-who {
  display: block; margin-top: var(--s-4);
  font-size: 13px; line-height: 18px; color: var(--text-tertiary);
}
.etile-who:hover { color: var(--text-primary); }

.etile-meta { font-size: 13px; line-height: 18px; color: var(--text-tertiary); }

/* Quiet until the tile is reached for, so a grid of hearts does not compete
   with the stills. It stays put on touch, where there is no hover to reveal it. */
.etile-save {
  flex: none;
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px;
  border-radius: var(--r-pill);
  color: var(--text-tertiary);
  opacity: 0;
  transition: opacity .15s ease, color .15s ease, background-color .15s ease;
}
.etile-save svg { width: 17px; height: 17px; }
.etile:hover .etile-save, .etile-save:focus-visible { opacity: 1; }
.etile-save:hover { background-color: var(--surface-light); color: var(--text-primary); }
.etile-save.on { opacity: 1; color: var(--primary); }

/* On a touch screen the control cannot wait for a hover that never comes, so it
   is always visible -- and always visible means always pressable, at the size a
   finger needs rather than the 30px a cursor could get away with. */
@media (hover: none) {
  .etile-save { opacity: 1; width: 44px; height: 44px; }
}

@media (max-width: 479px) {
  .etile-grid { grid-template-columns: 1fr; gap: var(--s-24); }
}


/* --------------------------------------------------------------------------
   Event page.

   A single column of lists. Almost nothing is described here because almost
   nothing is new: the rows are .rank-row in a .panel, the switch is .tabs, the
   chips are .chip, the toggles are .switch. What follows is the handful of
   arrangements those parts do not already cover.

   The page had a rail, an index, a lane switch made of cards and four panels
   of prose. All of it is gone. A workspace is read by people who know what
   they came for, and everything between them and the list was in the way.
   -------------------------------------------------------------------------- */

/* One column, at a measure that keeps a row of text readable. Left-aligned
   like every other page in the app: the sidebar is the edge this is read
   against, so spare width belongs on the far side. */
.ev-page { max-width: 860px; margin: 0 auto 0 0; }

/* The actions, on one line, all the same size. None of them is a card: three
   buttons say "three things you can do" without taking a third of the screen
   to say it. */
.ev-bar {
  display: flex; flex-wrap: wrap; gap: var(--s-8);
  margin-bottom: var(--s-24);
}

.ev-tabs { margin-bottom: var(--s-32); }

/* The rhythm of the page. A section is a heading and a list, and the space
   between them is the only thing separating one from the next -- no borders,
   no cards around cards. */
.ev-sec { margin-bottom: var(--s-40); }
.ev-sec:last-child { margin-bottom: 0; }
.ev-sec > .t-h2 { margin-bottom: var(--s-12); }
.ev-sec > .panel { overflow: hidden; }

/* A link riding on a heading, for the section that has more behind it. */
.ev-more {
  margin-left: var(--s-8);
  font-size: 14px; font-weight: 400; line-height: 20px; color: var(--text-tertiary);
}
.ev-more:hover { color: var(--primary); }

/* Saved, said quietly and briefly, beside the heading it belongs to. */
.ev-saved {
  margin-left: var(--s-8);
  font-size: 13px; font-weight: 400; line-height: 18px; color: var(--text-tertiary);
}

/* An empty section is one sentence with the way out of it underlined, not a
   dashed box with an icon and two buttons. */
.ev-empty {
  padding: var(--s-16) 0;
  font-size: 15px; line-height: 22px; color: var(--text-tertiary);
}
.ev-empty a { color: var(--primary); font-weight: 600; }
.ev-empty a:hover { text-decoration: underline; }

/* The line above the attending lists: what registering will use, and what is
   missing from it. A line, deliberately -- it is a nudge, not a section. */
.ev-note {
  margin-bottom: var(--s-24); padding-left: var(--s-12);
  border-left: 2px solid var(--primary);
  font-size: 14px; line-height: 21px; color: var(--text-tertiary);
}
.ev-note a { color: var(--text-primary); font-weight: 600; }
.ev-note a:hover { color: var(--primary); }

/* Where and what: four rows of chips with the question in the margin. The
   label is a fixed column so the chips of every row start on one line. */
.ev-facet { display: flex; gap: var(--s-16); padding: var(--s-12) 0; }
.ev-facet + .ev-facet { border-top: 1px solid var(--border-subtle); }
.ev-facet:first-child { padding-top: 0; }
.ev-facet:last-child { padding-bottom: 0; }
.ev-facet-k {
  flex: none; width: 64px; padding-top: 5px;
  font-size: 13px; font-weight: 600; line-height: 18px;
  letter-spacing: .06em; text-transform: uppercase; color: var(--text-tertiary);
}
/* Chips that wrap rather than scroll: .chip-row scrolls sideways, which is
   right in a filter bar and wrong in a panel you are reading. */
.ev-chips { display: flex; flex-wrap: wrap; gap: var(--s-8); min-width: 0; }
.ev-chips .chip { max-width: 100%; overflow: hidden; }
.ev-chips .t-num { color: var(--text-tertiary); }

/* A settings row: what it does on the left, the switch on the right. The whole
   row is the label, so the text is as pressable as the switch. */
.ev-set {
  display: flex; align-items: center; gap: var(--s-16);
  padding: var(--s-16); cursor: pointer;
  border-bottom: 1px solid var(--border-subtle);
}
.ev-set:last-child { border-bottom: 0; }
.ev-set:hover { background-color: rgba(255, 255, 255, 0.06); }
.ev-set-text { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.ev-set-text .t-h3 { display: block; }

/* Anything already over: still listed, no longer competing for the eye. */
.ev-quiet .t-h3 { color: var(--text-tertiary); font-weight: 400; }
.ev-quiet .datestamp { opacity: var(--o-608); }

/* The reference panel at the foot of the attending lane. Set as prose, because
   that is what it is. */
.ev-how p {
  font-size: 15px; line-height: 23px; color: var(--text-tertiary);
  max-width: 68ch;
}
.ev-how p + p { margin-top: var(--s-12); }
.ev-how b { color: var(--text-primary); font-weight: 600; }
.ev-how a { color: var(--primary); font-weight: 600; }
.ev-how a:hover { text-decoration: underline; }

@media (max-width: 639px) {
  /* The label stops being a margin and becomes a line of its own. */
  .ev-facet { flex-direction: column; gap: var(--s-8); }
  .ev-facet-k { width: auto; padding-top: 0; }
}


/* --------------------------------------------------------------------------
   Analytics — a board of readings.

   Panels rather than a column: these are the same window looked at from
   different angles, not steps in an argument, so they sit side by side and are
   read in any order. Every panel is the same object — a head with a title and
   at most one control, a body, sometimes a footer of facts.

   -------------------------------------------------------------------------- */

/* ---- the series palette ----
   These four steps carry Interested / Discussions / Memories / Followers on the
   engagement chart, and the same four slices on every ring and swatch.

   They used to be one brand orange followed by three steps of a neutral ramp --
   #FA4616, then the same orange at 52%, then white at 30%, then white at 13%.
   That is a *sequential* ramp, and it was doing a *categorical* job. A ramp
   encodes "more and less of one thing"; these four are four different things,
   none of them more than another, and drawing them light-to-dark said the
   opposite. Worse, the bottom two steps -- white at 30% and 13% on a black page
   -- are very nearly the same mark, so the two series a reader could least
   afford to confuse were the two hardest to tell apart.

   Four hues instead, one per series, assigned in fixed order and never cycled.
   The order is not arbitrary: it was checked with a validator rather than by
   eye, and it passes all six tests against this page's own #0B0B0B surface --
   the OKLCH lightness band, a chroma floor, colour-blind separation on adjacent
   pairs (worst ΔE 9.5 deutan), the normal-vision floor (worst ΔE 18.6), and 3:1
   contrast against the surface. The brand orange keeps slot one, so the series
   people read first is still the site's own colour.

   Colour is never the only encoding: the legend is always present and every
   series is direct-labelled, so a reader who cannot separate two hues still has
   the words. */
.tone-0 { --tone: #FA4616; }  /* brand orange */
.tone-1 { --tone: #3FA36B; }  /* green   */
.tone-2 { --tone: #4A9AE0; }  /* blue    */
.tone-3 { --tone: #D45BA8; }  /* magenta */

/* ------------------------------------------------------------------- head */
.an-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--s-16); flex-wrap: wrap;
  margin-bottom: var(--s-24);
}
.an-head p { margin-top: var(--s-4); }
.an-controls { display: flex; align-items: center; gap: var(--s-8); flex: none; }
/* min-width: 0 because the phone layout hands this flex: 1 and a flex item's
   default minimum is the width of its own content -- the button's label refused
   to shrink and pushed the controls row three pixels past the viewport, which
   is all it takes to put a horizontal scrollbar under the whole page. */
.an-menu { position: relative; min-width: 0; }
.an-menu > .btn { max-width: 100%; }
.an-menu > .btn > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.an-menu .btn.on { border-color: var(--border-medium); color: var(--text-primary); }
.an-caret { width: 13px !important; height: 13px !important; opacity: .6; }
.an-pop {
  position: absolute; top: calc(100% + var(--s-8)); right: 0;
  min-width: 210px; padding: var(--s-4);
  z-index: var(--z-dropdown);
}
.an-pop .pop-item.on { color: var(--primary); font-weight: 600; }

/* A filter that is on has to be visible from the button, or the page looks
   simply wrong rather than narrowed. */
.an-dot {
  width: 6px; height: 6px; margin-left: var(--s-4);
  border-radius: var(--r-pill); background-color: var(--primary);
}
.an-filter-note {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-16); flex-wrap: wrap;
  padding: var(--s-12) var(--s-16);
  margin-bottom: var(--s-16);
  border: 1px solid var(--primary); border-radius: var(--r-14);
  background-color: rgba(250, 70, 22, 0.06);
}

/* ------------------------------------------------------------ the figures */
.an-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(178px, 1fr));
  gap: var(--s-12);
  margin-bottom: var(--s-16);
}
.an-kpi {
  display: flex; flex-direction: column;
  padding: var(--s-16);
  border: 1px solid var(--border-subtle); border-radius: var(--r-14);
  background-color: var(--surface-light);
  min-width: 0;
}
.an-kpi-k {
  font-size: 13px; font-weight: 600; line-height: 18px;
  color: var(--text-tertiary);
}
.an-kpi-n {
  font-family: var(--font-display);
  font-size: 30px; font-weight: 700; line-height: 36px; letter-spacing: -.02em;
  color: var(--text-primary);
  margin: var(--s-8) 0 var(--s-4);
}
.an-kpi-d { font-size: 13px; line-height: 18px; color: var(--text-disabled); }
.an-kpi-d.up   { color: var(--accent-blue); }
.an-kpi-d.down { color: var(--danger-dark); }

/* The sparkline is the tile's floor, not a chart in its own right: no axis, no
   labels, and it takes the tile's own colour so it never outshouts the number
   above it. */
.an-spark {
  width: 100%; height: 34px; margin-top: var(--s-12);
  color: var(--text-disabled);
  overflow: visible;
}
.an-kpi:hover .an-spark { color: var(--primary); }

/* ---- the four questions ----
   The page was nine panels in a row, each correctly labelled and none of them
   saying why it was there: "Traffic Sources", "Audience Demographics",
   "Discovery Funnel". A reader opening the page for the first time had to work
   out both what each panel measured and which of them mattered.

   Nothing was removed. What was added is this: four headings, phrased as the
   questions somebody actually arrives with, with the panels that answer each
   one grouped underneath. The page reads top to bottom as an argument now --
   how many saw it, what they did about it, which posts did the work, who they
   were -- rather than as a wall to be scanned. */
.an-section { margin-bottom: var(--s-40); }
.an-section-head { margin-bottom: var(--s-16); }
.an-section-head .t-h2 { font-size: 24px; line-height: 30px; }
.an-section-head p { margin-top: var(--s-4); max-width: 70ch; }

/* -------------------------------------------------------------- the board */
.an-row { display: grid; gap: var(--s-16); margin-bottom: var(--s-16); }
.an-row:last-child { margin-bottom: 0; }
.an-row-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.an-row-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.an-row-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

.an-card {
  display: flex; flex-direction: column; min-width: 0;
  border: 1px solid var(--border-subtle); border-radius: var(--r-14);
  background-color: var(--surface-light);
  overflow: hidden;
}
.an-card-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-12);
  padding: var(--s-16) var(--s-16) 0;
}
.an-card-body { padding: var(--s-16); flex: 1; min-width: 0; }
.an-blank {
  display: flex; align-items: center;
  min-height: 140px;
  color: var(--text-tertiary);
}

/* Tabs inside a panel head. Smaller and quieter than the page's own tabs --
   they change a resolution, not a subject. */
.an-tabs { display: flex; gap: var(--s-4); flex: none; flex-wrap: wrap; }
.an-tabs button {
  padding: var(--s-4) var(--s-12);
  border: 0; border-radius: var(--r-pill);
  background: transparent; cursor: pointer;
  font-size: 13px; font-weight: 600; line-height: 20px;
  color: var(--text-tertiary);
  white-space: nowrap;
}
.an-tabs button:hover { background-color: rgba(255, 255, 255, 0.06); color: var(--text-primary); }
.an-tabs button.on { background-color: var(--gray-medium); color: var(--text-primary); }
.an-tabs .count { margin-left: var(--s-4); color: var(--text-disabled); }
.an-tabs button.on .count { color: var(--primary); }

/* ------------------------------------------------------------- line charts */
.an-plot { position: relative; margin-top: var(--s-16); }

/* Gridlines carry their own label, so the y axis costs no extra column. */
.an-grid { position: absolute; inset: 0; pointer-events: none; }
.an-grid span {
  position: absolute; left: 0; right: 0;
  border-top: 1px solid var(--border-subtle);
}
.an-grid b {
  position: absolute; left: 0; top: -9px;
  padding-right: var(--s-8);
  background-color: var(--surface-light);
  color: var(--text-disabled); font-weight: 400;
}

.an-svg { display: block; width: 100%; height: 210px; }
.an-line-stroke {
  fill: none; stroke: var(--primary); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.an-area { fill: rgba(250, 70, 22, 0.1); stroke: none; }
.an-band { fill: var(--tone); stroke: none; }

/* The x axis is absolutely placed by percentage, so the same markup labels a
   week and a year without the spacing being recomputed anywhere. */
.an-axis {
  position: relative; height: 18px;
  margin-top: var(--s-8);
  padding-top: var(--s-8);
  border-top: 1px solid var(--border-subtle);
}
.an-axis span {
  position: absolute; top: var(--s-8);
  transform: translateX(-50%);
  color: var(--text-disabled); white-space: nowrap;
}
.an-axis span:first-child { transform: none; }
.an-axis span:last-child  { transform: translateX(-100%); }

/* -------------------------------------------------------------- bar charts */
.an-bars {
  display: flex; align-items: flex-end; gap: 2px;
  height: 210px;
}
.an-bar-col {
  flex: 1; min-width: 0;
  display: flex; align-items: flex-end; justify-content: center;
  height: 100%; border-radius: var(--r-4);
}
.an-bar-col:hover { background-color: rgba(255, 255, 255, 0.06); }
.an-bar-col i {
  display: block; width: 100%; min-height: 2px;
  border-radius: var(--r-4);
  background-color: var(--text-disabled);
}
.an-bar-col:hover i { background-color: var(--primary); }

/* The registrations panel: chart on the left, the three windows beside it. */
.an-split { display: grid; grid-template-columns: minmax(0, 1fr) 168px; gap: var(--s-24); }
.an-rail { display: grid; gap: var(--s-12); align-content: start; }
.an-rail-row {
  padding-bottom: var(--s-12);
  border-bottom: 1px solid var(--border-subtle);
}
.an-rail-row:last-child { border-bottom: 0; padding-bottom: 0; }
.an-rail-line { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-8); }

/* ------------------------------------------------------- the fact footer */
.an-facts {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  border-top: 1px solid var(--border-subtle);
}
.an-facts > div {
  padding: var(--s-16);
  border-right: 1px solid var(--border-subtle);
  min-width: 0;
}
.an-facts > div:last-child { border-right: 0; }
.an-fact-k {
  display: block;
  font-size: 13px; font-weight: 600; line-height: 18px;
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-disabled);
}
.an-fact-v {
  display: block; margin-top: var(--s-8);
  font-family: var(--font-display);
  font-size: 19px; font-weight: 700; line-height: 24px; letter-spacing: -.01em;
  color: var(--text-primary);
}
.an-fact-h { display: block; font-size: 13px; line-height: 18px; color: var(--text-tertiary); }

/* --------------------------------------------------------- engagement mini */
.an-mini-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s-12); }
.an-mini {
  padding: var(--s-12);
  border: 1px solid var(--border-subtle); border-radius: var(--r-8);
  min-width: 0;
}
.an-mini-n {
  font-family: var(--font-display);
  font-size: 24px; font-weight: 700; line-height: 30px; letter-spacing: -.02em;
  color: var(--text-primary);
  margin-top: var(--s-4);
}
.an-mini .an-spark { height: 26px; margin-top: var(--s-8); }

.an-legend { display: flex; flex-wrap: wrap; gap: var(--s-16); margin-top: var(--s-24); }
.an-legend-item {
  display: flex; align-items: center; gap: var(--s-8); min-width: 0;
  font-size: 13px; line-height: 18px; color: var(--text-tertiary);
}
.an-legend-item b { margin-left: auto; color: var(--text-primary); font-weight: 600; }
.an-swatch {
  width: 9px; height: 9px; flex: none;
  border-radius: 2px; background-color: var(--tone);
}

/* --------------------------------------------------------------- the rings */
.an-ring { display: flex; align-items: center; gap: var(--s-16); flex-wrap: wrap; }
.an-ring svg { flex: none; transform: rotate(-90deg); }
.an-ring-track { fill: none; stroke: var(--gray-medium); }
.an-ring-seg { fill: none; stroke: var(--tone); }
.an-ring-key { display: grid; gap: var(--s-8); flex: 1; min-width: 118px; }

.an-ring-side {
  padding-left: var(--s-24);
  border-left: 1px solid var(--border-subtle);
  min-width: 0;
}
.an-demo { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr); gap: var(--s-24); }

/* ------------------------------------------------------------ meter rows */
.an-meter {
  display: flex; align-items: center; gap: var(--s-12);
  margin-top: var(--s-12);
}
.an-meter-k { flex: 0 0 84px; min-width: 0; font-size: 13px; line-height: 18px; color: var(--text-tertiary); }
.an-meter .bar { flex: 1; min-width: 40px; }
.an-meter-n { flex: none; font-size: 13px; line-height: 18px; color: var(--text-primary); }
.an-meter-n b { color: var(--text-disabled); font-weight: 400; }

/* ------------------------------------------------------------- the dials */
.an-device {
  display: flex; align-items: center; gap: var(--s-12);
  padding: var(--s-12) 0;
  border-bottom: 1px solid var(--border-subtle);
}
.an-device:last-of-type { border-bottom: 0; }
.an-device .an-meter-k { flex: 1; }
.an-device-n {
  font-family: var(--font-display);
  font-size: 17px; font-weight: 700; line-height: 22px;
  color: var(--text-primary);
}
.an-dial { width: 34px; height: 34px; flex: none; }
.an-dial svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.an-dial-track { fill: none; stroke: var(--gray-medium); stroke-width: 4; }
.an-dial-fill  { fill: none; stroke: var(--primary); stroke-width: 4; stroke-linecap: round; }

/* ------------------------------------------------------------- the funnel */
.an-funnel {
  position: relative;
  display: flex; align-items: center; gap: var(--s-8);
  padding: var(--s-12);
  margin-top: var(--s-4);
  border-radius: var(--r-8);
  overflow: hidden;
}
/* The bar is the row's own background rather than a separate track, so each
   step reads as a width of the whole rather than a line beside a label. */
.an-funnel-bar {
  position: absolute; left: 0; top: 0; bottom: 0;
  background-color: var(--gray-medium);
  border-radius: var(--r-8);
}
.an-funnel-k, .an-funnel-n, .an-funnel-p { position: relative; }
.an-funnel-k { flex: 1; min-width: 0; font-size: 13px; line-height: 18px; color: var(--text-primary); }
.an-funnel-n { flex: none; font-size: 13px; font-weight: 600; color: var(--text-primary); }
.an-funnel-p { flex: none; width: 5ch; text-align: right; font-size: 13px; color: var(--text-disabled); }

/* ------------------------------------------------------------- the heatmap */
.an-heat { display: grid; gap: 2px; min-width: 0; }
/* The indent clears the day column beside the cells, so it is that column's
   width plus the gap -- 30 + 4. The two were 22 and 26 and had to move
   together when the labels came up to the type floor. */
.an-heat-hours {
  display: flex; justify-content: space-between;
  padding-left: 34px; margin-bottom: var(--s-4);
  color: var(--text-disabled);
  font-size: 13px; line-height: 18px;
}
.an-heat-hours b { font-weight: 400; }
.an-heat-row { display: flex; align-items: center; gap: var(--s-4); min-width: 0; }
.an-heat-day { width: 30px; flex: none; color: var(--text-disabled); font-size: 13px; }
.an-heat-cells { display: flex; gap: 2px; flex: 1; min-width: 0; }
.an-heat-cell {
  flex: 1; min-width: 0; aspect-ratio: 1;
  border-radius: 2px;
  background-color: var(--gray-medium);
}
/* Four steps of the brand colour. Reading is the thing being measured, so it
   is the thing that gets the colour. */
.an-heat-cell.lv-1 { background-color: rgba(250, 70, 22, 0.28); }
.an-heat-cell.lv-2 { background-color: rgba(250, 70, 22, 0.5); }
.an-heat-cell.lv-3 { background-color: rgba(250, 70, 22, 0.75); }
.an-heat-cell.lv-4 { background-color: var(--primary); }
.an-heat-key {
  display: flex; align-items: center; gap: var(--s-4);
  margin-top: var(--s-12);
  color: var(--text-disabled);
}
.an-heat-key .an-heat-cell { width: 11px; height: 11px; flex: none; aspect-ratio: auto; }

/* --------------------------------------------------------------- the table */
.an-tbl-scroll { overflow-x: auto; margin-top: var(--s-12); }
.an-tbl { width: 100%; border-collapse: collapse; min-width: 520px; }
.an-tbl th, .an-tbl td {
  padding: var(--s-12) var(--s-16);
  text-align: left;
  border-bottom: 1px solid var(--border-subtle);
}
.an-tbl thead th {
  font-size: 13px; font-weight: 600; line-height: 18px;
  letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-disabled); white-space: nowrap;
}
.an-tbl tbody tr:last-child td { border-bottom: 0; }
.an-tbl tbody tr:hover { background-color: rgba(255, 255, 255, 0.06); }
.an-tbl .num { text-align: right; width: 1%; white-space: nowrap; }

.an-ev { display: flex; align-items: center; gap: var(--s-12); min-width: 0; color: inherit; }
.an-ev-art {
  width: 34px; height: 34px; flex: none;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-8); overflow: hidden;
  background-color: var(--gray-medium);
}
.an-ev-art img { width: 100%; height: 100%; object-fit: cover; }
.an-ev-blank {
  font-family: var(--font-display);
  font-size: 15px; font-weight: 700; color: var(--text-tertiary);
}
.an-ev-name {
  display: block;
  font-size: 15px; font-weight: 600; line-height: 21px;
  color: var(--text-primary);
}
.an-ev:hover .an-ev-name { color: var(--primary); }

.an-badge {
  padding: 2px var(--s-8);
  border: 1px solid var(--border-medium); border-radius: var(--r-pill);
  font-size: 13px; font-weight: 600; line-height: 18px;
  color: var(--text-tertiary); white-space: nowrap;
}
.an-badge.on { border-color: var(--primary); background-color: var(--primary); color: var(--white); }

.an-more {
  display: flex; align-items: center; gap: var(--s-8);
  padding: var(--s-12) var(--s-16);
  border-top: 1px solid var(--border-subtle);
  font-size: 13px; font-weight: 600; color: var(--text-tertiary);
}
.an-more:hover { color: var(--primary); }
.an-more svg { width: 14px; height: 14px; }

/* ------------------------------------------------------------- narrowing

   The board collapses in two steps rather than one. Four small panels go to
   two before anything else moves, because they are the panels that survive
   being narrow; the charts are the last thing to lose their width. */
@media (max-width: 1279px) {
  .an-row-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .an-row-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .an-split { grid-template-columns: 1fr; }
  .an-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .an-facts > div:nth-child(2n) { border-right: 0; }
  .an-facts > div:nth-child(-n+2) { border-bottom: 1px solid var(--border-subtle); }
}

@media (max-width: 1023px) {
  .an-row-2 { grid-template-columns: 1fr; }
  .an-demo { grid-template-columns: 1fr; }
  .an-ring-side { padding-left: 0; border-left: 0; padding-top: var(--s-16); border-top: 1px solid var(--border-subtle); }
}

@media (max-width: 639px) {
  .an-row-4 { grid-template-columns: 1fr; }
  .an-row-3 { grid-template-columns: 1fr; }
  .an-section-head .t-h2 { font-size: 20px; line-height: 26px; }
  .an-head { align-items: flex-start; }
  .an-controls { width: 100%; }
  .an-controls .an-menu, .an-controls .btn { flex: 1; }
  .an-mini-row { grid-template-columns: 1fr; }
  .an-facts { grid-template-columns: 1fr; }
  .an-facts > div { border-right: 0; border-bottom: 1px solid var(--border-subtle); }
  .an-facts > div:last-child { border-bottom: 0; }
  /* A day is under 4px wide on a phone, which reads as noise rather than a
     week. The heatmap keeps its full week and scrolls. */
  .an-heat-cells { min-width: 340px; }
  .an-heat-row { overflow-x: auto; }
}

/* The page is a report, so it has to survive being printed. The app's chrome
   goes, the panels lose their fills, and nothing is allowed to break across a
   page in the middle of a chart. */
@media print {
  .side, .topbar, .dock, .an-controls, .an-more { display: none !important; }
  .shell { margin-left: 0; }
  .page { padding: 0; }
  .an-card { break-inside: avoid; border-color: var(--border-medium); }
}


/* --------------------------------------------------------------------------
   Event page — the six action cards.

   Deliberately almost no CSS. The card IS the Post Feed's card: .studio-grid
   for three to a row, .studio-card for the size and shape, .studio-thumb for
   the 80x100 slot. Everything here is only what differs -- an icon where the
   artwork goes, and a button that has to sit in a grid pretending to be a link.
   -------------------------------------------------------------------------- */

/* Two of the six open on this page rather than going anywhere, so they are
   buttons. A button carries a UA stylesheet a link does not, and every line
   here is undoing one of those so the six cards are one object. */
button.ev-card {
  font: inherit; color: inherit; text-align: left;
  width: 100%; cursor: pointer; appearance: none;
}

/* The thumbnail slot holds a mark instead of a poster. */
.ev-card .studio-thumb { color: var(--text-tertiary); }
.ev-card .studio-thumb svg { width: 28px; height: 28px; }
.ev-card-brand .studio-thumb { background-color: var(--primary); color: var(--white); }

/* In the Post Feed this line is a date, so it never wraps. Here it is a
   sentence saying what the card does, and two lines is where it stops -- past
   that the cards in a row stop being the height they were. */
.ev-card .studio-when {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* The foot says what pressing the card does. It is a span, not a control: the
   whole card is already the control, and a button inside a button is neither. */
.ev-card .studio-card-foot .btn { pointer-events: none; }


/* The two profile pages -- the one people read and the one you edit it on --
   are the same size as each other. They are the same page from two sides, and
   a form that is narrower than the thing it fills in makes the editor feel
   like a lesser view of it.

   Both take the width they are given and then stop short of the window edge:
   content pressed against the edge of a screen reads as content that has been
   cut off.

   The room on the right is padding rather than a tighter ceiling, because a
   ceiling low enough to leave a visible margin on a wide screen is a ceiling
   that wastes the width on every screen below it. The padding is only added
   where there is width to spare; narrower than that and the page keeps the
   ordinary gutter, which is all it can afford. */
.page-profile { max-width: 2200px; margin: 0 auto 0 0; }

@media (min-width: 1440px) {
  /* Enough to see that the page ends before the window does, and no more. It
     was 64: comfortable, but a step of that back is better spent on the page
     than on proving there is a margin. 40 is on the spacing scale; 48 is not. */
  .page-profile { padding-right: var(--s-40); }
}

/* The reading column grows with the page instead of stopping at the measure a
   post feed wants. Without this the header panel stretches to the full width
   and everything under it stays 920px wide, which reads as a page that has
   come apart in the middle.

   Scoped above the breakpoint where .feed-layout drops its rail: this is more
   specific than that rule, so left unscoped it would win and a phone would
   keep a two-column profile. It tracks that breakpoint rather than naming its
   own -- when the rail moved from 1280 to 1440 this had to move with it, and a
   second hard-coded number is how the two came apart in the first place. */
@media (min-width: 1440px) {
  .page-profile .feed-layout {
    grid-template-columns: minmax(0, 1fr) var(--rail-w);
    /* This page's column is a record to scan rather than prose to read, and its
       header spans the whole layout -- so it takes the page's width instead of
       the reading measure, which is what stops the header and the body below it
       ending at two different places. */
    max-width: none;
  }
}

/* --------------------------------------------------------------------------
   About — the profile record, edited on /me/ and read on a public profile.

   Two sides, one shape. The editor needs rows that can be added and taken
   away; the reader needs the same information set as facts, a list and a few
   links. Everything else it uses -- .field, .input, .chip, .hairline,
   .mark-tile -- is already described above.
   -------------------------------------------------------------------------- */

/* A repeatable row in the editor: the field, and the way to remove it. The
   remove button is quiet until the row is reached for -- a column of crosses
   down the side of a form reads as a list of things to delete. */
.about-row {
  display: flex; align-items: center; gap: var(--s-8);
  margin-bottom: var(--s-8);
}
.about-row .input { flex: 1; min-width: 0; }
.about-row .icon-btn { flex: none; color: var(--text-disabled); }
.about-row:hover .icon-btn, .about-row .icon-btn:focus-visible { color: var(--text-primary); }

/* A link row is a name and an address, and the name is the shorter of the
   two by a long way. */
.about-row-pair { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) auto; }

/* The social block. Not eight named slots laid out at once -- almost nobody
   keeps eight accounts, and the empty ones read as work left undone. It is a
   short stack of "which account, and where", three deep to start, and it grows
   a row at a time. */
.about-socials { display: grid; gap: var(--s-8); }

/* The picker is the narrow half: a platform name is one word, an address is
   not. */
.about-row-social {
  display: grid;
  /* The picker takes a share of the line rather than a fixed slice, but never
     more than a platform name needs -- the address is the field being read. */
  grid-template-columns: clamp(112px, 32%, 168px) minmax(0, 1fr) auto;
}
.about-row-social .select { min-width: 0; }

/* ------------------------------------------------------------- the reading */

/* Category, founded and size: checked rather than read, so they are set as a
   row of facts and not as three headed sections. */
.about-facts {
  display: flex; flex-wrap: wrap; gap: var(--s-24);
  margin-top: var(--s-16);
}
.about-fact { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

/* What we do. A list, marked the way a list is, held to a measure that keeps
   a line of it readable. */
.about-services { display: grid; gap: var(--s-8); max-width: 62ch; }
.about-services li {
  position: relative; padding-left: var(--s-16);
  font-size: 16px; line-height: 24px; color: var(--text-primary);
}
.about-services li::before {
  content: ''; position: absolute; left: 0; top: 10px;
  width: 5px; height: 5px; border-radius: var(--r-pill);
  background-color: var(--primary);
}

/* Contact rows are actions, not text: each one is the link that uses it. */
.about-contact {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
  gap: var(--s-8);
}
.about-contact-row {
  display: flex; align-items: center; gap: var(--s-12);
  padding: var(--s-12);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-14);
  transition: border-color .15s ease, background-color .15s ease;
}
.about-contact-row:hover { border-color: var(--border-medium); background-color: var(--surface-light); }
.about-contact-row .mark-tile { width: 40px; height: 40px; }
.about-contact-row .mark-tile svg { width: 18px; height: 18px; }

@media (max-width: 639px) {
  /* At this width the name and the address each want the whole line, and the
     remove button goes with the address rather than orphaning itself. */
  .about-row-pair { grid-template-columns: minmax(0, 1fr) auto; }
  .about-row-pair .input:first-child { grid-column: 1 / -1; }

  /* Same at this width: the picker takes the line above its address. */
  .about-row-social { grid-template-columns: minmax(0, 1fr) auto; }
  .about-row-social .select { grid-column: 1 / -1; }
}

/* --------------------------------------------------------------------------
   Redesigned Profile Elements
   -------------------------------------------------------------------------- */

.profile-hero {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-24);
  border: 1px solid var(--border-subtle);
  background: var(--surface-light);
  margin-bottom: var(--s-24);
  box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.5);
  transition: border-color 0.2s ease;
}

.profile-banner {
  position: relative;
  height: 220px;
  background: radial-gradient(ellipse at 85% 15%, rgba(250, 70, 22, 0.2), transparent 50%),
              radial-gradient(ellipse at 15% 85%, rgba(108, 180, 238, 0.12), transparent 45%),
              linear-gradient(135deg, #0d0d11 0%, #15151b 100%);
  overflow: hidden;
}

.profile-banner::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(0,0,0,0) 40%, rgba(11,11,11,0.92) 100%);
  pointer-events: none;
}

.profile-banner-pattern {
  position: absolute;
  inset: 0;
  opacity: 0.15;
  background-image: radial-gradient(rgba(255, 255, 255, 0.15) 1px, transparent 1px);
  background-size: 18px 18px;
  pointer-events: none;
}

.profile-banner img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.profile-body {
  position: relative;
  padding: 0 var(--s-28) var(--s-28);
  margin-top: -52px;
}

.profile-avatar-wrap {
  position: relative;
  flex-shrink: 0;
}

.profile-avatar {
  width: 104px;
  height: 104px;
  border-radius: 26px;
  border: 4px solid var(--surface-light);
  background: var(--gray-medium);
  box-shadow: 0 12px 28px rgba(0,0,0,0.6), 0 0 0 1px var(--border-medium);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 34px;
  font-weight: 700;
  color: var(--white);
}

.profile-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.profile-name-group {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  position: relative;
  top: 4px;
}

.profile-display-name {
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  line-height: 1.2;
}

.profile-handle {
  font-size: 14px;
  color: var(--text-tertiary);
  font-weight: 500;
}

.profile-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-10);
}

.profile-stat-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 38px;
  padding: 0 16px;
  border-radius: var(--r-10);
  border: 1px solid var(--border-medium);
  background: rgba(255, 255, 255, 0.05);
  color: var(--text-primary);
  font-size: 13.5px;
  font-weight: 500;
  white-space: nowrap;
  flex-shrink: 0;
  cursor: pointer;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
  transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
}

.profile-stat-chip:hover {
  background: rgba(255, 255, 255, 0.1);
  border-color: var(--border-control-hover);
  color: var(--white);
  transform: translateY(-1px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}

.profile-stat-chip .t-num {
  font-weight: 700;
  color: var(--white);
  font-size: 14.5px;
}

.profile-stat-chip span:not(.t-num) {
  color: var(--text-tertiary);
}

.profile-stat-chip:hover span:not(.t-num) {
  color: var(--white);
}

.profile-bio-text {
  font-size: 15px;
  line-height: 1.65;
  color: rgba(255, 255, 255, 0.88);
  max-width: 72ch;
  margin-top: var(--s-16);
}

.profile-meta-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-8) var(--s-20);
  margin-top: var(--s-16);
  padding-top: var(--s-16);
  border-top: 1px solid var(--border-subtle);
  font-size: 13px;
  color: var(--text-tertiary);
}

.profile-meta-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.profile-meta-item svg {
  width: 15px;
  height: 15px;
  color: var(--text-disabled);
  flex-shrink: 0;
}

/* Redesigned Bento Grid for Facts */
.profile-bento-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--s-12);
  margin-top: var(--s-16);
}

.profile-bento-card {
  display: flex;
  align-items: center;
  gap: var(--s-12);
  padding: var(--s-12) var(--s-16);
  background: var(--surface-light);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-14);
  transition: all 0.15s ease;
}

.profile-bento-card:hover {
  border-color: var(--border-medium);
  background: rgba(255, 255, 255, 0.04);
}

.profile-bento-icon {
  width: 36px;
  height: 36px;
  border-radius: var(--r-8);
  background: rgba(250, 70, 22, 0.12);
  color: var(--primary);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.profile-bento-icon svg {
  width: 18px;
  height: 18px;
}

/* Service tags */
.profile-service-pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-8);
  margin-top: var(--s-12);
}

.profile-service-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-radius: var(--r-pill);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border-subtle);
  font-size: 14px;
  color: var(--text-primary);
  transition: all 0.15s ease;
}

.profile-service-pill:hover {
  border-color: var(--border-medium);
  background: rgba(255, 255, 255, 0.06);
}

.profile-service-pill-dot {
  width: 6px;
  height: 6px;
  border-radius: var(--r-pill);
  background-color: var(--primary);
  flex-shrink: 0;
}

/* Social link cards */
.profile-social-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-radius: var(--r-10);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border-subtle);
  color: var(--text-primary);
  font-size: 13.5px;
  font-weight: 500;
  text-decoration: none;
  transition: all 0.15s ease;
}

.profile-social-link:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: var(--border-control-hover);
  color: var(--white);
  transform: translateY(-1px);
}

.profile-social-link svg {
  width: 16px;
  height: 16px;
}

/* Tabs Redesign */
.profile-nav-tabs {
  display: flex;
  align-items: center;
  gap: 4px;
  background: var(--surface-light);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-14);
  padding: 4px;
  margin-bottom: var(--s-24);
  overflow-x: auto;
  scrollbar-width: none;
}

.profile-nav-tabs::-webkit-scrollbar {
  display: none;
}

.profile-nav-tab {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border-radius: var(--r-10);
  border: none;
  background: transparent;
  color: var(--text-tertiary);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
}

.profile-nav-tab:hover {
  color: var(--text-primary);
  background: rgba(255, 255, 255, 0.04);
}

.profile-nav-tab.on {
  color: var(--white);
  background: rgba(255, 255, 255, 0.1);
  font-weight: 600;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}

.profile-nav-tab svg {
  width: 16px;
  height: 16px;
  opacity: 0.7;
}

.profile-nav-tab.on svg {
  opacity: 1;
  color: var(--primary);
}

.profile-nav-tab .tab-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--r-pill);
  font-size: 11px;
  font-weight: 700;
  background: rgba(255, 255, 255, 0.08);
  color: var(--text-tertiary);
}

.profile-nav-tab.on .tab-count {
  background: rgba(250, 70, 22, 0.2);
  color: var(--primary);
}

/* Rail Bento Cards */
.rail-bento-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--s-8);
  margin-top: var(--s-12);
}

.rail-bento-stat {
  padding: var(--s-12);
  border-radius: var(--r-10);
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border-subtle);
}

.rail-bento-stat dt {
  font-size: 12px;
  color: var(--text-tertiary);
  margin-bottom: 2px;
}

.rail-bento-stat dd {
  font-size: 18px;
  font-weight: 700;
  color: var(--text-primary);
}


/* --------------------------------------------------------------------------
   The edit page — a form column with an index beside it.

   Not a form stretched across the window. A field gets harder to use the wider
   it gets: past roughly 70 characters the eye has to travel from the end of a
   label to the start of its input, and a page of those is tiring to fill in.
   So the column keeps its measure and the leftover width does something the
   form cannot -- says where you are in it, and keeps the counts in sight.
   -------------------------------------------------------------------------- */

.edit-layout {
  display: grid;
  /* The same two tracks and the same gutter as .feed-layout, which is what a
     profile is read in. The edit page and the profile page are one page from
     two sides: if the form column and the rail do not land on the same lines
     as the ones you were just looking at, saving feels like it moved the page
     rather than changed it. The rail only needs 264 for its own contents, but
     matching matters more than the 86 pixels. */
  grid-template-columns: minmax(0, 1fr) 350px;
  gap: var(--s-32);
  align-items: start;
}

/* No max-width. The column fills its track, and the track is what is capped. */
.edit-col { min-width: 0; }
/* A grid item's automatic minimum is its min-content width, which for a panel
   of side-by-side fields is wider than the track. Without this the column
   overflows and body's overflow-x: hidden quietly clips it. */
.edit-col > .dash-section { min-width: 0; }

/* The one save, at the foot of the column. It sits outside the panels on
   purpose: it is not the last field of the last group, it is the button for
   everything above it. */
.edit-save { display: flex; justify-content: flex-end; }

.edit-rail {
  display: grid; gap: var(--s-16); min-width: 0;
  position: sticky; top: calc(var(--rail-h) + var(--s-16));
}

.edit-index { display: grid; gap: 2px; margin-bottom: var(--s-12); }
.edit-index a {
  display: block;
  padding: var(--s-8) var(--s-12);
  border-radius: var(--r-8);
  font-size: 15px; line-height: 22px; color: var(--text-tertiary);
  transition: background-color .15s ease, color .15s ease;
}
.edit-index a:hover { background-color: rgba(255, 255, 255, 0.08); color: var(--text-primary); }
.edit-index a.on { color: var(--text-primary); font-weight: 700; }

/* The three counts, at the size a rail card holds rather than as three tiles
   across the top of the page. */
.edit-counts { display: grid; gap: var(--s-12); }
.edit-count {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-12);
}
.edit-count .val {
  font-family: var(--font-display);
  font-size: 24px; font-weight: 700; line-height: 30px; color: var(--text-primary);
}

/* Below the desktop breakpoint the rail stops being a sidecar. The index goes
   -- an index you have to scroll past to reach the thing it indexes is not one
   -- and the counts come down under the form, where they are still worth
   having. */
@media (max-width: 1023px) {
  .edit-layout { grid-template-columns: minmax(0, 1fr); gap: var(--s-24); }
  /* Stacked, the rail is no longer a column beside anything, so it goes back
     to the page's own rhythm rather than the profile's two-column gutter. */
  .edit-col { max-width: none; }
  .edit-rail { position: static; }
  .edit-index { display: none; }
  .edit-rail .rail-card:first-child { display: none; }
  .edit-counts { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .edit-count { flex-direction: column; align-items: flex-start; gap: 2px; }
}


/* --------------------------------------------------------------------------
   Sequel — the second axis of the board.

   The Post Line runs down the page for chronology. A series runs across it for
   relation: the post that started one carries a tag, and the tag fans the rest
   out sideways without moving anything around it. Everything here describes
   that fan-out, plus the page where the links are made.
   -------------------------------------------------------------------------- */

/* ---- the Sequel page ---- */

.seq-note { margin-bottom: var(--s-32); max-width: 78ch; }
.seq-note strong { color: var(--text-primary); font-weight: 600; }

.seq-list { display: grid; gap: var(--s-16); }
.seq-chain { overflow: hidden; }

.seq-step {
  display: flex; align-items: center; gap: var(--s-16);
  padding: var(--s-16);
  border-bottom: 1px solid var(--border-subtle);
  transition: background-color .15s ease;
}
.seq-step:last-child { border-bottom: 0; }
.seq-step:hover { background-color: rgba(255, 255, 255, 0.06); }

/* The reading order, stated. This is the order the tag fans out in. */
.seq-n {
  display: flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; flex: none;
  border-radius: var(--r-pill);
  background-color: var(--surface-light);
  border: 1px solid var(--border-subtle);
  font-size: 13px; font-weight: 700; line-height: 18px;
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}
.seq-step:first-child .seq-n {
  background-color: var(--primary); border-color: var(--primary); color: var(--white);
}

.seq-thumb {
  position: relative;
  width: 56px; height: 44px; flex: none;
  overflow: hidden;
  border-radius: var(--r-8);
  background-color: var(--surface-light);
  border: 1px solid var(--border-subtle);
}
.seq-thumb img { width: 100%; height: 100%; object-fit: cover; }
.seq-thumb-blank {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 600; color: var(--text-disabled);
}

/* A row in the picker is a choice, so it is a button -- and a button carries a
   UA stylesheet a div does not. */
button.seq-pick {
  width: 100%; font: inherit; color: inherit; cursor: pointer;
  background: none; border: 0; border-bottom: 1px solid var(--border-subtle);
  text-align: left;
}
button.seq-pick:last-child { border-bottom: 0; }
button.seq-pick:disabled { opacity: var(--o-50); cursor: not-allowed; }
.seq-go { width: 16px; height: 16px; color: var(--text-disabled); }
.seq-pick:hover .seq-go { color: var(--primary); }

@media (max-width: 639px) {
  .seq-step { flex-wrap: wrap; }
}


/* ---- the series on an event page ----
   Where you are in it, not what is in it. The part being read fills the page
   below; this says which part that is and moves to the next one. */
.series-panel {
  margin-bottom: var(--s-24);
  padding: var(--s-12) var(--s-16);
  background-color: var(--surface-light);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-24);
}

.series-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-16);
}
.series-nav { display: flex; align-items: center; gap: var(--s-8); flex: none; }
.series-nav .icon-btn { border: 1px solid var(--border-subtle); border-radius: var(--r-pill); }
.series-nav .btn:disabled, .series-nav .icon-btn:disabled { opacity: var(--o-50); cursor: not-allowed; }

/* One mark per part: the length of the series and the place in it, read
   without counting. Each is a way straight to that part. */
.series-pips { display: flex; align-items: center; gap: var(--s-4); margin-top: var(--s-12); }
/* A 4px bar is the right mark and an impossible target -- it was four pixels of
   height to hit, on a phone, with a thumb. The bar keeps its size; the pressable
   area is a transparent band centred on it, thrown by a pseudo-element so the
   row's own height and rhythm are untouched. */
.series-pip {
  position: relative;
  display: block; width: 100%; max-width: 64px; height: 4px;
  border-radius: var(--r-pill);
  background-color: var(--border-medium);
  transition: background-color .15s ease;
}
.series-pip::after {
  content: ''; position: absolute;
  left: 0; right: 0; top: 50%;
  height: 44px; transform: translateY(-50%);
}
.series-pip:hover { background-color: var(--text-tertiary); }
.series-pip.on { background-color: var(--primary); }
/* A part its owner has linked but not posted yet. Only they can see it, and it
   is drawn as an outline rather than a fill -- there, but not out. */
.series-pip.draft {
  background-color: transparent;
  box-shadow: inset 0 0 0 1px var(--border-medium);
}

/* ---- the slide ----
   Two parts on one track, twice the width of the page; the track is what
   moves, so the part leaving and the part arriving cannot drift out of step.
   The stage clips it, and both are gone again the moment the slide ends. */
.slide-stage { overflow: hidden; width: 100%; }
.slide-track {
  display: flex; width: 200%;
  transition: transform .32s cubic-bezier(.4, 0, .2, 1);
}
.slide-track > * { width: 50%; flex: none; min-width: 0; }

@media (prefers-reduced-motion: reduce) {
  .slide-track { transition: none; }
}

@media (max-width: 639px) {
  .series-panel { padding: var(--s-12); }
  .series-head { flex-wrap: wrap; }
}


/* ==========================================================================
   Touch targets — the last word on control height

   This block is last on purpose. The responsive section higher up raises every
   control wearing a .btn class to 48px on a phone, but a good half of the app's
   controls are styled as themselves rather than as buttons -- filter chips, the
   segmented switches in the composer, the month steppers on the calendar, the
   range tabs on the console -- and several of those are defined *below* that
   section. At equal specificity the later rule wins, so raising them up there
   silently did nothing for the two that mattered most.

   Everything that is pressed rather than read belongs here, at the end, where
   source order is on its side. 44px is the floor: it is the smaller of the two
   common platform minimums, and every value below was under it.
   ========================================================================== */

@media (max-width: 639px) {
  .chip,
  .cat-btn                    { height: 44px; }
  .seg button,
  .seg-sm button              { height: 44px; }
  .cal-step                   { width: 44px; height: 44px; }
  .an-tabs button             { min-height: 44px; padding-inline: var(--s-16); }

  /* Rows that are a control without looking like one. They keep their type and
     their alignment and only gain the height a finger needs. */
  .fold-toggle,
  .an-more,
  .an-badge                   { min-height: 44px; display: flex; align-items: center; }
  .an-badge                   { display: inline-flex; justify-content: center; }
}

/* A pointer that cannot hover is a finger whatever the window is doing, so the
   floor is not really a width question. Tablets in particular sat above the
   639px cutoff and below any reasonable idea of a mouse. */
@media (hover: none) and (pointer: coarse) {
  .chip, .cat-btn, .seg button, .seg-sm button { min-height: 44px; }
  .cal-step { width: 44px; height: 44px; }
  .an-tabs button { min-height: 44px; }
}

/* The native radios and checkboxes the composer uses. They were 13px squares --
   the label around them is the real target, but the control itself should still
   be visible from reading distance, and accent-color is what keeps them on the
   brand instead of system blue. */
input[type="radio"],
input[type="checkbox"] {
  width: 18px; height: 18px;
  accent-color: var(--primary);
  cursor: pointer;
}
/* ---- standalone links that are navigation, not prose ----
   A link inside a sentence takes the sentence's line height and should: padding
   it out would tear a hole in the paragraph. A link that stands on its own -- a
   footer column, the way out of the sign-in form -- is a navigation control
   wearing link clothes, and those were landing at 16-22px tall. They are listed
   by where they sit rather than by a blanket rule, precisely so the ones set in
   running text are left alone. */
@media (max-width: 639px), (hover: none) and (pointer: coarse) {
  .site-foot ul a,
  .auth-bar a,
  .hint a,
  .auth-card > p a {
    display: inline-flex; align-items: center;
    min-height: 44px;
  }
  /* The list gap can come off once each row carries its own height, or the
     column grows by 44 a line and the footer doubles in length. */
  .site-foot ul { gap: 0 !important; }
}

/* ==========================================================================
   The top bar's suggestions

   The panel under the search field. It is not a second search -- it is the
   first three answers, arriving early: the endpoint behind it ranks with the
   same scorer the results page uses, so what is offered here is what will be at
   the top of that page. A suggestion that disagreed with the results would be
   worse than no suggestion.
   ========================================================================== */

.suggest {
  position: absolute; left: 0; right: 0; top: calc(100% + var(--s-8));
  z-index: var(--z-dropdown);
  max-height: 60vh; overflow-y: auto;
  padding: var(--s-4);
  background-color: var(--surface);
  border: 1px solid var(--border-control);
  border-radius: var(--r-14);
  box-shadow: var(--shadow-raised);
}

.suggest-none { padding: var(--s-16); text-align: center; }

.suggest-row {
  display: flex; align-items: center; gap: var(--s-12);
  padding: var(--s-8) var(--s-12);
  min-height: 44px;
  border-radius: var(--r-10);
  color: var(--text-primary);
  transition: background-color .12s ease;
}
/* One highlight, driven by the cursor rather than by :hover, so the keyboard
   and the mouse cannot disagree about which row is selected. */
.suggest-row.on { background-color: rgba(255, 255, 255, 0.1); }

/* What kind of thing this is -- a post, an account, a category. It is set as a
   label rather than an icon because the three are not visually distinct enough
   to guess, and a dropdown is read at speed. */
.suggest-kind {
  flex: none; width: 62px;
  font-size: 13px; font-weight: 600; line-height: 18px;
  letter-spacing: .02em; text-transform: uppercase;
  color: var(--text-disabled);
}
.suggest-body { display: flex; flex-direction: column; flex: 1; min-width: 0; }
.suggest-title { font-size: 15px; font-weight: 600; line-height: 20px; }
.suggest-meta { font-size: 13px; line-height: 18px; color: var(--text-tertiary); }

/* On a phone the field is narrow and the panel would be too. It breaks out to
   the width of the screen instead -- a dropdown that is 195px wide cannot show
   a title. */
@media (max-width: 639px) {
  .suggest {
    position: fixed;
    left: var(--s-8); right: var(--s-8);
    top: calc(var(--rail-h) - var(--s-4));
  }
  .suggest-kind { width: 54px; }
}

/* ---- what each kind costs to post ----
   The three choices at the top of the composer are not three shapes of the same
   form any more: a flier asks for artwork and a title, a teaser for a video and
   a little more, an event for the full profile. That difference is the whole
   point of having three, and it was invisible at the moment of choosing -- three
   identical rows, and you only discovered which one you had picked by how long
   the form underneath turned out to be. The second line says it up front. */
.pick-name { display: flex; flex-direction: column; flex: 1; min-width: 0; gap: 2px; }
.pick-cost {
  font-size: 13px; font-weight: 400; line-height: 18px;
  color: var(--text-tertiary);
}
/* The row's own children are laid out by .pick-row; this replaces the single
   label that used to sit in that slot, so it takes the same growth. */
.pick-row > .pick-name { flex: 1; }

/* ==========================================================================
   Finder's answer, on the board

   A search does not open a page of its own. It narrows the Post Line, and this
   is the strip that says so: what was asked, how much came back, the way out,
   and the accounts that matched. Everything below it is the ordinary column of
   ordinary cards -- which is the point. A result and a post are the same object
   and should not be two designs.
   ========================================================================== */

.found { margin-bottom: var(--s-16); }

.found-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-16);
  padding-bottom: var(--s-12);
  border-bottom: 1px solid var(--border-subtle);
}
.found-head .btn { flex: none; }
.found-n { color: var(--primary); }

/* The accounts that matched. A short horizontal shelf rather than a grid: it is
   an aside to the column, and two or three of them should not push the first
   post below the fold. */
.found-people {
  display: grid; gap: var(--s-8);
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  margin-top: var(--s-16);
}
.found-person {
  display: flex; align-items: center; gap: var(--s-12);
  min-width: 0; padding: var(--s-8) var(--s-12);
  border: 1px solid var(--border-subtle);
  border-radius: var(--r-14);
  transition: border-color .15s ease, background-color .15s ease;
}
.found-person:hover {
  border-color: var(--border-control);
  background-color: rgba(255, 255, 255, 0.06);
}

@media (max-width: 639px) {
  .found-head { flex-wrap: wrap; }
  .found-people { grid-template-columns: 1fr; }
}

/* ---- what a figure counts ----
   The line under each headline number. It is deliberately quiet and
   deliberately present: the label above is a name ("Engagements"), and a name
   is not a definition. This is the sentence that lets somebody act on the
   figure instead of guessing at it. */
.an-kpi-help {
  display: block;
  margin-top: var(--s-8);
  font-size: 13px; line-height: 18px;
  color: var(--text-tertiary);
}