+ top:${topPx}px;height:${heightPx}px;left:calc(8px + ${col} * (100% - 16px) / ${totalCols});width:calc((100% - 16px) / ${totalCols} - 2px);background:${es.bgColor};border-left-color:${ev.source_color || "#6366f1"};border-left-style:${es.borderStyle};opacity:${es.opacity}">
${this.esc(ev.title)}${likelihoodBadge}
${this.formatTime(ev.start_time)} \u2013 ${this.formatTime(ev.end_time)}${virtualBadge}
${ev.location_name ? `
${this.esc(ev.location_name)}
` : ""}
@@ -1758,17 +1841,30 @@ class FolkCalendarView extends HTMLElement {
let eventsOverlay = "";
for (let i = 0; i < 7; i++) {
const ds = this.dateStr(days[i]);
- for (const ev of this.getEventsForDate(ds)) {
- const start = new Date(ev.start_time), end = new Date(ev.end_time);
- if ((end.getTime() - start.getTime()) >= 86400000) continue;
- const startMin = start.getHours() * 60 + start.getMinutes();
- const endMin = end.getHours() * 60 + end.getMinutes();
- const duration = Math.max(endMin - startMin, 20);
+ const dayTimed = this.getEventsForDate(ds).filter(ev => {
+ const s = new Date(ev.start_time), en = new Date(ev.end_time);
+ return (en.getTime() - s.getTime()) < 86400000;
+ }).sort((a, b) => a.start_time.localeCompare(b.start_time));
+
+ // Compute sub-columns for overlapping events within this day
+ const intervals = dayTimed.map(ev => {
+ const s = new Date(ev.start_time), e = new Date(ev.end_time);
+ const startMin = s.getHours() * 60 + s.getMinutes();
+ const endMin = e.getHours() * 60 + e.getMinutes();
+ return { startMin, endMin: Math.max(endMin, startMin + 20) };
+ });
+ const dayLayout = this.computeEventColumns(intervals);
+
+ for (let j = 0; j < dayTimed.length; j++) {
+ const ev = dayTimed[j];
+ const { startMin, endMin } = intervals[j];
+ const { col, totalCols } = dayLayout[j];
+ const duration = endMin - startMin;
const topPx = ((startMin - START_HOUR * 60) / 60) * HOUR_HEIGHT;
const heightPx = Math.max((duration / 60) * HOUR_HEIGHT, 18);
const es = this.getEventStyles(ev);
- const colLeft = `calc(44px + ${i} * ((100% - 44px) / 7) + 2px)`;
- const colWidth = `calc((100% - 44px) / 7 - 4px)`;
+ const colLeft = `calc(44px + ${i} * ((100% - 44px) / 7) + 2px + ${col} * ((100% - 44px) / 7 - 4px) / ${totalCols})`;
+ const colWidth = `calc(((100% - 44px) / 7 - 4px) / ${totalCols} - 1px)`;
const showMeta = heightPx >= 36;
const timeStr = `${this.formatTime(ev.start_time)}\u2013${this.formatTime(ev.end_time)}`;
const locName = ev.location_name ? this.esc(ev.location_name) : "";
@@ -2785,10 +2881,10 @@ class FolkCalendarView extends HTMLElement {
.synodic-marker { position: absolute; top: -2px; font-size: 12px; transform: translateX(-50%); pointer-events: none; }
/* ── Month Grid ── */
- .weekdays { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; margin-bottom: 4px; }
+ .weekdays { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 2px; margin-bottom: 4px; }
.wd { text-align: center; font-size: 11px; color: var(--rs-text-muted); padding: 4px; font-weight: 600; }
- .grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; position: relative; flex: 1; }
- .day { background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 6px; min-height: 0; padding: 6px; cursor: pointer; position: relative; -webkit-tap-highlight-color: transparent; }
+ .grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 2px; position: relative; flex: 1; }
+ .day { background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 6px; min-height: 0; min-width: 0; overflow: hidden; padding: 6px; cursor: pointer; position: relative; -webkit-tap-highlight-color: transparent; }
.day:hover { border-color: var(--rs-border-strong); }
.day.today { border-color: var(--rs-primary-hover); background: var(--rs-bg-active); }
.day.expanded { border-color: var(--rs-primary-hover); background: rgba(99,102,241,0.1); }
@@ -2916,7 +3012,7 @@ class FolkCalendarView extends HTMLElement {
.hour-row { display: flex; min-height: 48px; border-bottom: 1px solid var(--rs-border-subtle); position: relative; }
.hour-label { position: absolute; left: -48px; top: -7px; width: 40px; text-align: right; font-size: 10px; color: var(--rs-text-muted); font-variant-numeric: tabular-nums; }
.hour-content { flex: 1; position: relative; padding-left: 8px; }
- .tl-event { position: absolute; left: 8px; right: 8px; border-radius: 6px; padding: 4px 8px; font-size: 11px; overflow: hidden; cursor: pointer; border-left: 3px solid; z-index: 1; transition: opacity 0.15s; }
+ .tl-event { position: absolute; border-radius: 6px; padding: 4px 8px; font-size: 11px; overflow: hidden; cursor: pointer; border-left: 3px solid; z-index: 1; transition: opacity 0.15s; box-sizing: border-box; }
.tl-event:hover { opacity: 0.85; }
.tl-event-title { font-weight: 600; color: var(--rs-text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-event-time { font-size: 10px; color: var(--rs-text-secondary); }
@@ -2939,7 +3035,7 @@ class FolkCalendarView extends HTMLElement {
.week-time-label { font-size: 10px; color: var(--rs-text-muted); text-align: right; padding-right: 6px; font-variant-numeric: tabular-nums; height: 48px; }
.week-cell { border-left: 1px solid var(--rs-border-subtle); border-bottom: 1px solid var(--rs-border-subtle); min-height: 48px; position: relative; }
.week-cell.today { background: rgba(99,102,241,0.04); }
- .week-event { position: absolute; left: 2px; right: 2px; border-radius: 4px; padding: 2px 4px; font-size: 10px; overflow: hidden; cursor: pointer; border-left: 2px solid; z-index: 1; }
+ .week-event { position: absolute; border-radius: 4px; padding: 2px 4px; font-size: 10px; overflow: hidden; cursor: pointer; border-left: 2px solid; z-index: 1; box-sizing: border-box; }
.week-event:hover { opacity: 0.85; }
.week-event-title { font-weight: 600; color: var(--rs-text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.week-event-meta { font-size: 9px; color: var(--rs-text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@@ -3015,7 +3111,7 @@ class FolkCalendarView extends HTMLElement {
.dh-hours { position: relative; height: 24px; border-bottom: 1px solid var(--rs-border); }
.dh-hour { position: absolute; top: 0; height: 24px; font-size: 10px; color: var(--rs-text-muted); text-align: center; border-left: 1px solid var(--rs-border-subtle); line-height: 24px; }
.dh-events { position: relative; min-height: 140px; padding-top: 8px; }
- .dh-event { position: absolute; top: 32px; height: auto; min-height: 48px; border-radius: 6px; padding: 4px 8px; font-size: 11px; overflow: hidden; cursor: pointer; border-top: 3px solid; z-index: 1; }
+ .dh-event { position: absolute; height: auto; min-height: 48px; border-radius: 6px; padding: 4px 8px; font-size: 11px; overflow: hidden; cursor: pointer; border-top: 3px solid; z-index: 1; box-sizing: border-box; }
.dh-event:hover { opacity: 0.85; }
.dh-now { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--rs-error); z-index: 5; }
.dh-now::before { content: ""; position: absolute; top: -3px; left: -3px; width: 8px; height: 8px; border-radius: 50%; background: var(--rs-error); }