/* required styles */

.leaflet-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-container,
.leaflet-pane > svg,
.leaflet-pane > canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
	position: absolute;
	left: 0;
	top: 0;
	}
.leaflet-container {
	overflow: hidden;
	}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
	-webkit-user-select: none;
	   -moz-user-select: none;
	        user-select: none;
	  -webkit-user-drag: none;
	}
/* Prevents IE11 from highlighting tiles in blue */
.leaflet-tile::selection {
	background: transparent;
}
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
.leaflet-safari .leaflet-tile {
	image-rendering: -webkit-optimize-contrast;
	}
/* hack that prevents hw layers "stretching" when loading new tiles */
.leaflet-safari .leaflet-tile-container {
	width: 1600px;
	height: 1600px;
	-webkit-transform-origin: 0 0;
	}
.leaflet-marker-icon,
.leaflet-marker-shadow {
	display: block;
	}
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container .leaflet-overlay-pane svg {
	max-width: none !important;
	max-height: none !important;
	}
.leaflet-container .leaflet-marker-pane img,
.leaflet-container .leaflet-shadow-pane img,
.leaflet-container .leaflet-tile-pane img,
.leaflet-container img.leaflet-image-layer,
.leaflet-container .leaflet-tile {
	max-width: none !important;
	max-height: none !important;
	width: auto;
	padding: 0;
	}

.leaflet-container img.leaflet-tile {
	/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
	mix-blend-mode: plus-lighter;
}

.leaflet-container.leaflet-touch-zoom {
	-ms-touch-action: pan-x pan-y;
	touch-action: pan-x pan-y;
	}
.leaflet-container.leaflet-touch-drag {
	-ms-touch-action: pinch-zoom;
	/* Fallback for FF which doesn't support pinch-zoom */
	touch-action: none;
	touch-action: pinch-zoom;
}
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
	-ms-touch-action: none;
	touch-action: none;
}
.leaflet-container {
	-webkit-tap-highlight-color: transparent;
}
.leaflet-container a {
	-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
}
.leaflet-tile {
	filter: inherit;
	visibility: hidden;
	}
.leaflet-tile-loaded {
	visibility: inherit;
	}
.leaflet-zoom-box {
	width: 0;
	height: 0;
	-moz-box-sizing: border-box;
	     box-sizing: border-box;
	z-index: 800;
	}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
	-moz-user-select: none;
	}

.leaflet-pane         { z-index: 400; }

.leaflet-tile-pane    { z-index: 200; }
.leaflet-overlay-pane { z-index: 400; }
.leaflet-shadow-pane  { z-index: 500; }
.leaflet-marker-pane  { z-index: 600; }
.leaflet-tooltip-pane   { z-index: 650; }
.leaflet-popup-pane   { z-index: 700; }

.leaflet-map-pane canvas { z-index: 100; }
.leaflet-map-pane svg    { z-index: 200; }

.leaflet-vml-shape {
	width: 1px;
	height: 1px;
	}
.lvml {
	behavior: url(#default#VML);
	display: inline-block;
	position: absolute;
	}


/* control positioning */

.leaflet-control {
	position: relative;
	z-index: 800;
	pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
	pointer-events: auto;
	}
.leaflet-top,
.leaflet-bottom {
	position: absolute;
	z-index: 1000;
	pointer-events: none;
	}
.leaflet-top {
	top: 0;
	}
.leaflet-right {
	right: 0;
	}
.leaflet-bottom {
	bottom: 0;
	}
.leaflet-left {
	left: 0;
	}
.leaflet-control {
	float: left;
	clear: both;
	}
.leaflet-right .leaflet-control {
	float: right;
	}
.leaflet-top .leaflet-control {
	margin-top: 10px;
	}
.leaflet-bottom .leaflet-control {
	margin-bottom: 10px;
	}
.leaflet-left .leaflet-control {
	margin-left: 10px;
	}
.leaflet-right .leaflet-control {
	margin-right: 10px;
	}


/* zoom and fade animations */

.leaflet-fade-anim .leaflet-popup {
	opacity: 0;
	-webkit-transition: opacity 0.2s linear;
	   -moz-transition: opacity 0.2s linear;
	        transition: opacity 0.2s linear;
	}
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
	opacity: 1;
	}
.leaflet-zoom-animated {
	-webkit-transform-origin: 0 0;
	    -ms-transform-origin: 0 0;
	        transform-origin: 0 0;
	}
svg.leaflet-zoom-animated {
	will-change: transform;
}

.leaflet-zoom-anim .leaflet-zoom-animated {
	-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
	   -moz-transition:    -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
	        transition:         transform 0.25s cubic-bezier(0,0,0.25,1);
	}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile {
	-webkit-transition: none;
	   -moz-transition: none;
	        transition: none;
	}

.leaflet-zoom-anim .leaflet-zoom-hide {
	visibility: hidden;
	}


/* cursors */

.leaflet-interactive {
	cursor: pointer;
	}
.leaflet-grab {
	cursor: -webkit-grab;
	cursor:    -moz-grab;
	cursor:         grab;
	}
.leaflet-crosshair,
.leaflet-crosshair .leaflet-interactive {
	cursor: crosshair;
	}
.leaflet-popup-pane,
.leaflet-control {
	cursor: auto;
	}
.leaflet-dragging .leaflet-grab,
.leaflet-dragging .leaflet-grab .leaflet-interactive,
.leaflet-dragging .leaflet-marker-draggable {
	cursor: move;
	cursor: -webkit-grabbing;
	cursor:    -moz-grabbing;
	cursor:         grabbing;
	}

/* marker & overlays interactivity */
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-image-layer,
.leaflet-pane > svg path,
.leaflet-tile-container {
	pointer-events: none;
	}

.leaflet-marker-icon.leaflet-interactive,
.leaflet-image-layer.leaflet-interactive,
.leaflet-pane > svg path.leaflet-interactive,
svg.leaflet-image-layer.leaflet-interactive path {
	pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
	pointer-events: auto;
	}

/* visual tweaks */

.leaflet-container {
	background: #ddd;
	outline-offset: 1px;
	}
.leaflet-container a {
	color: #0078A8;
	}
.leaflet-zoom-box {
	border: 2px dotted #38f;
	background: rgba(255,255,255,0.5);
	}


/* general typography */
.leaflet-container {
	font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
	font-size: 12px;
	font-size: 0.75rem;
	line-height: 1.5;
	}


/* general toolbar styles */

.leaflet-bar {
	box-shadow: 0 1px 5px rgba(0,0,0,0.65);
	border-radius: 4px;
	}
.leaflet-bar a {
	background-color: #fff;
	border-bottom: 1px solid #ccc;
	width: 26px;
	height: 26px;
	line-height: 26px;
	display: block;
	text-align: center;
	text-decoration: none;
	color: black;
	}
.leaflet-bar a,
.leaflet-control-layers-toggle {
	background-position: 50% 50%;
	background-repeat: no-repeat;
	display: block;
	}
.leaflet-bar a:hover,
.leaflet-bar a:focus {
	background-color: #f4f4f4;
	}
.leaflet-bar a:first-child {
	border-top-left-radius: 4px;
	border-top-right-radius: 4px;
	}
.leaflet-bar a:last-child {
	border-bottom-left-radius: 4px;
	border-bottom-right-radius: 4px;
	border-bottom: none;
	}
.leaflet-bar a.leaflet-disabled {
	cursor: default;
	background-color: #f4f4f4;
	color: #bbb;
	}

.leaflet-touch .leaflet-bar a {
	width: 30px;
	height: 30px;
	line-height: 30px;
	}
.leaflet-touch .leaflet-bar a:first-child {
	border-top-left-radius: 2px;
	border-top-right-radius: 2px;
	}
.leaflet-touch .leaflet-bar a:last-child {
	border-bottom-left-radius: 2px;
	border-bottom-right-radius: 2px;
	}

/* zoom control */

.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
	font: bold 18px 'Lucida Console', Monaco, monospace;
	text-indent: 1px;
	}

.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out  {
	font-size: 22px;
	}


/* layers control */

.leaflet-control-layers {
	box-shadow: 0 1px 5px rgba(0,0,0,0.4);
	background: #fff;
	border-radius: 5px;
	}
.leaflet-control-layers-toggle {
	background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAQAAAADQ4RFAAACf0lEQVR4AY1UM3gkARTePdvdoTxXKc+qTl3aU5U6b2Kbkz3Gtq3Zw6ziLGNPzrYx7946Tr6/ee/XeCQ4D3ykPtL5tHno4n0d/h3+xfuWHGLX81cn7r0iTNzjr7LrlxCqPtkbTQEHeqOrTy4Yyt3VCi/IOB0v7rVC7q45Q3Gr5K6jt+3Gl5nCoDD4MtO+j96Wu8atmhGqcNGHObuf8OM/x3AMx38+4Z2sPqzCxRFK2aF2e5Jol56XTLyggAMTL56XOMoS1W4pOyjUcGGQdZxU6qRh7B9Zp+PfpOFlqt0zyDZckPi1ttmIp03jX8gyJ8a/PG2yutpS/Vol7peZIbZcKBAEEheEIAgFbDkz5H6Zrkm2hVWGiXKiF4Ycw0RWKdtC16Q7qe3X4iOMxruonzegJzWaXFrU9utOSsLUmrc0YjeWYjCW4PDMADElpJSSQ0vQvA1Tm6/JlKnqFs1EGyZiFCqnRZTEJJJiKRYzVYzJck2Rm6P4iH+cmSY0YzimYa8l0EtTODFWhcMIMVqdsI2uiTvKmTisIDHJ3od5GILVhBCarCfVRmo4uTjkhrhzkiBV7SsaqS+TzrzM1qpGGUFt28pIySQHR6h7F6KSwGWm97ay+Z+ZqMcEjEWebE7wxCSQwpkhJqoZA5ivCdZDjJepuJ9IQjGGUmuXJdBFUygxVqVsxFsLMbDe8ZbDYVCGKxs+W080max1hFCarCfV+C1KATwcnvE9gRRuMP2prdbWGowm1KB1y+zwMMENkM755cJ2yPDtqhTI6ED1M/82yIDtC/4j4BijjeObflpO9I9MwXTCsSX8jWAFeHr05WoLTJ5G8IQVS/7vwR6ohirYM7f6HzYpogfS3R2OAAAAAElFTkSuQmCC);
	width: 36px;
	height: 36px;
	}
.leaflet-retina .leaflet-control-layers-toggle {
	background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADQAAAA0CAQAAABvcdNgAAAEsklEQVR4AWL4TydIhpZK1kpWOlg0w3ZXP6D2soBtG42jeI6ZmQTHzAxiTbSJsYLjO9HhP+WOmcuhciVnmHVQcJnp7DFvScowZorad/+V/fVzMdMT2g9Cv9guXGv/7pYOrXh2U+RRR3dSd9JRx6bIFc/ekqHI29JC6pJ5ZEh1yWkhkbcFeSjxgx3L2m1cb1C7bceyxA+CNjT/Ifff+/kDk2u/w/33/IeCMOSaWZ4glosqT3DNnNZQ7Cs58/3Ce5HL78iZH/vKVIaYlqzfdLu8Vi7dnvUbEza5Idt36tquZFldl6N5Z/POLof0XLK61mZCmJSWjVF9tEjUluu74IUXvgttuVIHE7YxSkaYhJZam7yiM9Pv82JYfl9nptxZaxMJE4YSPty+vF0+Y2up9d3wwijfjZbabqm/3bZ9ecKHsiGmRflnn1MW4pjHf9oLufyn2z3y1D6n8g8TZhxyzipLNPnAUpsOiuWimg52psrTZYnOWYNDTMuWBWa0tJb4rgq1UvmutpaYEbZlwU3CLJm/ayYjHW5/h7xWLn9Hh1vepDkyf7dE7MtT5LR4e7yYpHrkhOUpEfssBLq2pPhAqoSWKUkk7EDqkmK6RrCEzqDjhNDWNE+XSMvkJRDWlZTmCW0l0PHQGRZY5t1L83kT0Y3l2SItk5JAWHl2dCOBm+fPu3fo5/3v61RMCO9Jx2EEYYhb0rmNQMX/vm7gqOEJLcXTGw3CAuRNeyaPWwjR8PRqKQ1PDA/dpv+on9Shox52WFnx0KY8onHayrJzm87i5h9xGw/tfkev0jGsQizqezUKjk12hBMKJ4kbCqGPVNXudyyrShovGw5CgxsRICxF6aRmSjlBnHRzg7Gx8fKqEubI2rahQYdR1YgDIRQO7JvQyD52hoIQx0mxa0ODtW2Iozn1le2iIRdzwWewedyZzewidueOGqlsn1MvcnQpuVwLGG3/IR1hIKxCjelIDZ8ldqWz25jWAsnldEnK0Zxro19TGVb2ffIZEsIO89EIEDvKMPrzmBOQcKQ+rroye6NgRRxqR4U8EAkz0CL6uSGOm6KQCdWjvjRiSP1BPalCRS5iQYiEIvxuBMJEWgzSoHADcVMuN7IuqqTeyUPq22qFimFtxDyBBJEwNyt6TM88blFHao/6tWWhuuOM4SAK4EI4QmFHA+SEyWlp4EQoJ13cYGzMu7yszEIBOm2rVmHUNqwAIQabISNMRstmdhNWcFLsSm+0tjJH1MdRxO5Nx0WDMhCtgD6OKgZeljJqJKc9po8juskR9XN0Y1lZ3mWjLR9JCO1jRDMd0fpYC2VnvjBSEFg7wBENc0R9HFlb0xvF1+TBEpF68d+DHR6IOWVv2BECtxo46hOFUBd/APU57WIoEwJhIi2CdpyZX0m93BZicktMj1AS9dClteUFAUNUIEygRZCtik5zSxI9MubTBH1GOiHsiLJ3OCoSZkILa9PxiN0EbvhsAo8tdAf9Seepd36lGWHmtNANTv5Jd0z4QYyeo/UEJqxKRpg5LZx6btLPsOaEmdMyxYdlc8LMaJnikDlhclqmPiQnTEpLUIZEwkRagjYkEibQErwhkTAKCLQEbUgkzJQWc/0PstHHcfEdQ+UAAAAASUVORK5CYII=);
	background-size: 26px 26px;
	}
.leaflet-touch .leaflet-control-layers-toggle {
	width: 44px;
	height: 44px;
	}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
	display: none;
	}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
	display: block;
	position: relative;
	}
.leaflet-control-layers-expanded {
	padding: 6px 10px 6px 6px;
	color: #333;
	background: #fff;
	}
.leaflet-control-layers-scrollbar {
	overflow-y: scroll;
	overflow-x: hidden;
	padding-right: 5px;
	}
.leaflet-control-layers-selector {
	margin-top: 2px;
	position: relative;
	top: 1px;
	}
.leaflet-control-layers label {
	display: block;
	font-size: 13px;
	font-size: 1.08333em;
	}
.leaflet-control-layers-separator {
	height: 0;
	border-top: 1px solid #ddd;
	margin: 5px -10px 5px -6px;
	}

/* Default icon URLs */
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
	background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAApCAYAAADAk4LOAAAFgUlEQVR4Aa1XA5BjWRTN2oW17d3YaZtr2962HUzbDNpjszW24mRt28p47v7zq/bXZtrp/lWnXr337j3nPCe85NcypgSFdugCpW5YoDAMRaIMqRi6aKq5E3YqDQO3qAwjVWrD8Ncq/RBpykd8oZUb/kaJutow8r1aP9II0WmLKLIsJyv1w/kqw9Ch2MYdB++12Onxee/QMwvf4/Dk/Lfp/i4nxTXtOoQ4pW5Aj7wpici1A9erdAN2OH64x8OSP9j3Ft3b7aWkTg/Fm91siTra0f9on5sQr9INejH6CUUUpavjFNq1B+Oadhxmnfa8RfEmN8VNAsQhPqF55xHkMzz3jSmChWU6f7/XZKNH+9+hBLOHYozuKQPxyMPUKkrX/K0uWnfFaJGS1QPRtZsOPtr3NsW0uyh6NNCOkU3Yz+bXbT3I8G3xE5EXLXtCXbbqwCO9zPQYPRTZ5vIDXD7U+w7rFDEoUUf7ibHIR4y6bLVPXrz8JVZEql13trxwue/uDivd3fkWRbS6/IA2bID4uk0UpF1N8qLlbBlXs4Ee7HLTfV1j54APvODnSfOWBqtKVvjgLKzF5YdEk5ewRkGlK0i33Eofffc7HT56jD7/6U+qH3Cx7SBLNntH5YIPvODnyfIXZYRVDPqgHtLs5ABHD3YzLuespb7t79FY34DjMwrVrcTuwlT55YMPvOBnRrJ4VXTdNnYug5ucHLBjEpt30701A3Ts+HEa73u6dT3FNWwflY86eMHPk+Yu+i6pzUpRrW7SNDg5JHR4KapmM5Wv2E8Tfcb1HoqqHMHU+uWDD7zg54mz5/2BSnizi9T1Dg4QQXLToGNCkb6tb1NU+QAlGr1++eADrzhn/u8Q2YZhQVlZ5+CAOtqfbhmaUCS1ezNFVm2imDbPmPng5wmz+gwh+oHDce0eUtQ6OGDIyR0uUhUsoO3vfDmmgOezH0mZN59x7MBi++WDL1g/eEiU3avlidO671bkLfwbw5XV2P8Pzo0ydy4t2/0eu33xYSOMOD8hTf4CrBtGMSoXfPLchX+J0ruSePw3LZeK0juPJbYzrhkH0io7B3k164hiGvawhOKMLkrQLyVpZg8rHFW7E2uHOL888IBPlNZ1FPzstSJM694fWr6RwpvcJK60+0HCILTBzZLFNdtAzJaohze60T8qBzyh5ZuOg5e7uwQppofEmf2++DYvmySqGBuKaicF1blQjhuHdvCIMvp8whTTfZzI7RldpwtSzL+F1+wkdZ2TBOW2gIF88PBTzD/gpeREAMEbxnJcaJHNHrpzji0gQCS6hdkEeYt9DF/2qPcEC8RM28Hwmr3sdNyht00byAut2k3gufWNtgtOEOFGUwcXWNDbdNbpgBGxEvKkOQsxivJx33iow0Vw5S6SVTrpVq11ysA2Rp7gTfPfktc6zhtXBBC+adRLshf6sG2RfHPZ5EAc4sVZ83yCN00Fk/4kggu40ZTvIEm5g24qtU4KjBrx/BTTH8ifVASAG7gKrnWxJDcU7x8X6Ecczhm3o6YicvsLXWfh3Ch1W0k8x0nXF+0fFxgt4phz8QvypiwCCFKMqXCnqXExjq10beH+UUA7+nG6mdG/Pu0f3LgFcGrl2s0kNNjpmoJ9o4B29CMO8dMT4Q5ox8uitF6fqsrJOr8qnwNbRzv6hSnG5wP+64C7h9lp30hKNtKdWjtdkbuPA19nJ7Tz3zR/ibgARbhb4AlhavcBebmTHcFl2fvYEnW0ox9xMxKBS8btJ+KiEbq9zA4RthQXDhPa0T9TEe69gWupwc6uBUphquXgf+/FrIjweHQS4/pduMe5ERUMHUd9xv8ZR98CxkS4F2n3EUrUZ10EYNw7BWm9x1GiPssi3GgiGRDKWRYZfXlON+dfNbM+GgIwYdwAAAAASUVORK5CYII=);
	}


/* attribution and scale controls */

.leaflet-container .leaflet-control-attribution {
	background: #fff;
	background: rgba(255, 255, 255, 0.8);
	margin: 0;
	}
.leaflet-control-attribution,
.leaflet-control-scale-line {
	padding: 0 5px;
	color: #333;
	line-height: 1.4;
	}
.leaflet-control-attribution a {
	text-decoration: none;
	}
.leaflet-control-attribution a:hover,
.leaflet-control-attribution a:focus {
	text-decoration: underline;
	}
.leaflet-attribution-flag {
	display: inline !important;
	vertical-align: baseline !important;
	width: 1em;
	height: 0.6669em;
	}
.leaflet-left .leaflet-control-scale {
	margin-left: 5px;
	}
.leaflet-bottom .leaflet-control-scale {
	margin-bottom: 5px;
	}
.leaflet-control-scale-line {
	border: 2px solid #777;
	border-top: none;
	line-height: 1.1;
	padding: 2px 5px 1px;
	white-space: nowrap;
	-moz-box-sizing: border-box;
	     box-sizing: border-box;
	background: rgba(255, 255, 255, 0.8);
	text-shadow: 1px 1px #fff;
	}
.leaflet-control-scale-line:not(:first-child) {
	border-top: 2px solid #777;
	border-bottom: none;
	margin-top: -2px;
	}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
	border-bottom: 2px solid #777;
	}

.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
	box-shadow: none;
	}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
	border: 2px solid rgba(0,0,0,0.2);
	background-clip: padding-box;
	}


/* popup */

.leaflet-popup {
	position: absolute;
	text-align: center;
	margin-bottom: 20px;
	}
.leaflet-popup-content-wrapper {
	padding: 1px;
	text-align: left;
	border-radius: 12px;
	}
.leaflet-popup-content {
	margin: 13px 24px 13px 20px;
	line-height: 1.3;
	font-size: 13px;
	font-size: 1.08333em;
	min-height: 1px;
	}
.leaflet-popup-content p {
	margin: 17px 0;
	margin: 1.3em 0;
	}
.leaflet-popup-tip-container {
	width: 40px;
	height: 20px;
	position: absolute;
	left: 50%;
	margin-top: -1px;
	margin-left: -20px;
	overflow: hidden;
	pointer-events: none;
	}
.leaflet-popup-tip {
	width: 17px;
	height: 17px;
	padding: 1px;

	margin: -10px auto 0;
	pointer-events: auto;

	-webkit-transform: rotate(45deg);
	   -moz-transform: rotate(45deg);
	    -ms-transform: rotate(45deg);
	        transform: rotate(45deg);
	}
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
	background: white;
	color: #333;
	box-shadow: 0 3px 14px rgba(0,0,0,0.4);
	}
.leaflet-container a.leaflet-popup-close-button {
	position: absolute;
	top: 0;
	right: 0;
	border: none;
	text-align: center;
	width: 24px;
	height: 24px;
	font: 16px/24px Tahoma, Verdana, sans-serif;
	color: #757575;
	text-decoration: none;
	background: transparent;
	}
.leaflet-container a.leaflet-popup-close-button:hover,
.leaflet-container a.leaflet-popup-close-button:focus {
	color: #585858;
	}
.leaflet-popup-scrolled {
	overflow: auto;
	}

.leaflet-oldie .leaflet-popup-content-wrapper {
	-ms-zoom: 1;
	}
.leaflet-oldie .leaflet-popup-tip {
	width: 24px;
	margin: 0 auto;

	-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
	filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
	}

.leaflet-oldie .leaflet-control-zoom,
.leaflet-oldie .leaflet-control-layers,
.leaflet-oldie .leaflet-popup-content-wrapper,
.leaflet-oldie .leaflet-popup-tip {
	border: 1px solid #999;
	}


/* div icon */

.leaflet-div-icon {
	background: #fff;
	border: 1px solid #666;
	}


/* Tooltip */
/* Base styles for the element that has a tooltip */
.leaflet-tooltip {
	position: absolute;
	padding: 6px;
	background-color: #fff;
	border: 1px solid #fff;
	border-radius: 3px;
	color: #222;
	white-space: nowrap;
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
	pointer-events: none;
	box-shadow: 0 1px 3px rgba(0,0,0,0.4);
	}
.leaflet-tooltip.leaflet-interactive {
	cursor: pointer;
	pointer-events: auto;
	}
.leaflet-tooltip-top:before,
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
	position: absolute;
	pointer-events: none;
	border: 6px solid transparent;
	background: transparent;
	content: "";
	}

/* Directions */

.leaflet-tooltip-bottom {
	margin-top: 6px;
}
.leaflet-tooltip-top {
	margin-top: -6px;
}
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-top:before {
	left: 50%;
	margin-left: -6px;
	}
.leaflet-tooltip-top:before {
	bottom: 0;
	margin-bottom: -12px;
	border-top-color: #fff;
	}
.leaflet-tooltip-bottom:before {
	top: 0;
	margin-top: -12px;
	margin-left: -6px;
	border-bottom-color: #fff;
	}
.leaflet-tooltip-left {
	margin-left: -6px;
}
.leaflet-tooltip-right {
	margin-left: 6px;
}
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
	top: 50%;
	margin-top: -6px;
	}
.leaflet-tooltip-left:before {
	right: 0;
	margin-right: -12px;
	border-left-color: #fff;
	}
.leaflet-tooltip-right:before {
	left: 0;
	margin-left: -12px;
	border-right-color: #fff;
	}

/* Printing */

@media print {
	/* Prevent printers from removing background-images of controls. */
	.leaflet-control {
		-webkit-print-color-adjust: exact;
		print-color-adjust: exact;
		}
	}
:root {
  color-scheme: dark;

  /* ── Surface & ink ─────────────────────────────────────────────────── */
  --bg: #0f172a;
  --glass: rgba(15, 23, 42, 0.18);
  --glass-strong: rgba(15, 23, 42, 0.28);
  --glass-soft: rgba(15, 23, 42, 0.34);
  --ink: #f8fafc;
  --muted: #a7b4c8;
  --ghost: #6366f1;
  --line: rgba(255, 255, 255, 0.16);
  --line-strong: rgba(255, 255, 255, 0.28);
  --specular: linear-gradient(135deg, rgba(255, 255, 255, 0.52), rgba(255, 255, 255, 0.14) 36%, rgba(15, 23, 42, 0.34));

  /* ── Lens glass material ───────────────────────────────────────────
     The fill is lit from the top-left and sinks to a dark tint at the
     bottom-right; the rim is a conic highlight that peaks at the two
     corners a light above-left would catch. The blur token is the
     cross-browser blur; Chromium swaps in the #pogo-lens SVG filter
     (index.html) for edge refraction. */
  --glass-fill: linear-gradient(135deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.035) 42%, rgba(2, 6, 23, 0.26) 100%);
  --glass-base: rgba(15, 23, 42, 0.26);
  --glass-rim: conic-gradient(from 0deg at 50% 50%,
    rgba(255, 255, 255, 0.36) 0deg,
    rgba(255, 255, 255, 0.10) 62deg,
    rgba(255, 255, 255, 0.60) 135deg,
    rgba(255, 255, 255, 0.08) 212deg,
    rgba(255, 255, 255, 0.88) 315deg,
    rgba(255, 255, 255, 0.36) 360deg);
  --glass-edge: rgba(255, 255, 255, 0.07);
  --glass-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.14),
    inset 0 -16px 30px rgba(2, 6, 23, 0.24),
    0 18px 54px rgba(0, 0, 0, 0.3);
  --glass-blur: blur(18px) saturate(150%);

  /* ── Status / response colors (best/works/maybe/no) ──────────────── */
  --best: #a7e4d3;
  --best-fill: rgba(167, 228, 211, 0.2);
  --works: #aec6cf;
  --works-fill: rgba(174, 198, 207, 0.2);
  --maybe: #e8b971;
  --maybe-fill: rgba(232, 185, 113, 0.2);
  --no: #ef4444;
  --rail: #e0e7ff;
  --rail-core: #6366f1;

  /* ── Band palette ──────────────────────────────────────────────────
     Single source of truth for the 8-color cycle used by overlapping
     ranges, time blocks, LED dots, and tray chips. Override these to
     re-theme (e.g. `:root { --band-0: #aaa; ... }` for a grayscale
     theme). The .overlay-band-N / .led-band-N / .setup-band-N classes
     read from these via local --band-color / --led-color / --block-color. */
  --band-0: #a7e4d3;  /* mint teal      */
  --band-1: #fbbf24;  /* amber gold     */
  --band-2: #93c5fd;  /* sky blue       */
  --band-3: #f9a8d4;  /* rose pink      */
  --band-4: #34d399;  /* emerald green  */
  --band-5: #fb923c;  /* warm orange    */
  --band-6: #c4b5fd;  /* soft lavender  */
  --band-7: #38bdf8;  /* bright cyan    */

  /* ── Tile material (calendar day cells) ──────────────────────────── */
  --tile-empty: rgba(255, 255, 255, 0.04);
  --tile-edge: rgba(255, 255, 255, 0.08);
  --tile-highlight: inset 1px 1px 0px rgba(255, 255, 255, 0.15);
  --tile-lowlight: inset -1px -1px 2px rgba(2, 6, 23, 0.42), inset 0 -18px 32px rgba(2, 6, 23, 0.14);
  --tile-floor-shadow: 0 16px 30px rgba(2, 6, 23, 0.32), 0 3px 8px rgba(0, 0, 0, 0.2);
  --shadow: 0 32px 90px rgba(0, 0, 0, 0.38);
  --shadow-soft: 0 18px 54px rgba(0, 0, 0, 0.28);

  /* ── Motion ─────────────────────────────────────────────────────────
     Tokens shared by hover lifts, tray transitions, and the range
     glow pulse. --ease-spring is the dominant feel; --ease-pop is the
     punchier curve used for grid/density changes. */
  --lift-y: -4px;          /* hover translateY for cells, rings, glass */
  --lift-scale: 1.022;     /* hover scale matched to the lift          */
  --ease-spring: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-pop: cubic-bezier(0.2, 0.9, 0.2, 1.08);
  --motion-fast: 180ms;
  --motion-base: 220ms;
  --motion-slow: 420ms;

  /* ── Ring stack geometry ────────────────────────────────────────────
     The neon ring around a committed range sits at outline-offset
     `ring-base + depth × ring-step`px, where depth is the number of
     other ranges stacked beneath it on shared cells. main.jsx mirrors
     these constants when emitting inline `outline-offset`. */
  --ring-base: 1px;
  --ring-step: 4px;

  /* ── Layout ─────────────────────────────────────────────────────── */
  --top-h: clamp(140px, 14vh, 156px);
  --right-rail: 78px;
  --right-open: min(390px, 34vw);
  --bottom-rail: 108px;
  /* Collapsed setup-mode height — kept here as a token so the
     :not(:has(.setup-invites.expanded)) rule below can reuse it. */
  --bottom-rail-collapsed: 44px;
  --bottom-open: min(300px, 36vh);
  --gap: 12px;
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-variant-numeric: tabular-nums;
}

* {
  box-sizing: border-box;
}

html,
body,
#root {
  width: 100%;
  min-height: 100%;
  overflow: hidden;
}

body {
  height: 100vh;
  margin: 0;
  overflow: hidden;
  background: var(--bg);
  color: var(--ink);
}

body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.38;
  background-image:
    radial-gradient(circle at 12% 20%, rgba(255, 255, 255, 0.1) 0 1px, transparent 1px),
    radial-gradient(circle at 82% 72%, rgba(255, 255, 255, 0.08) 0 1px, transparent 1px);
  background-size: 4px 4px, 6px 6px;
  mix-blend-mode: screen;
}

button,
input {
  font: inherit;
}

button {
  cursor: pointer;
}

.app-shell {
  position: relative;
  z-index: 1;
  width: 100vw;
  height: 100vh;
  /* Left padding reserves the 56px icon rail (logo + Guide icon +
     Tip Me) along the left edge. The rail itself is position:fixed,
     so the shell content slides over to clear it. */
  padding: var(--gap) var(--gap) var(--gap) 64px;
  display: grid;
  /* Row 1: the title-island top tray. Row 2: the calendar/content
     area. Setup (non-preview) mode adds a leading auto row for the
     setup-header strip below — the strip only renders in that mode,
     so the base grid must NOT reserve a row for it: an empty `auto`
     row would pull top-chrome/stage-shell up one track and collapse
     the calendar (grid auto-placement doesn't skip empty rows). */
  grid-template-rows: var(--top-h) minmax(0, 1fr);
  gap: var(--gap);
  overflow: hidden;
}

/* Setup header strip row — only when the strip actually renders. */
.app-shell.setup-mode:not(.setup-preview) {
  grid-template-rows: auto var(--top-h) minmax(0, 1fr);
}

.ambient-scene {
  background-color: var(--bg);
}

/* Ambient background + cursor parallax architecture.
   See public/backgrounds/README.md for the contract. Variant CSS files
   provide ONLY visuals (background, gradients, drift keyframes); the
   structural rules below stay shared so parallax can never be broken
   by a variant accidentally redefining the wrong selector. */
.ambient-background {
  position: fixed;
  inset: 0;
  overflow: hidden;
  z-index: 0;
  pointer-events: none;
}

.orb-parallax-shell {
  position: absolute;
  inset: 0;
  pointer-events: none;
  transition: transform 260ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.ambient-orb {
  position: absolute;
  border-radius: 50%;
}

.top-chrome,
.edge-tray {
  border: 1px solid transparent;
  border-image: var(--specular) 1;
  border-radius: 8px;
  background: var(--glass-strong);
  box-shadow: var(--shadow-soft);
  backdrop-filter: blur(24px) saturate(180%);
}

/* Top header strip — only rendered in setup mode. Shows the
   section title + draft badge on the left, and Preview / Poll
   Guests action buttons on the right. Sits ABOVE the title-island
   top tray (app-shell adds a dedicated grid row for it). */
.setup-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 4px 4px 0;
}

.setup-header-text {
  min-width: 0;
}

.setup-header-title {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin: 0;
  font-size: clamp(1.2rem, 1.8vw, 1.6rem);
  font-weight: 820;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.setup-header-badge {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #fbbf24;
  background: rgba(251, 191, 36, 0.12);
  border: 1px solid rgba(251, 191, 36, 0.35);
}

.setup-header-subtitle {
  margin: 4px 0 0;
  font-size: 0.86rem;
  color: rgba(248, 250, 252, 0.55);
}

.setup-header-subtitle b,
.setup-header-subtitle strong {
  color: rgba(248, 250, 252, 0.85);
  font-weight: 700;
}

.setup-header-actions {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.setup-header-action {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 16px;
  border-radius: 10px;
  font-size: 0.86rem;
  font-weight: 600;
  font-family: inherit;
  letter-spacing: 0.01em;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}

.setup-header-action--preview {
  background: rgba(15, 23, 42, 0.55);
  border-color: rgba(255, 255, 255, 0.14);
  color: rgba(248, 250, 252, 0.88);
}
.setup-header-action--preview:hover {
  background: rgba(15, 23, 42, 0.75);
  border-color: rgba(255, 255, 255, 0.26);
  color: #fff;
}

.setup-header-action--poll {
  background: linear-gradient(180deg, #818cf8, #6366f1);
  border-color: rgba(99, 102, 241, 0.6);
  color: #ffffff;
  box-shadow: 0 6px 18px rgba(99, 102, 241, 0.32);
}
.setup-header-action--poll:hover:not(:disabled) {
  background: linear-gradient(180deg, #a5b4fc, #818cf8);
  border-color: rgba(167, 139, 250, 0.85);
}

.setup-header-action:disabled,
.setup-header-action[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  box-shadow: none;
}

.top-chrome {
  display: grid;
  /* Brand-area is now position:fixed (left icon rail), so the first
     grid column would be 0 — drop it entirely and run a 2-column
     grid: title-island | organizer-setup-panel. */
  grid-template-columns: minmax(0, 1fr) 220px;
  align-items: start;
  gap: 14px;
  padding: 0;
  overflow: visible;
  border: 0;
  background: transparent;
  box-shadow: none;
  backdrop-filter: none;
}

/* Setup (non-preview) mode hides the right "Organizer Setup Page"
   panel — Preview + Poll Guests live in the new top header strip
   instead — so the title-island can stretch to the full width. */
.app-shell.setup-mode:not(.setup-preview) .top-chrome {
  grid-template-columns: minmax(0, 1fr);
}
.app-shell.setup-mode:not(.setup-preview) .mode-action-bar {
  display: none;
}

.mode-action-bar {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 8px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: rgba(15, 23, 42, 0.42);
  box-shadow: 0 18px 38px rgba(2, 6, 23, 0.18);
  backdrop-filter: blur(20px) saturate(170%);
  align-self: start;
  height: var(--top-h);
  box-sizing: border-box;
}

.mode-action-bar .mode-title {
  margin: 0 0 2px;
  font-size: clamp(1.25rem, 1.5vw, 1.55rem);
  font-weight: 820;
  letter-spacing: 0;
  line-height: 1.05;
  text-align: center;
}

.mode-action-bar .mode-action-buttons {
  display: grid;
  gap: 6px;
}

.mode-action-bar .launch-button {
  min-height: 36px;
  border-radius: 8px;
  border: 1px solid var(--line);
  background: rgba(15, 23, 42, 0.4);
  color: var(--ink);
  font-weight: 760;
  padding: 0 12px;
}

.mode-action-bar .launch-button.primary {
  border-color: rgba(167, 139, 250, 0.55);
  background: rgba(124, 92, 234, 0.42);
  color: white;
}

.mode-action-bar .launch-button.secondary {
  background: rgba(15, 23, 42, 0.55);
}

.mode-action-bar .launch-button[disabled],
.mode-action-bar .launch-button[aria-disabled="true"],
.mode-action-bar .launch-button.is-disabled {
  opacity: 0.42;
  filter: saturate(0.6);
  /* Use a regular cursor so the browser's native `title` tooltip
     surfaces on hover. The `not-allowed` cursor felt unhelpful and
     prevented the explanation from appearing. */
  cursor: default;
}


.brand {
  color: var(--ink);
  text-decoration: none;
  position: relative;
  width: auto;
  /* Was min-width:148/min-height:134 when the brand cell was a
     top-header tile. In the icon-rail layout the link just wraps
     the logo, so size to content. */
  min-width: 0;
  min-height: 0;
  justify-content: center;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  backdrop-filter: none;
  font-weight: 780;
  display: inline-flex;
  align-items: center;
  gap: 0;
  letter-spacing: 0;
  /* The brand is a <button> (it returns to the flow picker in-app
     rather than hard-reloading "/"), so zero out the UA button box. */
  padding: 0;
  font: inherit;
  appearance: none;
  transition: transform var(--motion-fast) var(--ease-pop);
}

.brand:hover .brand-logo-wrap { transform: scale(1.06); }
.brand:active .brand-logo-wrap { transform: scale(0.98); }

.brand:focus-visible {
  outline: 2px solid var(--ghost);
  outline-offset: 4px;
  border-radius: 10px;
}

.brand-logo-wrap {
  flex-shrink: 0;
  /* Icon-rail sizing — was 72×88 when the logo sat in the top
     header. The rail is 56px wide so the logo scales to ~40×48 to
     fit comfortably with breathing room on each side. */
  width: 40px;
  height: 48px;
  border-radius: 0;
  background: transparent;
  border: none;
  box-shadow: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: visible;
}

.brand-logo {
  width: 40px;
  height: 48px;
  display: block;
  object-fit: contain;
  object-position: center;
  filter: drop-shadow(0 4px 12px rgba(0,0,0,0.5));
  /* Hidden until the image finishes decoding, so the user never
     sees a momentary frame at the file's natural pixel size before
     CSS shrinks it to 72×88. The JSX flips data-loaded="true" on
     the <img>'s onLoad event. */
  opacity: 0;
  transition: opacity 140ms ease-out;
}
.brand-logo[data-loaded="true"] {
  opacity: 1;
}

.title-island {
  position: relative;
  justify-self: stretch;
  width: 100%;
  max-width: none;
  padding: 11px 16px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-rows: auto 1fr;
  /* Tightened the column-gap from 14 to 8 — the larger gap read as
     a noticeable empty channel between the description text and the
     photo/map slot. */
  column-gap: 8px;
  row-gap: 4px;
  align-items: start;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(15, 23, 42, 0.22);
  box-shadow: 0 18px 54px rgba(0, 0, 0, 0.18);
  backdrop-filter: blur(24px) saturate(180%);
  z-index: 5;
  max-height: var(--top-h);
  overflow: hidden;
  transition: max-height 280ms var(--ease-pop);
}

/* Bottom-edge tab label — mirrors the right tray "Date/Time Blocks"
   and bottom tray "Invitations" labels. Same typography (matches
   .invites-title: 1rem / 820 / var(--ink) / no transform) and the
   same expand-icon pattern. Positioned absolutely at the bottom
   edge of the title-island so it sits above the calendar like a
   named handle. */
/* Note: .title-island > * sets position: relative + z-index: 1 on
   every direct child. That overrode this rule (same specificity,
   later source) and dropped the tab back into grid auto-flow as a
   third row that fell below the collapsed-tray clip line. We re-
   qualify with .title-island > .title-island-tab to win specificity. */
.title-island > .title-island-tab {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 4px 8px;
  /* z-index: 100 — well above every other in-context child (the
     description editor reaches z-index: 2 on focus, lists go to 30) */
  z-index: 100;
  background: rgba(2, 6, 23, 0.92);
  border-top: 1px solid var(--line);
  pointer-events: none;
  user-select: none;
  color: var(--ink);
  min-width: 0;
  min-height: 28px;
}
.title-island-tab-label {
  /* Scales down on narrow viewports so "Event Details" doesn't get
     clipped, and ellipses as a final safety net. */
  font-size: clamp(0.78rem, 1.6vw, 1rem);
  font-weight: 820;
  color: var(--ink);
  letter-spacing: 0;
  text-transform: none;
  line-height: 1.1;
  min-width: 0;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Right-justified count summary on the title-island bottom tab.
   Visible only when the tray is minimized in setup mode (see the
   conditional render in TopChrome). Smaller + lighter than the
   "Event Details" label so it reads as supporting metadata. */
.title-island-tab-count {
  position: absolute;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 0.78rem;
  font-weight: 500;
  color: rgba(248, 250, 252, 0.7);
  letter-spacing: 0;
  line-height: 1.1;
  white-space: nowrap;
  pointer-events: none;
}

.title-island.hover-expanded,
.title-island:focus-within,
.title-island.pinned-open {
  /* Lock the tray to a tall, fixed size when open — using only
     max-height let the island stay short because its inner content
     (title + description + photo + address) is shorter than the
     target. Setting both min-height AND max-height pads the tray
     with empty space at the bottom so the calendar underneath
     shows ≈1 full row + partials above/below.
     calc(100vh - 340px) reserves 252px for the calendar row band
     and 88px for the bottom-rail + gaps + padding. Clamped 360px
     – 1000px for very short / very tall viewports. */
  min-height: clamp(360px, calc(100vh - 340px), 1000px);
  max-height: clamp(360px, calc(100vh - 340px), 1000px);
  overflow: hidden;
  background: rgba(2, 6, 23, 0.95);
  backdrop-filter: blur(28px) saturate(180%);
  z-index: 60;
}

.title-island.hover-expanded .description-editor-wrap:not(:focus-within) .description-clamp {
  display: block;
  -webkit-line-clamp: unset;
  overflow: visible;
}

/* While the description editor is focused, the clamp grows with
   the content all the way down to the bottom of the available
   row in the title-island. The wrap's `max-height: 100%` (paired
   with the column flex chain) keeps it from pushing the
   title-island taller — once the description fills the row, the
   inner editor scrolls. We deliberately do NOT cap at a fixed
   px value here: that used to make the editor start scrolling
   long before it reached the visible bottom of the tray. */
.description-editor-wrap:focus-within .description-clamp {
  -webkit-line-clamp: unset;
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
  overflow: hidden;
}

.title-island::before,
.edge-tray::before,
.send-gate::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  pointer-events: none;
  background:
    linear-gradient(135deg, rgba(125, 211, 252, 0.16), transparent 28%),
    linear-gradient(315deg, rgba(244, 114, 182, 0.11), transparent 30%);
  opacity: 0;
  mix-blend-mode: screen;
  transition: opacity 260ms ease;
}

.title-island > *,
.edge-tray > *,
.send-gate > * {
  position: relative;
  z-index: 1;
}

.title-island.hover-expanded::before,
.title-island:focus-within::before,
.edge-tray:hover::before,
.edge-tray.pinned::before,
.send-gate::before {
  opacity: 0.72;
}

.eyebrow {
  margin: 0 0 5px;
  color: var(--muted);
  font-size: 0.88rem;
  font-weight: 650;
}

.title-island .title-editor,
.title-island h1 {
  grid-column: 1;
  grid-row: 1;
  min-width: 0;
}

.title-island > .description-editor-wrap {
  grid-column: 1;
  grid-row: 2;
  min-width: 0;
  /* Leave room for the absolutely-positioned "Event Details" tab
     at the bottom of the title-island so the wrap's purple focus
     border can't touch (or cross) the tab's gray top border. The
     tab is ~36px tall (min-height 28px + 4px*2 padding); cap the
     wrap to just above it. */
  max-height: calc(100% - 40px);
}

.title-island > .title-island-media {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: start;
  width: 320px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* Photo + map sit side-by-side at the top of the media column; the
   address textarea (when in edit mode) stacks underneath. */
.title-island-photo-row {
  display: flex;
  flex-direction: row;
  gap: 6px;
}
.title-island-photo-row > .title-island-photo,
.title-island-photo-row > .title-island-map {
  flex: 1 1 0;
  min-width: 0;
}

.title-island-photo,
.title-island-map {
  width: 100%;
  aspect-ratio: 3 / 2;
  border-radius: 8px;
  border: 1px solid var(--line);
  background:
    linear-gradient(135deg, rgba(99, 102, 241, 0.18), rgba(167, 228, 211, 0.06)),
    rgba(15, 23, 42, 0.4);
  overflow: hidden;
  position: relative;
  cursor: pointer;
  flex: 0 0 auto;
}

.title-island-photo img,
.title-island-map img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}


.title-island-photo .photo-overlay-label {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #f8fafc;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: rgba(15, 23, 42, 0.32);
  backdrop-filter: blur(1px);
  opacity: 0;
  transition: opacity 160ms ease;
  pointer-events: none;
}

/* "Modify Photo" only appears on direct hover of the photo. The
   focus-within trigger (which used to show it whenever ANY input in
   the title-island gained focus) is removed — having the modify
   label flicker on while editing the title was confusing. */
.title-island-photo:not(.hidden):hover .photo-overlay-label {
  opacity: 1;
}
/* When there's no photo yet, the "Add Photo" label is ALWAYS visible
   so the user has an obvious invite to upload one. Same for the map
   slot — "Add Location" stays on until a coordinate is set, at which
   point it falls back to the regular Google Maps badge. */
.title-island-photo.is-empty:not(.hidden) .photo-overlay-label,
.title-island-map.is-empty .photo-overlay-label {
  opacity: 1;
}
.title-island-map .photo-overlay-label {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #f8fafc;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: rgba(15, 23, 42, 0.32);
  backdrop-filter: blur(1px);
  opacity: 0;
  transition: opacity 160ms ease;
  pointer-events: none;
}

.title-island-photo.hidden {
  background: transparent;
  border-color: transparent;
}

.title-island-photo.hidden:hover {
  border: 1px dashed rgba(199, 210, 254, 0.55);
}

.title-island-map.preview-active {
  border-color: rgba(99, 102, 241, 0.55);
}

.title-island-map .map-preview-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  background: rgba(15, 23, 42, 0.4);
}

.title-island-map .leaflet-container {
  width: 100%;
  height: 100%;
  background: rgba(15, 23, 42, 0.4);
  font-family: inherit;
}

.title-island-map .leaflet-control-attribution {
  font-size: 9px;
  padding: 0 3px;
  background: rgba(15, 23, 42, 0.6);
  color: rgba(199, 210, 254, 0.85);
}

.title-island-map .leaflet-control-attribution a {
  color: rgba(167, 228, 211, 0.95);
}

.title-island-map .map-preview-badge {
  position: absolute;
  top: 6px;
  right: 6px;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 6px;
  border-radius: 4px;
  background: rgba(15, 23, 42, 0.78);
  color: #e0e7ff;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  pointer-events: none;
}

.address-wrap {
  position: relative;
  width: 100%;
}

.address-input {
  width: 100%;
  padding: 5px 24px 5px 8px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: rgba(15, 23, 42, 0.4);
  color: var(--ink);
  font: inherit;
  font-size: 0.78rem;
  line-height: 1.35;
  outline: 0;
  box-sizing: border-box;
  resize: none;
  display: block;
}

.address-done-btn {
  position: absolute;
  top: 4px;
  right: 4px;
  display: none;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border: none;
  border-radius: 50%;
  background: rgba(148, 163, 184, 0.18);
  color: var(--muted);
  font-size: 11px;
  cursor: pointer;
  transition: background 140ms, color 140ms;
}

.address-done-btn:hover {
  background: rgba(167, 228, 211, 0.28);
  color: #a7e4d3;
}

.address-wrap:focus-within .address-done-btn {
  display: flex;
}

.address-input::placeholder {
  color: rgba(199, 210, 254, 0.48);
}

.address-input:focus {
  border-color: rgba(99, 102, 241, 0.55);
  background: rgba(15, 23, 42, 0.62);
}

.address-readonly {
  margin: 0;
  padding: 5px 2px;
  font-size: 0.78rem;
  color: var(--muted);
  word-break: break-word;
}

/* Setup-mode block that lives under the address in the top tray —
   holds the date-entry SetupCommandBar + the SetupChipsPanel (status
   card + chip list). Was previously a right-side rail; now slotted
   into TopChrome's right column so the calendar can use the full
   width of the page. Caps height + scrolls internally so adding
   many date options doesn't push the top tray taller and taller. */
.setup-tray-block {
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* Sizes to content; capped at 320 so it never pushes the
     title-island taller than the design grid allows. The chip
     list inside scrolls past the cap. */
  max-height: 320px;
  overflow: hidden;
}
/* The SetupCommandBar's `persistent` styling assumed it sat in a
   fixed-bottom wrap. Inside the top tray it just needs to flow
   normally — strip any positioning the .persistent variant set
   AND let it grow to fill the tray's free vertical space so the
   date textbox occupies the unused area instead of leaving a gap. */
.setup-tray-block > .setup-command-bar.persistent {
  position: static;
  margin: 0;
  width: 100%;
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  /* Stretch children to full width so the DATE label sits at the
     left edge like the ADDRESS label. */
  align-items: stretch;
  gap: 4px;
}

.top-chrome h1 {
  margin: 0;
  font-size: clamp(1.35rem, 2vw, 2rem);
  line-height: 1;
  letter-spacing: 0;
}

.title-editor {
  width: min(940px, 100%);
  margin: 0;
  border: 2px solid rgba(255, 255, 255, 0.1);
  border-radius: 10px;
  padding: 4px 10px;
  background: transparent;
  color: var(--ink);
  font-size: clamp(1.35rem, 2vw, 2rem);
  font-weight: 820;
  line-height: 1;
  letter-spacing: 0;
  outline: 0;
  transition: border-color 160ms ease, background 160ms ease, box-shadow 160ms ease;
}
.title-editor:hover {
  border-color: rgba(255, 255, 255, 0.18);
}
.title-editor:focus {
  border-color: var(--ghost);
  background: rgba(99, 102, 241, 0.08);
  box-shadow:
    0 0 0 3px color-mix(in srgb, var(--ghost) 22%, transparent),
    0 8px 24px color-mix(in srgb, var(--ghost) 18%, transparent);
}

.title-editor::placeholder {
  color: rgba(199, 210, 254, 0.55);
}

.description-editor-wrap {
  position: relative;
  width: min(1040px, 100%);
  margin-top: 6px;
  /* Grow with content, but cap at the available row-2 height so
     content overflow scrolls INSIDE the editor instead of pushing
     the title-island taller. The 2px border keeps the editor
     visually distinct as a field. */
  max-height: 100%;
  display: flex;
  flex-direction: column;
  min-height: 0;
  border: 2px solid rgba(255, 255, 255, 0.1);
  border-radius: 10px;
  padding: 6px 10px;
  transition: border-color 160ms ease, background 160ms ease, box-shadow 160ms ease;
}
/* When content fits, the editor stays at its natural height. When
   it overflows the available row-2 height, the inner editor
   scrolls internally. */
.description-editor-wrap > .description-clamp {
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.description-editor-wrap > .description-clamp > .description-editor {
  overflow-y: auto;
  min-height: 0;
}
.description-editor-wrap:hover {
  border-color: rgba(255, 255, 255, 0.18);
}
.description-editor-wrap:focus-within {
  border-color: var(--ghost);
  background: rgba(99, 102, 241, 0.08);
  box-shadow:
    0 0 0 3px color-mix(in srgb, var(--ghost) 22%, transparent),
    0 8px 24px color-mix(in srgb, var(--ghost) 18%, transparent);
}

/* Description suggestion tip — uses the same coach-mark vocabulary
   as "Drag across days to propose options" etc. (.coach-mark base
   styling) but with a slightly wider max-width and a small bullet
   list. Portal-rendered to body so the title-island never has to
   expand to fit it. */
.coach-mark--description-tip {
  max-width: 280px;
  padding: 12px 16px 12px 18px;
}
.coach-mark--description-tip .coach-mark-title {
  margin: 0 0 4px;
  font-size: 0.86rem;
  font-weight: 820;
  letter-spacing: -0.005em;
  color: #ffffff;
}
.coach-mark--description-tip .coach-mark-sub {
  margin: 0 0 8px;
  font-size: 0.78rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.78);
}
.coach-mark--description-tip ul {
  list-style: disc;
  padding-left: 16px;
  margin: 0;
  font-size: 0.8rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.94);
  font-weight: 620;
}
.coach-mark--description-tip li + li { margin-top: 1px; }

/* Date entry tip — same vocabulary as the description tip but
   tighter, with code snippets for the shorthand formats. */
.coach-mark--date-entry-tip {
  max-width: 320px;
  padding: 12px 16px 12px 18px;
}

/* INLINE coach mark variant — used by DateEntryTipPortal. Renders
   as a block element in the layout flow, pushing siblings (status
   card, chips, etc.) down instead of floating over them.
   IMPORTANT: the base .coach-mark sets `position: absolute` further
   down in this file. On equal specificity, source order wins, so
   the base used to clobber this rule and the tip stayed floating.
   The two-class selector below (.coach-mark.coach-mark--inline)
   raises specificity so this rule actually applies. */
.coach-mark.coach-mark--inline {
  position: relative;
  z-index: auto;
  display: block;
  margin-top: 22px; /* room for the up-from-below connector */
  margin-bottom: 6px;
  max-width: 320px;
}
.coach-mark--date-entry-tip .coach-mark-title {
  margin: 0 0 6px;
  font-size: 0.86rem;
  font-weight: 820;
  letter-spacing: -0.005em;
  color: #ffffff;
}
.coach-mark--date-entry-tip .coach-mark-sub {
  margin: 6px 0 0;
  font-size: 0.76rem;
  color: rgba(255, 255, 255, 0.7);
}
.coach-mark--date-entry-tip ul {
  list-style: none;
  padding: 0;
  margin: 0;
  font-size: 0.8rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.94);
}
.coach-mark--date-entry-tip li {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  padding: 4px 0 6px;
}
.coach-mark--date-entry-tip li + li {
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}
.coach-mark--date-entry-tip .format-desc {
  font-size: 0.74rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.7);
  padding-left: 2px;
}
.coach-mark--date-entry-tip code {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 4px;
  background: rgba(15, 23, 42, 0.6);
  border: 1px solid rgba(255, 255, 255, 0.12);
  font-family: "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.76rem;
  color: #fde68a;
}
.coach-mark--date-entry-tip .coach-mark-warn {
  margin: 8px 0 0;
  padding: 6px 8px;
  border-radius: 6px;
  border: 1px solid rgba(250, 204, 21, 0.45);
  background: rgba(250, 204, 21, 0.12);
  font-size: 0.76rem;
  font-weight: 620;
  line-height: 1.4;
  color: #fde68a;
}
.coach-mark--date-entry-tip .coach-mark-warn span {
  margin-right: 4px;
}

/* Date entry prefix chip — pinned to the left edge of the input
   wrap with a fixed width. Both the textarea and the mirror get
   matching left padding via `.command-input-wrap.has-prefix` so the
   user's typed text and the mirror tokens both start AFTER the
   prefix, keeping caret/glyph alignment intact. */
.command-input-wrap.has-prefix {
  position: relative;
}
/* Sits at the textarea's text-start position — same line-height +
   the same padding offset as the textarea's content, so the prefix
   and the typed dates share one visual baseline ("Dates: Jun 8-9").
   The textarea + mirror padding-left is set inline in JS based on
   the measured chip width, so the space between prefix and dates
   is exactly one space character regardless of which label is
   active ("Dates:" vs "Date/times:"). */
.command-input-wrap .command-prefix-chip {
  position: absolute;
  left: 10px;
  /* Stretch the chip's flex box to the full height of the wrap and
     align-items: center its inner text. The chip uses the SAME
     line-height as the textarea/mirror (22px) so the prefix glyphs
     and the typed dates land on the same baseline regardless of
     where the textarea places its line-box internally. */
  top: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  z-index: 2;
  pointer-events: none;
  white-space: pre;
  font: inherit;
  font-weight: 760;
  line-height: 22px;
  color: var(--ink);
}

.description-editor {
  color: var(--muted);
  font-size: 0.84rem;
  line-height: 1.5;
  outline: 0;
  white-space: pre-wrap;
  word-break: break-word;
}

.description-editor-wrap:not(:focus-within) .description-clamp {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.description-editor:empty::before {
  content: "Event Description";
  color: rgba(199, 210, 254, 0.48);
}

.description-editor:focus {
  color: var(--ink);
}

/* Title row holds the title editor (or h1) AND the description-done
   check button on the same line, so the check stays out of the path
   of the description text below. */
.title-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}
/* Mobile-only photo/map toggle button — hidden on desktop where the
   media slot is always visible. The mobile @media block re-shows it. */
.title-island-mobile-toggle {
  display: none;
}
.title-row > .title-editor,
.title-row > h1 {
  flex: 1 1 auto;
  min-width: 0;
}

/* Auto-saved + X pair — mirrors the TimePicker header convention. The
   "Auto-saved" pill quietly reassures the organizer that there is no
   submit step; the X is a deliberate way out (blurs the editor). Both
   appear together only while the description is being edited. */
.description-editing-status {
  flex: 0 0 auto;
  display: none;
  align-items: center;
  gap: 6px;
}
.description-autosaved {
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(15, 23, 42, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.08);
  color: var(--muted);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.description-close-btn {
  background: transparent;
  border: 0;
  color: var(--muted);
  cursor: pointer;
  padding: 4px;
  border-radius: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.description-close-btn:hover {
  background: rgba(255, 255, 255, 0.06);
  color: var(--ink);
}
/* Reveal only while the description editor is focused. Uses :has() on
   the island so the status block can live in the title row, not inside
   the description wrap. */
.title-island:has(.description-editor:focus) .description-editing-status {
  display: inline-flex;
}

/* Rich text formatting toolbar — hidden until description is focused.
   Icons sized + tinted for clear discoverability against the dark glass
   panel; the muted-icon look at original 13px / 0.55 opacity blended
   into the panel and read as decorative. position: relative so the
   emoji-picker popover can anchor to it. */
.description-toolbar {
  position: relative;
  display: none;
  align-items: center;
  gap: 2px;
  flex-basis: 100%;
  margin-top: 4px;
  padding: 3px 4px;
  border: 1px solid rgba(99, 102, 241, 0.32);
  border-radius: 7px;
  background: rgba(15, 23, 42, 0.62);
  backdrop-filter: blur(8px);
  width: fit-content;
}
.description-editor-wrap:focus-within .description-toolbar {
  display: flex;
}

.desc-fmt-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: 5px;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: background 100ms, color 100ms;
}
.desc-fmt-btn:hover {
  background: rgba(99, 102, 241, 0.28);
  color: #fff;
}
.desc-fmt-btn:active {
  background: rgba(99, 102, 241, 0.42);
  color: #fff;
}

/* Active state for the Emoji button while its popover is open. */
.desc-fmt-btn--active {
  background: rgba(99, 102, 241, 0.32);
  color: #fff;
}

/* Emoji picker popover. Default position: BELOW the toolbar (one line
   below). When the toolbar sits low in the viewport (description
   editor near bottom of screen, or scrolled position), JS flips the
   `--above` modifier so the picker drops upward instead. Categorized
   grid of native glyphs. */
.emoji-picker-wrap {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 240;
  width: 320px;
  max-height: 320px;
  overflow-y: auto;
  padding: 6px 8px 8px;
  border: 1px solid rgba(99, 102, 241, 0.32);
  border-radius: 10px;
  background: rgba(15, 23, 42, 0.92);
  backdrop-filter: blur(12px) saturate(160%);
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.45);
}
.emoji-picker-wrap--above {
  top: auto;
  bottom: calc(100% + 8px);
}
.emoji-picker-group + .emoji-picker-group {
  margin-top: 6px;
}
.emoji-picker-group-label {
  margin: 4px 2px;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(199, 210, 254, 0.7);
}
.emoji-picker-grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 2px;
}
.emoji-picker-cell {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 1 / 1;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: transparent;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  transition: background 80ms;
}
.emoji-picker-cell:hover {
  background: rgba(99, 102, 241, 0.28);
}

/* Inline rich-text styles inside the description editor. The contenteditable
   keeps formatting via execCommand which produces these tag patterns.
   Cursor stays as text inside the editor — link click-through happens
   from the hover tooltip, not the link text itself, so the user can
   place their caret freely to edit linked content. */
.description-editor a {
  color: #a7b4ff;
  text-decoration: underline;
  cursor: text;
}
.description-editor ul,
.description-editor ol {
  margin: 4px 0;
  padding-left: 22px;
}
.description-editor strong,
.description-editor b {
  font-weight: 700;
  color: var(--ink);
}

/* Link hover tooltip — shows the URL above the link, click-through
   opens it in a new tab. Uses portal positioning (set inline). The
   hand cursor signals the click-to-open affordance. */
.link-hover-tooltip {
  position: fixed;
  z-index: 280;
  max-width: 320px;
  padding: 6px 10px;
  border-radius: 6px;
  background: rgba(15, 23, 42, 0.94);
  border: 1px solid rgba(99, 102, 241, 0.35);
  color: #a7b4ff;
  font-size: 0.78rem;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.45);
}
.link-hover-tooltip:hover {
  color: #c7d2fe;
  border-color: rgba(99, 102, 241, 0.6);
}

/* Link right-click menu — same compact glass treatment as other
   contextual menus in the app. */
.link-context-menu {
  position: fixed;
  z-index: 290;
  min-width: 140px;
  padding: 4px;
  border: 1px solid rgba(255, 255, 255, 0.55);
  border-radius: 8px;
  background: rgba(15, 23, 42, 0.86);
  backdrop-filter: blur(20px) saturate(160%);
  box-shadow: var(--shadow-soft);
}
.link-context-menu button {
  display: block;
  width: 100%;
  padding: 6px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--ink);
  text-align: left;
  font-size: 0.84rem;
  cursor: pointer;
}
.link-context-menu button:hover {
  background: rgba(99, 102, 241, 0.28);
}

/* Link dialog — modal-like overlay centered over the workspace.
   Backdrop dims the canvas; clicking outside the dialog dismisses. */
.link-dialog-overlay {
  position: fixed;
  inset: 0;
  z-index: 295;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(15, 23, 42, 0.55);
  backdrop-filter: blur(2px);
}
.link-dialog {
  width: min(420px, 90vw);
  padding: 16px 18px 14px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 12px;
  background: rgba(15, 23, 42, 0.96);
  color: var(--ink);
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.55);
}
.link-dialog-title {
  margin-bottom: 10px;
  font-size: 0.98rem;
  font-weight: 700;
}
.link-dialog-field {
  display: block;
  margin-bottom: 10px;
}
.link-dialog-field span {
  display: block;
  margin-bottom: 4px;
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}
.link-dialog-field input {
  width: 100%;
  padding: 7px 10px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 6px;
  background: rgba(15, 23, 42, 0.6);
  color: var(--ink);
  font-size: 0.88rem;
  outline: 0;
}
.link-dialog-field input:focus {
  border-color: rgba(99, 102, 241, 0.6);
  background: rgba(15, 23, 42, 0.78);
}
.link-dialog-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 6px;
}
.link-dialog-spacer { flex: 1; }
.link-dialog-actions button {
  padding: 7px 12px;
  border-radius: 6px;
  border: 1px solid transparent;
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
}
.link-dialog-test {
  background: rgba(99, 102, 241, 0.18);
  border-color: rgba(99, 102, 241, 0.42);
  color: #c7d2fe;
}
.link-dialog-test:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.link-dialog-test:hover:not(:disabled) {
  background: rgba(99, 102, 241, 0.32);
}
.link-dialog-cancel {
  background: transparent;
  border-color: rgba(255, 255, 255, 0.18);
  color: var(--muted);
}
.link-dialog-cancel:hover {
  background: rgba(255, 255, 255, 0.06);
  color: var(--ink);
}
.link-dialog-save {
  background: rgba(99, 102, 241, 0.85);
  color: #fff;
}
.link-dialog-save:hover {
  background: rgba(99, 102, 241, 1);
}

/* Read-only description display (non-editing). Same typography as the
   contenteditable so switching between modes doesn't reflow. Links
   open in a new tab via the anchor's own click. */
.description-readonly {
  margin-top: 4px;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--muted);
  word-break: break-word;
}
.description-readonly a,
.description-readonly a:link,
.description-readonly a:visited {
  color: #a7b4ff;
  text-decoration: underline;
  cursor: pointer;
}
.description-readonly ul,
.description-readonly ol {
  margin: 4px 0;
  padding-left: 22px;
}

.band-picker,
.rsvp-buttons {
  display: inline-flex;
  align-items: center;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(15, 23, 42, 0.42);
  gap: 3px;
}

.band-button,
.rsvp,
.quiet-button,
.more-button,
.close-button,
.icon-button,
.rail-open,
.send-button {
  border: 0;
  border-radius: 6px;
  min-height: 34px;
  padding: 0 12px;
  background: transparent;
  color: var(--muted);
}

.stage-shell {
  position: relative;
  min-height: 0;
  min-width: 0;
  overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--right-rail);
  grid-template-rows: minmax(0, 1fr) var(--bottom-rail);
  gap: var(--gap);
  transition:
    grid-template-columns 420ms var(--ease-pop),
    grid-template-rows 420ms var(--ease-pop),
    filter 420ms var(--ease-pop),
    opacity 420ms var(--ease-pop),
    transform 420ms var(--ease-pop);
}

.app-shell.options-pinned .stage-shell {
  grid-template-columns: minmax(0, 1fr) var(--right-open) !important;
  transition: none !important;
}

.stage-shell:has(.options-rail:hover:not(.suppressed)),
.stage-shell:has(.options-rail:focus-within:not(.suppressed)),
/* Also expand whenever the rail has the .pinned class. App-side React
   state (setupCommandFocused, context menus, etc.) drives this class
   independent of browser focus, so the rail stays grid-expanded even
   when the user alt-tabs and Chrome drops :focus-within. */
.stage-shell:has(.options-rail.pinned) {
  grid-template-columns: minmax(0, 1fr) var(--right-open);
}

/* Hover-only expansion gets a 200ms delay; pinning is instant. */
.stage-shell:has(.options-rail:hover:not(.suppressed):not(.pinned)) {
  transition-delay: 200ms;
}

.stage-shell:has(.bottom-rail:not(.setup-only):hover:not(.suppressed)),
.app-shell.bottom-pinned:not(.setup-mode) .stage-shell {
  grid-template-rows: minmax(0, 1fr) var(--bottom-open);
}

/* Setup-mode invites tray sizes to its own content in any state.
   Also: in setup mode the right-side OptionsRail is hidden entirely
   (its date entry + chip list moved into TopChrome's right column),
   so the grid drops the right column and the calendar fills the
   full width of the page. */
.app-shell.setup-mode .stage-shell {
  grid-template-columns: minmax(0, 1fr) !important;
  grid-template-rows: minmax(0, 1fr) auto !important;
}

.calendar-canvas {
  grid-column: 1;
  grid-row: 1;
  min-width: 0;
  min-height: 0;
  padding: 14px;
  position: relative;
  overflow: hidden;
  border: 0;
  background: transparent;
  box-shadow: none;
  backdrop-filter: none;
  transition:
    filter 340ms var(--ease-spring),
    opacity 340ms var(--ease-spring),
    transform 340ms var(--ease-spring);
}

.stage-shell:has(.options-rail:hover:not(.suppressed)) .calendar-canvas,
.stage-shell:has(.bottom-rail:hover:not(.suppressed)) .calendar-canvas,
.app-shell.options-pinned .calendar-canvas,
.app-shell.bottom-pinned .calendar-canvas {
  opacity: 0.84;
  filter: blur(0.8px) saturate(0.96);
  transform: scale(0.992);
  transform-origin: top left;
}

.setup-dates-active.options-pinned .calendar-canvas,
.setup-dates-active .stage-shell:has(.options-rail:hover:not(.suppressed)) .calendar-canvas,
.setup-mode.options-pinned .calendar-canvas,
.setup-mode .stage-shell:has(.options-rail:hover:not(.suppressed)) .calendar-canvas,
.poll-mode.options-pinned .calendar-canvas,
.poll-mode .stage-shell:has(.options-rail:hover:not(.suppressed)) .calendar-canvas {
  opacity: 1;
  filter: none;
  transform: none;
}

.setup-dates-active .bottom-rail {
  opacity: 0.46;
  filter: saturate(0.8);
  pointer-events: none;
}

.calendar-panel {
  position: relative;
  height: 100%;
  min-width: 0;
  min-height: 0;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  gap: 10px;
}

/* Calendar overflow chevrons — appear at the top/bottom of the
   calendar viewport in poll mode when the published Option Blocks
   don't all fit on screen. Click scrolls ~70% of viewport height in
   the indicated direction. Sits above .calendar-scroll. */
.calendar-more-btn {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  z-index: 30;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: rgba(15, 23, 42, 0.7);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: var(--ink);
  font-size: 0.72rem;
  font-weight: 720;
  letter-spacing: 0.02em;
  cursor: pointer;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.32);
  transition: transform 160ms ease, background 160ms ease;
}
.calendar-more-btn:hover {
  background: rgba(30, 41, 59, 0.86);
  transform: translateX(-50%) translateY(-1px);
}
.calendar-more-btn--top { top: 36px; }
.calendar-more-btn--bottom { bottom: 6px; }
.calendar-more-btn-label { line-height: 1; }

.tray-title-row,
.sub-tray-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.icon-button:disabled {
  cursor: default;
  opacity: 0.36;
}

.icon-button,
.close-button,
.more-button {
  width: 38px;
  padding: 0;
  display: inline-grid;
  place-items: center;
  background: rgba(15, 23, 42, 0.36);
  border: 1px solid var(--line);
  color: var(--ink);
}

button svg {
  stroke-width: 1.45;
}

.weekday-row,
.calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
}

.weekday-row {
  gap: 12px;
  padding: 0 3px;
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 750;
  text-align: center;
}

.calendar-grid {
  min-height: 0;
  gap: 12px;
  align-content: start;
  grid-auto-rows: var(--calendar-row-min, 168px);
  transition: grid-auto-rows 420ms var(--ease-pop);
  will-change: grid-auto-rows;
}

.calendar-grid-wrap {
  position: relative;
  /* Container query so the day-cell corner radius can scale with the
     actual grid width (and so the cell width, since cells are 1fr × 7).
     Combined with --calendar-row-min, this gives us radius that tracks
     the smaller of cell width / cell height. */
  container-type: inline-size;
  /* Outer cell radius. Scales with cell dimensions:
       - rowMin × 0.1 covers the height-driven case (compressed densities)
       - 1.4cqw ≈ cell-width × 0.1 covers the width-driven case (narrow viewport)
     Whichever produces the smaller value wins, then clamped to [6px, 16px]
     so isolated isolated tiles never look pillish or square-cornered. */
  --day-cell-radius: clamp(6px, min(calc(var(--calendar-row-min, 168px) * 0.1), 1.4cqw), 16px);
  /* Inner radius for the .day-cell::after frame which is inset 4px. */
  --day-cell-inner-radius: max(2px, calc(var(--day-cell-radius) - 4px));
}

.range-overlay-grid {
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* z=2: above tiles (z=1). Background is transparent so tiles and LED dots
     show through via normal alpha compositing. Only the outline/glow is visible. */
  z-index: 2;
  overflow: visible;
}

.calendar-scroll {
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  scroll-padding-top: 42px;
  /* Top padding (24px) leaves room for the floating month-label chips above row 0.
     Side padding (10px) keeps the neon ring outline from clipping at the edges. */
  padding: 24px 10px 8px;
}

.calendar-scroll {
  scrollbar-width: none;
}

.calendar-scroll::-webkit-scrollbar {
  display: none;
}

.calendar-scroll::-webkit-scrollbar-track {
  background: rgba(15, 23, 42, 0.18);
  border-radius: 999px;
}

.calendar-scroll::-webkit-scrollbar-thumb {
  border: 2px solid rgba(15, 23, 42, 0.2);
  border-radius: 999px;
  background: rgba(248, 250, 252, 0.28);
}

.calendar-blank {
  min-height: var(--calendar-row-min, 168px);
  transition: min-height 420ms var(--ease-pop);
}

.calendar-scroll.density-compact .day-cell,
.calendar-scroll.density-condensed .day-cell {
  padding: 8px;
}

.calendar-scroll.density-condensed .day-cell {
  padding: 6px;
  border-radius: 12px;
}

/* Day-number is vertically centered in every cell, regardless of
   density or mode. Centering keeps the day# in a consistent
   relative position as cells shrink — the note text bottom-anchors
   above the cell floor (or vote-spread bar in poll mode) and wraps
   around the centered day# via the JS-driven shape-outside polygon
   in <DayNote>. */
.calendar-scroll .day-cell .day-number {
  position: absolute;
  top: 50%;
  left: 6px;
  z-index: 4;
  pointer-events: none;
  margin-top: 0;
  transform: translateY(-50%);
}

/* Collapsed-week cells in poll mode: tiny height, no padding, just a
   faint divider so the user can still see we're skipping inactive
   weeks. The day number stays for rough orientation. */
.day-cell.collapsed-week {
  min-height: 0;
  padding: 1px 6px;
  font-size: 0.55rem;
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  opacity: 0.32;
  pointer-events: none;
  align-self: stretch;
  overflow: hidden;
}
/* Collapsed-week cells are only ~10-16px tall; the centered day#
   would still fit but disable the translate so it doesn't push
   above the visible cell area. */
.day-cell.collapsed-week .day-number {
  font-size: 0.55rem;
  opacity: 0.7;
  top: 1px;
  left: 4px;
  transform: none;
}
.day-cell.collapsed-week::before,
.day-cell.collapsed-week::after { display: none; }

.day-cell {
  position: relative;
  z-index: 0;
  min-height: 0;
  padding: 11px;
  /* Suppress iOS native long-press callout (Copy / Add to Calendar /
     Share). Our long-press detector dispatches a synthetic
     contextmenu event that our existing handlers consume. */
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  border: 1px solid var(--tile-edge);
  border-radius: var(--day-cell-radius, 16px);
  overflow: visible;
  background: var(--tile-empty);
  backdrop-filter: blur(12px) saturate(140%);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
  color: var(--ink);
  text-align: left;
  outline: 2px solid transparent;
  outline-offset: 0;
  box-shadow:
    var(--tile-highlight),
    var(--tile-floor-shadow);
  transition:
    transform 220ms var(--ease-pop),
    padding 420ms var(--ease-pop),
    border-radius 420ms var(--ease-pop),
    outline-color 180ms ease,
    outline-offset 180ms ease,
    box-shadow 180ms ease,
    background 180ms ease;
}

.day-cell::before,
.day-cell::after {
  content: "";
  position: absolute;
  pointer-events: none;
}

.day-cell::before {
  z-index: 1;
  inset: 0;
  border-radius: inherit;
  border: 1px solid transparent;
  background:
    linear-gradient(135deg, rgba(255, 255, 255, 0.76), rgba(255, 255, 255, 0.1) 28%, rgba(2, 6, 23, 0.42) 100%) border-box;
  mask:
    linear-gradient(#000 0 0) padding-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
  -webkit-mask:
    linear-gradient(#000 0 0) padding-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  opacity: 0.86;
}

.day-cell::after {
  z-index: 4;
  inset: 4px;
  border-radius: var(--day-cell-inner-radius, 12px);
  opacity: 0;
  transition:
    opacity 200ms ease,
    box-shadow 280ms ease,
    left 280ms var(--ease-spring),
    right 280ms var(--ease-spring),
    border-radius 280ms var(--ease-spring);
}

/* ── Floating month label chips ──────────────────────────────────────────────
   Chips are absolutely positioned inside .calendar-grid-wrap; their top is
   driven by JS (repositionChips) so they float at the viewport top while the
   current month is visible, then scroll away with the last row of the month. */
.month-chip-overlay {
  position: absolute;
  inset: 0;
  overflow: visible;
  pointer-events: none;
  z-index: 30;
}

.day-month-label {
  position: absolute;
  /* left is set by JS (repositionChips) to center the chip over the column
     containing the 1st of that month. translateX(-50%) handles centering. */
  top: -18px; /* overridden by JS; fallback keeps chip above row 0 */
  min-height: 34px;
  display: inline-grid;
  place-items: center;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--ink);
  background: rgba(15, 23, 42, 0.62);
  box-shadow: var(--shadow-soft);
  backdrop-filter: blur(20px) saturate(170%);
  font-size: 0.92rem;
  font-weight: 820;
  white-space: nowrap;
  transform: translateX(-50%);
  pointer-events: none;
  transition: opacity 180ms ease;
}
/* Fade out the month label when the currently focused option block
   includes day 1 of that month — the slider chip + tier pill on day 1
   would otherwise sit underneath the label. */
.day-month-label.is-faded {
  opacity: 0.12;
}

.day-cell:hover {
  z-index: 80;
  transform: translateY(var(--lift-y)) scale(var(--lift-scale));
  box-shadow:
    var(--tile-highlight),
    var(--tile-lowlight),
    0 22px 44px rgba(0, 0, 0, 0.34),
    0 6px 14px rgba(0, 0, 0, 0.2);
}

.setup-mode .day-cell:focus:not(:focus-visible) {
  outline-color: transparent;
  outline-offset: 0;
}

.day-cell.active {
  transform: translateY(-6px) scale(1.05);
  outline-color: rgba(167, 228, 211, 0.82);
  outline-offset: 4px;
  box-shadow:
    0 28px 54px rgba(0, 0, 0, 0.42),
    0 0 38px rgba(167, 228, 211, 0.24),
    var(--tile-highlight),
    var(--tile-lowlight);
}

.day-cell.today {
  background:
    radial-gradient(circle at 50% 54%, rgba(99, 102, 241, 0.22), rgba(99, 102, 241, 0.06) 54%, transparent 76%),
    linear-gradient(135deg, rgba(255, 255, 255, 0.12), transparent 38%),
    rgba(15, 23, 42, 0.24);
  border-color: rgba(99, 102, 241, 0.55);
}

.day-cell.muted {
  opacity: 0.58;
  background:
    radial-gradient(circle at 44% 32%, rgba(255, 255, 255, 0.075), transparent 42%),
    rgba(255, 255, 255, 0.035);
  box-shadow:
    var(--tile-highlight),
    var(--tile-lowlight),
    0 9px 18px rgba(2, 6, 23, 0.22);
}

.setup-mode .day-cell.muted.adjacent-selectable {
  opacity: 0.72;
}

.day-cell.empty {
  border-color: var(--tile-edge);
  border-style: solid;
}

.day-cell.best {
  background:
    radial-gradient(circle at 50% 54%, rgba(167, 228, 211, 0.34), rgba(167, 228, 211, 0.08) 44%, transparent 72%),
    var(--tile-empty);
  border-color: var(--tile-edge);
}

.day-cell.works {
  background:
    radial-gradient(circle at 50% 54%, rgba(174, 198, 207, 0.32), rgba(174, 198, 207, 0.08) 44%, transparent 72%),
    var(--tile-empty);
  border-color: var(--tile-edge);
}

.day-cell.maybe {
  background:
    radial-gradient(circle at 50% 54%, rgba(232, 185, 113, 0.34), rgba(232, 185, 113, 0.08) 44%, transparent 72%),
    var(--tile-empty);
  border-color: var(--tile-edge);
  color: var(--ink);
}

.day-cell.no {
  background:
    radial-gradient(circle at 44% 32%, rgba(255, 255, 255, 0.105), transparent 42%),
    var(--tile-empty);
  border-color: var(--tile-edge);
}

.setup-mode .day-cell {
  border-color: var(--tile-edge);
}

/* Transient drag/preview chip — contained outline within the tile */
.setup-mode .day-cell.setup-range-transient {
  --block-color: rgba(148, 163, 184, 0.9);
  transform: translateY(-3px) scale(1.018);
}

.setup-mode .day-cell.setup-range-transient::after {
  opacity: 1;
  left: 4px;
  right: 4px;
  border: 2px solid var(--block-color, var(--rail));
  border-radius: 12px;
  box-shadow:
    0 0 10px color-mix(in srgb, var(--block-color) 60%, transparent),
    inset 0 0 8px color-mix(in srgb, var(--block-color) 20%, transparent);
}

/* Main tile grid sits at z=3 — above the ring (z=2) so month-label pills,
   which live inside this stacking context, render on top of the neon ring.
   Exclude both overlay grids so their position:absolute / z-index declarations
   aren't overridden by this higher-specificity (0,2,0) selector.
   Tiles within committed ranges are transparent, so the ring still shows. */
.calendar-grid-wrap .calendar-grid:not(.range-glass-grid):not(.range-ring-grid):not(.range-vote-grid) {
  position: relative;
  z-index: 3;
}

/* Poll-mode tray — chip stack grouped by tier with horizontal divider
   headers between the four bands (Best / Works / Maybe / No), plus an
   "Not yet voted" section for OptionBlocks the user hasn't ranked.
   The tray IS the user's response display — there's no separate
   "My response" card. */
.poll-stack {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 0 4px;
  overflow-y: auto;
  /* Anchor for absolute-positioned dragged rows so they're placed
     relative to the stack, not the viewport. */
  position: relative;
}
.poll-tier {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.poll-tier-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 2px 2px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
/* Centered tier title with rules on either side. */
.poll-tier-header::before,
.poll-tier-header::after {
  content: "";
  flex: 1;
  height: 1px;
  background: rgba(255, 255, 255, 0.14);
}
/* Tier-colored title text (matches chip colors) so the band identity
   reads even on collapsed/empty sections. */
.poll-tier-best  .poll-tier-header { color: var(--best); }
.poll-tier-works .poll-tier-header { color: var(--works); }
.poll-tier-maybe .poll-tier-header { color: var(--maybe); }
.poll-tier-no    .poll-tier-header { color: var(--no); }
/* Tier chips area mirrors setup-option-list: a 2-col grid where
   each .poll-option-group uses subgrid so the date-chip column
   auto-sizes to the widest chip across the WHOLE tier (time blocks
   under different dates left-align to the same x — same look as
   the setup tray). */
.poll-tier-chips {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 8px;
  row-gap: 8px;
  min-height: 12px;
  align-items: stretch;
}
.poll-tier-chips > * {
  grid-column: 1 / -1;
}
.poll-option-group {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / -1;
  column-gap: 8px;
  row-gap: 4px;
  align-items: center;
}
.poll-chip-header {
  grid-column: 1;
  grid-row: 1;
  align-self: stretch;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 30px;
  padding: 0 4px;
  cursor: grab;
  user-select: none;
  -webkit-user-select: none;
}
.poll-chip-header:active {
  cursor: grabbing;
}
.poll-chip-header.is-dragging:not(.poll-drag-clone) {
  outline: 1px dashed rgba(255, 255, 255, 0.45);
  outline-offset: -1px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.04);
}
.poll-chip-header.is-dragging:not(.poll-drag-clone) > * {
  visibility: hidden;
}
/* Floating group-drag clone — sized like a chip pill with a meta. */
.poll-drag-clone.poll-chip-header {
  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.18);
  background: rgba(15, 23, 42, 0.92);
  border-radius: 8px;
  padding: 4px 8px;
  opacity: 0.97;
}
/* Floating clone for a WHOLE-DATE group drag — replicates the
   tray's .poll-option-group layout (chip header in column 1, time
   blocks auto-flowing in column 2) so the floating ghost looks
   identical to how the group sits when it lands. The clone is
   outside the tray's parent grid so we can't use subgrid; instead
   use an explicit 2-col grid that mirrors the parent. */
.poll-group-clone.poll-drag-clone {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 8px;
  row-gap: 4px;
  align-items: center;
  padding: 6px;
  background: rgba(15, 23, 42, 0.92);
  border-radius: 8px;
  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.18);
  opacity: 0.97;
}
.poll-group-clone .poll-chip-header {
  grid-column: 1;
  grid-row: 1;
  align-self: stretch;
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 30px;
  padding: 0 4px;
}
.poll-group-clone .setup-time-block-row {
  grid-column: 2;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 10px;
  min-height: 30px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.06);
}
/* The tier color rules for .date-chip live under .poll-tier and
   .poll-option-row, so the clone (portaled to body) misses them and
   the chip would render with no fill. Re-apply via the clone scope
   so the floating snapshot looks like the real tray chip. */
.poll-drag-clone .date-chip.vote-best  { background: var(--best);  color: #0f172a; }
.poll-drag-clone .date-chip.vote-works { background: var(--works); color: #0f172a; }
.poll-drag-clone .date-chip.vote-maybe { background: var(--maybe); color: #1a1206; }
.poll-drag-clone .date-chip.vote-no    { background: var(--no);    color: #fff; }
/* Vertical color band on the left of the chip header, matching
   setup-option-row::before. The --block-color is set by the
   setup-band-N class on the chip header element. */
.poll-chip-header::before {
  content: "";
  display: block;
  align-self: stretch;
  flex: 0 0 auto;
  width: 4px;
  min-height: 22px;
  border-radius: 999px;
  background: var(--block-color, #a7e4d3);
  box-shadow: 0 0 16px color-mix(in srgb, var(--block-color, #a7e4d3) 55%, transparent);
}
.poll-option-group > .setup-time-block-row {
  grid-column: 2;
  cursor: grab;
}
/* In poll mode, time-block rows aren't click-to-delete — neutralize
   the yellow setup-hover so the row reads as draggable, not warning. */
.poll-option-group > .setup-time-block-row:hover {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.12);
  box-shadow: none;
}
.poll-option-group > .setup-time-block-row:hover .setup-time-block-label {
  color: #f1f5f9;
  font-weight: 700;
}
.poll-chip-row:hover {
  background: rgba(15, 23, 42, 0.55);
}
/* Override .option-row's 48px so day/range rows match time-block-row height. */
.poll-row.poll-chip-row {
  min-height: 30px;
}
/* Duration label in poll rows: pushed left of the rank — rank claims
   the far-right slot. `margin-left: auto` on dur still right-clusters
   them; the natural gap to the rank reads as "dur, then rank". */
.poll-row .poll-block-dur {
  margin-left: auto;
  margin-right: 0;
}
.poll-block-rank {
  flex: 0 0 auto;
  min-width: 32px;
  text-align: right;
  font-size: 0.74rem;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
  padding-left: 40px;
}
.poll-option-group--solo > .poll-chip-row {
  grid-column: 1 / -1;
}
/* The solo chip-row (day/range option) carries the drag affordance
   itself — chip + meta inline, no separate header. */
.poll-chip-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
  min-height: 30px;
  cursor: grab;
  background: rgba(15, 23, 42, 0.32);
  border-radius: 8px;
}
.poll-chip-row .setup-time-block-dur {
  margin-left: auto;
}
/* Dragged source slot — dashed outline; content hidden because the
   floating clone follows the cursor. Applies to BOTH time-block and
   chip-row variants since they share the .poll-row marker. */
.poll-row.is-dragging:not(.poll-drag-clone) {
  outline: 1px dashed rgba(255, 255, 255, 0.45);
  outline-offset: -1px;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.04);
}
.poll-row.is-dragging:not(.poll-drag-clone) > * {
  visibility: hidden;
}
/* Vote-tier coloring for chips inside the poll tier list. Mirrors
   the previous .poll-option-row .date-chip rules so chips show the
   tier they sit in. */
.poll-tier .date-chip.vote-best  { background: var(--best);  color: #0f172a; }
.poll-tier .date-chip.vote-works { background: var(--works); color: #0f172a; }
.poll-tier .date-chip.vote-maybe { background: var(--maybe); color: #1a1206; }
.poll-tier .date-chip.vote-no    { background: var(--no);    color: #fff; }
.poll-tier-empty {
  /* Match a chip's vertical footprint so when a placeholder
     replaces it during a drag, neighbor tiers don't shift. */
  height: 32px;
  padding: 0 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.78rem;
  color: rgba(226, 232, 240, 0.42);
  font-style: italic;
  text-align: center;
}
.poll-option-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border: 0;
  border-radius: 8px;
  background: rgba(15, 23, 42, 0.32);
  color: var(--ink);
  cursor: grab;
  text-align: left;
  user-select: none;
  -webkit-user-select: none;
  transition: background 140ms ease, transform 140ms ease, box-shadow 140ms ease;
}
.poll-option-row:hover {
  background: rgba(15, 23, 42, 0.55);
  transform: translateX(2px);
}
.poll-option-row:active {
  cursor: grabbing;
}
/* The "source slot" appearance for the dragged row. The row STAYS
   mounted (so pointer capture survives across renders), but its
   content is hidden and its box becomes a dashed placeholder. The
   actual chip lives in the floating clone (.poll-drag-clone) that is
   portaled to <body> and follows the cursor. */
.poll-option-row.is-dragging:not(.poll-drag-clone) {
  background: rgba(255, 255, 255, 0.04);
  outline: 1px dashed rgba(255, 255, 255, 0.45);
  outline-offset: -1px;
  border-radius: 8px;
  cursor: grabbing;
}
.poll-option-row.is-dragging:not(.poll-drag-clone) > * {
  visibility: hidden;
}
/* Suppress the tier-band stripe (rendered via ::before) on the
   dashed source slot — the gap should be a clean dashed outline,
   not a colored rule. Rows carry the .poll-row marker (the older
   .poll-option-row variant is kept for the legacy list styles). */
.poll-option-row.is-dragging:not(.poll-drag-clone)::before,
.poll-row.is-dragging:not(.poll-drag-clone)::before {
  display: none;
}
/* Calendar drag uses the same dashed source-slot appearance as a
   tray drag — content hidden, dashed border. The "lifted chip"
   visual is the floating mirror clone (see .poll-drag-clone), so
   no override needed here. */
/* Tier band that the cursor is currently over while dragging — gives
   the user a clear target for the drop. */
.poll-tier.drop-target {
  background: rgba(255, 255, 255, 0.02);
}
/* Dashed slot — the "where the chip will land" indicator. Matches
   the row height so the visual swap looks deliberate. */
.drop-placeholder {
  /* Match the real time-block-row height (.poll-block-row sets
     min-height: 30px) so the "hole" reads as the size of the chip
     that would land there. */
  height: 30px;
  border-radius: 6px;
  border: 1px dashed rgba(255, 255, 255, 0.45);
  background: rgba(255, 255, 255, 0.04);
}
/* Empty slot left at the source position when the chip is dragged
   into an empty tier (no chip displaces back here). Same height as
   a row but invisible — just reserves space so nothing reflows. */
.drop-row-gap {
  height: 30px;
}
/* Floating clone that follows the cursor during a tray drag. Sits
   above everything and ignores pointer events so we don't capture
   our own drag. */
.poll-drag-clone {
  pointer-events: none;
  z-index: 9999;
  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.18);
  background: rgba(15, 23, 42, 0.92);
  opacity: 0.97;
}
/* Mirror clone (calendar slider drag) — same visual as tray drag.
   The only addition is a smooth top/left transition so it slides
   between tier slots as the cursor crosses tier boundaries on the
   calendar slider. */
.poll-drag-clone.is-mirror-clone {
  transition:
    top 200ms cubic-bezier(0.22, 1, 0.36, 1),
    left 200ms cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes drop-placeholder-in {
  from { opacity: 0; transform: scaleY(0.6); }
  to { opacity: 1; transform: scaleY(1); }
}
/* Removed the per-tier inset box-shadow ring — the dashed-placeholder
   inside the tier already conveys 'drop here' clearly enough. */
/* Drag-handle grip on the left of every row — the visible affordance
   that the row is reorderable. Lucide GripVertical icon. */
.poll-grip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 16px;
  height: 16px;
  color: rgba(199, 210, 254, 0.45);
  cursor: grab;
}
.poll-option-row:hover .poll-grip {
  color: rgba(199, 210, 254, 0.9);
}
.poll-option-row .date-chip {
  flex: 1 1 auto;
  min-width: 0;
}
/* Compact two-column grid inside a poll option: date chip in
   column 1, time-block rows auto-flow in column 2. The first time
   block sits next to the date chip; subsequent blocks indent under
   it. Same structure as setup-mode's setup-option-group. */
.poll-option-row-body {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 8px;
  row-gap: 4px;
  align-items: center;
}
.poll-option-row-body > .date-chip {
  grid-column: 1;
  grid-row: 1;
  align-self: stretch;
  /* Match the time-row height so the chip row and every block
     row are the same height in the tier list. */
  min-height: 30px;
}
.poll-option-row-body > .poll-time-row {
  grid-column: 2;
}
.poll-time-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 10px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.05);
  min-height: 30px;
}
.poll-time-chip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  padding: 1px 7px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.92);
  color: #0f172a;
  font-size: 0.62rem;
  font-weight: 700;
  line-height: 1.5;
  white-space: nowrap;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
}
.poll-time-dur {
  margin-left: auto;
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 720;
  white-space: nowrap;
}
/* (Per-block subgrid layout has been replaced by .poll-option-group
   mirroring setup. Old helper classes intentionally removed.) */
.poll-option-row.focus-glow {
  background: rgba(99, 102, 241, 0.22);
}
.poll-option-row .date-chip {
  display: inline-flex;
  align-items: center;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
  color: #0f172a;
}
.poll-option-row .date-chip.vote-best  { background: var(--best); }
.poll-option-row .date-chip.vote-works { background: var(--works); }
.poll-option-row .date-chip.vote-maybe { background: var(--maybe); color: #1a1206; }
.poll-option-row .date-chip.vote-no    { background: var(--no);    color: #fff; }
.poll-option-row .date-chip.vote-none {
  background: rgba(255, 255, 255, 0.14);
  color: var(--ink);
}
/* Tiny "not yet voted" dot inside the chip. Disappears once the user
   touches the chip (votes or drags it). */
.poll-unvoted-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.55;
  margin-right: 6px;
  flex-shrink: 0;
}

/* Helper subtext above the tier stack explaining the drag gesture —
   needed because there's no "tap to vote" affordance any more,
   votes happen via drag. */
.poll-stack-hint {
  margin: 0 2px 6px;
  font-size: 0.74rem;
  color: var(--muted);
}
.poll-option-row .option-duration {
  flex: 0 0 auto;
  font-size: 0.74rem;
  color: var(--muted);
}

/* Poll-mode vote chip overlay — a tier-colored pill anchored on the
   first segment of an OptionBlock the user has voted on. Sits ABOVE
   the range glass + ring layers so it reads as the block's header. */
.range-vote-grid {
  position: absolute;
  inset: 0;
  z-index: 5;
  pointer-events: none;
}
/* Container for the chip + tier track + vote bar. Sits in the first
   day cell of a block. The chip slides horizontally on its row; the
   track below it marks the four discrete stops (No | Maybe | Works |
   Best, left-to-right); the vote bar at the bottom shows aggregate
   invitee responses. */
.range-vote-slot {
  position: relative;
  align-self: stretch;
  justify-self: stretch;
  margin: 4px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-height: 0;
  /* Slot itself is pass-through; only the chip captures pointer
     events so the underlying day cell can still receive clicks
     (cycle-on-click) outside the chip. */
  pointer-events: none;
}
/* Vote spread anchored at the BOTTOM of the cell. Day-note (in poll
   mode) sits ABOVE the spread so the personal note remains the most
   readable element and the aggregate count is the small bottom-edge
   summary. */
.range-vote-spread {
  margin-top: auto;
  margin-bottom: 4px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.range-vote-spread-label {
  font-size: 0.5rem;
  color: rgba(226, 232, 240, 0.55);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* No-note: spread can sit even closer to bottom edge. */
.range-vote-slot.no-note .range-vote-spread {
  margin-bottom: 4px;
}
.range-vote-slot .range-vote-chip {
  pointer-events: auto;
}
/* Small ordinal badge ("1st", "2nd", …) anchored at the top-left of
   the slot — it shows the block's global rank across the whole tray
   stack. Sits ABOVE the slider chip so it stays visible even when
   the chip is parked at the "No" tier left edge. */
/* Poll-mode "See other votes" toggle in the right tray's title row.
   A small inline checkbox that gates whether the per-block vote-
   spread bar appears on calendar blocks. Off by default. */
.see-other-votes-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.72rem;
  color: var(--muted);
  cursor: pointer;
  user-select: none;
}
.see-other-votes-toggle input[type="checkbox"] {
  cursor: pointer;
  accent-color: var(--indigo, #6366f1);
}

.range-rank-badge {
  position: absolute;
  /* Pulled up and well to the left so it sits in the empty space
     outside the block's neon ring at the top-left corner, just
     overlapping the ring slightly — out of the way of the slider
     chip, day-number, and vote-spread bar. */
  top: -12px;
  left: -22px;
  z-index: 9;
  padding: 1px 6px;
  border-radius: 999px;
  background: rgba(15, 23, 42, 0.82);
  border: 1px solid rgba(255, 255, 255, 0.22);
  color: var(--ink);
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  line-height: 1;
  pointer-events: none;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.6);
}
.range-vote-row {
  position: relative;
  height: 18px;
}
/* Bullet character rendered INSIDE the chip ('• MAYBE') for ranges
   the user hasn't explicitly voted on yet. Renders inline so it
   travels with the chip as it slides between tier stops. */
.range-vote-bullet {
  display: inline-block;
  font-size: 1.1em;
  line-height: 0.5;
  margin-right: 1px;
}
.range-vote-chip {
  position: absolute;
  top: 0;
  /* `left` is set per-tier below. translateX(-50%) centers the chip
     on the corresponding stop. min-width keeps the box dimensions
     stable across tier changes so nothing reflows mid-slide. */
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 3px 9px;
  min-width: 54px;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #0f172a;
  white-space: nowrap;
  cursor: grab;
  user-select: none;
  touch-action: none;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(0, 0, 0, 0.18);
  transition:
    left 260ms var(--ease-pop),
    background-color 260ms var(--ease-pop),
    color 260ms var(--ease-pop);
  will-change: left, background-color;
}
.range-vote-chip.dragging {
  cursor: grabbing;
  transition: none;
  z-index: 6;
}
.range-vote-chip.vote-best  { background: var(--best);  color: #0f172a; }
.range-vote-chip.vote-works { background: var(--works); color: #0f172a; }
.range-vote-chip.vote-maybe { background: var(--maybe); color: #1a1206; }
.range-vote-chip.vote-no    { background: var(--no);    color: #fff;    }
/* Spatial axis: No (left) | Maybe | Works | Best (right). The chip
   is centered on each stop via translateX(-50%). 12.5%/37.5%/62.5%/87.5%
   are the centerlines for four equal segments across the cell width. */
.range-vote-slot[data-tier="no"]    .range-vote-chip { left: 12.5%; }
.range-vote-slot[data-tier="maybe"] .range-vote-chip { left: 37.5%; }
.range-vote-slot[data-tier="works"] .range-vote-chip { left: 62.5%; }
.range-vote-slot[data-tier="best"]  .range-vote-chip { left: 87.5%; }
.range-vote-slot.untouched .range-vote-chip { opacity: 0.78; }
/* Slider track sits below the chip. Five vertical separator lines
   at 0/25/50/75/100% mark the four tier zones (No|Maybe|Works|Best),
   and one dot sits between them at the chip's continuous rank
   position — never on a line, always between two of them. */
.range-vote-track {
  position: relative;
  height: 8px;
  pointer-events: none;
}
/* Faint horizontal rule running across the track between the lines. */
.range-vote-track::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  background: rgba(255, 255, 255, 0.22);
  transform: translateY(-50%);
}
.range-vote-line {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: rgba(255, 255, 255, 0.45);
  transform: translateX(-50%);
}
.range-vote-dot {
  position: absolute;
  top: 50%;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.18);
  transform: translate(-50%, -50%);
  transition: left 220ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* Aggregate-vote bar — segmented horizontal stack of tier-colored
   blocks proportional to the invitee count. The trailing muted
   segment represents people who haven't responded yet. */
.range-vote-bar {
  display: flex;
  width: 100%;
  height: 5px;
  border-radius: 3px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.06);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.25);
}
.range-vote-bar .vb-seg {
  display: block;
  height: 100%;
  flex-basis: 0;
}
.range-vote-bar .vb-best  { background: var(--best); }
.range-vote-bar .vb-works { background: var(--works); }
.range-vote-bar .vb-maybe { background: var(--maybe); }
.range-vote-bar .vb-no    { background: var(--no); }
.range-vote-bar .vb-none  { background: rgba(255, 255, 255, 0.18); }

/* Poll-mode cell filters.
   .poll-disabled — date-only poll: cells outside the published Option-
                    Blocks. Layout preserved, but visually faded and
                    non-interactive (no hover lift, no click).
   .poll-hidden   — time-mode poll: cells without time blocks. Hidden
                    via visibility so the 7-column grid keeps its
                    shape but the cell renders nothing visibly. */
.day-cell.poll-disabled {
  opacity: 0.32;
  cursor: default;
  pointer-events: none;
}
.day-cell.poll-disabled:hover {
  transform: none;
  box-shadow: var(--tile-highlight), var(--tile-floor-shadow);
}
.day-cell.poll-hidden {
  /* In time-mode poll, cells without time blocks aren't voting
     targets — but the calendar structure should still be readable.
     Fade them like .poll-disabled (date-only poll's non-options)
     instead of fully hiding so the user can orient by month / week. */
  opacity: 0.32;
  pointer-events: none;
}
.day-cell.poll-hidden:hover {
  transform: none;
  box-shadow: var(--tile-highlight), var(--tile-floor-shadow);
}

/* ── z=0: Unified glass block ──────────────────────────────────────────────
   One frosted panel per row-segment of each committed range, sitting
   BEHIND the transparent tile shells.  backdrop-filter blurs the ambient
   background behind it, giving a genuine glass look. */
.range-glass-grid {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  overflow: visible;
}

.range-glass-block {
  backdrop-filter: blur(10px) saturate(130%);
  -webkit-backdrop-filter: blur(10px) saturate(130%);
  background: color-mix(in srgb, var(--band-color, rgba(148,163,184)) 10%, rgba(255,255,255,0.06));
  border: 1px solid color-mix(in srgb, var(--band-color, rgba(255,255,255,0.2)) 25%, rgba(255,255,255,0.06));
  box-shadow:
    inset 0 0 40px color-mix(in srgb, var(--band-color) 12%, transparent),
    0 10px 30px rgba(0,0,0,0.22);
  transition:
    opacity 180ms ease,
    box-shadow 280ms ease,
    transform 220ms ease;
}

/* ── z=2: Neon ring overlay ────────────────────────────────────────────────
   Outline-only, fully transparent — sits above tiles so it's always visible.
   Each band gets a different outline-offset so overlapping block rings are
   clearly separated (band 0 at 3px, band 1 at 7px, etc.). */
.range-ring-grid {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
  overflow: visible;
}

.range-ring-block {
  background: transparent;
  outline: 2px solid color-mix(in srgb, var(--band-color, rgba(148, 163, 184, 0.9)) 78%, transparent);
  /* Flush by default — the ring sits ~1px outside the cell. The staggered
     3px/7px depths only apply via .has-neighbor (set in main.jsx) when an
     adjacent range would otherwise collide. */
  outline-offset: 1px;
  /* Subtle resting glow — kept low so newly added or unfocused ranges don't
     read as "super-highlighted." The .overlay-lit state below provides the
     bright accent on hover/focus. */
  box-shadow:
    0 0 6px color-mix(in srgb, var(--band-color) 42%, transparent),
    0 0 14px color-mix(in srgb, var(--band-color) 18%, transparent);
  transition:
    opacity 180ms ease,
    outline-color 220ms ease,
    outline-offset 220ms ease,
    box-shadow 280ms ease,
    transform 220ms ease;
}

/* Shared band colors — 8-color palette for better differentiation with many blocks */
/* Band classes alias the central palette tokens. To recolor the whole
   8-color cycle (calendar ranges, TimePicker blocks, LED dots, tray
   chips), override --band-0..--band-7 at :root — these classes pick
   the swap up automatically. */
.overlay-band-0 { --band-color: var(--band-0); }
.overlay-band-1 { --band-color: var(--band-1); }
.overlay-band-2 { --band-color: var(--band-2); }
.overlay-band-3 { --band-color: var(--band-3); }
.overlay-band-4 { --band-color: var(--band-4); }
.overlay-band-5 { --band-color: var(--band-5); }
.overlay-band-6 { --band-color: var(--band-6); }
.overlay-band-7 { --band-color: var(--band-7); }
/* Poll-mode tier coloring on the calendar overlays. The vote-{tier}
   class is added in poll mode only (see effectiveTierOf), so the
   range glass + ring inherit the tier color via --band-color. */
.range-glass-block.vote-best,  .range-ring-block.vote-best  { --band-color: var(--best); }
.range-glass-block.vote-works, .range-ring-block.vote-works { --band-color: var(--works); }
.range-glass-block.vote-maybe, .range-ring-block.vote-maybe { --band-color: var(--maybe); }
.range-glass-block.vote-no,    .range-ring-block.vote-no    { --band-color: var(--no); }

/* Outline-offset is set inline per segment in main.jsx based on the
   range's depth in the per-cell stack. Ranges that don't share any
   cell with another range stay flush at the base 1px; each layer of
   stacking pushes its ring out one step (4px) so adjacent rings clear
   each other without crowding. Smaller ranges sit innermost, larger
   ranges outermost — see depthByKey computation. */

/* ── C-shape ring clipping for Sat→Sun row-spanning ranges ────────────────
   When a range segment ends at Saturday (col 8) and the range continues on
   the next row, we hide the right edge so there's no visual cap — the ring
   becomes an open "C" on the right (⌐).  Symmetrically, the Sunday segment
   that picks up the range hides its left edge (¬ shape).  Large negative inset
   values let the other three sides overflow freely. */
.range-ring-block.ring-connects-right {
  clip-path: inset(-120px 0 -120px -120px);
}
.range-ring-block.ring-connects-left {
  clip-path: inset(-120px -120px -120px 0);
}

/* Hover-focus states — unified treatment shared by both calendar-cell
   hover and tray-chip hover. The ring and glass blocks lift in unison
   with the day cells (.setup-range-focused above) so the entire range
   reads as a single floating object, and a soft glow eases in then back
   out to the resting shadow. Symmetric resting → subtle-peak → resting
   keyframes (with `ease-in-out`) give a slow breath rather than a flash. */
.range-glass-block.overlay-lit,
.range-ring-block.overlay-lit {
  transform: translateY(var(--lift-y)) scale(var(--lift-scale));
}
.range-ring-block.overlay-lit {
  outline-width: 2.5px;
  animation: range-ring-pulse 1400ms ease-in-out forwards;
}
.range-glass-block.overlay-lit {
  animation: range-glass-pulse 1400ms ease-in-out forwards;
}

@keyframes range-ring-pulse {
  0% {
    box-shadow:
      0 0 6px color-mix(in srgb, var(--band-color) 42%, transparent),
      0 0 14px color-mix(in srgb, var(--band-color) 18%, transparent);
  }
  40% {
    box-shadow:
      0 0 12px color-mix(in srgb, var(--band-color) 70%, transparent),
      0 0 26px color-mix(in srgb, var(--band-color) 38%, transparent),
      0 0 46px color-mix(in srgb, var(--band-color) 18%, transparent);
  }
  100% {
    box-shadow:
      0 0 6px color-mix(in srgb, var(--band-color) 42%, transparent),
      0 0 14px color-mix(in srgb, var(--band-color) 18%, transparent);
  }
}

@keyframes range-glass-pulse {
  0% {
    box-shadow:
      inset 0 0 40px color-mix(in srgb, var(--band-color) 12%, transparent),
      0 10px 30px rgba(0, 0, 0, 0.22);
  }
  40% {
    box-shadow:
      inset 0 0 50px color-mix(in srgb, var(--band-color) 16%, transparent),
      0 14px 36px rgba(0, 0, 0, 0.28);
  }
  100% {
    box-shadow:
      inset 0 0 40px color-mix(in srgb, var(--band-color) 12%, transparent),
      0 10px 30px rgba(0, 0, 0, 0.22);
  }
}

/* No-op: previously dimmed non-focused ranges; users want every range
   to remain fully visible while one is highlighted. Kept the class so
   existing JSX doesn't need to change. */
.range-glass-block.overlay-dim,
.range-ring-block.overlay-dim {
  /* intentionally unstyled */
}

/* ── Transparent tile shells ───────────────────────────────────────────────
   Tiles within a committed range become invisible containers — their glass
   appearance comes entirely from the z=0 range-glass-block behind them.
   Date numbers and LED dots remain visible at z=1 / z=12. */
.day-cell.setup-range-start,
.day-cell.setup-range-middle,
.day-cell.setup-range-end,
.day-cell.setup-range-single {
  background: transparent !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  border-color: transparent !important;
  box-shadow: none !important;
}

/* Hide the ::before gradient border (the highlight/shadow bevel) on committed range tiles.
   Without this, the bevel gradient still renders even with a transparent background. */
.day-cell.setup-range-start::before,
.day-cell.setup-range-middle::before,
.day-cell.setup-range-end::before,
.day-cell.setup-range-single::before {
  opacity: 0 !important;
}

/* Per-day hover lift is suppressed inside committed ranges — the whole
   range lifts together via .setup-range-focused (driven by hover over
   any cell in the range) instead. Without this, hovering a single day
   in poll mode would visually break the range out of the glass block. */
.day-cell.setup-range-start:hover,
.day-cell.setup-range-middle:hover,
.day-cell.setup-range-end:hover,
.day-cell.setup-range-single:hover {
  transform: none;
  box-shadow: var(--tile-highlight), var(--tile-lowlight);
}

/* LED dot indicators — one per range on each day in setup mode */
.led-dot-row {
  position: absolute;
  top: 7px;
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  gap: 4px;
  pointer-events: none;
  z-index: 12;
}

.led-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--led-color, rgba(148, 163, 184, 0.9));
  box-shadow:
    0 0 5px var(--led-color, rgba(148, 163, 184, 0.9)),
    0 0 12px color-mix(in srgb, var(--led-color, rgba(148, 163, 184, 0.9)) 65%, transparent);
  opacity: 0.9;
  transition:
    opacity 160ms ease,
    box-shadow 160ms ease,
    transform 160ms ease;
}

.led-band-0 { --led-color: var(--band-0); }
.led-band-1 { --led-color: var(--band-1); }
.led-band-2 { --led-color: var(--band-2); }
.led-band-3 { --led-color: var(--band-3); }
.led-band-4 { --led-color: var(--band-4); }
.led-band-5 { --led-color: var(--band-5); }
.led-band-6 { --led-color: var(--band-6); }
.led-band-7 { --led-color: var(--band-7); }

.led-dot.led-dim {
  opacity: 0.12;
  filter: saturate(0.4);
  box-shadow: none;
}

.led-dot.led-lit {
  opacity: 1;
  transform: scale(1.25);
  box-shadow:
    0 0 8px var(--led-color),
    0 0 18px color-mix(in srgb, var(--led-color) 70%, transparent),
    0 0 32px color-mix(in srgb, var(--led-color) 40%, transparent);
}

.day-number {
  position: relative;
  z-index: 12;
}

.range-label,
.consensus-badge,
.blocker-badge,
.my-marker {
  position: relative;
  z-index: 6;
}

/* Edge resize affordances. A thin slice on the leftmost / rightmost
   ~9px of a cell that hosts an actual range extreme. Cursor + hover
   hint live here; the existing range outline pulses (via focusedSetup
   Option) so the user sees which range they're about to grab. The
   z-index sits above the range glass + ring (which are z=0 and z=2)
   so the cursor hits the resize zone first. */
.day-cell-resize-edge {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 9px;
  z-index: 8;
  cursor: col-resize;
  /* Match the cell's outer corner radius so the affordance hugs the
     rounded corners of the underlying range tile. */
  border-radius: var(--day-cell-radius, 16px);
}
.day-cell-resize-edge--left {
  left: 0;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.day-cell-resize-edge--right {
  right: 0;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}
/* Soft tint on hover so the affordance is discoverable but doesn't
   obscure the range it's resizing. Pairs with the focused-option pulse
   on the range itself for a clear "this is the edge" cue. */
.day-cell-resize-edge:hover {
  background: rgba(255, 255, 255, 0.06);
}


.day-number {
  color: var(--ink);
  font-weight: 600;
}

.today-marker {
  position: absolute;
  z-index: 6;
  right: 9px;
  top: 9px;
  min-height: 22px;
  display: inline-grid;
  place-items: center;
  padding: 0 8px;
  border-radius: 999px;
  color: white;
  background: rgba(99, 102, 241, 0.76);
  border: 1px solid rgba(224, 231, 255, 0.72);
  box-shadow: 0 0 16px rgba(99, 102, 241, 0.44);
  font-size: 0.7rem;
  font-weight: 840;
}

/* Day-note: the note's TOP aligns with the day-number's top (4px in
   setup, 32px in poll) so the first line of text appears on the same
   row as the day# — `text-indent` on line 1 pushes that first line
   past the day# column, and subsequent lines wrap back to the left
   edge of the cell, beneath the day#. This is the word-processor
   "wrap around the inline image" behaviour the user asked for. We
   use `display: -webkit-box` + `-webkit-line-clamp` so the trailing
   `…` ellipsis appears cleanly when text overflows. */
/* Day-note: the JSX-side <DayNote> component computes `top` and a
   floated `shape-outside` polygon at runtime so the text bottom-
   anchors above the bar (or cell floor), wraps around the day# if
   it grows into the day#'s row, and clamps at the slider top. All
   geometry lives in inline styles set by the component; the CSS
   here just supplies typography + overflow defaults. */
.day-note,
.day-note-editor {
  position: absolute;
  z-index: 12;
  left: 6px;
  right: 6px;
  color: var(--ink);
  font-size: 0.72rem;
  font-weight: 720;
  line-height: 1.3;
  display: block;
  text-align: left;
  white-space: normal;
  word-break: break-word;
  overflow: hidden;
}

/* Note-editor uses the same `top`/`bottom` defaults as the static
   note: top-anchored at the day# row, bottom above the bar/floor.
   The editor is rendered while editing only, so it doesn't go
   through the <DayNote> measurement path. */
.day-note-editor {
  top: 40px;
  bottom: 6px;
}
.poll-mode .day-cell .day-note-editor {
  bottom: 28px;
}

.calendar-scroll.density-compact .day-cell .day-note {
  font-size: 0.66rem;
}
.calendar-scroll.density-condensed .day-cell .day-note {
  font-size: 0.58rem;
}

.day-note-editor {
  min-height: 38px;
  resize: none;
  border: 1px solid rgba(99, 102, 241, 0.55);
  border-radius: 8px;
  padding: 4px 8px;
  background: rgba(2, 6, 23, 0.92);
  outline: none;
  font: inherit;
  text-align: center;
  /* Auto-grow with content; no internal scrollbars. JS in the
     onChange/auto-size handler updates inline height to scrollHeight
     so the box just expands instead of clipping with a scrollbar. */
  overflow: hidden;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.day-note-editor::-webkit-scrollbar { display: none; }

.day-time-chips {
  position: absolute;
  z-index: 7;
  top: 6px;
  left: 36px;
  right: 4px;
  bottom: 4px;
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  gap: 1px;
  justify-content: flex-start;
  align-items: flex-start;
  pointer-events: none;
  overflow: hidden;
  contain: paint;
}

/* When the day has a note, leave room for the 2-line note at the bottom. */
.day-cell:has(.day-note) .day-time-chips,
.day-cell:has(.day-note-editor) .day-time-chips {
  bottom: 36px;
}

/* Inline-flex wrapper that keeps a time chip and its tier-name label
   glued together as one item — prevents the label from wrapping below
   the chip when the day-cell column is narrow. The .day-time-chips
   column flex still wraps BETWEEN pairs as needed. */
.day-time-chip-pair {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
  max-width: 100%;
}

.day-time-chip {
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  padding: 0 5px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.92);
  color: #0f172a;
  font-size: 0.62rem;
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: 0;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
  white-space: nowrap;
  width: max-content;
  max-width: none;
  flex-shrink: 0;
  /* Re-enable pointer events on the chip itself — the parent
     .day-time-chips wrapper has pointer-events: none so clicks
     fall through to the day cell, but each chip individually
     needs to receive hover for the cross-surface time-block
     highlighting. */
  pointer-events: auto;
  cursor: pointer;
  transition: background 140ms ease, box-shadow 140ms ease, transform 140ms ease;
}
/* Outside of setup mode the chips aren't deletable; keep them
   informational with the default cursor and no hover lift. */
.day-time-chip.is-readonly {
  cursor: default;
}
/* Poll-mode tier coloring: the calendar's per-block chip background
   reflects the priority the user has voted that block into. Set only
   when voteTierForBlock supplied a class (poll mode); setup mode keeps
   the neutral white pill. */
.day-time-chip.vote-best  { background: var(--best);  color: #0f172a; }
.day-time-chip.vote-works { background: var(--works); color: #0f172a; }
.day-time-chip.vote-maybe { background: var(--maybe); color: #1a1206; }
.day-time-chip.vote-no    { background: var(--no);    color: #fff; }
/* Short tier-name label rendered to the right of each calendar chip
   AFTER the user has touched it (untouched blocks keep their maybe
   color but stay label-less so the calendar reads quiet at rest). */
.day-time-tier-label {
  display: inline-flex;
  align-items: center;
  padding: 0 4px;
  border-radius: 3px;
  font-size: 0.58rem;
  font-weight: 720;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1.5;
  white-space: nowrap;
  pointer-events: none;
}
.day-time-tier-label.vote-best  { color: var(--best); }
.day-time-tier-label.vote-works { color: var(--works); }
.day-time-tier-label.vote-maybe { color: var(--maybe); }
.day-time-tier-label.vote-no    { color: var(--no); }
/* Tray-row counterpart — same word, lives between the chip and the
   duration. Slightly larger than the calendar pill since the tray has
   the real estate. */
.poll-tier-label {
  flex: 0 0 auto;
  margin-left: 6px;
  padding: 0 4px;
  border-radius: 3px;
  font-size: 0.62rem;
  font-weight: 720;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1.5;
  white-space: nowrap;
}
.poll-tier-label.vote-best  { color: var(--best); }
.poll-tier-label.vote-works { color: var(--works); }
.poll-tier-label.vote-maybe { color: var(--maybe); }
.poll-tier-label.vote-no    { color: var(--no); }

/* Long-press tier slider — matches the full-day-mode
   .range-vote-chip / .range-vote-track pattern: a tier-colored pill
   that slides between 4 stops above a thin track marked by 5
   vertical lines, with a dot indicator on the track at the chip's
   position. Driven by document-level pointer listeners (see
   TierSliderOverlay), so this layout itself is pointer-events: none. */
.tier-slider {
  z-index: 11000;
  padding: 8px 12px 10px;
  border-radius: 12px;
  background: rgba(15, 23, 42, 0.92);
  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(255, 255, 255, 0.18);
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}
.tier-slider-row {
  position: relative;
  height: 22px;
}
.tier-slider-chip {
  position: absolute;
  top: 0;
  /* left is set inline per stop; translateX(-50%) centers on stop. */
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 3px 9px;
  min-width: 60px;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #0f172a;
  white-space: nowrap;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(0, 0, 0, 0.18);
  transition:
    left 220ms var(--ease-pop),
    background-color 220ms var(--ease-pop),
    color 220ms var(--ease-pop);
  will-change: left, background-color;
}
.tier-slider-chip.vote-best  { background: var(--best);  color: #0f172a; }
.tier-slider-chip.vote-works { background: var(--works); color: #0f172a; }
.tier-slider-chip.vote-maybe { background: var(--maybe); color: #1a1206; }
.tier-slider-chip.vote-no    { background: var(--no);    color: #fff; }
.tier-slider-track {
  position: relative;
  margin-top: 6px;
  height: 8px;
  pointer-events: none;
}
.tier-slider-track::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  background: rgba(255, 255, 255, 0.22);
  transform: translateY(-50%);
}
.tier-slider-line {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: rgba(255, 255, 255, 0.45);
  transform: translateX(-50%);
}
.tier-slider-dot {
  position: absolute;
  top: 50%;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.18);
  transform: translate(-50%, -50%);
  transition: left 220ms cubic-bezier(0.22, 1, 0.36, 1);
}
.day-time-chip.is-readonly.is-hovered {
  transform: none;
}
.day-time-chip.is-hovered {
  background: linear-gradient(180deg, #fde68a, #fbbf24);
  box-shadow:
    0 0 0 2px rgba(250, 204, 21, 0.85),
    0 4px 14px rgba(250, 204, 21, 0.45);
  transform: translateY(-1px);
  z-index: 2;
}

/* Size tiers no longer change font; chips are uniform size right of day#. */
.day-time-chips.tp-chip-lg .day-time-chip,
.day-time-chips.tp-chip-md .day-time-chip,
.day-time-chips.tp-chip-sm .day-time-chip { font-size: 0.62rem; padding: 0 5px; line-height: 1.5; }

.day-time-chip-overflow {
  background: rgba(255, 255, 255, 0.6);
  color: rgba(15, 23, 42, 0.85);
  font-size: 0.62rem !important;
  padding: 0 6px !important;
  letter-spacing: 0.06em;
}

/* Hidden measurement layer for chip-fit calculation. */
.day-time-chips.day-time-chips-measure {
  visibility: hidden;
  pointer-events: none;
}

/* Ellipsis indicator overlaid where the next chip would sit. Plain
   left-aligned text — no chip background — so the column reads as
   "the list continues here" rather than as another pill. */
.day-time-chips-ellipsis {
  position: absolute;
  z-index: 10;
  left: 36px;
  right: 4px;
  bottom: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
  padding: 0;
  background: transparent;
  color: rgba(226, 232, 240, 0.95);
  font-size: 0.78rem;
  font-weight: 800;
  line-height: 1.4;
  pointer-events: none;
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}

.day-time-chips-ellipsis-dots {
  letter-spacing: 0.18em;
  font-weight: 900;
}

.day-time-chips-ellipsis-count {
  font-size: 0.62rem;
  color: rgba(199, 210, 254, 0.95);
  letter-spacing: 0.04em;
  font-weight: 700;
}

/* When a note is present the ellipsis chip sits just above it. */
.day-cell:has(.day-note) .day-time-chips-ellipsis,
.day-cell:has(.day-note-editor) .day-time-chips-ellipsis {
  bottom: 38px;
}

/* Popover layout: list of chips styled like the day-cell chips so the
   user gets the same color/format treatment in the hover. */
.day-time-chips-popover-list {
  display: flex;
  flex-direction: column;
  gap: 3px;
  align-items: stretch;
}

.day-time-chips-popover-chip {
  font-size: 0.72rem;
  padding: 1px 6px;
  width: 100%;
  justify-content: flex-start;
}

.day-time-chip-overnight {
  background: linear-gradient(90deg, rgba(255, 255, 255, 0.92) 0%, rgba(255, 255, 255, 0.92) 50%, rgba(99, 102, 241, 0.78) 50%, rgba(99, 102, 241, 0.78) 100%);
  color: #0f172a;
}

.day-time-chips-popover-row.overnight {
  color: #c7d2fe;
}

/* Popover is now a direct child of .day-cell (sibling of .day-time-chips)
   so it isn't clipped by the chip wrap's overflow:hidden. */
.day-time-chips-popover {
  position: absolute;
  top: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  display: none;
  flex-direction: column;
  gap: 3px;
  padding: 8px 10px;
  border-radius: 8px;
  background: rgba(2, 6, 23, 0.96);
  border: 1px solid rgba(255, 255, 255, 0.16);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.55);
  z-index: 200;
  min-width: max-content;
  max-height: 280px;
  overflow-y: auto;
  pointer-events: none;
}

.setup-status-warn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: 6px;
  padding: 0 6px;
  height: 18px;
  border-radius: 9px;
  background: rgba(245, 158, 11, 0.85);
  color: #1a1206;
  font-size: 0.75rem;
  font-weight: 800;
  line-height: 1;
}

/* Setup date textarea placeholder: italic so the prompt reads as
   a hint, not literal sample input. */
#setup-command::placeholder {
  font-style: italic;
  opacity: 0.7;
}

.setup-command-times {
  flex-basis: 100%;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.setup-command-times-row {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.78rem;
  color: rgba(226, 232, 240, 0.85);
}

.setup-command-times-day {
  font-weight: 700;
  color: #f1f5f9;
}

.setup-command-times-list {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 3px;
}

.setup-command-times-chip {
  display: inline-flex;
  align-items: center;
  padding: 1px 6px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.92);
  color: #0f172a;
  font-size: 0.72rem;
  font-weight: 700;
  white-space: nowrap;
}

.setup-command-warn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: 8px;
  padding: 0 6px;
  height: 18px;
  border-radius: 9px;
  background: rgba(99, 102, 241, 0.85);
  color: #f1f5f9;
  font-size: 0.75rem;
  font-weight: 800;
  line-height: 1;
}

.day-time-warning {
  position: absolute;
  top: 4px;
  right: 6px;
  z-index: 8;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: rgba(245, 158, 11, 0.85);
  color: #1a1206;
  font-size: 0.65rem;
  font-weight: 800;
  line-height: 1;
  pointer-events: none;
  box-shadow: 0 0 0 1px rgba(245, 158, 11, 0.4);
}

.day-add-times-btn {
  position: absolute;
  z-index: 9;
  bottom: 6px;
  left: 50%;
  transform: translateX(-50%);
  display: none;
  padding: 4px 10px;
  border-radius: 6px;
  border: 1px solid rgba(99, 102, 241, 0.55);
  background: rgba(99, 102, 241, 0.34);
  color: #e0e7ff;
  font-size: 0.65rem;
  font-weight: 720;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
  backdrop-filter: blur(8px);
  transition:
    background 140ms ease,
    border-color 140ms ease,
    color 140ms ease,
    box-shadow 180ms ease,
    transform 140ms ease;
}

.day-add-times-btn:hover {
  background: rgba(129, 140, 248, 0.85);
  border-color: rgba(199, 210, 254, 0.95);
  color: #ffffff;
  box-shadow:
    0 8px 22px rgba(99, 102, 241, 0.5),
    0 0 0 1px rgba(199, 210, 254, 0.4) inset;
  transform: translateX(-50%) translateY(-1px) scale(1.04);
}
.day-add-times-btn:active {
  transform: translateX(-50%) translateY(0) scale(1.0);
}

.day-cell:hover .day-add-times-btn,
.day-add-times-btn.always-visible {
  display: inline-flex;
}

.day-time-chips-popover-row {
  font-size: 0.72rem;
  font-weight: 600;
  color: #e2e8f0;
  white-space: nowrap;
  letter-spacing: 0;
  text-align: center;
}

/* "Tip Me" floating button — sits above the bottom invitations rail.
   --tip-bottom is set dynamically by JS in index.html based on the
   actual rail height (changes for hover-expand, pin-expand, and setup
   mode's auto sizing). Falls back to --bottom-rail when JS hasn't run. */
/* Icon-rail variant — circular tip jar at the bottom of the left
   icon rail, sitting above the bottom invitations rail. --tip-me-bottom
   is JS-driven (index.html) so the chip stays clear of the bottom
   rail as it hover/pin/setup-expands. */
.tip-me-button {
  position: fixed;
  z-index: 1000;
  /* Centered in the 56px left rail. */
  left: 12px;
  bottom: calc(var(--tip-me-bottom, 120px) + 8px);
  width: 32px;
  height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: #f45d22;
  color: #fff;
  text-decoration: none;
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow:
    0 6px 18px rgba(244, 93, 34, 0.35),
    0 2px 6px rgba(0, 0, 0, 0.25);
  transition:
    transform 180ms ease,
    box-shadow 180ms ease,
    background 180ms ease;
}

.tip-me-button:hover {
  background: #ff6a30;
  transform: translateY(-1px);
  box-shadow:
    0 10px 26px rgba(244, 93, 34, 0.45),
    0 3px 8px rgba(0, 0, 0, 0.3);
}

.tip-me-button-icon {
  font-size: 1rem;
  line-height: 1;
}

/* The "Tip Me" word inside the chip — hidden in the icon-rail
   variant; the full label reads as the data-tooltip on hover. */
.tip-me-button-label {
  display: none;
}

/* (Bottom now driven by --tip-bottom set in index.html, which already
   reflects pinned/hover/setup-mode rail heights.) */

/* Hover tooltip. */
.tip-me-button[data-tooltip]::before {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 10px);
  left: 0;
  width: max-content;
  max-width: 280px;
  padding: 8px 12px;
  border-radius: 10px;
  background: rgba(2, 6, 23, 0.96);
  color: #f1f5f9;
  font-size: 0.78rem;
  font-weight: 500;
  line-height: 1.35;
  border: 1px solid rgba(255, 255, 255, 0.16);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55);
  pointer-events: none;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 180ms ease, transform 180ms ease;
  z-index: 1001;
  white-space: normal;
  letter-spacing: 0;
}

.tip-me-button[data-tooltip]:hover::before {
  opacity: 1;
  transform: translateY(0);
}

.day-time-chips-popover-title {
  font-size: 0.62rem;
  font-weight: 700;
  color: rgba(226, 232, 240, 0.7);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding-bottom: 4px;
  margin-bottom: 2px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  text-align: center;
  white-space: nowrap;
}

.day-cell:hover .day-time-chips-popover {
  display: flex;
}

.range-label {
  display: block;
  margin-top: 8px;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--ink);
}

.consensus-badge,
.blocker-badge,
.my-marker {
  position: absolute;
  display: inline-grid;
  place-items: center;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 820;
}

.consensus-badge {
  right: 8px;
  top: 8px;
  min-width: 28px;
  height: 24px;
  background: rgba(15, 23, 42, 0.58);
  color: var(--ink);
  border: 1px solid var(--line);
}

.blocker-badge {
  right: -5px;
  top: -6px;
  min-width: 34px;
  height: 26px;
  padding: 0 8px;
  color: white;
  background: var(--no);
  box-shadow: 0 0 0 3px rgba(15, 23, 42, 0.92), 0 10px 22px rgba(239, 68, 68, 0.34);
}

.my-marker {
  left: 8px;
  bottom: 8px;
  height: 22px;
  padding: 0 8px;
  background: rgba(15, 23, 42, 0.88);
  color: white;
}

.guide-chip {
  color: rgba(248, 250, 252, 0.95);
  border: 1px solid rgba(255, 255, 255, 0.22);
  background: rgba(15, 23, 42, 0.78);
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.06),
    0 12px 36px rgba(0, 0, 0, 0.34);
  backdrop-filter: blur(18px) saturate(160%);
}

.calendar-guide {
  position: absolute;
  z-index: 4;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  min-height: 38px;
  display: inline-grid;
  place-items: center;
  border-radius: 999px;
  padding: 0 16px;
  font-size: 0.86rem;
  font-weight: 760;
  opacity: 0.58;
  pointer-events: none;
  animation: guide-pulse 2.8s ease-in-out infinite;
}

/* Setup-mode welcome card — replaces the lone pill chip when the
   calendar is wholly empty. Belongs to the Guide Layer per
   STYLE_GUIDE.md (var(--ghost) accent), so it reads as instructional
   rather than data. pointer-events: none lets the user click straight
   through to the calendar to begin selecting — the card disappears
   the moment any range is committed. */
.calendar-welcome {
  position: absolute;
  z-index: 6;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  width: min(360px, calc(100% - 40px));
  padding: 16px 18px 14px;
  border-radius: 16px;
  text-align: left;
  pointer-events: auto;
  cursor: grab;
  user-select: none;
}
.calendar-welcome.is-dragging {
  cursor: grabbing;
  transition: none;
}
/* When the user drags the welcome card, JS sets inline `top`/`left`
   and clears `translate` — but we keep the centered default for the
   first render. */
.calendar-welcome[data-dragged="true"] {
  /* JS supplies viewport-relative left/top from getBoundingClientRect.
     Position must be fixed so those coords map to the viewport, not the
     offsetParent — otherwise the card jumps on first pointerdown. */
  position: fixed;
  translate: 0 0;
  /* Override the .guide-chip pulse animation — for a long instructional
     surface a steady look is more readable than a slow opacity pulse. */
  animation: none;
  opacity: 1;
  /* Indigo-tinted glass per Guide Layer rules. */
  color: rgba(248, 250, 252, 0.95);
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--ghost) 18%, transparent), transparent 40%),
    rgba(15, 23, 42, 0.78);
  border: 1px solid color-mix(in srgb, var(--ghost) 45%, rgba(255, 255, 255, 0.16));
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.06),
    0 24px 60px rgba(0, 0, 0, 0.42),
    0 0 32px color-mix(in srgb, var(--ghost) 20%, transparent);
  backdrop-filter: blur(18px) saturate(160%);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
}

.calendar-welcome-title {
  margin: 0 0 8px;
  font-size: 1.18rem;
  font-weight: 820;
  letter-spacing: 0;
  color: var(--ink);
}

.calendar-welcome-lede {
  margin: 0 0 14px;
  font-size: 0.88rem;
  font-weight: 600;
  line-height: 1.4;
  color: rgba(248, 250, 252, 0.78);
}

.calendar-welcome-list {
  margin: 0 0 14px;
  padding: 0 0 0 18px;
  font-size: 0.86rem;
  font-weight: 600;
  line-height: 1.5;
  color: rgba(248, 250, 252, 0.92);
}

.calendar-welcome-list li {
  margin-bottom: 4px;
}

.calendar-welcome-list li:last-child {
  margin-bottom: 0;
}

.calendar-welcome-list strong {
  color: var(--ink);
  font-weight: 760;
}

.calendar-welcome-list code {
  display: inline-block;
  padding: 0 6px;
  margin: 0 1px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--ghost) 20%, rgba(15, 23, 42, 0.6));
  border: 1px solid color-mix(in srgb, var(--ghost) 35%, rgba(255, 255, 255, 0.12));
  font-family: "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.78rem;
  font-weight: 600;
  color: rgba(248, 250, 252, 0.95);
}

.calendar-welcome-cta {
  margin: 0;
  padding-top: 10px;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1.45;
  color: rgba(248, 250, 252, 0.82);
}

.calendar-welcome-cta strong {
  color: var(--ink);
  font-weight: 760;
  white-space: nowrap;
}

/* Three-step wizard primitives. The welcome card now hosts a small
   "1 of 3" step indicator at the top-left + a column of pill-shaped
   answer buttons that advance through the framing questions. */
.welcome-step-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}
.welcome-step-indicator {
  display: inline-block;
  padding: 2px 10px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--ghost) 22%, rgba(15, 23, 42, 0.5));
  border: 1px solid color-mix(in srgb, var(--ghost) 38%, rgba(255, 255, 255, 0.14));
  font-size: 0.7rem;
  font-weight: 720;
  letter-spacing: 0.04em;
  color: rgba(248, 250, 252, 0.82);
  text-transform: lowercase;
}
.wizard-stack {
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  margin-top: 12px;
}
.wizard-stack .welcome-cta {
  text-align: center;
  font-size: 0.88rem;
  padding: 10px 14px;
}
.wizard-sample {
  margin-top: 6px;
}
.wizard-back-link {
  appearance: none;
  border: 0;
  background: transparent;
  color: rgba(248, 250, 252, 0.62);
  font-size: 0.78rem;
  font-weight: 620;
  padding: 6px 0 0;
  margin-top: 6px;
  cursor: pointer;
  align-self: flex-start;
}
.wizard-back-link:hover {
  color: var(--ink);
}

/* New compact welcome card primitives (Pick dates / Try sample / X). */
.calendar-welcome-close {
  width: 24px;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.06);
  color: rgba(248, 250, 252, 0.7);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.calendar-welcome-close:hover {
  background: rgba(255, 255, 255, 0.12);
  color: var(--ink);
}
.welcome-guide-points {
  list-style: none;
  margin: 14px 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.welcome-guide-points li {
  font-size: 0.9rem;
  line-height: 1.45;
  color: rgba(248, 250, 252, 0.92);
}
.welcome-guide-points li strong {
  color: var(--ink);
  font-weight: 760;
  margin-right: 2px;
}
.welcome-arrow {
  color: var(--ghost);
  /* A glowing indigo pip so the arrows read as part of the same
     guide-layer vocabulary as the coach-mark dots. */
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--ghost) 60%, transparent));
  justify-self: center;
}

/* ============================================================
   Onboarding welcome card (portaled to body). Highest z-index so
   nothing in the workspace can occlude it. The card itself is a
   small centered tile; hand-drawn arrows are drawn separately by
   the OnboardingArrow component into its own viewport overlay.
   ============================================================ */
/* Backdrop scrim shown behind the welcome card + arrows. Sits
   above all page content (z-index 100000) but below the card
   (100001) and arrows (100002), so the page reads as visually
   inactive while the card is up. Click anywhere to dismiss the
   card (the JSX wires onClick → triggerClose). */
.onboarding-scrim {
  position: fixed;
  inset: 0;
  z-index: 100000;
  background: rgba(2, 6, 23, 0.55);
  backdrop-filter: saturate(60%) brightness(70%);
  -webkit-backdrop-filter: saturate(60%) brightness(70%);
  opacity: 1;
  transition: opacity 180ms ease-out;
  cursor: pointer;
}
.onboarding-scrim[data-closing="true"] {
  opacity: 0;
  pointer-events: none;
}

.onboarding-card {
  position: fixed;
  z-index: 100001;
  /* Card sits in the empty band of the title-island between the
     description text (left) and the photo/map column (right),
     about 45% down the viewport. From there the amber arrow swings
     up-left to the Event Details heading and the violet arrow
     swings up-right to the Poll Guests button (or the dates list),
     both with comparable lengths. */
  top: 48%;
  left: 54%;
  /* Mouse parallax — JS sets --card-parallax-x/-y from the same
     pointermove handler that drives the background orbs. Smaller
     magnitude than the orbs so the card reads as a closer layer
     hovering in front of the moving lights. NO transition: the
     card must snap to each parallax value INSTANTLY, otherwise the
     arrow (which reads the icon's live rect each rAF) runs on a
     different clock from the card's interpolation and the start
     point visibly warbles a few px behind the icon. */
  transform: translate(
    calc(-50% + var(--card-parallax-x, 0px)),
    calc(-50% + var(--card-parallax-y, 0px))
  );
  /* Aggressive fluid sizing — the card lives over the layout, so
     it MUST shrink with the viewport or it overlaps the right
     column (and the purple arrow's target hides behind it). The
     upper tray's middle column is `1fr` after a 220px right column
     and an `auto` left column, so it scales ~1:1 with viewport
     width. We track that with calc(26vw - 110px), tuned so the
     card sits at ~380px at 1880px-wide screens and shrinks to
     ~230px around 1300px (where the Poll Guests button would
     otherwise hide behind the card). Padding + font follow. */
  width: clamp(180px, calc(26vw - 110px), 380px);
  padding: clamp(8px, calc(1.6vw - 6px), 22px) clamp(10px, calc(1.8vw - 6px), 24px);
  font-size: clamp(0.7rem, calc(1.1vw - 0.18rem), 0.94rem);
  border-radius: 20px;
  text-align: center;
  color: rgba(248, 250, 252, 0.96);
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--ghost) 22%, transparent), transparent 55%),
    rgba(15, 23, 42, 0.88);
  border: 1px solid color-mix(in srgb, var(--ghost) 55%, rgba(255, 255, 255, 0.16));
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.06),
    0 30px 70px rgba(0, 0, 0, 0.5),
    0 0 50px color-mix(in srgb, var(--ghost) 28%, transparent);
  backdrop-filter: blur(20px) saturate(170%);
  -webkit-backdrop-filter: blur(20px) saturate(170%);
}

/* On narrow viewports the centered card overlaps the top-tray's
   right column (date entry textbox + Poll Guests button), so the
   purple arrow can't reach its target cleanly. Slide the card
   LEFT of center until the screen is wide enough for both. The
   breakpoint sits above the point at which the card would start
   to occlude the right column. */
@media (max-width: 1500px) {
  .onboarding-card {
    left: 50%;
  }
}

.onboarding-card-close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  color: rgba(248, 250, 252, 0.75);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.onboarding-card-close:hover {
  background: rgba(255, 255, 255, 0.16);
  color: #ffffff;
}

.onboarding-card-title {
  margin: 0 0 clamp(6px, calc(1.3vw - 6px), 18px);
  font-size: clamp(0.92rem, calc(1.9vw - 0.36rem), 1.92rem);
  font-weight: 840;
  letter-spacing: -0.02em;
  color: #ffffff;
  text-align: center;
}

.onboarding-rows {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(3px, calc(0.9vw - 6px), 10px);
}
.onboarding-row {
  display: grid;
  grid-template-columns: clamp(24px, calc(3.2vw - 12px), 44px) 1fr;
  align-items: center;
  gap: clamp(6px, calc(1.1vw - 4px), 14px);
  padding: clamp(2px, 0.4vw, 6px) clamp(2px, 0.4vw, 4px);
}
.onboarding-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: clamp(24px, calc(3.2vw - 12px), 44px);
  height: clamp(24px, calc(3.2vw - 12px), 44px);
  border-radius: clamp(7px, 0.8vw, 12px);
  color: #ffffff;
}
.onboarding-icon--amber {
  background: linear-gradient(180deg, #fbbf24, #f59e0b);
  box-shadow: 0 8px 20px color-mix(in srgb, #fbbf24 45%, transparent);
}
.onboarding-icon--violet {
  background: linear-gradient(180deg, #a78bfa, #7c3aed);
  box-shadow: 0 8px 20px color-mix(in srgb, #a78bfa 45%, transparent);
}
.onboarding-text {
  margin: 0;
  line-height: 1.3;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.onboarding-lead {
  font-weight: 820;
  font-size: clamp(0.78rem, calc(1.3vw - 0.24rem), 1.22rem);
  letter-spacing: -0.01em;
  line-height: 1.15;
}
.onboarding-lead--amber { color: #fcd34d; }
.onboarding-lead--violet { color: #c4b5fd; }
.onboarding-body {
  color: rgba(255, 255, 255, 0.92);
  font-weight: 600;
  font-size: clamp(0.62rem, calc(0.95vw - 0.14rem), 0.78rem);
}
/* Inline accent inside .onboarding-body — used to highlight the
   "Event Page" phrase so the call-to-action stands out from the
   surrounding description text. */
.onboarding-body-accent {
  color: #5eead4;
  font-weight: 720;
}
.onboarding-body-accent--violet {
  color: #f9a8d4;
}

/* Bold, curvy hand-drawn-style arrows that extend OUTSIDE the welcome
   card to physically point at the top chrome (event details) and the
   calendar grid. They sit behind the card content via pointer-events:
   none so dragging the card still works. */
.welcome-pointer {
  position: absolute;
  pointer-events: none;
  color: var(--ghost);
  filter:
    drop-shadow(0 0 10px color-mix(in srgb, var(--ghost) 65%, transparent))
    drop-shadow(0 4px 14px rgba(15, 23, 42, 0.55));
  overflow: visible;
  z-index: 1;
}
.welcome-pointer-up {
  /* Curves up-and-left out of the card's top-left corner toward the
     top chrome (event details / title row). */
  top: -150px;
  left: -120px;
  width: 240px;
  height: 200px;
}
.welcome-pointer-down {
  /* Curves down-and-right out of the card's bottom-right corner toward
     the calendar grid. */
  bottom: -150px;
  right: -120px;
  width: 240px;
  height: 200px;
}
.welcome-pointer path {
  fill: none;
  stroke: currentColor;
  stroke-width: 7;
  stroke-linecap: round;
  stroke-linejoin: round;
}
@media (max-width: 720px) {
  .welcome-pointer-up { top: -90px; left: -50px; width: 160px; height: 130px; }
  .welcome-pointer-down { bottom: -90px; right: -50px; width: 160px; height: 130px; }
  .welcome-pointer path { stroke-width: 5; }
}

.calendar-welcome-actions {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}
.welcome-cta {
  appearance: none;
  border: 0;
  border-radius: 10px;
  padding: 8px 14px;
  font-size: 0.86rem;
  font-weight: 720;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: background 140ms ease, transform 140ms ease, box-shadow 140ms ease;
}
.welcome-cta.primary {
  background: var(--ghost, #6366f1);
  color: var(--ink);
  box-shadow: 0 6px 18px color-mix(in srgb, var(--ghost) 40%, transparent);
}
.welcome-cta.primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 10px 22px color-mix(in srgb, var(--ghost) 55%, transparent);
}
.welcome-cta.secondary {
  background: rgba(255, 255, 255, 0.08);
  color: var(--ink);
  border: 1px solid rgba(255, 255, 255, 0.18);
}
.welcome-cta.secondary:hover {
  background: rgba(255, 255, 255, 0.14);
}

/* Compact setup-progress checklist that takes over once the welcome
   card is dismissed. Same Guide-Layer styling (indigo glass) but
   smaller and anchored top-left so it doesn't cover calendar cells. */
.setup-checklist {
  position: absolute;
  z-index: 4;
  top: 12px;
  left: 12px;
  width: 220px;
  padding: 12px 14px 12px 16px;
  border-radius: 14px;
  text-align: left;
  pointer-events: auto;
  color: rgba(248, 250, 252, 0.95);
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--ghost) 14%, transparent), transparent 40%),
    rgba(15, 23, 42, 0.78);
  border: 1px solid color-mix(in srgb, var(--ghost) 35%, rgba(255, 255, 255, 0.14));
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.05),
    0 16px 36px rgba(0, 0, 0, 0.32);
  backdrop-filter: blur(14px) saturate(150%);
  -webkit-backdrop-filter: blur(14px) saturate(150%);
  animation: none;
}
.setup-checklist-close {
  position: absolute;
  top: 6px;
  right: 8px;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: rgba(248, 250, 252, 0.6);
  font-size: 0.95rem;
  cursor: pointer;
}
.setup-checklist-close:hover {
  background: rgba(255, 255, 255, 0.1);
  color: var(--ink);
}
.setup-checklist-title {
  margin: 0 0 8px;
  font-size: 0.82rem;
  font-weight: 760;
  letter-spacing: 0.02em;
  color: var(--ink);
}
.setup-checklist-items {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.78rem;
  font-weight: 620;
  line-height: 1.4;
  color: rgba(248, 250, 252, 0.82);
}
.setup-checklist-items li {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 0;
}
.setup-checklist-items li > span {
  display: inline-block;
  width: 14px;
  text-align: center;
  color: color-mix(in srgb, var(--ghost) 70%, rgba(255, 255, 255, 0.4));
  font-weight: 800;
}
.setup-checklist-items li.done {
  color: rgba(248, 250, 252, 0.55);
  text-decoration: line-through;
  text-decoration-color: rgba(248, 250, 252, 0.25);
}
.setup-checklist-items li.done > span {
  color: var(--best, #a7e4d3);
}

/* The brand cell now lives as a fixed-position vertical icon rail
   along the left edge of the viewport. Holds the logo at the top
   and the "? Guide" pill below it. Tip Me (appended to body by
   index.html) is independently positioned to sit at the bottom of
   the same column. .app-shell reserves padding-left:64px so the
   shell content clears the rail. */
.brand-area {
  position: fixed;
  top: 0;
  left: 0;
  width: 56px;
  /* Stop above the bottom rail so the Tip Me chip (fixed-positioned
     against the bottom-rail's top edge via --tip-me-bottom) has
     room. Visual continuity comes from the rail's transparent
     background — there's no painted strip behind the logo. */
  bottom: calc(var(--tip-me-bottom, 120px) + 44px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 12px 0;
  z-index: 50;
  pointer-events: none;
}
.brand-area > * {
  pointer-events: auto;
}

/* Persistent setup command bar — always visible in setup mode,
   anchored at the same vertical level as the Tip Me button (using
   the same --tip-me-bottom variable JS updates as the bottom rail
   expands). Spans from just right of the Tip Me button to the
   right edge of the viewport. Single-line by default, auto-grows
   up to ~6 lines as the user types more. */
.persistent-command-bar-wrap {
  position: fixed;
  z-index: 1000;
  /* Spans the full viewport width — the Tip Me button used to sit
     to its left, but now lives ABOVE the bar so the bar has the
     whole row. */
  left: 16px;
  right: 16px;
  bottom: var(--tip-me-bottom, 120px);
  display: flex;
  flex-direction: column;
  gap: 0;
  pointer-events: none;
}
.persistent-command-bar-wrap > .setup-command-bar,
.persistent-command-bar-wrap > .coach-mark {
  pointer-events: auto;
}
/* Override base .setup-command-bar (which is position:fixed + left:50%
   centered) so the persistent variant lives INSIDE its wrapping
   container. The wrap controls position; the bar itself is a
   relative-flow element with its own glass styling. */
.setup-command-bar.persistent {
  position: relative;
  top: auto;
  left: auto;
  right: auto;
  bottom: auto;
  width: 100%;
  min-height: 0;
  transform: none;
  display: block;
  /* No outer outline — the textarea inside carries the visible
     border so there's just ONE pill shape (matching the user's
     request to remove the double outline). The wrap stays
     transparent so it acts as a pure positioning container. */
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0;
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.setup-command-bar.persistent::before {
  display: none;
}
/* The persistent variant used to hide its label when it lived at
   the bottom of the calendar (the label felt redundant in a
   permanently-visible bar). Now that the persistent SetupCommandBar
   lives in the top tray next to the Address field, the label is
   the only thing distinguishing the two inputs — show it. */
.setup-command-bar.persistent .setup-command-label {
  display: flex;
}
/* Slightly rounded textarea inside the pill bar — keep matching
   border-radius on mirror so colored tokens align with the caret. */
.setup-command-bar.persistent .command-input-wrap {
  width: 100%;
}
.setup-command-bar.persistent textarea {
  /* Two-line minimum (was single-line) so the input visually reads
     as a multi-entry field, not a one-shot input. Font size matches
     .address-input (0.78rem) so the two inputs look like one system.
     With a smaller font, the parsed token chips inside the mirror
     also scale down proportionally since they're `font: inherit`. */
  min-height: 50px;
  height: 50px;
  font-size: 0.78rem;
  line-height: 18px;
  border-radius: 10px;
  padding: 6px 10px;
  resize: none;
  /* Wrap to a second line + auto-scroll past 6 lines instead of
     pushing typed content off-screen to the left. Overrides the
     `white-space: nowrap; overflow-x: hidden` inherited from the
     base .setup-command-bar textarea rule. */
  white-space: pre-wrap;
  word-break: break-word;
  overflow-x: hidden;
  overflow-y: auto;
  /* Fully opaque so the calendar / overflow text underneath the
     date-entry bar never bleeds through and confuses the eye. */
  background: #020617;
  border-color: rgba(255, 255, 255, 0.14);
}
.setup-command-bar.persistent .command-mirror {
  /* Mirror MUST match the textarea's padding + border-radius +
     line-height + font-size + wrap mode so caret and visible
     tokens stay pixel-aligned. */
  padding: 6px 10px;
  border-radius: 10px;
  font-size: 0.78rem;
  line-height: 18px;
  white-space: pre-wrap;
  word-break: break-word;
  overflow-x: hidden;
  overflow-y: hidden;
}

/* ─────────────── Coach marks (Guide Layer) ───────────────
   Small pill-shaped callouts anchored to key surfaces with a
   thin connector line + dot pointing at the relevant target.
   Indigo-tinted glass to match the rest of the Guide Layer.
   Each instance positions itself via a parent-relative anchor;
   the connector + dot point toward the target it explains. */
.coach-mark {
  position: absolute;
  z-index: 250;
  padding: 12px 28px 12px 16px;
  border-radius: 14px;
  /* Solid indigo pill — matches the example mock's coach mark
     style (no glass, no glow halo, just a clean violet box). */
  background: color-mix(in srgb, var(--ghost) 88%, rgba(15, 23, 42, 0.2));
  border: 1px solid color-mix(in srgb, var(--ghost) 50%, #ffffff);
  box-shadow:
    0 6px 18px rgba(15, 23, 42, 0.35),
    0 2px 4px rgba(15, 23, 42, 0.25);
  color: #ffffff;
  font-size: 0.82rem;
  font-weight: 720;
  line-height: 1.35;
  max-width: 240px;
  pointer-events: auto;
}
/* Portal-rendered coach marks live on document.body. They must clear
   every in-app stacking context (top chrome, edge trays, command
   bar) so onboarding callouts never get swallowed by a backdrop-filter
   parent. The onboarding card itself sits at 100001; coach marks
   sit just below so the welcome card overlays them if both visible. */
.coach-mark--portal {
  z-index: 99999 !important;
}

/* Dismiss × in the top-right of a coach mark. Bumps the right
   padding so it doesn't crowd the text. */
.coach-mark--has-dismiss {
  padding-right: 32px;
}
.coach-mark-dismiss {
  position: absolute;
  top: 4px;
  right: 6px;
  width: 22px;
  height: 22px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: rgba(255, 255, 255, 0.7);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
}
.coach-mark-dismiss:hover {
  background: rgba(255, 255, 255, 0.16);
  color: #ffffff;
}
/* While the welcome card is open, secondary coach marks fade back so
   the centered card is the unmistakable focal point. They become
   fully visible once the welcome card is dismissed. */
.coach-mark.is-dimmed {
  opacity: 0.35;
  filter: saturate(0.55);
  transition: opacity 240ms ease, filter 240ms ease;
}
.coach-mark p {
  margin: 0;
}
.coach-mark-close {
  position: absolute;
  top: 4px;
  right: 6px;
  width: 20px;
  height: 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: rgba(248, 250, 252, 0.55);
  font-size: 0.9rem;
  line-height: 1;
  cursor: pointer;
}
.coach-mark-close:hover {
  background: rgba(255, 255, 255, 0.12);
  color: var(--ink);
}

/* Connector lines + dot — point from the callout toward the
   anchored target. Two directions for v1: --right (line going
   right) and --up (line going up). */
.coach-mark-connector {
  position: absolute;
  background: color-mix(in srgb, var(--ghost) 60%, #ffffff);
  opacity: 0.85;
}
.coach-mark-dot {
  position: absolute;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  /* Indigo outer ring with a clear WHITE center pip — drawn via a
     radial gradient so it reads as a target marker against any
     background. */
  background: radial-gradient(
    circle at center,
    #ffffff 0 28%,
    var(--ghost) 32% 100%
  );
  box-shadow:
    0 0 12px color-mix(in srgb, var(--ghost) 75%, transparent),
    0 0 0 1.5px rgba(15, 23, 42, 0.8);
}
.coach-mark-connector--right {
  /* Thin horizontal line extending right from the callout. */
  top: 50%;
  left: 100%;
  width: 36px;
  height: 1.5px;
  transform: translateY(-50%);
}
.coach-mark-dot--right {
  top: 50%;
  left: calc(100% + 30px);
  transform: translate(0, -50%);
}
.coach-mark-connector--left {
  /* Mirror of --right: thin horizontal line extending LEFT from the
     callout, pointing into the element that sits to its left. Used
     by the description tip when the editor has scrolled and the tip
     has slid out to the side. */
  top: 50%;
  left: -36px;
  width: 36px;
  height: 1.5px;
  transform: translateY(-50%);
}
.coach-mark-dot--left {
  top: 50%;
  left: -44px;
  transform: translate(0, -50%);
}
.coach-mark-connector--up {
  /* Thin vertical line going up from the callout. */
  left: 50%;
  top: -28px;
  width: 1.5px;
  height: 28px;
  transform: translateX(-50%);
}
.coach-mark-dot--up {
  left: 50%;
  top: -36px;
  transform: translateX(-50%);
}

/* Per-instance placement. */
.coach-mark--calendar {
  /* Sits over the empty calendar area, pointing right toward
     the first selectable day. */
  top: 200px;
  left: 80px;
}
.coach-mark--rail {
  /* Inside .rail-content, above the title row, pointing up
     toward where the date options will appear. */
  position: relative;
  top: auto;
  left: auto;
  margin: 28px 8px 18px;
}
.coach-mark--rail .coach-mark-connector--up {
  /* Anchored differently inside a flow item — point downward
     INTO the section below (where the date list appears) by
     flipping the connector to extend BELOW the callout. */
  top: auto;
  bottom: -28px;
}
.coach-mark--rail .coach-mark-dot--up {
  top: auto;
  bottom: -36px;
}
.coach-mark--publish {
  /* Inside .mode-action-bar, anchored below the buttons,
     pointing upward toward Create Event. */
  position: absolute;
  top: calc(100% + 12px);
  right: 8px;
  left: auto;
}

/* Anchored below the title-island, pointing up toward the
   title + description. */
.coach-mark--title {
  position: absolute;
  top: calc(100% + 12px);
  left: 50%;
  transform: translateX(-50%);
}
.coach-mark-connector--up-from-below {
  /* Same as --up but inside a child positioned BELOW the target,
     so the line/dot sit above the callout pointing INTO the
     element directly above it. */
  left: 50%;
  top: -28px;
  width: 1.5px;
  height: 28px;
  transform: translateX(-50%);
  background: color-mix(in srgb, var(--ghost) 70%, rgba(255, 255, 255, 0.4));
  position: absolute;
}
.coach-mark-dot--up-from-below {
  /* Sit at the connector's far end, ABOVE the callout, pointing
     into the element above. Inherits the base .coach-mark-dot's
     white-center radial gradient (do NOT override `background`
     here — solid purple read as a flat dot vs the gradient's
     pip-target look the user expects). */
  left: 50%;
  top: -36px;
  transform: translateX(-50%);
  position: absolute;
}

/* Anchored above the persistent command bar, pointing DOWN at it. */
.coach-mark--command {
  position: relative;
  left: auto;
  top: auto;
  align-self: center;
  margin-bottom: 36px;
}

/* Anchored above the bottom invitations tray, pointing DOWN
   toward the invite avatars + Invite button. */
.coach-mark--invite {
  position: absolute;
  bottom: calc(100% + 12px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 12;
}
.coach-mark-connector--down {
  left: 50%;
  top: 100%;
  width: 1.5px;
  height: 28px;
  transform: translateX(-50%);
}
.coach-mark-dot--down {
  left: 50%;
  top: calc(100% + 28px);
  transform: translateX(-50%);
}

/* Persistent Guide affordance — icon-only circle in the left rail.
   Just a "?" glyph; the full "Guide" word reads as a tooltip on
   hover. Setup mode only. */
.guide-reopen-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: transparent;
  color: rgba(248, 250, 252, 0.6);
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0;
  cursor: pointer;
  transition: color 140ms ease, border-color 140ms ease, background 140ms ease;
  font-size: 0;
}
.guide-reopen-btn::after {
  content: "?";
  font-size: 0.95rem;
  line-height: 1;
}
.guide-reopen-btn:hover {
  color: rgba(248, 250, 252, 0.92);
  border-color: color-mix(in srgb, var(--ghost) 45%, rgba(255, 255, 255, 0.18));
  background: color-mix(in srgb, var(--ghost) 14%, transparent);
}
/* Brief pulse when the welcome card finishes its minimize animation
   onto this button — gives the user a clear "it landed here" cue. */
.guide-reopen-btn.is-receiving {
  animation: guide-receive 700ms cubic-bezier(0.22, 1, 0.36, 1);
  color: #ffffff;
}
@keyframes guide-receive {
  0% {
    transform: scale(1);
    box-shadow: 0 0 0 0 rgba(167, 139, 250, 0);
    background: color-mix(in srgb, var(--ghost) 14%, transparent);
  }
  30% {
    transform: scale(1.22);
    box-shadow:
      0 0 0 6px color-mix(in srgb, var(--ghost) 30%, transparent),
      0 0 22px 4px color-mix(in srgb, var(--ghost) 70%, transparent),
      0 0 40px 12px color-mix(in srgb, #fbbf24 35%, transparent);
    background: color-mix(in srgb, var(--ghost) 55%, transparent);
    border-color: color-mix(in srgb, var(--ghost) 90%, transparent);
  }
  100% {
    transform: scale(1);
    box-shadow: 0 0 0 0 rgba(167, 139, 250, 0);
    background: color-mix(in srgb, var(--ghost) 14%, transparent);
  }
}

/* Subtle motion blur on the card as it streaks toward the guide
   pill — ramps in mid-flight then clears so the start/end remain
   crisp. */
.onboarding-card.is-flying {
  animation: card-fly-blur 520ms ease-in-out;
}
@keyframes card-fly-blur {
  0% { filter: blur(0); }
  35% { filter: blur(2.5px); }
  70% { filter: blur(4px); }
  100% { filter: blur(0); }
}

/* HoverHint — contextual tooltip surface. Belongs to the Guide Layer per
   STYLE_GUIDE.md: indigo accent (var(--ghost)) so instructions stay
   mentally separate from data colors and frosted glass surfaces. Single
   element rendered via portal at <body>; positioned by JS to anchor
   above (or below, when there's no headroom) the triggering element. */
.hover-hint {
  position: fixed;
  z-index: 240;
  max-width: 280px;
  padding: 6px 10px;
  border-radius: 8px;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.3;
  color: rgba(248, 250, 252, 0.96);
  background: color-mix(in srgb, var(--ghost) 22%, rgba(15, 23, 42, 0.88));
  border: 1px solid color-mix(in srgb, var(--ghost) 55%, rgba(255, 255, 255, 0.18));
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.06),
    0 12px 28px rgba(0, 0, 0, 0.36),
    0 0 22px color-mix(in srgb, var(--ghost) 25%, transparent);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  pointer-events: none;
  animation: hover-hint-fade var(--motion-fast) ease-out forwards;
}

/* Fade only — translation is set inline (anchor-relative) so animating
   it here would fight the inline value. */
@keyframes hover-hint-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .hover-hint {
    animation: none;
    opacity: 1;
  }
}

.edge-tray {
  position: relative;
  overflow: hidden;
  background: var(--glass);
  box-shadow: 0 32px 90px rgba(0, 0, 0, 0.22);
  transition:
    opacity 240ms ease,
    box-shadow 240ms ease,
    background 240ms ease,
    transform 420ms var(--ease-pop);
}

.edge-tray:hover,
.edge-tray.pinned {
  background: var(--glass-strong);
}

/* Hover glow: subtle 1px outline edge on the tray when hovered. */
.edge-tray:hover:not(.suppressed):not(.pinned) {
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.18),
    0 32px 90px rgba(0, 0, 0, 0.22);
}

/* Locked: keep the tray more opaque and less blurry to signal stability. */
.edge-tray.pinned {
  backdrop-filter: blur(8px) saturate(160%);
  background: rgba(15, 23, 42, 0.78);
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.1),
    0 32px 90px rgba(0, 0, 0, 0.32);
}

/* While the focus is inside the rail, drop backdrop-filter so the
   floating, position:fixed command bar inside is anchored to the
   viewport instead of being trapped by the tray's containing block. */
.edge-tray:focus-within {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.edge-tray:not(:hover):not(.pinned) {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
  backdrop-filter: none;
}

/* --- Pushpin toggle ---------------------------------------------------- */

.tray-pin {
  position: absolute;
  top: 8px;
  left: 8px;
  z-index: 10;
  width: 28px;
  height: 28px;
  display: inline-grid;
  place-items: center;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 8px;
  background: rgba(15, 23, 42, 0.4);
  color: rgba(248, 250, 252, 0.78);
  cursor: pointer;
  transition:
    background 180ms ease,
    color 180ms ease,
    border-color 180ms ease;
}

.tray-pin:hover {
  background: rgba(15, 23, 42, 0.55);
  color: rgba(248, 250, 252, 1);
  border-color: rgba(255, 255, 255, 0.28);
}

.tray-pin.locked {
  color: #facc15;
  border-color: rgba(250, 204, 21, 0.45);
  background: rgba(30, 22, 0, 0.55);
}

.tray-pin.locked:hover {
  color: #fde047;
  border-color: rgba(253, 224, 71, 0.6);
}

/* Pin inside the title row — visually identical to .tray-pin used
   on every other tray (.options-rail, .setup-invites). Defined AFTER
   .tray-pin so the cascade wins on `position`: the title-island pin
   is an inline flex item, NOT absolutely positioned to the top-left
   of its containing tray. */
.title-row > .title-island-pin {
  position: static;
  flex: 0 0 auto;
  margin: 0 6px 0 0;
}

.options-rail {
  grid-column: 2;
  grid-row: 1;
  min-width: 0;
  min-height: 0;
}

/* When the tray is open, let the focused command bar grow leftward over the
   calendar without being clipped by the tray's overflow:hidden. */
.options-rail.pinned,
.options-rail:hover:not(.suppressed) {
  overflow: visible;
}

.setup-dates-active:not(.options-pinned) .options-rail {
  pointer-events: none;
}

/* Unified hover treatment: every cell in the focused range lifts in
   unison (matches the .day-cell:hover lift), and the corresponding LED
   and ring overlay light up. The same `focusedSetupOption` state powers
   this whether the hover originated on a calendar day cell OR on the
   tray chip — so the two surfaces produce the same visual response.
   The heavy white outline on ::after has been removed in favor of this
   coherent lift+LED+brief-glow combo. */
.day-cell.setup-range-focused {
  z-index: 36;
  transform: translateY(var(--lift-y)) scale(var(--lift-scale));
  box-shadow:
    var(--tile-highlight),
    var(--tile-lowlight),
    0 22px 44px rgba(0, 0, 0, 0.34),
    0 6px 14px rgba(0, 0, 0, 0.2);
}

.rail-tab {
  position: absolute;
  inset: 0 auto 0 0;
  width: var(--right-rail);
  z-index: 2;
  display: grid;
  grid-template-rows: minmax(0, 1fr) auto;
  place-items: center;
  padding: 8px;
  transition: opacity 180ms ease;
}

.rail-open {
  width: 100%;
  height: 100%;
  min-height: 0;
  display: grid;
  place-items: center;
  gap: 8px;
  color: var(--ink);
  border: 1px solid var(--line);
  background: rgba(15, 23, 42, 0.34);
}

.options-rail .rail-open {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.rail-tab .rail-open {
  min-height: 0;
}

.options-rail .rail-open span {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  font-weight: 820;
}

.rail-teasers {
  position: absolute;
  left: 31px;
  top: 119px;
  z-index: 4;
  width: calc(var(--right-open) - 47px);
  display: grid;
  gap: 8px;
  pointer-events: none;
}

.rail-teaser {
  min-height: 48px;
  width: 100%;
  max-width: none;
  overflow: hidden;
  filter: blur(0.1px);
  opacity: 0.42;
}

.rail-teaser.focus-glow {
  opacity: 1 !important;
  filter: none;
}

.rail-teaser.focus-dim {
  opacity: 0.18 !important;
}

.rail-teaser .date-chip {
  flex: 0 0 auto;
}

.rail-content {
  position: relative;
  height: 100%;
  min-height: 0;
  min-width: 300px;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  opacity: 0;
  pointer-events: none;
  /* No transform — a transformed ancestor creates a containing block for
     position:fixed descendants, which would trap the floating command bar.
     Opacity transition alone is enough for the open/close feel. */
  transition: opacity 240ms ease;
}

.options-rail:not(:hover):not(.pinned):not(:focus-within) .rail-content {
  visibility: hidden;
}

/* When the user explicitly unpinned (.suppressed), force-hide the rail
   content even if a descendant is focused. The user's "close" gesture
   overrides the auto-keep that focus normally provides. */
.options-rail.suppressed:not(:hover) .rail-content {
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
}

.options-rail:hover:not(.suppressed) .rail-tab,
.options-rail.pinned .rail-tab,
.options-rail:focus-within:not(.suppressed) .rail-tab {
  opacity: 0;
  pointer-events: none;
}

.options-rail:hover:not(.suppressed):not(.pinned) .rail-content {
  transition-delay: 200ms;
}

.options-rail:hover:not(.suppressed):not(.pinned) .rail-tab {
  transition-delay: 200ms;
}

.options-rail:hover:not(.suppressed) .rail-content,
.options-rail.pinned .rail-content,
.options-rail:focus-within:not(.suppressed) .rail-content {
  opacity: 1;
  pointer-events: auto;
}

.tray-title-row h2,
.event-card h2 {
  margin: 0;
  letter-spacing: 0;
}

/* When the pin lives at top-left, push the title clear of it. */
.options-rail .rail-content .tray-title-row {
  padding-left: 28px;
}

.response-card,
.winner-card,
.event-card {
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(15, 23, 42, 0.38);
  padding: 13px;
}

.setup-status-card {
  position: relative;
  border: 0;
  background: transparent;
  padding: 0 0 6px;
}
.setup-status-count {
  margin: 0 0 4px !important;
  font-size: 0.95rem !important;
  font-weight: 800 !important;
  color: var(--ink);
  letter-spacing: -0.005em;
}
/* "Poll your guests on the options below..." / "Select more than
   one day..." copy that replaced the old subtitle + instr lines. */
.setup-status-card .setup-status-hint {
  margin: 0 0 8px !important;
  font-size: 0.78rem !important;
  font-weight: 500 !important;
  color: var(--muted);
  line-height: 1.4;
  opacity: 0.92;
}
/* Poll Guests button moved out of the top-right action bar and
   into the status card itself. Same visual vocabulary as
   .launch-button.primary so it still reads as a primary action. */
.setup-status-poll-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--ghost) 70%, rgba(15, 23, 42, 0.3));
  border: 1px solid color-mix(in srgb, var(--ghost) 80%, transparent);
  color: #ffffff;
  font: inherit;
  font-weight: 700;
  font-size: 0.84rem;
  cursor: pointer;
  margin-top: 2px;
}
.setup-status-poll-btn:hover {
  background: color-mix(in srgb, var(--ghost) 85%, rgba(15, 23, 42, 0.2));
}

/* Poll Guests button + 'N options' count side-by-side, count on the
   right. The count's `<p>` would normally stack above the button on
   its own line — wrapping them in this flex row puts them on one
   horizontal line with the button anchored left and the count
   pushed right via margin-left: auto. */
.setup-status-action-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 2px;
}
.setup-status-action-row .setup-status-poll-btn {
  margin-top: 0;
}
.setup-status-action-row .setup-status-count {
  margin: 0 0 0 auto !important;
}

/* Small field-label style — shared by the new Address: and Date:
   labels above their respective inputs. Uppercase muted text, same
   weight + spacing across both so they read as one system. */
.field-label {
  display: block;
  margin: 0 0 4px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}
.field-label--command {
  /* The SetupCommandBar label already has its own flex layout (it
     contains the warn pill). Override the field-label `display:
     block` so the inline children stay aligned. */
  display: flex;
  align-items: center;
  gap: 6px;
  text-transform: uppercase;
}

.guide-layer-card {
  border-color: rgba(99, 102, 241, 0.52);
  background:
    linear-gradient(135deg, rgba(99, 102, 241, 0.12), rgba(99, 102, 241, 0.035)),
    rgba(15, 23, 42, 0.36);
  box-shadow:
    0 0 0 1px rgba(99, 102, 241, 0.08),
    0 18px 38px rgba(2, 6, 23, 0.24),
    inset 1px 1px 1px rgba(255, 255, 255, 0.14);
}

.guide-layer-card strong {
  color: #6366f1;
}

.guide-layer-card p {
  color: #6366f1;
  opacity: 0.82;
}

.setup-status-card p {
  margin: 7px 0 0;
  font-size: 0.84rem;
  line-height: 1.35;
}

.setup-status-hint {
  font-size: 0.66rem !important;
  font-weight: 400 !important;
  opacity: 0.7;
  letter-spacing: 0.01em;
  margin-top: 4px !important;
}

.response-label {
  color: var(--muted);
  font-size: 0.85rem;
}

.response-card strong,
.winner-card strong {
  display: block;
  margin-top: 5px;
  font-size: 1.1rem;
}

.winner-card p,
.event-card p {
  margin: 6px 0 0;
  color: var(--muted);
  line-height: 1.35;
}

.band-picker {
  width: 100%;
  margin-top: 12px;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.band-button {
  min-width: 0;
  padding: 0 7px;
  font-size: 0.82rem;
  font-weight: 820;
}

.band-button.active.best,
.date-chip.best,
.rsvp.yes.active {
  background: rgba(167, 228, 211, 0.18);
  color: #dbfff5;
}

.band-button.active.works,
.date-chip.works {
  background: rgba(174, 198, 207, 0.18);
  color: #e7f7ff;
}

.band-button.active.maybe,
.date-chip.maybe,
.rsvp.maybe.active {
  background: rgba(232, 185, 113, 0.2);
  color: #ffeccc;
}

.band-button.active.no,
.date-chip.no,
.rsvp.no.active {
  background: var(--no);
  color: white;
}

.option-list {
  display: grid;
  gap: 8px;
}

.setup-option-list {
  flex: 1 1 auto;
  /* 2-column grid shared across every option-group via subgrid, so
     the date-chip column auto-sizes to the widest chip in the WHOLE
     list. That makes every time-block row left-align to the same x
     regardless of whether the date is "Jun 9" or "Jun 10". */
  display: grid;
  grid-template-columns: auto 1fr;
  align-content: start;
  column-gap: 8px;
  row-gap: 8px;
  min-height: 0;
  overflow-y: auto;
  padding-right: 3px;
}

.setup-option-list::-webkit-scrollbar {
  width: 8px;
}

.setup-option-list::-webkit-scrollbar-track {
  background: rgba(15, 23, 42, 0.16);
  border-radius: 999px;
}

.setup-option-list::-webkit-scrollbar-thumb {
  border-radius: 999px;
  background: rgba(248, 250, 252, 0.24);
}

.setup-option-list > .empty-draft { grid-column: 1 / -1; }
.empty-draft {
  border: 1px dashed rgba(255, 255, 255, 0.24);
  border-radius: 8px;
  padding: 14px;
  color: var(--muted);
  background: rgba(15, 23, 42, 0.32);
}

.option-row {
  min-height: 48px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(15, 23, 42, 0.36);
  padding: 8px;
  text-align: left;
  box-shadow:
    inset 1px 1px 1px rgba(255, 255, 255, 0.14),
    inset -1px -1px 1px rgba(0, 0, 0, 0.24);
}

.option-row.active {
  border-color: rgba(99, 102, 241, 0.45);
  box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.12);
}

.setup-option-row.focus-glow,
.setup-option-row:hover,
.setup-option-row:focus-visible {
  border-color: color-mix(in srgb, var(--block-color, #a7e4d3) 58%, transparent);
  box-shadow:
    0 0 0 3px color-mix(in srgb, var(--block-color, #a7e4d3) 16%, transparent),
    0 0 28px color-mix(in srgb, var(--block-color, #a7e4d3) 22%, transparent),
    inset 1px 1px 1px rgba(255, 255, 255, 0.18),
    inset -1px -1px 1px rgba(0, 0, 0, 0.24);
}

.setup-option-row {
  position: relative;
  overflow: hidden;
  justify-content: flex-start;
}

.setup-option-row::before {
  content: "";
  align-self: stretch;
  width: 4px;
  min-height: 30px;
  border-radius: 999px;
  background: var(--block-color, #a7e4d3);
  box-shadow: 0 0 16px color-mix(in srgb, var(--block-color, #a7e4d3) 55%, transparent);
}

.setup-band-0 { --block-color: var(--band-0); }
.setup-band-1 { --block-color: var(--band-1); }
.setup-band-2 { --block-color: var(--band-2); }
.setup-band-3 { --block-color: var(--band-3); }
.setup-band-4 { --block-color: var(--band-4); }
.setup-band-5 { --block-color: var(--band-5); }
.setup-band-6 { --block-color: var(--band-6); }
.setup-band-7 { --block-color: var(--band-7); }

.date-chip {
  display: inline-flex;
  align-items: center;
  min-height: 28px;
  padding: 0 9px;
  border-radius: 999px;
  font-weight: 820;
  font-size: 0.82rem;
}

.date-chip.setup {
  background:
    linear-gradient(90deg, color-mix(in srgb, var(--block-color, #a7e4d3) 22%, transparent), transparent 68%),
    rgba(2, 6, 23, 0.3);
  color: #dbeafe;
  border: 1px solid color-mix(in srgb, var(--block-color, #a7e4d3) 38%, rgba(255, 255, 255, 0.16));
  box-shadow:
    inset 1px 1px 2px rgba(0, 0, 0, 0.38),
    inset -1px -1px 2px rgba(255, 255, 255, 0.1);
}

.option-duration {
  margin-left: auto;
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 760;
  white-space: nowrap;
}

/* Compact layout: the date chip sits in column 1, time-block rows
   auto-flow in column 2. The first time block lands on row 1 next
   to the date chip; subsequent blocks indent under it because their
   column-1 cell is left empty. */
.setup-option-group {
  /* Subgrid inherits the parent .setup-option-list's 2 columns so
     every chip across every group shares one auto-sized column —
     time blocks line up across "Jun 9", "Jun 10", "Jun 11", etc. */
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / -1;
  column-gap: 8px;
  row-gap: 4px;
  align-items: center;
}
.setup-option-group > .setup-option-row {
  grid-column: 1;
  grid-row: 1;
  align-self: stretch;
  /* tighten so the button hugs the date chip — no longer carries
     the time-block count, just the chip itself. Match the
     time-block row height so all lines align. */
  flex: 0 0 auto;
  padding: 0 4px;
  min-height: 30px;
}
/* Wide variant: when the option-group has no time-block rows (full-day
   mode, or any multi-day range), stretch the chip row across BOTH
   subgrid columns so its outline reaches the right edge of the tray
   and the day-count meta sits on the far right. */
.setup-option-group > .setup-option-row--wide {
  grid-column: 1 / -1;
  flex: 1 1 auto;
  padding: 0 8px;
  justify-content: flex-start;
}
.setup-option-group > .setup-option-row--wide .option-duration {
  margin-left: auto;
}
.setup-option-group > .setup-time-block-row {
  grid-column: 2;
}

.setup-time-block-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 10px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.06);
  cursor: pointer;
  /* Every row in the option group has the same height so the date
     chip row and every time-block row align visually. */
  min-height: 30px;
}

.setup-time-block-row:hover,
.setup-time-block-row.is-hovered {
  background: linear-gradient(180deg, rgba(253, 230, 138, 0.2), rgba(251, 191, 36, 0.18));
  border-color: rgba(250, 204, 21, 0.7);
  box-shadow: 0 0 0 1px rgba(250, 204, 21, 0.3),
    0 4px 12px rgba(250, 204, 21, 0.18);
}
.setup-time-block-row.is-hovered .setup-time-block-label {
  color: #fde68a;
  font-weight: 760;
}

.setup-time-block-label {
  font-size: 0.78rem;
  font-weight: 700;
  color: #f1f5f9;
  letter-spacing: 0;
}

.setup-time-block-dur {
  margin-left: auto;
  font-size: 0.7rem;
  color: var(--muted);
  font-weight: 600;
  white-space: nowrap;
}

.option-score {
  color: var(--muted);
  font-size: 0.84rem;
  white-space: nowrap;
}

.event-art {
  height: 110px;
  border-radius: 8px;
  margin-bottom: 12px;
  background:
    linear-gradient(135deg, rgba(15, 139, 141, 0.82), rgba(16, 185, 129, 0.72)),
    url("data:image/svg+xml,%3Csvg viewBox='0 0 420 180' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 128h420v52H0z' fill='%23f7d66d'/%3E%3Ccircle cx='340' cy='42' r='26' fill='%23fff3b0'/%3E%3Cpath d='M48 128c28-38 56-52 84-42s47 2 76-24 67-31 114 66z' fill='%23d8f4e1'/%3E%3Cpath d='M88 128c28-25 50-28 74-9s45 19 67 0 54-24 95 9z' fill='%231d2523' opacity='.12'/%3E%3C/svg%3E");
  background-size: cover;
}

.event-art span {
  display: block;
  width: 72px;
  height: 72px;
  margin: 26px 0 0 26px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.72);
  box-shadow: inset 0 -12px 0 rgba(17, 24, 39, 0.08);
}

.rsvp-buttons {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin-top: 12px;
}

.drawer-footer {
  margin-top: auto;
  padding-top: 10px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.mode-title.setup {
  color: #a78bfa;
}

.mode-title.poll {
  color: var(--best);
}

.mode-title.event {
  color: var(--maybe);
}

.mode-title.preview {
  color: var(--muted);
}

.setup-launchpad,
.send-gate-actions {
  flex: 0 0 auto;
  display: grid;
  gap: 8px;
}

.quiet-button,
.more-button,
.send-button {
  border: 1px solid var(--line);
  background: rgba(15, 23, 42, 0.38);
  color: var(--ink);
  font-weight: 760;
}

.launch-button {
  min-height: 42px;
  border: 1px solid var(--line);
  border-radius: 8px;
  font-weight: 820;
  color: var(--ink);
  background: rgba(15, 23, 42, 0.4);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 0 14px;
}
.launch-button > svg {
  flex: 0 0 auto;
}

.launch-button.primary {
  border-color: rgba(99, 102, 241, 0.5);
  background: rgba(99, 102, 241, 0.34);
  color: white;
}

.launch-button.secondary {
  background: rgba(15, 23, 42, 0.52);
}

.launch-button[disabled],
.launch-button[aria-disabled="true"],
.launch-button.is-disabled {
  opacity: 0.42;
  cursor: default;
  filter: saturate(0.6);
}

.bottom-rail {
  position: relative;
  grid-column: 1 / -1;
  grid-row: 2;
  min-width: 0;
  min-height: 0;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  gap: 8px;
  padding: 10px;
}

.bottom-handle {
  min-height: 34px;
  display: grid;
  overflow: hidden;
  place-items: center;
  align-items: center;
  gap: 8px;
  transition:
    opacity 180ms ease,
    min-height 220ms ease,
    transform 220ms ease;
}

.bottom-handle .rail-open {
  width: min(260px, 100%);
  height: 34px;
  display: inline-flex;
  justify-content: center;
  padding: 0 12px;
}

.bottom-handle .close-button {
  opacity: 0;
  pointer-events: none;
}

.bottom-rail:hover:not(.suppressed) .bottom-handle .close-button,
.bottom-rail.pinned .bottom-handle .close-button {
  opacity: 1;
  pointer-events: auto;
}

/* --- Setup-mode Invites tray --- */

/* In setup mode, the Invitations tray defaults to a single-line
   handle (title + pin) and only grows to full size when the user
   expands it. Collapsing the bottom row to ~44px keeps the tray
   from showing any names while still leaving the title visible. */
.app-shell.setup-mode:not(:has(.setup-invites.expanded)) {
  --bottom-rail: var(--bottom-rail-collapsed);
}

.setup-invites {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 2px 10px 6px;
}

.setup-invites.expanded {
  padding-bottom: 4px;
}

.invites-header {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  padding: 4px 0 6px;
}

.invites-open {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 10px;
  border: 0;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  border-radius: 6px;
}

.invites-open:hover {
  background: rgba(255, 255, 255, 0.05);
}

.setup-invites.expanded .invites-open {
  cursor: default;
}

.setup-invites.expanded .invites-open:hover {
  background: transparent;
}

.invites-title {
  font-size: 1rem;
  font-weight: 820;
  color: var(--ink);
  letter-spacing: 0;
  text-transform: none;
  line-height: 1.1;
}

/* Keep the title's font identical between collapsed + expanded
   states — earlier it grew from 1rem → 1.5rem on expand, which
   was disruptive. */

.invites-body {
  min-width: 0;
  display: block;
}

.setup-invites .people-strip {
  width: 100%;
  align-items: stretch;
}

.bottom-content {
  min-width: 0;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  opacity: 0;
  pointer-events: none;
  transition: grid-template-columns 280ms var(--ease-spring);
}

.invite-teasers {
  flex: 1;
  min-width: 0;
  width: 100%;
  display: flex;
  justify-content: center;
  gap: 5px;
  overflow: hidden;
  pointer-events: none;
}

.invite-teaser {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 26px;
  border-radius: 999px;
  padding: 2px 8px 2px 3px;
  color: rgba(248, 250, 252, 0.88);
  background: rgba(15, 23, 42, 0.48);
  border: 1px solid rgba(255, 255, 255, 0.18);
  white-space: nowrap;
  flex-shrink: 0;
}

.invitee-avatar {
  display: inline-grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(248, 250, 252, 0.84);
  color: #0f172a;
  font-weight: 850;
  flex-shrink: 0;
  overflow: hidden;
}

.invitee-avatar.has-image {
  background: transparent;
  color: transparent;
}

.invitee-avatar.has-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.invitee-avatar.square {
  border-radius: 4px;
}

.invite-teaser-avatar {
  width: 20px;
  height: 20px;
  font-size: 0.55rem;
}

.invite-teaser-name-full,
.invite-teaser-name-first {
  font-size: 0.72rem;
  font-weight: 740;
  padding-right: 2px;
}

/* Tier 1: avatar + full name (or full phone/email) */
.invite-teasers[data-tier="1"] .invite-teaser-name-full { display: inline; }
.invite-teasers[data-tier="1"] .invite-teaser-name-first { display: none; }

/* Tier 2: avatar + first-word-of-name (phones/emails stay full) */
.invite-teasers[data-tier="2"] .invite-teaser-name-full { display: none; }
.invite-teasers[data-tier="2"] .invite-teaser-name-first { display: inline; }

/* Tier 3: avatar only */
.invite-teasers[data-tier="3"] .invite-teaser { padding: 2px 3px; }
.invite-teasers[data-tier="3"] .invite-teaser-name-full,
.invite-teasers[data-tier="3"] .invite-teaser-name-first { display: none; }

.bottom-rail:hover:not(.suppressed) .bottom-content,
.bottom-rail.pinned .bottom-content {
  opacity: 1;
  pointer-events: auto;
}

.bottom-content.invites-only {
  grid-template-columns: 1fr;
}

.bottom-content.people-active,
.app-shell.people-pinned .bottom-content {
  grid-template-columns: minmax(0, 1.65fr) minmax(220px, 0.55fr);
}

.bottom-content.comments-active,
.app-shell.comments-pinned .bottom-content {
  grid-template-columns: minmax(220px, 0.55fr) minmax(0, 1.65fr);
}

.sub-tray {
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(15, 23, 42, 0.36);
  padding: 8px;
}

.sub-tray-head {
  color: var(--muted);
  font-weight: 820;
}

.sub-tray-actions {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.sub-tray .close-button {
  width: 32px;
  min-height: 32px;
}

.sub-tray-body {
  min-width: 0;
  margin-top: 8px;
}

.people-strip {
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: center;
  gap: 9px;
  padding: 2px;
}

.people-input-row {
  display: flex;
  gap: 8px;
  margin-top: 4px;
  align-items: stretch;
}

.people-input {
  flex: 1 1 80%;
  min-width: 0;
  height: 38px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(15, 23, 42, 0.44);
  /* Padding + line-height mirror the date-entry textarea exactly
     (`.setup-command-bar textarea` → padding 8/11, line-height 22).
     Padding shoulders the vertical centering instead of a clever
     line-height trick — same content-box math = same baseline =
     same caret position the mirror paints under. */
  padding: 8px 12px;
  line-height: 22px;
  color: var(--ink);
  font: inherit;
  /* === Caret/mirror glyph alignment ===
     ANY <input> that sits on top of a transparent-text mirror MUST
     pin these metrics. Browsers apply kerning + ligatures + locale-
     dependent number variants to <input> by default, but a generic
     <div> mirror (display:flex / inline-block / etc.) doesn't —
     leading to a 1-3px caret drift that grows across the string.
     Same defensive set used by .setup-command-bar input. Treat as
     a checklist item for every new typing tray with overlays. */
  font-kerning: none;
  font-feature-settings: normal;
  font-variant-ligatures: none;
  font-variant-numeric: normal;
  letter-spacing: 0;
  word-spacing: 0;
}

.people-input::placeholder {
  color: rgba(248, 250, 252, 0.5);
  font-weight: 400;
}

.add-from-lists-button {
  flex: 0 0 20%;
  min-width: 0;
  height: 38px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(15, 23, 42, 0.52);
  color: var(--ink);
  font-weight: 720;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.add-from-lists-button:hover {
  background: rgba(15, 23, 42, 0.7);
}

.person-chip {
  flex: 0 0 auto;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  background: rgba(15, 23, 42, 0.48);
  padding: 2px 10px 2px 3px;
  height: 28px;
  text-align: left;
  display: inline-flex;
  align-items: center;
  transition: background 0.12s ease, border-color 0.12s ease,
              box-shadow 0.12s ease, transform 0.12s ease;
}

/* Mutual-highlight from the Guest List input ↔ chip hover. Picks up
   the same indigo / ghost palette used by the date-entry chip
   highlight so the two surfaces feel like one system. */
.person-chip--hovered {
  border-color: color-mix(in srgb, var(--ghost) 60%, rgba(255, 255, 255, 0.25));
  background: color-mix(in srgb, var(--ghost) 22%, rgba(15, 23, 42, 0.6));
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--ghost) 45%, transparent),
    0 0 14px color-mix(in srgb, var(--ghost) 35%, transparent);
  transform: translateY(-1px);
}

/* Right-click context menu on guest chips. Portal-rendered, so it
   sits above everything else without z-index gymnastics. */
.guest-context-menu {
  z-index: 9999;
  min-width: 160px;
  background: rgba(15, 23, 42, 0.96);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 10px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.guest-context-menu button {
  appearance: none;
  background: transparent;
  border: 0;
  color: var(--ink);
  text-align: left;
  padding: 8px 10px;
  border-radius: 6px;
  font: inherit;
  cursor: pointer;
}
.guest-context-menu button:hover {
  background: rgba(255, 255, 255, 0.06);
}
.guest-context-menu button.danger {
  color: #fda4af;
}
.guest-context-menu button.danger:hover {
  background: rgba(244, 63, 94, 0.14);
}

/* Edit-guest popover. Scrim swallows outside clicks; the card
   itself anchors above (or below) the chip via inline `top/left`. */
.guest-edit-scrim {
  position: fixed;
  inset: 0;
  background: rgba(2, 6, 23, 0.35);
  z-index: 9998;
}
.guest-edit-popover {
  z-index: 9999;
  background: rgba(15, 23, 42, 0.98);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 12px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.55);
  padding: 14px 14px 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.guest-edit-header {
  font-weight: 800;
  font-size: 0.95rem;
  color: var(--ink);
  margin-bottom: 2px;
}
.guest-edit-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.guest-edit-field > span {
  font-size: 0.74rem;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.guest-edit-field input,
.guest-edit-field textarea {
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(2, 6, 23, 0.5);
  color: var(--ink);
  padding: 6px 10px;
  font: inherit;
  resize: vertical;
}
.guest-edit-field input {
  height: 32px;
  padding: 0 10px;
}
.guest-edit-field textarea {
  min-height: 60px;
  line-height: 1.4;
}
.guest-edit-field input:focus,
.guest-edit-field textarea:focus {
  outline: none;
  border-color: color-mix(in srgb, var(--ghost) 60%, rgba(255, 255, 255, 0.3));
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--ghost) 35%, transparent);
}
.guest-edit-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 4px;
}
.guest-edit-actions button {
  appearance: none;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--ink);
  border-radius: 8px;
  padding: 6px 12px;
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}
.guest-edit-actions button:hover {
  background: rgba(255, 255, 255, 0.1);
}
.guest-edit-actions button.primary {
  background: color-mix(in srgb, var(--ghost) 70%, rgba(15, 23, 42, 0.3));
  border-color: color-mix(in srgb, var(--ghost) 80%, transparent);
}
.guest-edit-actions button.primary:hover {
  background: color-mix(in srgb, var(--ghost) 85%, rgba(15, 23, 42, 0.2));
}

/* Guest List input — token mirror. Real <input> sits on top with
   transparent text so the caret/selection still work; the mirror
   div sits behind and paints each person token with chip-style
   pills. Mouse hover is captured via elementsFromPoint on the
   input's onMouseMove, so the mirror keeps pointer-events:none
   and doesn't intercept clicks or drag-selection. */
.guest-input-wrap {
  position: relative;
  flex: 1 1 80%;
  min-width: 0;
  display: flex;
}
.guest-input-wrap input.people-input {
  position: relative;
  z-index: 2;
  flex: 1 1 100%;
  background: rgba(15, 23, 42, 0.44);
  color: transparent;
  caret-color: var(--ink);
}
.guest-input-wrap input.people-input::placeholder {
  color: rgba(248, 250, 252, 0.5);
}
/* === Mirror glyph alignment ===
   Padding / border MUST match the input EXACTLY so the content box
   sits in the same place. Token spans render as `display: inline`
   with background+border-radius only — NO padding / margin / border
   on the spans, because any of those would shift the glyphs left
   or right of where the caret in the input below is drawn.
   See .people-input above for the matching font-metric reset. */
.guest-mirror {
  position: absolute;
  inset: 0;
  z-index: 1;
  border: 1px solid transparent;
  border-radius: 8px;
  /* MUST match .people-input padding + line-height EXACTLY so the
     mirror's first text baseline lands on the input's text baseline.
     Flex would turn every span into a flex item and break
     glyph-by-glyph alignment, so we stick with block + line-height. */
  padding: 8px 12px;
  line-height: 22px;
  box-sizing: border-box;
  display: block;
  white-space: pre;
  /* The input scrolls horizontally when content outgrows its width;
     the mirror has to scroll in lock-step (see input onScroll
     handler). Hidden scrollbar so it doesn't peek through. */
  overflow-x: scroll;
  overflow-y: hidden;
  scrollbar-width: none;
  color: transparent;       /* default — only person tokens carry ink */
  font: inherit;
  font-kerning: none;
  font-feature-settings: normal;
  font-variant-ligatures: none;
  font-variant-numeric: normal;
  letter-spacing: 0;
  word-spacing: 0;
  pointer-events: none;
}
.guest-mirror::-webkit-scrollbar { display: none; }
/* Guest tokens — visual treatment matches the date entry's pattern:
   - Base state: faux-bold via text-shadow, no background pill
     (same as .command-mirror .token-date in the date tray).
   - Hover state: bright purple pill + ring + heavier text-shadow,
     identical formula to .command-mirror .token-hovered.
   Inline display + background-only pill is the rule for ANY token
   sitting on a transparent-text input — padding / margin / border
   / font-weight would shift glyphs away from the caret. */
.guest-token {
  display: inline;
  color: var(--ink);
  /* Faux-bold for parsed-guest readability without changing glyph
     advance widths — same trick as .command-mirror .token-date. */
  text-shadow:
    0.4px 0 0 currentColor,
    -0.4px 0 0 currentColor;
}
.guest-token-hovered {
  color: #ffffff;
  background: color-mix(in srgb, var(--ghost) 60%, transparent);
  border-radius: 4px;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--ghost) 80%, transparent);
  text-shadow:
    0.6px 0 0 currentColor,
    -0.6px 0 0 currentColor;
}
.guest-token-warn {
  color: #fde68a;
}
.guest-token-warn.guest-token-hovered {
  background: linear-gradient(180deg, rgba(250, 204, 21, 0.32), rgba(251, 191, 36, 0.42));
  color: #1f1300;
  box-shadow: 0 0 0 1px rgba(250, 204, 21, 0.7);
}
.guest-token-group {
  color: #fbcfe8;
}
.guest-token-group.guest-token-hovered {
  background: color-mix(in srgb, #ec4899 60%, transparent);
  color: #ffffff;
  box-shadow: 0 0 0 1px rgba(244, 114, 182, 0.8);
}
.guest-sep {
  color: rgba(255, 255, 255, 0.5);
}

/* Guest-tip connector — vertical line + dot extending DOWN from
   the bottom of the tip, through the chip strip, into the input.
   The line length is set by the JS via --guest-tip-connector-gap
   so it always clears the live chip-strip height. */
.coach-mark-connector--guest-tip {
  left: 50%;
  top: 100%;
  width: 1.5px;
  height: var(--guest-tip-connector-gap, 28px);
  transform: translateX(-50%);
  background: color-mix(in srgb, var(--ghost) 70%, rgba(255, 255, 255, 0.4));
  position: absolute;
}
.coach-mark-dot--guest-tip {
  left: 50%;
  top: calc(100% + var(--guest-tip-connector-gap, 28px) - 6px);
  transform: translateX(-50%);
}

.avatar-row {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.person-chip-avatar {
  width: 22px;
  height: 22px;
  font-size: 0.6rem;
}

.person-name {
  font-size: 0.82rem;
  font-weight: 820;
  color: rgba(248, 250, 252, 0.95);
  white-space: nowrap;
}

.comment-box {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 8px;
  align-items: end;
}

.comment-box label {
  grid-column: 1 / -1;
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 760;
}

.comment-box input {
  min-width: 0;
  min-height: 38px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(15, 23, 42, 0.44);
  padding: 0 11px;
  color: var(--ink);
}

.setup-command-bar {
  position: fixed;
  z-index: 50;
  left: 50%;
  bottom: calc(var(--bottom-rail) - 44px);
  width: calc(100vw - 56px);
  min-height: 58px;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 10px;
  border: 1px solid rgba(255, 255, 255, 0.52);
  border-radius: 999px;
  padding: 9px 12px;
  background: rgba(15, 23, 42, 0.68);
  box-shadow: var(--shadow);
  backdrop-filter: blur(26px) saturate(180%);
  transform: translateX(-50%);
}

.setup-command-bar::before {
  content: "";
  position: absolute;
  left: 22px;
  right: 22px;
  top: -14px;
  height: 14px;
  border-radius: 999px 999px 0 0;
  background: radial-gradient(ellipse at center, rgba(99, 102, 241, 0.35), transparent 68%);
  box-shadow: 0 -5px 24px rgba(99, 102, 241, 0.22);
}

.setup-command-bar.embedded {
  position: relative;
  top: auto;
  left: auto;
  right: auto;
  bottom: auto;
  width: auto;
  min-height: 0;
  margin: 12px 0 0;
  padding: 10px 12px;
  border-radius: 12px;
  background: rgba(15, 23, 42, 0.55);
  border-color: rgba(255, 255, 255, 0.18);
  transform: none;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  /* Snap between focused and embedded shapes — never animate. Animating
     position/size/border-radius produces an obviously-broken intermediate
     "double line rounded" frame as the pill collapses back into a panel. */
  transition: none !important;
  box-shadow: none;
  backdrop-filter: none;
  z-index: 5;
}

.setup-command-bar.embedded::before {
  display: none;
}

.setup-command-bar.embedded .setup-command-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: rgba(226, 232, 240, 0.65);
  margin: 0;
}

.setup-command-bar.embedded .command-input-wrap {
  width: 100%;
}

.setup-command-warn-inline {
  position: relative;
  margin-left: 0;
  cursor: pointer;
  outline: none;
}

.setup-command-warn-inline:hover,
.setup-command-warn-inline:focus-visible {
  background: rgba(99, 102, 241, 1);
  box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.3);
}

.setup-command-warn-tooltip {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  display: none;
  flex-direction: column;
  gap: 3px;
  padding: 8px 10px;
  border-radius: 8px;
  background: rgba(2, 6, 23, 0.96);
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
  z-index: 200;
  min-width: 130px;
  pointer-events: none;
  text-transform: none;
  letter-spacing: 0;
}

.setup-command-warn-inline:hover .setup-command-warn-tooltip,
.setup-command-warn-inline:focus-visible .setup-command-warn-tooltip,
.setup-command-warn-inline:focus .setup-command-warn-tooltip {
  display: flex;
}

.setup-command-warn-tooltip-title {
  font-size: 0.68rem;
  font-weight: 700;
  color: rgba(226, 232, 240, 0.7);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding-bottom: 3px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.setup-command-warn-tooltip-row {
  font-size: 0.78rem;
  font-weight: 600;
  color: #f1f5f9;
  white-space: nowrap;
}

/* Focus-within: expand horizontally to the left for more typing room.
   Position absolute so the wider bar overlays the calendar without
   reflowing the tray content. Pinned to the rail-content's right edge.
   Width is large so the input shows a long string of @-times without
   the right side getting cut off. */
.setup-command-bar.embedded.focused, .setup-command-bar.embedded:focus-within {
  /* Pin to the viewport so the bar can stretch nearly the full viewport
     width regardless of the rail's grid size. Sits ABOVE the bottom rail
     (the invitations / setup invitees row) so it doesn't cover it. */
  position: fixed;
  /* Override any inherited visibility:hidden from a closed rail (e.g.
     user unpinned the right tray while still typing) so the focused bar
     stays usable on screen. */
  visibility: visible;
  right: 16px;
  left: 16px;
  /* Clear the bottom rail (invitations / setup invitees), accounting for
     the app-shell gap. --bottom-rail is 108px in setup mode; total ~136px. */
  bottom: calc(var(--bottom-rail) + 28px);
  width: auto;
  max-width: none;
  margin: 0;
  padding: 10px 14px;
  background: rgba(15, 23, 42, 0.94);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55);
  border-color: rgba(99, 102, 241, 0.55);
  /* Above day-cell:hover (z=80) and most other in-page UI so the floating
     bar always paints on top of the calendar. */
  z-index: 220;
  /* Reserve room on the right for the Check submit button. */
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: stretch;
  gap: 10px;
}

.setup-command-bar.embedded.focused .setup-command-label,
.setup-command-bar.embedded:focus-within .setup-command-label {
  grid-column: 1 / 2;
}

.setup-command-bar.embedded.focused .command-input-wrap,
.setup-command-bar.embedded:focus-within .command-input-wrap {
  grid-column: 1 / 2;
}

.setup-command-submit {
  grid-column: 2;
  grid-row: 1 / 3;
  align-self: stretch;
  display: inline-grid;
  place-items: center;
  width: 44px;
  min-width: 44px;
  border: 1px solid rgba(99, 102, 241, 0.55);
  border-radius: 12px;
  background: rgba(99, 102, 241, 0.22);
  color: #e0e7ff;
  cursor: pointer;
  transition: background 160ms ease, color 160ms ease, border-color 160ms ease;
}

.setup-command-submit:hover {
  background: rgba(99, 102, 241, 0.42);
  color: #ffffff;
  border-color: rgba(167, 139, 250, 0.85);
}

/* Hide the Check button until the bar is in its expanded/focused state. */
.setup-command-bar.embedded:not(.focused):not(:focus-within) .setup-command-submit {
  display: none;
}

/* Persistent variant: the section is `display: block`, so the check
   button can't grid-place itself — overlay it absolutely against the
   right side of the textarea pill. Always visible (even when blurred)
   so the user has a clear "I'm done" affordance any time the bar is
   on screen. Textarea + mirror get padding-right to keep their text
   from sliding under the button. */
.setup-command-bar.persistent .setup-command-submit {
  position: absolute;
  /* Top-aligned (was vertically centered) so the check sits at the
     top-right corner of the textarea, mirroring the address-done-btn
     placement. Vertical center shifted around as the textarea grew
     with content, which felt unstable. */
  top: 4px;
  right: 4px;
  width: 28px;
  min-width: 28px;
  height: 24px;
  border-radius: 8px;
  z-index: 3;
  display: inline-grid;
  place-items: center;
}
.setup-command-bar.persistent .command-input-wrap textarea,
.setup-command-bar.persistent .command-input-wrap .command-mirror {
  padding-right: 40px;
}

/* Promote the rail to its own elevated stacking context while focused so
   the absolutely-positioned bar above isn't trapped under sibling elements
   that have transient z-index bumps (e.g. .day-cell:hover at z=80). */
.options-rail:focus-within {
  z-index: 220;
}

.options-rail .rail-content {
  overflow: visible;
}

/* Was 0.84rem / 760-weight from the era when the SetupCommandBar's
   label was its own visual style. Now this label uses the shared
   .field-label class (same as the Address: label) — match those
   metrics exactly so the two labels read as one system. */
.setup-command-bar label {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* Mirror token spans must NOT inherit the label's 0.84rem —
   they need to match the input's actual font size exactly.
   Reset here in case any ancestor rule bleeds in. */
.command-mirror .token-valid,
.command-mirror .token-invalid {
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
  word-spacing: inherit;
}

.setup-command-bar input,
.setup-command-bar textarea,
.setup-people-entry input {
  min-width: 0;
  min-height: 38px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(2, 6, 23, 0.34);
  padding: 8px 11px;
  color: var(--ink);
  /* Ensure font matches the mirror div exactly (inputs don't inherit font by default) */
  font: inherit;
  /* Defensive: Chrome/Safari can render textarea text with kerning or
     ligatures even when the same font is "inherit" on a sibling div,
     producing a 1–2 character horizontal drift between the caret and the
     mirror tokens. Pin both to identical metrics. */
  font-kerning: none;
  font-feature-settings: normal;
  font-variant-ligatures: none;
  font-variant-numeric: normal;
  letter-spacing: 0;
  word-spacing: 0;
}

.setup-command-bar textarea {
  display: block;
  resize: none;
  width: 100%;
  height: 38px;
  line-height: 22px;
  overflow-y: hidden;
  overflow-x: hidden;
  white-space: nowrap;
  outline: none;
  transition: none !important;
}

/* Focused embedded bar: textarea grows vertically and wraps long content
   instead of horizontal-scrolling a single line. The min-height forces
   at least three lines so it visibly opens up the moment focus lands,
   even before any wrapping kicks in. */
.setup-command-bar.embedded.focused textarea,
.setup-command-bar.embedded:focus-within textarea {
  height: 84px;
  min-height: 84px;
  max-height: 220px;
  overflow-y: auto;
  overflow-x: hidden;
  white-space: pre-wrap;
  word-break: break-word;
  border-radius: 14px;
  line-height: 22px;
}

/* ── Command bar mirror overlay ────────────────────────────────────────────
   The real <input> sits on top for editing (caret, selection, events) but
   its text is rendered transparent.  A sibling .command-mirror div is
   absolutely positioned behind it and shows each token with per-token color:
   committed text and commas stay white; only the current unparseable draft
   token turns red.  This avoids coloring the entire input red. */
.command-input-wrap {
  position: relative;
  min-width: 0;
}

.command-input-wrap input,
.command-input-wrap textarea {
  width: 100%;
  /* Text is transparent so only the mirror text is visible, but the caret
     and selection highlight still render normally. The caret needs to be
     visible on EVERY chip background — including amber/yellow bands where
     a white caret nearly disappeared. Indigo (#818cf8) sits between the
     warm and cool chip colors and reads on every band + on the dark
     between-chip background. */
  color: transparent;
  caret-color: #818cf8;
}

.command-input-wrap input::placeholder,
.command-input-wrap textarea::placeholder {
  /* Match .address-input::placeholder so the two inputs read as
     one system. */
  color: rgba(199, 210, 254, 0.48);
  font-style: normal;
}

/* Invalid state: red border + glow around the wrapper's input */
.command-input-wrap.has-invalid input,
.command-input-wrap.has-invalid textarea {
  border-color: rgba(239, 68, 68, 0.7);
  box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.14);
}

/* Mirror — absolutely overlays the input, pointer-events off so clicks pass through.
   Uses overflow-x: scroll (scrollbar hidden) so mirror.scrollLeft = input.scrollLeft
   in JS keeps the coloured text pixel-perfectly aligned with the invisible input text.
   Padding must match the textarea exactly (see .setup-command-bar textarea: 8px 11px)
   so the rendered tokens sit directly under the caret. */
.command-mirror {
  position: absolute;
  inset: 0;
  padding: 8px 11px;
  /* Match the textarea's 1px border so the inner content box aligns. */
  border: 1px solid transparent;
  box-sizing: border-box;
  display: block;
  pointer-events: none;
  overflow-x: scroll;
  overflow-y: hidden;
  scrollbar-width: none;
  border-radius: 999px;
  white-space: nowrap;
  font: inherit;
  line-height: 22px;
  /* Match the textarea's defensive font metrics so caret and mirror text
     render in the exact same x positions. */
  font-kerning: none;
  font-feature-settings: normal;
  font-variant-ligatures: none;
  font-variant-numeric: normal;
  letter-spacing: 0;
  word-spacing: 0;
}

/* When the bar is focus-expanded, the mirror wraps to match the textarea. */
.setup-command-bar.embedded.focused .command-mirror,
.setup-command-bar.embedded:focus-within .command-mirror {
  white-space: pre-wrap;
  word-break: break-word;
  overflow-x: hidden;
  border-radius: 14px;
}

.command-mirror::-webkit-scrollbar {
  display: none;
}

.command-mirror .token-valid {
  color: var(--ink);
}

.command-mirror .token-invalid {
  color: #fca5a5;
}

.command-mirror .token-date-time,
.command-mirror .token-date,
.command-mirror .token-time {
  white-space: nowrap;
}

/* Faux-bold via text-shadow, NOT font-weight: a real bold weight makes
   each glyph slightly wider than the textarea's regular-weight glyphs
   beneath, accumulating drift across a multi-token string. Stacked
   shadows thicken the strokes without changing advance widths, so
   caret + mirror stay glyph-aligned. Dates read distinctly heavier
   than times. */
.command-mirror .token-date {
  text-shadow:
    0.4px 0 0 currentColor,
    -0.4px 0 0 currentColor;
}

.command-mirror .token-time {
  opacity: 0.78;
}

/* Cross-highlight: when a day cell on the calendar or a chip in the
   right tray is hovered, the corresponding token in the text tray
   pops with a colored background pill + bolder text. Same faux-bold
   via stacked text-shadow trick so caret alignment stays intact.
   The background lives on the inner .token-date span (not the
   outer wrapper) so the " @ " separator between a date and its
   time chip stays UNCOLORED — wrap the date in a chip background,
   leave the @ glyph bare, then the time chip handles its own
   amber state via .token-time-hovered below. */
.command-mirror .token-hovered {
  color: #ffffff;
  opacity: 1;
  text-shadow:
    0.6px 0 0 currentColor,
    -0.6px 0 0 currentColor;
}
.command-mirror .token-hovered .token-date,
.command-mirror .token-hovered.token-date {
  background: color-mix(in srgb, var(--ghost) 60%, transparent);
  border-radius: 4px;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--ghost) 80%, transparent);
}
/* Token tokens that aren't compound date-time chunks still want
   the whole chip to pop on hover (e.g. a bare "Jun 14," or a
   trailing "4 PM" continuation). */
.command-mirror .token-hovered.token-time,
.command-mirror .token-hovered.token-date {
  /* Already styled above via .token-hovered .token-date for the
     compound case; for the standalone case the .token-hovered
     class IS on the colored span itself so we rely on the rule
     just above. */
}
/* The uncolored " @ " separator between a date and a time inside
   a compound date-time chunk. Always plain text — no chip
   background, no faux-bold, no opacity dimming. */
.command-mirror .token-at-sep {
  background: transparent !important;
  box-shadow: none !important;
  text-shadow: none !important;
  opacity: 0.65;
}

/* Time-block cross-highlight: AMBER variant for when a SPECIFIC time
   block is hovered on the calendar or rail. Only the inner `.token-time`
   slice of a date-time chunk lights up — the date portion stays in its
   normal indigo styling so "Jun 9 @ 1-2 AM" reads as "[purple date]
   [amber time]". For pure time tokens (kind="time"), the inner span
   IS the whole token so the effect covers the entire chunk. */
.command-mirror .token-time-hovered .token-time {
  color: #1f1300;
  opacity: 1;
  background: linear-gradient(180deg, #fde68a, #fbbf24);
  border-radius: 4px;
  box-shadow: 0 0 0 1px rgba(250, 204, 21, 0.7);
  text-shadow:
    0.6px 0 0 currentColor,
    -0.6px 0 0 currentColor;
}


.setup-command-bar strong {
  white-space: nowrap;
}

/* Note indicator on top-tray chips — small sticky-note glyph that
   appears on the right of the chip when dayNotes[dateKey] has
   content. Hovering shows the note text via the native title
   attribute; clicking opens the ChipNotePopover for editing. */
.setup-option-note-indicator {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  margin-left: 6px;
  border-radius: 4px;
  color: rgba(251, 191, 36, 0.95);
  background: rgba(251, 191, 36, 0.15);
  border: 1px solid rgba(251, 191, 36, 0.35);
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
  flex-shrink: 0;
}
.setup-option-note-indicator:hover,
.setup-option-note-indicator:focus-visible {
  outline: 0;
  background: rgba(251, 191, 36, 0.28);
  border-color: rgba(251, 191, 36, 0.6);
  color: #fde68a;
}

/* Popover-based note editor anchored to a chip. Same glass shell
   as the other portaled menus (.setup-context-menu) so it reads
   as part of the same UI family, just shaped like a small inline
   editor instead of a list of actions. */
.chip-note-popover {
  position: fixed;
  z-index: 100060;
  width: 280px;
  padding: 10px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 10px;
  background: rgba(15, 23, 42, 0.95);
  box-shadow: var(--shadow-soft), 0 16px 40px rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(22px) saturate(180%);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.chip-note-popover-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.chip-note-popover-title {
  font-size: 0.7rem;
  font-weight: 720;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(251, 191, 36, 0.9);
}
.chip-note-popover-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.06);
  color: rgba(248, 250, 252, 0.7);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.chip-note-popover-close:hover {
  background: rgba(255, 255, 255, 0.14);
  color: #fff;
}
.chip-note-popover-textarea {
  width: 100%;
  min-height: 64px;
  padding: 8px 10px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 8px;
  background: rgba(2, 6, 23, 0.55);
  color: var(--ink);
  font-family: inherit;
  font-size: 0.84rem;
  resize: vertical;
}
.chip-note-popover-textarea:focus-visible {
  outline: 0;
  border-color: color-mix(in srgb, var(--ghost) 65%, rgba(255, 255, 255, 0.2));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ghost) 25%, transparent);
}
.chip-note-popover-actions {
  display: flex;
  justify-content: flex-end;
}
.chip-note-popover-delete {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: rgba(252, 165, 165, 0.85);
  font-size: 0.78rem;
  font-weight: 700;
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.chip-note-popover-delete:hover {
  background: rgba(239, 68, 68, 0.12);
  color: #fca5a5;
}

.setup-context-menu {
  position: fixed;
  /* Above the invite tray (z=200ish), the publish/poll rails, and the
     calendar coach marks (which sit around 99999). Right-click menus
     should always render over everything except modals/portals. */
  z-index: 100050;
  min-width: 138px;
  padding: 5px;
  border: 1px solid rgba(255, 255, 255, 0.58);
  border-radius: 8px;
  background: rgba(15, 23, 42, 0.78);
  box-shadow: var(--shadow-soft);
  backdrop-filter: blur(22px) saturate(180%);
}

.setup-context-menu button {
  width: 100%;
  min-height: 34px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--ink);
  padding: 0 10px;
  text-align: left;
  font-weight: 760;
}

.setup-context-menu button:hover {
  background: rgba(255, 255, 255, 0.06);
  color: var(--ink);
}

.setup-context-menu button.destructive:hover {
  background: rgba(239, 68, 68, 0.1);
  color: #fca5a5;
}

/* Group separator between menu sections (Times / Notes / Block).
   Hairline rule with a touch of margin so the boundary reads without
   adding visual weight. Items keep tight 5px padding on the menu
   wrapper; the separator's margin extends slightly into that padding. */
.setup-context-menu-separator {
  height: 1px;
  margin: 4px -1px;
  background: rgba(255, 255, 255, 0.08);
  pointer-events: none;
}

.setup-people-entry {
  display: grid;
  gap: 8px;
}

.setup-people-entry label {
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 760;
}

.person-chip .avatar.works,
.person-chip .avatar.provisional {
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.24), 0 8px 18px rgba(17, 24, 39, 0.1);
}

.person-chip:has(.person-status):has(.avatar.works) {
  border-style: solid;
}

.send-gate-backdrop {
  position: fixed;
  inset: 0;
  /* Above the guide layer (coach marks 99999, welcome card 100001,
     chip-note popover 100060). A modal that a coach mark can cover is
     not a modal — this also fixes the pre-existing case where the
     description tip overlapped the send gate. */
  z-index: 100100;
  display: grid;
  place-items: center;
  background: rgba(2, 6, 23, 0.42);
  backdrop-filter: blur(7px);
}

.send-gate {
  position: relative;
  width: min(440px, calc(100vw - 40px));
  border: 1px solid rgba(255, 255, 255, 0.56);
  border-radius: 8px;
  padding: 16px;
  background: rgba(15, 23, 42, 0.78);
  box-shadow: var(--shadow);
  backdrop-filter: blur(28px) saturate(180%);
  animation: send-gate-lift 420ms var(--ease-pop) both;
}

.send-gate p {
  color: var(--muted);
  line-height: 1.4;
}

.app-shell.send-gate-open .top-chrome,
.app-shell.send-gate-open .stage-shell,
.app-shell.send-gate-open .setup-command-bar {
  opacity: 0.62;
  filter: blur(1.2px) saturate(0.78);
  transform: scale(0.95);
  transform-origin: center;
}

.app-shell.send-gate-open > .stage-shell {
  opacity: 0.62 !important;
  filter: blur(1.2px) saturate(0.78) !important;
  transform: scale(0.95) !important;
}

.app-shell.send-gate-open .calendar-canvas,
.app-shell.send-gate-open .calendar-panel,
.app-shell.send-gate-open .edge-tray {
  opacity: 0.62 !important;
  filter: blur(1.2px) saturate(0.78) !important;
  transform: scale(0.95) !important;
  transform-origin: center;
}

@keyframes guide-pulse {
  0%,
  100% {
    opacity: 0.72;
    transform: scale(1);
  }
  50% {
    opacity: 1;
    transform: scale(1.025);
  }
}

@keyframes send-gate-lift {
  from {
    opacity: 0;
    transform: translateY(18px) scale(0.96);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

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

  .ambient-orb {
    animation: none !important;
    translate: none !important;
    scale: none !important;
  }

  .orb-parallax-shell {
    transition: none !important;
  }
}

/* (Legacy @media (max-width: 900px) block removed — was hiding the
   options-rail entirely, zero-ing out --right-rail, and forcing the
   top-chrome / stage-shell to single columns. Superseded by the
   newer @media (max-width: 768px) + @media (hover: none) blocks
   at the very end of this file, which keep the rail visible as a
   thin labeled strip.) */

/* ─── Time picker (mock) ─────────────────────────────────────────────── */

.time-collapse-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(2, 6, 23, 0.5);
  backdrop-filter: blur(4px);
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
}

.time-collapse-modal {
  width: min(380px, calc(100vw - 32px));
  padding: 18px 22px 16px;
  border-radius: 14px;
  background: rgba(15, 23, 42, 0.94);
  border: 1px solid color-mix(in srgb, #fbbf24 30%, rgba(255, 255, 255, 0.12));
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
  color: var(--ink);
}
.time-collapse-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 10px;
  font-size: 1.05rem;
  font-weight: 800;
  letter-spacing: -0.005em;
  color: #fde68a;
}
.time-collapse-title svg { flex: 0 0 auto; }

.time-collapse-copy {
  margin: 0 0 14px;
  font-size: 0.88rem;
  line-height: 1.5;
  color: rgba(226, 232, 240, 0.94);
}
.time-collapse-copy strong {
  color: #ffffff;
  font-weight: 720;
}

.time-collapse-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

/* ─────────── Mode status pill ───────────
   Segmented "Mode: Full Day / Time slots" indicator centered at the
   top of the calendar canvas. Both sides are visible at all times;
   the currently active mode gets a colored background (indigo for
   day-block mode, amber for time-slot mode) so the user can read
   the state at a glance. Hover/focus drops an explanation card
   underneath. */
/* In flow above the weekday row — the wrap takes layout space so
   the calendar grid gets naturally pushed down underneath. Flex-
   centered horizontally; the tooltip below uses absolute positioning
   relative to this wrap so it still drops cleanly under the pill. */
.mode-status-pill-wrap {
  position: relative;
  display: flex;
  justify-content: center;
  width: 100%;
  margin: 0 auto 10px;
  z-index: 6;
  pointer-events: auto;
}
/* Inline variant — used by the duplicate pill next to the DATES:
   label inside the title-island. Auto width + no bottom margin so
   it sits as a sibling chip, not its own row. Also drops the
   redundant "Mode:" prefix and shrinks the inner padding so the
   "Full Day / Time slots" pair fits inside the 320px media column
   without clipping. The standalone pill on the calendar canvas
   keeps its full styling. */
.setup-command-label .mode-status-pill-wrap {
  width: auto;
  margin: 0;
  text-transform: none;
}
.setup-command-label .mode-status-pill-prefix {
  display: none;
}
.setup-command-label .mode-status-pill {
  padding: 3px 4px;
  gap: 4px;
  font-size: 0.7rem;
}
.setup-command-label .mode-status-pill-option {
  padding: 2px 7px;
  gap: 4px;
}
.setup-command-label .mode-status-pill-divider {
  margin: 0 -1px;
}
.mode-status-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 6px 4px 12px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(15, 23, 42, 0.78);
  color: rgba(248, 250, 252, 0.85);
  font-size: 0.74rem;
  font-weight: 720;
  letter-spacing: 0.02em;
  white-space: nowrap;
  cursor: default;
  user-select: none;
  transition: background 140ms ease, border-color 140ms ease;
}
.mode-status-pill:focus-visible {
  outline: 0;
  border-color: rgba(255, 255, 255, 0.3);
}
.mode-status-pill-prefix {
  color: rgba(248, 250, 252, 0.6);
  font-weight: 720;
  text-transform: uppercase;
  font-size: 0.66rem;
  letter-spacing: 0.08em;
}
.mode-status-pill-option {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 10px;
  border-radius: 999px;
  color: rgba(248, 250, 252, 0.55);
  background: transparent;
  border: 1px solid transparent;
  /* Button reset (the option is now a real <button> for click-to-
     switch). Keep the same visual treatment as the prior <span>. */
  font: inherit;
  letter-spacing: inherit;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}
.mode-status-pill-option:focus-visible {
  outline: 2px solid rgba(255, 255, 255, 0.4);
  outline-offset: 2px;
}
/* Hover on the INACTIVE option signals click-to-switch. The active
   option's hover is a no-op since clicking it does nothing. */
.mode-status-pill-wrap.is-times .mode-status-pill-option:not(.active):hover {
  background: color-mix(in srgb, var(--ghost) 22%, transparent);
  color: #ffffff;
  border-color: color-mix(in srgb, var(--ghost) 60%, transparent);
}
.mode-status-pill-wrap.is-days .mode-status-pill-option:not(.active):hover {
  background: rgba(250, 204, 21, 0.18);
  color: #fde68a;
  border-color: rgba(250, 204, 21, 0.5);
}
.mode-status-pill-option.active {
  cursor: default;
}
.mode-status-pill-option svg { flex: 0 0 auto; }
.mode-status-pill-option.active {
  color: #ffffff;
  font-weight: 800;
}
.mode-status-pill-wrap.is-days .mode-status-pill-option.active {
  color: #ffffff;
  background: color-mix(in srgb, var(--ghost) 70%, rgba(15, 23, 42, 0.4));
  border-color: color-mix(in srgb, var(--ghost) 80%, transparent);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.04) inset,
    0 4px 12px color-mix(in srgb, var(--ghost) 30%, transparent);
}
.mode-status-pill-wrap.is-times .mode-status-pill-option.active {
  color: #1f1300;
  background: linear-gradient(180deg, #fde68a, #fbbf24);
  border-color: rgba(250, 204, 21, 0.7);
  box-shadow: 0 4px 12px rgba(250, 204, 21, 0.28);
}
.mode-status-pill-divider {
  color: rgba(248, 250, 252, 0.22);
  font-weight: 400;
}

/* Hover/focus tooltip — sits centered below the pill. Hidden by
   default. The wrap itself is centered via translateX(-50%); the
   tooltip uses its own translateX(-50%) on `left: 50%` so it stays
   under the pill regardless of pill width. */
.mode-status-tooltip {
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  width: 320px;
  padding: 10px 12px;
  border-radius: 10px;
  background: rgba(15, 23, 42, 0.96);
  border: 1px solid rgba(255, 255, 255, 0.12);
  box-shadow: 0 16px 36px rgba(0, 0, 0, 0.45);
  color: rgba(248, 250, 252, 0.92);
  font-size: 0.78rem;
  line-height: 1.45;
  opacity: 0;
  pointer-events: none;
  transform: translate(-50%, -4px);
  transition: opacity 160ms ease, transform 160ms ease;
}
.mode-status-pill-wrap:hover .mode-status-tooltip,
.mode-status-pill-wrap:focus-within .mode-status-tooltip {
  opacity: 1;
  transform: translate(-50%, 0);
}
.mode-status-tooltip-title {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 6px;
  font-size: 0.82rem;
  font-weight: 760;
  color: #ffffff;
}
.mode-status-pill-wrap.is-days .mode-status-tooltip-title { color: #ddd6fe; }
.mode-status-pill-wrap.is-times .mode-status-tooltip-title { color: #fde68a; }
.mode-status-tooltip p {
  margin: 0 0 6px;
}
.mode-status-tooltip p:last-child { margin-bottom: 0; }
.mode-status-tooltip-foot {
  padding-top: 6px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  color: rgba(248, 250, 252, 0.72);
  font-size: 0.74rem;
}
.mode-status-tooltip code {
  display: inline-block;
  padding: 0 5px;
  border-radius: 4px;
  background: rgba(15, 23, 42, 0.7);
  border: 1px solid rgba(255, 255, 255, 0.1);
  font-family: "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.72rem;
  color: #fde68a;
}

.time-picker-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(2, 6, 23, 0.72);
  backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
}

.time-picker-shell {
  position: relative;
  border: 1px solid var(--line);
  border-radius: 18px;
  background: rgba(15, 23, 42, 0.62);
  box-shadow: var(--shadow);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  padding: 16px;
  max-height: 92vh;
  overflow: auto;
}

.time-picker-shell::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  pointer-events: none;
  background:
    linear-gradient(135deg, rgba(125, 211, 252, 0.16), transparent 28%),
    linear-gradient(315deg, rgba(244, 114, 182, 0.11), transparent 30%);
  opacity: 0.72;
  mix-blend-mode: screen;
}

.time-picker-shell > * {
  position: relative;
  z-index: 1;
}

.time-picker {
  display: flex;
  flex-direction: column;
  gap: 12px;
  user-select: none;
  width: var(--tp-width, 480px);
}

.time-picker-header {
  display: flex;
  align-items: center;
  gap: 12px;
}

.time-picker-day {
  flex: 1 1 auto;
  margin: 0;
  font-size: 1.25rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  color: var(--band-color, #f8fafc);
}

/* Status pill — quietly reassures the user that the picker has no
   "submit" step. Belongs to the Guide Layer color register but kept
   subtle so the day label stays primary. */
.time-picker-autosaved {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px;
  border-radius: 999px;
  background: rgba(15, 23, 42, 0.5);
  border: 1px solid rgba(255, 255, 255, 0.08);
  color: var(--muted);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  white-space: nowrap;
}

.time-picker-close {
  flex: 0 0 auto;
  background: transparent;
  border: 0;
  color: var(--muted);
  cursor: pointer;
  padding: 4px;
  border-radius: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.time-picker-close:hover {
  background: rgba(255, 255, 255, 0.06);
  color: var(--ink);
}

/* Footer: Auto-saved status on the left, persistent OK button on
   the right. Together they explain "nothing needs submitting" AND
   give the user a clear "I'm done" affordance. */
.time-picker-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 14px 12px;
  margin-top: 4px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.time-picker-ok {
  appearance: none;
  min-height: 36px;
  min-width: 96px;
  padding: 0 18px;
  border-radius: 10px;
  border: 1px solid color-mix(in srgb, var(--ghost) 60%, rgba(255, 255, 255, 0.18));
  background: color-mix(in srgb, var(--ghost) 36%, rgba(15, 23, 42, 0.6));
  color: #ffffff;
  font: inherit;
  font-weight: 760;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease, transform 140ms ease, box-shadow 140ms ease;
}
.time-picker-ok:hover {
  background: color-mix(in srgb, var(--ghost) 55%, rgba(15, 23, 42, 0.5));
  border-color: color-mix(in srgb, var(--ghost) 90%, transparent);
  box-shadow: 0 6px 18px color-mix(in srgb, var(--ghost) 35%, transparent);
  transform: translateY(-1px);
}
.time-picker-ok:active {
  transform: translateY(0);
}
.time-picker-ok:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--ghost) 80%, transparent);
  outline-offset: 2px;
}

.time-picker-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  /* Reserve room for two rows of pills so the picker height stays stable
     while a drag commits the third or fourth chip — keeps the cell under
     the cursor from shifting. */
  min-height: 64px;
  align-content: flex-start;
}

.time-picker-pills-empty {
  color: rgba(199, 210, 254, 0.65);
  font-size: 0.86rem;
  font-style: italic;
  align-self: center;
  padding: 4px 2px;
}

/* Drag-time tooltip — follows the cursor while the user is defining
   a block. Cousin of the HoverHint bubble but driven by mousemove
   directly (no delay), since the value updates every frame while
   the cursor moves. Same indigo Guide-Layer accent. */
.tp-drag-tooltip {
  position: fixed;
  z-index: 260;
  pointer-events: none;
  padding: 5px 9px;
  border-radius: 8px;
  font-size: 0.84rem;
  font-weight: 700;
  white-space: nowrap;
  color: rgba(248, 250, 252, 0.96);
  background: color-mix(in srgb, var(--ghost) 22%, rgba(15, 23, 42, 0.92));
  border: 1px solid color-mix(in srgb, var(--ghost) 55%, rgba(255, 255, 255, 0.18));
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.06),
    0 12px 28px rgba(0, 0, 0, 0.42),
    0 0 22px color-mix(in srgb, var(--ghost) 25%, transparent);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
}

.time-picker-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 6px 4px 11px;
  border: 1px solid color-mix(in srgb, var(--band-color, #a7e4d3) 60%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--band-color, #a7e4d3) 18%, rgba(15, 23, 42, 0.6));
  color: var(--ink);
  font-size: 0.86rem;
  font-weight: 700;
  box-shadow: 0 0 8px color-mix(in srgb, var(--band-color, #a7e4d3) 28%, transparent);
  cursor: default;
  transition: box-shadow 160ms ease, border-color 160ms ease, background 160ms ease;
}

.time-picker-pill.tp-pill-hovered {
  border-color: var(--band-color, #a7e4d3);
  background: color-mix(in srgb, var(--band-color, #a7e4d3) 32%, rgba(15, 23, 42, 0.6));
  box-shadow:
    0 0 14px var(--band-color, #a7e4d3),
    0 0 28px color-mix(in srgb, var(--band-color, #a7e4d3) 45%, transparent);
}

.time-picker-pill.time-picker-pill-draft {
  border-style: dashed;
  opacity: 0.92;
}

.time-picker-pill-duration {
  color: rgba(199, 210, 254, 0.7);
  font-weight: 500;
}

.time-picker-pill-remove {
  width: 16px;
  height: 16px;
  border: 0;
  background: transparent;
  color: rgba(255, 255, 255, 0.7);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  border-radius: 50%;
}

.time-picker-pill-remove:hover {
  background: rgba(239, 68, 68, 0.25);
  color: #fca5a5;
}

.time-picker-grid-wrap {
  position: relative;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.06);
  border-radius: 8px;
  padding: 4px;
  /* Outer corner radius for ring + glass blocks. Smaller than the
     calendar's --day-cell-radius because the picker's cells are
     thinner — keeps the rounding proportional to a 12-18px row
     rather than a 100+px calendar tile. */
  --tp-ring-radius: 6px;
  /* No overflow: hidden — let the neon outline glow extend past the grid
     edges so blocks at the top-left and bottom-right corners read fully,
     AND so the next-day label can sit OUTSIDE the wrap above col 4. */
}

/* Reserve a little extra room above the wrap for the next-day label so
   it doesn't crowd the chip pills. */
.time-picker-grid-wrap.tp-has-next-day {
  margin-top: 14px;
}

/* Vertical hairline in the day-break gap — just a quiet divider so the
   midnight transition reads at a glance. */
.tp-midnight-line {
  position: absolute;
  top: 4px;
  bottom: 4px;
  /* Center of the 18px spacer at grid col 5: 4px wrap padding + 4*96 col
     widths + 9px (half the gap) = 397px. Must stay in sync with the
     TP_COL_WIDTH (96) and DAY_BREAK_GAP_PX (18) constants in TimePicker.jsx. */
  left: calc(4px + 4 * 96px + 9px);
  width: 1px;
  background: rgba(255, 255, 255, 0.55);
  pointer-events: none;
}

/* Day-quadrant labels — "Midnight" anchors col 0 (12am-5:45am block);
   "Noon" anchors col 2 (12pm-5:45pm block). Floats above the wrap
   (outside the grid) so they don't compete with the in-grid hour
   labels. Same typographic treatment as tp-next-day-label so the eye
   reads them as one consistent header tier. */
.tp-quadrant-label {
  position: absolute;
  top: -14px;
  width: 96px;
  text-align: center;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(199, 210, 254, 0.78);
  pointer-events: none;
}
.tp-quadrant-label--midnight {
  /* Col 0 starts at 4px wrap padding. */
  left: 4px;
}
.tp-quadrant-label--noon {
  /* Col 2 starts at 4px + 2 * 96px. */
  left: calc(4px + 2 * 96px);
}

/* Floats ABOVE the wrap (outside the bordered box) over the start of the
   next-day cols, naming the new day so the user reads "Sun, Jun 14"
   without having to interpret the gap. */
.tp-next-day-label {
  position: absolute;
  top: -14px;
  left: calc(4px + 4 * 96px + 18px);
  width: 96px;
  text-align: center;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(199, 210, 254, 0.78);
  pointer-events: none;
}

.time-picker-grid {
  display: grid;
  /* Driven by the wrap's --tp-grid-cols var so the template matches the
     visible column count exactly. Without this the grid intrinsic width
     would exceed the picker's outer width and the rightmost column got
     clipped by the shell's overflow:auto. */
  grid-template-columns: var(--tp-grid-cols, repeat(5, 96px));
  /* Per hour: hour-label row (18px) + three 15-min rows (12px each).
     Total 54px per hour × 6 hours = 324px grid height (matches the
     gridHeight constant in TimePicker.jsx). */
  grid-template-rows: repeat(6, 18px 12px 12px 12px);
  gap: 0;
}

.tp-cell-grid {
  position: relative;
  z-index: 1;
}

.tp-glass-grid {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.tp-ring-grid {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  overflow: visible;
}

.tp-cell {
  position: relative;
  display: flex;
  align-items: center;
  padding: 0 8px;
  cursor: crosshair;
  background: rgba(15, 23, 42, 0.5);
  font-size: 12px;
  color: rgba(199, 210, 254, 0.6);
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  /* box-sizing: border-box keeps the cells visible footprint exactly the
     same as the grid track, so a 12px row is 12px of click target with no
     1px border-bottom spilling into the next row's hit zone. */
  box-sizing: border-box;
  user-select: none;
}

.tp-cell-hour {
  background: rgba(15, 23, 42, 0.65);
  font-weight: 700;
  font-size: 13px;
  color: var(--ink);
}

.tp-day {
  background: rgba(251, 191, 36, 0.13);
}

.tp-day.tp-cell-hour {
  background: rgba(251, 191, 36, 0.22);
}

.tp-night {
  background: rgba(30, 58, 138, 0.32);
}

.tp-night.tp-cell-hour {
  background: rgba(30, 58, 138, 0.48);
}

.tp-next-day {
  filter: brightness(0.7) saturate(0.6);
  cursor: not-allowed;
}

.tp-capped {
  opacity: 0.35;
  cursor: not-allowed;
}

.tp-cell-quarter-label {
  margin-left: 14px;
  color: rgba(199, 210, 254, 0.45);
}

.tp-sun-wrap {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  pointer-events: auto;
  cursor: default;
  line-height: 0;
}

.tp-sun-icon {
  color: rgba(255, 255, 255, 0.95);
  pointer-events: none;
  filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.55));
}

/* z=0 — frosted glass blocks (no inner color, just frost). Multiple
   overlapping glass blocks combine via stacked backdrop-filter. */
.tp-glass-block {
  background: rgba(255, 255, 255, 0.05);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  /* Pull the glass in horizontally so it doesn't extend past the time
     labels. Matches the ring's margin so the two stay aligned. */
  margin: 0 6px;
  /* Nudge the overlay down a few pixels so the ring sits closer to the
     hour labels rather than overshooting the cell tops/bottoms. The
     hover state lifts to 0 to match the calendar's lift treatment. */
  transform: translateY(4px);
  transition: transform var(--motion-base) ease, background var(--motion-fast) ease, box-shadow var(--motion-base) ease;
}

/* z=2 — neon ring drawn entirely on a ::before pseudo so all four edges
   share one rectangle and meet at the corners. Top/bottom of the pseudo
   pin to the parent's vertical edges (top:0; bottom:0) so the ring's
   TIME bounds never shift with depth. Left/right of the pseudo extend
   outward by `--tp-ring-side` (set inline per ring), so overlapping
   stacks fan out HORIZONTALLY without disturbing the time edges. */
.tp-ring-block {
  position: relative;
  background: transparent;
  /* Pull the ring in horizontally so it sits closer to the time
     labels — matches the glass margin. */
  margin: 0 6px;
  /* Subtle resting glow — matches the calendar's resting ring. */
  box-shadow:
    0 0 6px color-mix(in srgb, var(--band-color) 42%, transparent),
    0 0 14px color-mix(in srgb, var(--band-color) 18%, transparent);
  transition:
    opacity var(--motion-fast) ease,
    box-shadow var(--motion-base) ease,
    transform var(--motion-base) ease;
  /* Match the glass nudge so ring + glass stay in sync. */
  transform: translateY(4px);
}

.tp-ring-block::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(-1 * var(--tp-ring-side, 1px));
  right: calc(-1 * var(--tp-ring-side, 1px));
  border: 2px solid color-mix(in srgb, var(--band-color, rgba(148, 163, 184, 0.9)) 78%, transparent);
  border-radius: inherit;
  box-sizing: border-box;
  pointer-events: none;
  transition:
    border-color 220ms ease,
    border-width 220ms ease,
    left 220ms ease,
    right 220ms ease;
}

/* Hover: lift up and play the same soft pulse animation the calendar's
   range-ring uses, so the two surfaces feel related. */
.tp-ring-hovered {
  transform: translateY(0) scale(var(--lift-scale));
  animation: range-ring-pulse 1400ms ease-in-out forwards;
}

.tp-ring-hovered::before {
  border-width: 2.5px;
}

.tp-glass-hovered {
  background: color-mix(in srgb, var(--band-color, #a7e4d3) 10%, rgba(255, 255, 255, 0.06));
  transform: translateY(0) scale(var(--lift-scale));
}

/* Cross-col continuity. A span that wraps from the bottom of one column
   to the top of the next reads cleanest as TWO open-C rings: the leading
   seg shows top + sides + (no bottom), the trailing seg shows bottom +
   sides + (no top). Drop the relevant border on the pseudo for a clean
   open edge — no horizontal line, no leftover corner curvature. */
.tp-ring-connects-right::before {
  border-bottom: 0;
}
.tp-ring-connects-left::before {
  border-top: 0;
}
.tp-glass-connects-right {
  clip-path: inset(-120px -120px 0 -120px);
}
.tp-glass-connects-left {
  clip-path: inset(0 -120px -120px -120px);
}

/* Middle segs of a multi-col span carry both connects-* classes —
   no top, no bottom, just sides. */
.tp-ring-connects-right.tp-ring-connects-left::before {
  border-top: 0;
  border-bottom: 0;
}
.tp-glass-connects-right.tp-glass-connects-left {
  clip-path: inset(0 -120px 0 -120px);
}

/* Glass blocks share the open-bottom clip with the ring so the
   backdrop-filter boundary matches the ring's open-C edge — otherwise
   the blur boundary at the col bottom reads as a horizontal line. */
.tp-glass-open-bottom {
  clip-path: inset(-120px -120px 0 -120px);
}

/* Open-ended block (no end time): hide the bottom border so the ring is
   an open-C facing down — top + sides closed, bottom missing. */
.tp-ring-open-bottom::before {
  border-bottom: 0;
}

/* Drag-in-progress (draft). */
.tp-ring-draft::before {
  border-style: dashed;
}

/* During an active resize drag, suppress all transitions on rings and
   glass so the visual change tracks the cursor exactly. Without this,
   neighbouring blocks (whose stack depth shifts as the resized block
   grows past peers) animate their --tp-ring-side over 220ms, which
   reads as flickering size/color changes on rings the user isn't
   touching. */
.time-picker.tp-resizing .tp-ring-block,
.time-picker.tp-resizing .tp-ring-block::before,
.time-picker.tp-resizing .tp-glass-block {
  transition: none !important;
}

/* Picker edge resize affordances. Mirrors the calendar pattern: a thin
   strip on the top or bottom edge of a block segment (only the first
   segment hosts top, only the last hosts bottom; cross-midnight wrap
   segments stay open-C and unresizable). The ring grid is pointer-
   events:none so we re-enable on the edge child. z-index sits above
   the rest of the ring chrome. */
.tp-ring-resize-edge {
  position: absolute;
  left: 0;
  right: 0;
  height: 8px;
  cursor: row-resize;
  pointer-events: auto;
  z-index: 4;
}
.tp-ring-resize-edge--top { top: -3px; }
.tp-ring-resize-edge--bottom { bottom: -3px; }
.tp-ring-resize-edge:hover {
  background: rgba(255, 255, 255, 0.08);
  border-radius: 4px;
}

.tp-glass-draft {
  background: rgba(255, 255, 255, 0.08);
}

/* Block context menu: render above the picker overlay (z=100) so it's
   never clipped by stacking contexts. */
.tp-block-menu {
  z-index: 200;
}

.time-picker-text {
  position: relative;
  display: flex;
}

/* Mirror layer that paints the same text the textarea contains, but per
   token so unparseable segments can render red. The textareas own text is
   transparent while invalid tokens exist; the mirror is layered on top
   with the same padding/font/wrapping so glyphs line up. */
.time-picker-text-mirror {
  position: absolute;
  inset: 0;
  padding: 6px 10px;
  border: 1px solid transparent;
  font: inherit;
  font-size: 0.82rem;
  line-height: 1.35;
  color: var(--ink);
  white-space: pre-wrap;
  word-break: break-word;
  overflow-wrap: break-word;
  pointer-events: none;
  box-sizing: border-box;
}

.tp-token-valid {
  color: var(--ink);
}

.tp-token-invalid {
  color: #fca5a5;
  text-decoration: underline wavy #fca5a5;
  text-underline-offset: 2px;
}

.time-picker-text-input {
  flex: 1;
  width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(15, 23, 42, 0.42);
  color: var(--ink);
  font: inherit;
  font-size: 0.92rem;
  line-height: 1.4;
  outline: 0;
  resize: none;
  box-sizing: border-box;
}

.time-picker-text-input::placeholder {
  color: rgba(199, 210, 254, 0.42);
  font-style: italic;
}

.time-picker-text-input:focus {
  border-color: rgba(99, 102, 241, 0.55);
  background: rgba(15, 23, 42, 0.6);
}

.time-picker-text-input.tp-error {
  border-color: rgba(239, 68, 68, 0.7);
  color: transparent;
  caret-color: var(--ink);
}

/* =============================================================
   MOBILE — Step 1 layout pass (≤768px)
   Goal: prevent overlap, stack content sensibly, surface every
   region as a single touch target. This is NOT a finished mobile
   experience — interactions (tap / drag / long-press) still need
   their own pass. Keep desktop styles untouched above.
   ============================================================= */
@media (max-width: 768px) {
  /* App shell — fill the dynamic viewport (100dvh adjusts when the
     URL bar hides/shows) with NO outer scroll. The calendar's own
     scroller handles vertical scroll inside the layout, which is
     also what hides the URL bar in iOS Safari and Chrome on swipe.
     Trim the tray rail vars (--right-rail / --bottom-rail) so the
     stage-shell grid leaves only a thin labeled strip for each
     tray instead of the desktop's 78 / 108 px tracks. */
  .app-shell {
    height: 100dvh;
    padding: 6px;
    gap: 6px;
    grid-template-rows: auto 1fr;
    overflow: hidden;
    --right-rail: 32px;
    --bottom-rail: 36px;
  }
  .rail-tab {
    width: 32px !important;
  }

  /* Top chrome — three columns (brand | title | mode actions). Each
     column shrinks to its content. align-items: start so all three
     boxes share the same top edge (centring left the Setup Mode box
     visually higher than the title island when it was taller). */
  .top-chrome {
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: 6px;
    align-items: start;
  }

  /* Brand — slightly larger than the smallest pass; visually anchors
     the row without dominating. */
  .brand,
  .brand-logo-wrap {
    width: 44px;
    height: 44px;
    min-width: 44px;
    align-self: center;
  }
  .brand-logo {
    width: 44px;
    height: 44px;
  }

  /* Title island — stack vertically, no media column. The photo +
     map + address slot disappears in step 1; will return as a
     tap-to-expand thumbnail in step 2. Tight padding so the chrome
     row stays short. */
  .title-island {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto;
    padding: 5px 8px;
    column-gap: 0;
    max-height: none;
  }
  .title-island > .title-island-media { display: none; }
  /* Mobile-only photo/map/address tap-to-expand. The toggle button
     lives in the title-row; tapping it flips mobile-media-open which
     reveals the media slot inline below the description. */
  .title-island.mobile-media-open > .title-island-media {
    display: flex;
    grid-column: 1;
    grid-row: 3;
    width: 100%;
    margin-top: 8px;
  }
  .title-island.mobile-media-open .title-island-photo,
  .title-island.mobile-media-open .title-island-map {
    width: 100%;
    aspect-ratio: 16 / 9;
  }
  .title-island-mobile-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    padding: 0;
    margin-left: 4px;
    border: 1px solid rgba(255, 255, 255, 0.16);
    border-radius: 6px;
    background: rgba(15, 23, 42, 0.5);
    color: var(--muted);
    cursor: pointer;
  }
  .title-island.mobile-media-open .title-island-mobile-toggle {
    background: rgba(99, 102, 241, 0.32);
    color: var(--ink);
    border-color: rgba(99, 102, 241, 0.5);
  }
  .title-island h1 { font-size: 1.05rem; line-height: 1.2; }
  .title-island .title-editor { font-size: 1.05rem; }
  .description-readonly,
  .description-editor {
    font-size: 0.78rem;
  }
  .description-editor-wrap:not(:focus-within) .description-clamp {
    -webkit-line-clamp: 2;
  }
  /* Toolbar wraps on narrow widths so all 7 buttons stay reachable. */
  .description-toolbar { flex-wrap: wrap; }

  /* Mode action area — desktop forces height: var(--top-h) (~150px)
     to match the title-island. Override to auto so the box sizes to
     its actual content (label + two buttons). align-self: center
     in the row so it pairs with the title-island vertically. */
  .mode-action-bar {
    padding: 6px 8px;
    gap: 2px;
    min-width: 0;
    height: auto !important;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
  .mode-action-bar .mode-title {
    font-size: 0.66rem;
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    margin: 0 0 2px;
    text-align: center;
    line-height: 1;
  }
  .mode-action-bar .mode-action-buttons {
    display: flex;
    flex-direction: column;
    gap: 3px;
    justify-content: center;
    align-items: stretch;
    width: 100%;
  }
  .mode-action-bar .launch-button {
    padding: 4px 8px;
    font-size: 0.66rem;
    min-width: 0;
    min-height: 0;
    white-space: nowrap;
    width: 100%;
  }

  /* Right Options tray — collapsed: 32px labeled strip. Tapping the
     rail-open button (already wired) flips `pinned` true; on mobile
     the pinned state morphs into a fixed-position overlay panel
     covering the right ~82% of the viewport. Tap close (rail-close
     becomes visible while pinned) or tap outside to dismiss. */
  .options-rail {
    width: 32px;
    min-width: 32px;
  }
  .options-rail:not(.pinned) .rail-content,
  .options-rail:not(.pinned) .rail-pin,
  .options-rail:not(.pinned) .rail-close {
    display: none;
  }
  .options-rail.pinned {
    position: fixed;
    top: 6px;
    right: 6px;
    bottom: 6px;
    width: min(82vw, 360px);
    min-width: 0;
    z-index: 200;
    box-shadow: 0 14px 40px rgba(0, 0, 0, 0.55);
  }
  /* Stage-shell columns must stay 1fr / 32px even when the rail is
     pinned — the pinned rail floats as an overlay (position: fixed),
     so the underlying calendar should NOT shrink to make room and
     leave a visible gap. Same for hover/focus expansion (no hover
     on touch, but covers fallback paths). */
  .stage-shell:has(.options-rail.pinned),
  .stage-shell:has(.options-rail:hover:not(.suppressed)),
  .stage-shell:has(.options-rail:focus-within:not(.suppressed)),
  .app-shell.options-pinned .stage-shell {
    grid-template-columns: minmax(0, 1fr) var(--right-rail) !important;
  }
  .options-rail.pinned .rail-content {
    display: block;
  }
  .options-rail.pinned .rail-close {
    display: inline-flex;
  }

  /* Bottom Invitations tray — collapsed: 32px labeled strip at the
     bottom of the layout. Tapping the rail-open button toggles
     pinned, which on mobile morphs the rail into a fixed-position
     bottom sheet that EXPANDS UPWARD from its strip position. The
     rail itself never grows downward into off-screen territory. */
  .bottom-rail:not(.pinned) {
    height: 32px;
    min-height: 32px;
  }
  .bottom-rail:not(.pinned) .rail-content,
  .bottom-rail:not(.pinned) .people-chips,
  .bottom-rail:not(.pinned) .comments-list {
    display: none;
  }
  .bottom-rail.pinned {
    position: fixed;
    left: 6px;
    right: 6px;
    bottom: 6px;
    height: min(60vh, 480px);
    min-height: 0;
    z-index: 200;
    box-shadow: 0 -14px 40px rgba(0, 0, 0, 0.55);
  }
  .bottom-rail.pinned .rail-content,
  .bottom-rail.pinned .people-chips {
    display: block;
  }
  .bottom-rail.pinned .rail-close {
    display: inline-flex;
  }

  /* No "pin" button on mobile — taps already toggle open/close, so
     the pin metaphor is redundant and adds noise to the small
     header. Covers both naming variants (.rail-pin on Options /
     .tray-pin on the Setup Invites tray). */
  .options-rail .rail-pin,
  .bottom-rail .rail-pin,
  .bottom-rail .tray-pin,
  .setup-invites .tray-pin {
    display: none !important;
  }

  /* Calendar — keep all 7 columns. JS sets --calendar-row-min based
     on the desktop scroller's height, which is huge on a phone (the
     app-shell grew to fit). Override with !important so cells stay
     proportional to the column width (~viewport / 7). 14vw is close
     to 4:3 once the column gap is subtracted. */
  .calendar-grid-wrap {
    --day-cell-radius: clamp(4px, 2cqw, 10px);
  }
  .calendar-grid {
    gap: 4px;
    grid-auto-rows: clamp(58px, 17vw, 110px) !important;
  }
  /* Compact cell layout for mobile: top line for day# + LEDs + warning
     all on one row; time chips below in a smaller font; note pinned
     to the bottom in tiny text with a one-line clamp + truncation
     ellipsis indicating more info. Cell padding tightens to keep
     every cell tappable while showing meaningful content. */
  .day-cell {
    padding: 14px 3px 12px;
  }
  .day-cell .day-number {
    position: absolute;
    top: 2px;
    left: 4px;
    z-index: 12;
    font-size: 0.7rem;
    line-height: 1;
    font-weight: 700;
  }
  .day-cell .led-dot-row {
    top: 4px;
    left: auto;
    right: auto;
    gap: 2px;
  }
  .day-cell .led-dot {
    width: 4px;
    height: 4px;
    box-shadow:
      0 0 3px var(--led-color, rgba(148, 163, 184, 0.9)),
      0 0 6px color-mix(in srgb, var(--led-color, rgba(148, 163, 184, 0.9)) 60%, transparent);
  }
  .day-cell .day-time-warning {
    top: 2px;
    right: 3px;
    width: 11px;
    height: 11px;
    font-size: 0.52rem;
  }
  /* Time chips — start below the header row; fewer/smaller items. */
  .day-time-chips {
    top: 14px;
    left: 3px;
    right: 3px;
    bottom: 12px;
    gap: 1px;
  }
  .day-time-chip,
  .day-time-chips.tp-chip-lg .day-time-chip,
  .day-time-chips.tp-chip-md .day-time-chip,
  .day-time-chips.tp-chip-sm .day-time-chip {
    font-size: 0.52rem;
    padding: 0 3px;
    line-height: 1.4;
  }
  /* Day note — tiny text pinned to the bottom, single-line clamped
     with the native ellipsis as the "more info" indicator. The
     editor is a separate, larger overlay (see below) so the user
     gets a comfortable text area while typing. */
  .day-cell .day-note {
    left: 3px;
    right: 3px;
    bottom: 1px;
    font-size: 0.5rem;
    font-weight: 600;
    line-height: 1.1;
    -webkit-line-clamp: 1;
    line-clamp: 1;
    max-height: calc(0.5rem * 1.1);
    text-align: left;
  }
  /* Note editor on mobile floats as a larger overlay anchored to
     the cell's bottom edge so typing isn't squeezed into a tiny
     pixel grid. width: 80vw centres it across the screen; max
     height keeps it on-screen even when the cell is in the bottom
     row. z-index: 300 sits above the calendar grid (which itself
     uses z-index 3) and any range-ring/glass blocks. */
  .day-cell .day-note-editor {
    position: fixed;
    left: 50%;
    bottom: 16px;
    transform: translateX(-50%);
    width: min(80vw, 360px);
    min-height: 80px;
    max-height: 40vh;
    padding: 10px 12px;
    font-size: 0.95rem;
    line-height: 1.3;
    z-index: 300;
    border: 1px solid rgba(99, 102, 241, 0.6);
    border-radius: 10px;
    background: rgba(2, 6, 23, 0.96);
    box-shadow: 0 14px 40px rgba(0, 0, 0, 0.6);
  }
  /* The cell that owns the editor needs to win over its siblings'
     z-index so the editor (a child of that cell) isn't covered by
     a neighbouring tile's stacking context. */
  .day-cell:has(.day-note-editor) {
    z-index: 300 !important;
  }
  .day-cell:has(.day-note) .day-time-chips,
  .day-cell:has(.day-note-editor) .day-time-chips {
    bottom: 12px;
  }
  /* Today marker — shrink so it fits the new compact header row. */
  .today-marker {
    right: 3px;
    top: 2px;
    min-height: 14px;
    padding: 0 5px;
    font-size: 0.55rem;
  }

  /* Sticky month-label pill — smaller AND translucent on mobile so
     the day numbers underneath remain readable. The 0.92rem desktop
     pill covered roughly two cells. */
  .day-month-label {
    min-height: 20px;
    padding: 2px 8px;
    font-size: 0.62rem;
    font-weight: 700;
    background: rgba(15, 23, 42, 0.42);
    color: rgba(248, 250, 252, 0.78);
    border-color: rgba(255, 255, 255, 0.14);
    box-shadow: none;
    backdrop-filter: blur(8px) saturate(140%);
  }

  /* Setup command bar (date entry pill) — pin to the bottom-center
     edge of the calendar canvas; stays full-width minus a small
     side margin on narrow screens. */
  .setup-command-bar {
    width: calc(100% - 16px) !important;
    left: 8px !important;
    right: 8px !important;
  }

  /* Time picker — narrow phones (<420px) can't fit 4×96 + breaks.
     Step 1: scroll horizontally. Step 2 may redesign with a stacked
     vertical layout. */
  .time-picker-shell {
    max-width: calc(100vw - 16px);
    overflow-x: auto;
  }
}

/* Touch-only adjustments. Hover affordances (edge handles, tooltips,
   :focus-within auto-expand) won't work without a pointer device, so
   surface their actions via tap or always-on UI on touch-only inputs.
   Step 1 just hides the edge handles to prevent visual confusion;
   step 2 designs a touch-friendly resize gesture. */
@media (hover: none) {
  .day-cell-resize-edge,
  .tp-ring-resize-edge {
    display: none;
  }
  .link-hover-tooltip {
    /* Tooltip won't appear on touch since there's no hover; rely
       on the right-click (long-press) context menu. */
    display: none;
  }
}

/* ══ Launcher ═══════════════════════════════════════════════════════
   The first screen: a grid of floating glass tiles, one per way of
   organizing. Sits above the whole workspace — see src/Launcher.jsx.
   Tile accents come from the shared band palette so re-theming :root
   re-themes the launcher with everything else. */

.launch-band-0 { --tile-accent: var(--band-0); }
.launch-band-1 { --tile-accent: var(--band-1); }
.launch-band-2 { --tile-accent: var(--band-2); }
.launch-band-3 { --tile-accent: var(--band-3); }
.launch-band-4 { --tile-accent: var(--band-4); }
.launch-band-5 { --tile-accent: var(--band-5); }
.launch-band-6 { --tile-accent: var(--band-6); }
.launch-band-7 { --tile-accent: var(--band-7); }

/* Two derived steps so the illustrations can wash a shape without
   each SVG hard-coding an rgba of its own band color. */
[class*="launch-band-"] {
  --tile-accent-soft: color-mix(in srgb, var(--tile-accent) 34%, transparent);
  --tile-accent-wash: color-mix(in srgb, var(--tile-accent) 13%, transparent);
}

.launcher {
  position: relative;
  z-index: 1;
  width: 100vw;
  height: 100vh;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(18px, 3.4vh, 34px);
  padding: clamp(28px, 5vh, 56px) clamp(20px, 5vw, 64px);
}

.launcher-head {
  text-align: center;
  max-width: 620px;
}

.launcher-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 10px;
  padding: 4px 12px 4px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--glass-strong);
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 680;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  backdrop-filter: blur(18px) saturate(160%);
}

.launcher-eyebrow svg { color: var(--ghost); }

.launcher-title {
  margin: 0 0 8px;
  color: var(--ink);
  font-size: clamp(1.6rem, 3.6vw, 2.5rem);
  font-weight: 760;
  letter-spacing: -0.022em;
  line-height: 1.12;
}

.launcher-sub {
  margin: 0;
  color: var(--muted);
  font-size: clamp(0.82rem, 1.35vw, 0.95rem);
  line-height: 1.5;
}

.flow-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(12px, 1.5vw, 20px);
  width: 100%;
  max-width: 1120px;
}

.flow-tile {
  --tile-index: 0;
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: auto;
  align-items: start;
  gap: 14px;
  padding: 20px 18px 22px;
  border: 1px solid transparent;
  border-image: var(--specular) 1;
  border-radius: 14px;
  background: var(--glass-strong);
  box-shadow: var(--shadow-soft);
  backdrop-filter: blur(24px) saturate(180%);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
  /* Staggered rise on entry, then the tile comes to rest. An earlier
     version drifted forever, which looked pleasant and made a moving
     click target — bad for anyone with motor-control difficulty, and
     it never satisfies a pointer-stability check either. The floating
     quality lives in the material (glass, shadow, ambient depth), not
     in perpetual motion. */
  animation: flow-tile-in 520ms var(--ease-spring) backwards;
  animation-delay: calc(var(--tile-index) * 55ms);
  transition:
    transform var(--motion-base) var(--ease-spring),
    box-shadow var(--motion-base) var(--ease-spring),
    background var(--motion-base) ease;
}

@keyframes flow-tile-in {
  from { opacity: 0; transform: translateY(14px) scale(0.985); }
  to { opacity: 1; transform: none; }
}

.flow-tile:hover,
.flow-tile:focus-visible {
  background: color-mix(in srgb, var(--tile-accent) 9%, var(--glass-soft));
  box-shadow:
    var(--shadow),
    0 0 0 1px color-mix(in srgb, var(--tile-accent) 48%, transparent) inset,
    0 18px 46px color-mix(in srgb, var(--tile-accent) 22%, transparent);
  transform: translateY(var(--lift-y)) scale(var(--lift-scale));
  outline: none;
}

.flow-tile:active {
  transform: translateY(-1px) scale(0.997);
}

.flow-tile:focus-visible {
  outline: 2px solid color-mix(in srgb, var(--tile-accent) 80%, transparent);
  outline-offset: 3px;
}

.flow-art-wrap {
  display: grid;
  place-items: center;
  width: 58px;
  height: 58px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--tile-accent) 12%, rgba(2, 6, 23, 0.32));
  color: var(--tile-accent);
  transition: background var(--motion-base) ease, transform var(--motion-base) var(--ease-pop);
}

.flow-tile:hover .flow-art-wrap,
.flow-tile:focus-visible .flow-art-wrap {
  background: color-mix(in srgb, var(--tile-accent) 20%, rgba(2, 6, 23, 0.3));
  transform: rotate(-3deg) scale(1.05);
}

.flow-art { width: 38px; height: 38px; }

.flow-tile-body {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}

.flow-tile-meta {
  display: flex;
  align-items: center;
  gap: 7px;
  color: var(--tile-accent);
  font-size: 0.64rem;
  font-weight: 720;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}

.flow-tile-soon {
  padding: 2px 7px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(2, 6, 23, 0.36);
  color: var(--muted);
  font-size: 0.58rem;
  letter-spacing: 0.1em;
}

.flow-tile-title {
  color: var(--ink);
  font-size: 1.02rem;
  font-weight: 700;
  letter-spacing: -0.012em;
  line-height: 1.24;
}

.flow-tile-blurb {
  color: var(--muted);
  font-size: 0.79rem;
  line-height: 1.45;
}

.flow-tile-go {
  position: absolute;
  right: 14px;
  bottom: 14px;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--tile-accent) 16%, transparent);
  color: var(--tile-accent);
  opacity: 0;
  transform: translateX(-5px);
  transition: opacity var(--motion-fast) ease, transform var(--motion-fast) var(--ease-pop);
}

.flow-tile:hover .flow-tile-go,
.flow-tile:focus-visible .flow-tile-go {
  opacity: 1;
  transform: translateX(0);
}

.flow-tile.is-planned .flow-art-wrap { opacity: 0.82; }

.launcher-foot {
  max-width: 620px;
  margin: 0;
  color: var(--muted);
  font-size: 0.74rem;
  line-height: 1.5;
  text-align: center;
  opacity: 0.82;
}

/* ── Planned-flow scope panel ──────────────────────────────────────
   A planned tile opens this instead of dropping the user into a dead
   screen: what it will do, what it shares with the built flows, and
   the MULTIPARTY_SPEC factors blocking it. */

.flow-scope-backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(2, 6, 23, 0.62);
  backdrop-filter: blur(6px);
  animation: flow-scope-in 180ms var(--ease-spring);
}

@keyframes flow-scope-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

.flow-scope {
  width: min(520px, 100%);
  max-height: min(84vh, 720px);
  overflow-y: auto;
  padding: 22px 24px 20px;
  border: 1px solid transparent;
  border-image: var(--specular) 1;
  border-radius: 16px;
  background: var(--glass-soft);
  box-shadow: var(--shadow);
  backdrop-filter: blur(28px) saturate(180%);
  animation: flow-scope-rise 220ms var(--ease-spring);
}

@keyframes flow-scope-rise {
  from { opacity: 0; transform: translateY(10px) scale(0.985); }
  to { opacity: 1; transform: none; }
}

.flow-scope-head {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 14px;
}

.flow-scope-head h2 {
  margin: 4px 0 0;
  color: var(--ink);
  font-size: 1.14rem;
  font-weight: 730;
  letter-spacing: -0.014em;
}

.flow-scope-head .flow-tile-meta { margin: 0; }

.flow-scope-art {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 56px;
  height: 56px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--tile-accent) 14%, rgba(2, 6, 23, 0.34));
  color: var(--tile-accent);
}

.flow-scope-art .flow-art { width: 36px; height: 36px; }

.flow-scope-summary {
  margin: 0 0 18px;
  color: var(--ink);
  font-size: 0.87rem;
  line-height: 1.5;
}

.flow-scope-label {
  margin: 0 0 7px;
  color: var(--tile-accent);
  font-size: 0.63rem;
  font-weight: 720;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}

.flow-scope-list {
  margin: 0 0 16px;
  padding-left: 17px;
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1.55;
}

.flow-scope-list li { margin-bottom: 4px; }
.flow-scope-list li::marker { color: var(--tile-accent); }

.flow-scope-shares {
  margin: 0 0 16px;
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1.55;
}

.flow-scope-risk li::marker { color: var(--no); }

.flow-scope-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

.flow-scope-back,
.flow-scope-alt {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: rgba(15, 23, 42, 0.42);
  color: var(--ink);
  font-size: 0.78rem;
  font-weight: 620;
  cursor: pointer;
  transition: background var(--motion-fast) ease, border-color var(--motion-fast) ease;
}

.flow-scope-back:hover,
.flow-scope-alt:hover {
  background: rgba(15, 23, 42, 0.66);
  border-color: var(--line-strong);
}

.flow-scope-alt {
  margin-left: auto;
  border-color: color-mix(in srgb, var(--tile-accent) 46%, transparent);
  color: var(--tile-accent);
}

/* ── Return-to-launcher control (workspace header) ─────────────────── */

.launcher-return {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 4px;
  padding: 3px 9px 3px 6px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(15, 23, 42, 0.4);
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 640;
  cursor: pointer;
  transition: background var(--motion-fast) ease, color var(--motion-fast) ease;
}

.launcher-return:hover {
  background: rgba(15, 23, 42, 0.68);
  color: var(--ink);
}

@media (max-width: 1080px) {
  .flow-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 760px; }
}

@media (max-width: 720px) {
  .launcher { justify-content: flex-start; gap: 20px; }
  .flow-grid { grid-template-columns: minmax(0, 1fr); max-width: 460px; }
  .flow-tile { padding: 16px 15px 18px; gap: 12px; }
  .flow-art-wrap { width: 48px; height: 48px; }
  .flow-art { width: 32px; height: 32px; }
  .flow-tile-go { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .flow-tile { animation: none; }
  .flow-tile:hover,
  .flow-tile:focus-visible { transform: none; }
  .flow-scope-backdrop,
  .flow-scope { animation: none; }
  .flow-tile:hover .flow-art-wrap,
  .flow-tile:focus-visible .flow-art-wrap { transform: none; }
}

/* ══ People globe (prototype, /people) ═════════════════════════════
   A ball under cloth: invited people gather at the front pole, the
   rest of your social world wraps around the sphere and shrinks toward
   the horizon. Positions are written per frame by the engine in
   src/PeopleConstellation.jsx as --x / --y / --s / --o custom
   properties; CSS only turns those into a transform. See
   PEOPLE_GRAPH_SPEC.md. */

.pc-shell {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 300px minmax(0, 1fr);
  gap: var(--gap);
  width: 100vw;
  height: 100vh;
  padding: var(--gap);
  overflow: hidden;
}

.pc-rail {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-height: 0;
  padding: 16px;
  border: 1px solid transparent;
  border-image: var(--specular) 1;
  border-radius: 12px;
  background: var(--glass-strong);
  box-shadow: var(--shadow-soft);
  backdrop-filter: blur(24px) saturate(180%);
  overflow-y: auto;
}

.pc-rail-block { display: flex; flex-direction: column; gap: 8px; }
/* The rail scrolls as a whole, so nothing inside it may be squeezed to
   make the contents fit. Without this the import panel was shrunk to
   2px while holding 787px of content and, being overflow:hidden, it
   simply swallowed its own instructions — the panel looked cut off
   with no way to reach the rest. Everything keeps its natural height
   and the rail scrolls instead. */
.pc-rail > *:not(.pc-rail-grow) { flex-shrink: 0; }

/* The one block that may take the slack, with a floor: at min-height 0
   a tall sibling crushes it and its chip list spills out over
   everything below. The floor is the section title plus about three
   rows of chips. */
.pc-rail-grow { flex: 1 1 auto; min-height: 108px; }

.pc-rail-title {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  color: var(--ink);
  font-size: 0.72rem;
  font-weight: 720;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.pc-count {
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--ghost);
  color: #fff;
  font-size: 0.68rem;
}

.pc-rail-hint { margin: 0; color: var(--muted); font-size: 0.74rem; line-height: 1.45; }

.pc-recent {
  display: flex;
  flex-direction: column;
  gap: 5px;
  max-height: 236px;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  list-style: none;
}

.pc-recent-btn {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 100%;
  padding: 7px 10px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(15, 23, 42, 0.4);
  color: var(--ink);
  text-align: left;
  cursor: pointer;
  transition: background var(--motion-fast) ease, border-color var(--motion-fast) ease;
}

.pc-recent-btn:hover { background: rgba(15, 23, 42, 0.72); border-color: var(--line-strong); }
.pc-recent-btn.is-on { border-color: var(--ghost); background: color-mix(in srgb, var(--ghost) 18%, rgba(15, 23, 42, 0.5)); }
.pc-recent-name { display: inline-flex; align-items: center; gap: 5px; font-size: 0.79rem; font-weight: 640; }
.pc-recent-meta { color: var(--muted); font-size: 0.68rem; }

.pc-new-btn { border-style: dashed; }
.pc-new-btn svg { color: var(--ghost); }

.pc-seg { display: flex; gap: 4px; }

.pc-seg-btn {
  flex: 1;
  padding: 6px 4px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: rgba(15, 23, 42, 0.4);
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 620;
  cursor: pointer;
}

.pc-seg-btn.is-on { background: var(--ghost); border-color: var(--ghost); color: #fff; }

.pc-roster {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  max-height: 168px;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  list-style: none;
}

.pc-roster-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 7px 3px 9px;
  border: 1px solid color-mix(in srgb, var(--best) 40%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--best) 16%, rgba(15, 23, 42, 0.5));
  color: var(--ink);
  font-size: 0.73rem;
  cursor: pointer;
}

.pc-roster-chip:hover { background: color-mix(in srgb, var(--no) 22%, rgba(15, 23, 42, 0.5)); border-color: var(--no); }

/* Can this person actually be reached, and how. Both icons are always
   drawn so the pair reads as two fixed slots — a struck one is a fact
   about the contact, not a missing element.

   The glyphs are CSS masks rather than inline SVG on purpose. These
   also sit on all ~350 floating nodes, which the engine transforms
   every frame; as SVG subtrees they tripled frame cost (6.9ms to
   20.8ms median on this machine). A masked box rasterises far cheaper.
   The paths are lucide's mail and phone, so they match the SVG icons
   used elsewhere in the app. */
.pc-chip-reach {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-left: 1px;
}

.pc-chip-ch {
  --ch-size: 11px;
  position: relative;
  display: inline-block;
  width: var(--ch-size);
  height: var(--ch-size);
  vertical-align: -1px;
}

/* The mask lives on an inner box. A masked element paints only where
   its glyph is, which would clip the strike below to the icon's own
   strokes, so the strike has to be a sibling of the mask, not a child
   of it. */
.pc-chip-glyph {
  position: absolute;
  inset: 0;
  background-color: var(--best);
  -webkit-mask: var(--ch-glyph) center / contain no-repeat;
  mask: var(--ch-glyph) center / contain no-repeat;
}

.pc-chip-ch.ch-mail .pc-chip-glyph,
.pc-chip-glyph.ch-mail { --ch-glyph: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27%23000%27%20stroke-width%3D%272%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27m22%207-8.991%205.727a2%202%200%200%201-2.009%200L2%207%27%2F%3E%3Crect%20x%3D%272%27%20y%3D%274%27%20width%3D%2720%27%20height%3D%2716%27%20rx%3D%272%27%2F%3E%3C%2Fsvg%3E"); }
.pc-chip-ch.ch-phone .pc-chip-glyph,
.pc-chip-glyph.ch-phone { --ch-glyph: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%20fill%3D%27none%27%20stroke%3D%27%23000%27%20stroke-width%3D%272%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M13.832%2016.568a1%201%200%200%200%201.213-.303l.355-.465A2%202%200%200%201%2017%2015h3a2%202%200%200%201%202%202v3a2%202%200%200%201-2%202A18%2018%200%200%201%202%204a2%202%200%200%201%202-2h3a2%202%200%200%201%202%202v3a2%202%200%200%201-.8%201.6l-.468.351a1%201%200%200%200-.292%201.233%2014%2014%200%200%200%206.392%206.384%27%2F%3E%3C%2Fsvg%3E"); }

.pc-chip-ch.is-missing .pc-chip-glyph { background-color: rgba(248, 250, 252, 0.34); }

.pc-chip-ch.is-missing::after {
  content: "";
  position: absolute;
  top: 50%;
  left: -1px;
  right: -1px;
  height: 1.5px;
  border-radius: 1px;
  background: var(--no);
  transform: rotate(-32deg);
}

/* On the ball the icons sit after a longer label, so they need a
   clearer gap than they do on a roster chip. */
.pc-node .pc-chip-reach {
  margin-left: 5px;
}

.pc-warn {
  padding: 10px 12px;
  border: 1px solid color-mix(in srgb, var(--maybe) 46%, transparent);
  border-radius: 9px;
  background: color-mix(in srgb, var(--maybe) 12%, rgba(15, 23, 42, 0.5));
}

.pc-warn strong {
  display: block;
  margin-bottom: 6px;
  color: var(--maybe);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* Advisory lists have unknown length — cap and scroll them so a long
   one can't push the roster off screen. */
.pc-warn ul {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 152px;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  list-style: none;
}

.pc-warn button {
  display: flex;
  flex-direction: column;
  width: 100%;
  padding: 5px 8px;
  border: 0;
  border-radius: 6px;
  background: rgba(15, 23, 42, 0.45);
  color: var(--ink);
  font-size: 0.76rem;
  font-weight: 620;
  text-align: left;
  cursor: pointer;
}

.pc-warn button:hover { background: rgba(15, 23, 42, 0.75); }
.pc-warn button span { color: var(--muted); font-size: 0.66rem; font-weight: 500; }

.pc-warn--drop {
  border-color: color-mix(in srgb, var(--no) 46%, transparent);
  background: color-mix(in srgb, var(--no) 11%, rgba(15, 23, 42, 0.5));
}

.pc-warn--drop strong { color: var(--no); }

.pc-warn .pc-warn-all {
  align-items: center;
  margin-top: 6px;
  padding: 5px;
  border: 1px solid color-mix(in srgb, var(--no) 50%, transparent);
  background: transparent;
  color: var(--no);
  font-size: 0.72rem;
}

.pc-warn .pc-warn-all:hover { background: color-mix(in srgb, var(--no) 18%, transparent); }

.pc-reset {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 7px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  font-size: 0.73rem;
  cursor: pointer;
}

.pc-reset:hover { color: var(--ink); border-color: var(--line-strong); }

.pc-canvas-wrap {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: 10px;
  min-width: 0;
  min-height: 0;
}

.pc-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding: 2px 4px; }
.pc-head h1 { margin: 0 0 3px; color: var(--ink); font-size: 1.3rem; font-weight: 740; letter-spacing: -0.016em; }
.pc-head p { margin: 0; max-width: 62ch; color: var(--muted); font-size: 0.78rem; line-height: 1.45; }

.pc-search {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 12px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--glass-strong);
  color: var(--muted);
  backdrop-filter: blur(18px);
}

.pc-search input { width: 150px; border: 0; background: transparent; color: var(--ink); font-size: 0.79rem; outline: none; }

.pc-canvas {
  position: relative;
  min-height: 0;
  border: 1px solid transparent;
  border-image: var(--specular) 1;
  border-radius: 12px;
  background: var(--glass);
  overflow: hidden;
  cursor: grab;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  backdrop-filter: blur(10px);
}

.pc-canvas.is-dragging { cursor: grabbing; }
.pc-canvas:focus-visible { outline: 2px solid var(--ghost); outline-offset: -2px; }

.pc-links {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}

.pc-link { stroke: var(--best); stroke-width: 1.2; stroke-linecap: round; }

/* The event itself. Rides the ball like everything else (it is a point
   on the sphere, so it turns away when you drag), but during a switch
   between gatherings it holds the centre while the world turns under
   it. Lines from here mean "invited". */
.pc-hub {
  --x: 50%;
  --y: 50%;
  --s: 1;
  --o: 1;
  position: absolute;
  left: 0;
  top: 0;
  z-index: 1100;
  display: grid;
  place-content: center;
  gap: 2px;
  width: 124px;
  height: 124px;
  padding: 12px;
  border: 1px solid color-mix(in srgb, var(--best) 55%, transparent);
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 38%, color-mix(in srgb, var(--best) 26%, rgba(2, 6, 23, 0.9)), rgba(2, 6, 23, 0.94) 70%);
  box-shadow:
    0 0 0 6px color-mix(in srgb, var(--best) 7%, transparent),
    0 0 42px color-mix(in srgb, var(--best) 20%, transparent);
  color: var(--ink);
  text-align: center;
  opacity: var(--o);
  transform: translate3d(var(--x), var(--y), 0) translate(-50%, -50%) scale(var(--s));
  pointer-events: none;
  will-change: transform, opacity;
}

.pc-hub-title {
  display: -webkit-box;
  overflow: hidden;
  font-size: 0.86rem;
  font-weight: 740;
  line-height: 1.15;
  letter-spacing: -0.01em;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

/* Rename the group. Only this control is clickable inside the hub —
   .pc-hub stays pointer-events:none so a drag started anywhere in the
   middle of the ball still turns it. */
.pc-hub-rename {
  max-width: 100%;
  padding: 2px 4px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 0.86rem;
  font-weight: 740;
  line-height: 1.15;
  letter-spacing: -0.01em;
  text-align: center;
  pointer-events: auto;
  cursor: text;
  transition: background 140ms ease, border-color 140ms ease;
}

.pc-hub-rename:hover {
  border-color: color-mix(in srgb, var(--best) 50%, transparent);
  background: rgba(255, 255, 255, 0.06);
}

.pc-hub-input {
  width: 96px;
  justify-self: center;
  padding: 2px 4px;
  border: 1px solid color-mix(in srgb, var(--best) 70%, transparent);
  border-radius: 6px;
  background: rgba(2, 6, 23, 0.9);
  color: var(--ink);
  font: inherit;
  font-size: 0.8rem;
  font-weight: 740;
  text-align: center;
  pointer-events: auto;
  outline: none;
}

.pc-hub-meta {
  color: var(--best);
  font-size: 0.64rem;
  font-weight: 650;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.pc-node {
  --x: 0px;
  --y: 0px;
  --s: 1;
  --o: 0;
  --boost: 1;
  position: absolute;
  left: 0;
  top: 0;
  padding: 4px 11px;
  border: 1px solid color-mix(in srgb, var(--node-color) 42%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--node-color) 15%, rgba(2, 6, 23, 0.66));
  color: var(--ink);
  font-size: 0.8rem;
  font-weight: 620;
  white-space: nowrap;
  cursor: pointer;
  opacity: var(--o);
  transform: translate3d(var(--x), var(--y), 0) translate(-50%, -50%) scale(calc(var(--s) * var(--boost)));
  will-change: transform, opacity;
  transition: background var(--motion-fast) ease, border-color var(--motion-fast) ease;
}

.pc-node[data-reachable="false"] { pointer-events: none; }

.pc-node:hover,
.pc-node:focus-visible {
  --boost: 1.1;
  background: color-mix(in srgb, var(--node-color) 34%, rgba(2, 6, 23, 0.55));
  border-color: var(--node-color);
  outline: none;
}

.pc-node:focus-visible { box-shadow: 0 0 0 2px var(--node-color); }

/* Invited reads unmistakably different from suggested — on a sphere,
   position alone can't carry "in" vs "nearby". */
.pc-node.is-invited {
  border-color: color-mix(in srgb, var(--best) 70%, transparent);
  background: color-mix(in srgb, var(--best) 18%, rgba(2, 6, 23, 0.7));
  font-weight: 700;
}

.pc-canvas.is-dragging .pc-node { cursor: grabbing; }

.pc-circle-0 { --node-color: var(--band-0); }
.pc-circle-1 { --node-color: var(--band-1); }
.pc-circle-2 { --node-color: var(--band-2); }
.pc-circle-3 { --node-color: var(--band-3); }
.pc-circle-4 { --node-color: var(--band-4); }
.pc-circle-5 { --node-color: var(--band-5); }
.pc-circle-6 { --node-color: var(--band-6); }
.pc-circle-7 { --node-color: var(--band-7); }

.pc-legend { display: flex; flex-wrap: wrap; gap: 12px; padding: 0 4px; }

.pc-legend-item { display: inline-flex; align-items: center; gap: 5px; color: var(--muted); font-size: 0.69rem; }

.pc-legend-item::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--node-color);
}

.pc-legend-invited::before {
  width: 14px;
  height: 2px;
  border-radius: 1px;
  background: var(--best);
}

@media (max-width: 900px) {
  .pc-shell { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); overflow-y: auto; }
  .pc-rail { max-height: 40vh; }
}

.pc-recenter {
  position: absolute;
  right: 12px;
  bottom: 12px;
  z-index: 1200;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(15, 23, 42, 0.7);
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 620;
  cursor: pointer;
  backdrop-filter: blur(12px);
}

.pc-recenter:hover { color: var(--ink); border-color: var(--line-strong); }

/* ── Lens glass material ────────────────────────────────────────────────
   Appended last on purpose: it overrides the per-panel glass declarations
   above at equal specificity while every mode-specific reset (setup-mode
   top-chrome, send-gate dimming, hover lifts) keeps winning. The rim is a
   ::after ring so it honors border-radius, which border-image never did.
   .top-chrome stays out: it is a transparent layout container, and giving
   it a backdrop-filter creates a stacking context that traps the
   title-island's z-index under the calendar. */
.edge-tray,
.title-island,
.mode-action-bar,
.flow-tile {
  position: relative;
  border: 1px solid var(--glass-edge);
  border-image: none;
  background: var(--glass-fill), var(--glass-base);
  box-shadow: var(--glass-shadow);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  backdrop-filter: url(#pogo-lens) saturate(150%);
}

.edge-tray::after,
.title-island::after,
.mode-action-bar::after,
.flow-tile::after {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: 2;
  padding: 1px;
  border-radius: inherit;
  background: var(--glass-rim);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
}

/* Contacts import — a quiet block in the rail. The privacy line is part
   of the interface, not a footnote: people are being asked for an
   address book and deserve to know it stays in their browser. */
.pc-import {
  border: 1px solid var(--line);
  border-radius: 9px;
  background: rgba(15, 23, 42, 0.36);
  overflow: hidden;
}

.pc-import-toggle {
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  padding: 9px 11px;
  border: 0;
  background: transparent;
  color: var(--ink);
  font-size: 0.76rem;
  font-weight: 660;
  cursor: pointer;
}

.pc-import-toggle:hover { background: rgba(15, 23, 42, 0.5); }
.pc-import-toggle svg { color: var(--ghost); }
.pc-import-caret { margin-left: auto; color: var(--muted); font-size: 0.9rem; }

.pc-import-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 0 11px 11px;
}

.pc-import-lead { margin: 0; color: var(--muted); font-size: 0.72rem; line-height: 1.5; }
.pc-import-lead strong { color: var(--ink); font-weight: 680; }

.pc-import-steps {
  margin: 0;
  padding-left: 18px;
  color: var(--muted);
  font-size: 0.72rem;
  line-height: 1.6;
}

.pc-import-steps strong { color: var(--ink); }
.pc-import-steps a { color: var(--ghost); }
.pc-import-steps li::marker { color: var(--ghost); }

/* Hidden but focusable: the styled button drives it. */
.pc-import-file {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.pc-import-btn {
  padding: 7px 10px;
  border: 1px solid color-mix(in srgb, var(--ghost) 55%, transparent);
  border-radius: 7px;
  background: color-mix(in srgb, var(--ghost) 20%, transparent);
  color: var(--ink);
  font-size: 0.74rem;
  font-weight: 640;
  cursor: pointer;
}

.pc-import-btn:hover { background: color-mix(in srgb, var(--ghost) 34%, transparent); }

.pc-import-btn--alt {
  border-color: var(--line);
  background: transparent;
  color: var(--muted);
}

.pc-import-note {
  margin: 0;
  padding: 7px 9px;
  border-radius: 6px;
  background: rgba(2, 6, 23, 0.45);
  color: var(--ink);
  font-size: 0.72rem;
  line-height: 1.45;
}

.pc-import-forget {
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--muted);
  font-size: 0.68rem;
  text-align: left;
  text-decoration: underline;
  cursor: pointer;
}

.pc-import-forget:hover { color: var(--no); }

.pc-import-source {
  margin: 0;
  padding: 0 11px 9px;
  color: var(--best);
  font-size: 0.68rem;
  font-weight: 620;
}

.pc-import-source[data-sample="true"] { color: var(--muted); font-weight: 500; }

.pc-import-head {
  /* Row, so the "How do I get this?" link can sit out at the end. */
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin: 6px 0 0;
  color: var(--ink);
  font-size: 0.68rem;
  font-weight: 720;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.pc-import-code {
  padding: 1px 5px;
  border-radius: 4px;
  background: rgba(2, 6, 23, 0.6);
  color: var(--best);
  font-family: ui-monospace, SFMono-Regular, monospace;
  font-size: 0.68rem;
}

.pc-import-fine { margin: 0; color: var(--muted); font-size: 0.66rem; line-height: 1.45; opacity: 0.85; }

.pc-account { padding: 0 11px 10px; }

.pc-signin {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid color-mix(in srgb, var(--ghost) 60%, transparent);
  border-radius: 8px;
  background: color-mix(in srgb, var(--ghost) 26%, transparent);
  color: var(--ink);
  font-size: 0.75rem;
  font-weight: 660;
  cursor: pointer;
}

.pc-signin:hover { background: color-mix(in srgb, var(--ghost) 40%, transparent); }

/* Paste-out buttons. Mail and Messages want different things: Mail
   parses "Name <addr>", Messages recognises bare numbers only. */
.guest-copy-row { display: flex; gap: 6px; margin-left: 8px; }

.guest-copy-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 11px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: rgba(15, 23, 42, 0.45);
  color: var(--ink);
  font-size: 0.72rem;
  font-weight: 620;
  white-space: nowrap;
  cursor: pointer;
}

.guest-copy-btn:hover:not(:disabled) { background: rgba(15, 23, 42, 0.72); border-color: var(--line-strong); }
.guest-copy-btn:disabled { opacity: 0.4; cursor: default; }

.guest-copy-count {
  padding: 0 6px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--best) 24%, transparent);
  color: var(--best);
  font-size: 0.66rem;
}

/* Clerk has a session but Convex refused the token. This is a dead end
   the user cannot click their way out of, so it states the cause and
   offers the only action that helps: sign out and back in. */
.pc-account-stuck {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-start;
  max-width: 320px;
  padding: 10px 12px;
  border: 1px solid rgba(214, 120, 60, 0.45);
  border-radius: 10px;
  background: rgba(214, 120, 60, 0.08);
  font-size: 12px;
  line-height: 1.45;
  color: var(--ink);
}
.pc-account-stuck p { margin: 0; }
.pc-account-stuck-fix { opacity: 0.8; }
.pc-account-stuck code {
  font-size: 11px;
  padding: 0 3px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.12);
}

/* The chosen list as text you can paste. Two targets, two formats:
   Mail parses "Name <addr>", Messages recognises bare numbers only.
   Each line is selectable text (drag-select and copy still work) with
   a copy button at the end for the one-click path. */
.pc-copy { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }

.pc-paste { display: flex; flex-direction: column; gap: 4px; }

.pc-paste-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.pc-paste-label {
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.pc-paste-count { color: var(--muted); font-size: 0.64rem; }

.pc-paste-body {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  padding: 7px 7px 7px 9px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: rgba(15, 23, 42, 0.45);
}

.pc-paste-text {
  flex: 1;
  min-width: 0;
  max-height: 84px;
  margin: 0;
  overflow-y: auto;
  color: var(--ink);
  font-size: 0.72rem;
  line-height: 1.6;
  /* Addresses and numbers are long, unbroken, and worth reading
     exactly — wrap anywhere rather than overflowing the rail. */
  overflow-wrap: anywhere;
  user-select: all;
}

.pc-paste-empty { color: var(--muted); user-select: none; }

.pc-paste-sep { color: rgba(255, 255, 255, 0.5); }

/* Same highlight formula as .guest-token / .guest-token-hovered in the
   event workspace, so a lit entry reads identically on both surfaces.
   Nothing sits under this text, so unlike the input mirror it is free
   to use padding without pushing glyphs off the caret. */
.pc-paste-token {
  border-radius: 4px;
  text-shadow:
    0.4px 0 0 currentColor,
    -0.4px 0 0 currentColor;
  transition: background 140ms ease, box-shadow 140ms ease;
}

.pc-paste-token.is-lit {
  color: #ffffff;
  background: color-mix(in srgb, var(--ghost) 60%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--ghost) 80%, transparent);
  text-shadow:
    0.6px 0 0 currentColor,
    -0.6px 0 0 currentColor;
}

.pc-paste-copy {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease;
}

.pc-paste-copy:hover:not(:disabled) {
  background: color-mix(in srgb, var(--ghost) 40%, transparent);
  border-color: color-mix(in srgb, var(--ghost) 70%, transparent);
  color: var(--ink);
}

.pc-paste-copy:disabled { opacity: 0.4; cursor: default; }


/* People who are only a phone number. Kept out of the ball because a
   ball of phone numbers cannot be read, but still invitable. */
.pc-unknown { margin-top: 10px; font-size: 0.72rem; }
.pc-unknown > summary {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 0;
  cursor: pointer;
  color: var(--ink);
}
.pc-unknown > summary span { color: var(--muted); }
.pc-unknown-lead { margin: 4px 0 8px; color: var(--muted); line-height: 1.45; }
.pc-unknown ul {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 180px;
  overflow-y: auto;
}
.pc-unknown li button {
  width: 100%;
  padding: 3px 6px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 0.7rem;
  text-align: left;
  cursor: pointer;
}
.pc-unknown li button:hover { background: color-mix(in srgb, var(--ghost) 40%, transparent); color: var(--ink); }
.pc-unknown li button.is-invited { color: var(--ink); font-weight: 600; }

/* Identity questions the merge refused to answer by itself. Styled
   apart from .pc-unknown's plain list: these are open questions, not a
   roster, and each line is a sentence rather than a name. */
.pc-identity > summary strong { color: var(--maybe); }

.pc-identity ul {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 6px 0 0;
  padding: 0;
  list-style: none;
}

.pc-identity li {
  padding: 6px 8px;
  border: 1px solid color-mix(in srgb, var(--maybe) 32%, transparent);
  border-radius: 7px;
  background: color-mix(in srgb, var(--maybe) 9%, rgba(15, 23, 42, 0.5));
  color: var(--muted);
  font-size: 0.7rem;
  line-height: 1.45;
}

.pc-identity li strong { color: var(--ink); font-weight: 680; }

/* ── How do I get this? ─────────────────────────────────────────────
   The import panel keeps its three-line summary for someone who has
   done this before; the full walkthrough lives here so it does not
   bury the controls. */
.pc-help-link {
  margin-left: auto;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ghost);
  font: inherit;
  font-size: 0.68rem;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.pc-help-link:hover { color: var(--ink); }

.pc-help-scrim {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: grid;
  place-items: center;
  padding: 24px;
  background: rgba(2, 6, 23, 0.72);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

.pc-help {
  width: min(560px, 100%);
  max-height: min(82vh, 720px);
  overflow-y: auto;
  padding: 20px 22px 18px;
  border: 1px solid var(--glass-edge);
  border-radius: 14px;
  background: var(--glass-fill), rgba(8, 15, 31, 0.96);
  box-shadow: var(--glass-shadow);
  color: var(--ink);
  outline: none;
}

.pc-help-head {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 10px;
}

.pc-help-head h2 {
  flex: 1;
  margin: 0;
  font-size: 1.02rem;
  font-weight: 760;
  letter-spacing: -0.01em;
}

.pc-help-close {
  flex: 0 0 auto;
  display: inline-flex;
  padding: 4px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}

.pc-help-close:hover { background: rgba(255, 255, 255, 0.08); color: var(--ink); }

.pc-help-lead {
  margin: 0 0 14px;
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.55;
}

.pc-help-steps {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0 0 14px;
  padding-left: 20px;
}

.pc-help-steps li { color: var(--muted); font-size: 0.78rem; line-height: 1.5; }
.pc-help-steps strong { display: block; color: var(--ink); font-weight: 680; }

.pc-help-code {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 8px 8px 11px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: rgba(2, 6, 23, 0.6);
}

.pc-help-code code {
  flex: 1;
  color: var(--best);
  font-size: 0.76rem;
  user-select: all;
}

.pc-help-code button {
  display: inline-flex;
  padding: 5px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}

.pc-help-code button:hover {
  background: color-mix(in srgb, var(--ghost) 40%, transparent);
  color: var(--ink);
}

.pc-help-fine {
  margin: 12px 0 0;
  color: var(--muted);
  font-size: 0.71rem;
  line-height: 1.5;
}

/* The optional second command, set apart so the required one above it
   still reads as the answer to "what do I run?". */
.pc-help-also {
  margin-top: 12px;
  padding: 11px 12px;
  border: 1px solid color-mix(in srgb, var(--ghost) 32%, transparent);
  border-radius: 10px;
  background: color-mix(in srgb, var(--ghost) 9%, rgba(15, 23, 42, 0.4));
}

.pc-help-also strong {
  display: block;
  margin-bottom: 4px;
  color: var(--ink);
  font-size: 0.74rem;
  font-weight: 700;
}

.pc-help-also p {
  margin: 0 0 9px;
  color: var(--muted);
  font-size: 0.74rem;
  line-height: 1.5;
}

/* Messages will not take a pasted list — it keeps the first number and
   drops the rest — so the text box alone is a dead end on a Mac. This
   link hands the whole list to a new conversation. */
.pc-paste-open {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-top: 5px;
  padding: 6px 9px;
  border: 1px solid color-mix(in srgb, var(--best) 42%, transparent);
  border-radius: 8px;
  background: color-mix(in srgb, var(--best) 12%, rgba(15, 23, 42, 0.45));
  color: var(--ink);
  font-size: 0.72rem;
  font-weight: 650;
  text-decoration: none;
}

.pc-paste-open:hover { background: color-mix(in srgb, var(--best) 22%, rgba(15, 23, 42, 0.45)); }
.pc-paste-open span { color: var(--muted); font-size: 0.66rem; font-weight: 500; }

.guest-copy-open {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  border-color: color-mix(in srgb, var(--best) 45%, transparent);
  background: color-mix(in srgb, var(--best) 12%, rgba(15, 23, 42, 0.5));
}

.guest-copy-open:hover { background: color-mix(in srgb, var(--best) 22%, rgba(15, 23, 42, 0.5)); }

/* Searching for a group by name. The rail shows the top handful; a
   gathering you remember clearly can sit far below it, and asking for
   it by name is the only reliable way to reach it. */
.pc-find { position: relative; }

.pc-group-hits {
  position: absolute;
  z-index: 40;
  top: calc(100% + 6px);
  right: 0;
  width: min(340px, 80vw);
  max-height: 320px;
  overflow-y: auto;
  margin: 0;
  padding: 5px;
  list-style: none;
  border: 1px solid var(--glass-edge);
  border-radius: 11px;
  background: var(--glass-fill), rgba(8, 15, 31, 0.97);
  box-shadow: var(--glass-shadow);
}

.pc-group-hits button {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 100%;
  padding: 7px 9px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.pc-group-hits button:hover { background: color-mix(in srgb, var(--ghost) 38%, transparent); }

.pc-group-hit-name { font-size: 0.78rem; font-weight: 680; }
.pc-group-hit-meta { color: var(--muted); font-size: 0.68rem; }

/* The crowd's other names. One group that starts a new chat every
   time is only recognisable if you can see what it was called before. */
.pc-recent-aka {
  display: block;
  margin-top: 2px;
  color: var(--muted);
  font-size: 0.64rem;
  font-style: italic;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
