diff --git a/app.css b/app.css index 484b0dc..71a817c 100644 --- a/app.css +++ b/app.css @@ -3891,3 +3891,169 @@ footer a:hover { color: var(--accent-2); } .ticket-number { transform: none; margin-bottom: 0; } .ticket-admit { border-top: none; border-left: 1px solid #e0e0e0; padding-top: 0; padding-left: .5rem; width: auto; } } + +/* ── ORDER (confirmation email card) ── */ +.card.order-card { + background: var(--card); + border-radius: var(--radius); +} +.card.order-card .hd.hidden { display: none; } + +.order-conf-header { + text-align: center; + padding: 2rem 1.5rem 1.2rem; +} + +.order-check { + display: inline-flex; + align-items: center; + justify-content: center; + width: 56px; + height: 56px; + border-radius: 50%; + background: #16a34a; + color: #fff; + font-size: 1.6rem; + font-weight: 700; + line-height: 1; + margin-bottom: .8rem; + box-shadow: 0 0 0 6px rgba(22,163,74,.15); + animation: order-pop .4s ease-out; +} +@keyframes order-pop { + 0% { transform: scale(0); opacity: 0; } + 60% { transform: scale(1.15); } + 100% { transform: scale(1); opacity: 1; } +} + +.order-conf-label { + font-size: 1.35rem; + font-weight: 700; + color: var(--ink); + letter-spacing: -.01em; + margin-bottom: .25rem; +} + +.order-conf-name { + font-size: .9rem; + color: var(--muted); + font-weight: 500; +} + +.order-number-chip { + text-align: center; + margin: .8rem auto; + display: inline-block; + font-family: var(--mono); + font-size: .82rem; + background: var(--surface); + border: 1px solid var(--line); + padding: .35rem .9rem; + border-radius: 6px; + color: var(--ink-2); + letter-spacing: .02em; + width: 100%; + box-sizing: border-box; + display: block; +} + +.order-status-pill { + text-align: center; + margin: .6rem 0 1rem; +} +.order-status-pill span, +.order-status-pill { + display: inline-block; + font-size: .75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: .04em; + padding: .3rem .85rem; + border-radius: 100px; + line-height: 1.4; +} +.order-status-amber { + background: #fef3c7; + color: #92400e; +} +.order-status-green { + background: #dcfce7; + color: #166534; +} +.order-status-red { + background: #fee2e2; + color: #991b1b; +} + +.order-summary-box { + background: var(--surface); + border: 1px solid var(--line); + border-radius: var(--radius-sm); + padding: 1rem 1.2rem; + margin: .4rem 0; +} + +.order-summary-row { + display: flex; + justify-content: space-between; + align-items: baseline; + padding: .45rem 0; +} +.order-summary-row + .order-summary-row { + border-top: 1px solid var(--line-2); +} + +.order-summary-label { + font-size: .82rem; + color: var(--muted); + font-weight: 500; +} + +.order-summary-value { + font-size: .88rem; + color: var(--ink); + font-weight: 600; +} + +.order-note { + font-size: .85rem; + color: var(--muted); + line-height: 1.55; + padding: .8rem 0; + border-top: 1px solid var(--line-2); + margin-top: .6rem; +} + +.order-cta { + display: flex; + align-items: center; + justify-content: center; + gap: .4rem; + margin-top: 1.2rem; + padding: .75rem 1.5rem; + background: var(--accent); + color: #fff; + font-weight: 600; + font-size: .92rem; + border-radius: 10px; + text-decoration: none; + transition: background .15s, transform .1s; +} +.order-cta:hover { + filter: brightness(1.08); + transform: translateY(-1px); +} +.order-cta-arrow { + font-size: 1.1em; + transition: transform .15s; +} +.order-cta:hover .order-cta-arrow { + transform: translateX(3px); +} + +@media (max-width: 480px) { + .order-conf-header { padding: 1.4rem 1rem .8rem; } + .order-check { width: 46px; height: 46px; font-size: 1.3rem; } + .order-conf-label { font-size: 1.15rem; } + .order-summary-box { padding: .8rem; } +} diff --git a/app.js b/app.js index 727fd0e..fd25a38 100644 --- a/app.js +++ b/app.js @@ -2426,6 +2426,106 @@ handled.add("expectedArrivalUntil"); handled.add("itemShipped"); handled.add("deliveryAddress"); } + // ── ORDER (confirmation email card) ── + if (cfg.type === "Order") { + const card = document.querySelector(".card"); + if (card) card.classList.add("order-card"); + + // Hide normal header + const hd = document.querySelector(".hd"); + if (hd) hd.classList.add("hidden"); + + // Remove generic hero if present + const heroMount = document.getElementById("hero-mount"); + if (heroMount) heroMount.querySelector('.hero')?.remove(); + + // ── Confirmation header: big checkmark + title ── + const confHeader = document.createElement("div"); confHeader.className = "order-conf-header"; + + // Animated checkmark circle + const checkCircle = document.createElement("div"); checkCircle.className = "order-check"; + checkCircle.innerHTML = "\u2713"; + confHeader.appendChild(checkCircle); + + // "Order Confirmed" label + const confLabel = document.createElement("div"); confLabel.className = "order-conf-label"; + confLabel.textContent = "Order Confirmed"; + confHeader.appendChild(confLabel); + + // Order name as subtitle + const orderName = data["name"]; + if (orderName) { + const nameEl = document.createElement("div"); nameEl.className = "order-conf-name"; + nameEl.textContent = orderName; + confHeader.appendChild(nameEl); + } + + view.appendChild(confHeader); + + // ── Order number chip (monospace) ── + const orderNum = data["orderNumber"]; + if (orderNum) { + const chip = document.createElement("div"); chip.className = "order-number-chip"; + chip.textContent = orderNum; + view.appendChild(chip); + } + + // ── Status pill ── + const status = data["orderStatus"]; + if (status) { + const pill = document.createElement("div"); pill.className = "order-status-pill"; + // Extract short label from URL like https://schema.org/OrderProcessing + const shortStatus = status.split("/").pop().replace("Order", ""); + pill.textContent = shortStatus; + // Color by status + if (status.includes("Delivered") || status.includes("Complete")) pill.classList.add("order-status-green"); + else if (status.includes("Cancelled")) pill.classList.add("order-status-red"); + else pill.classList.add("order-status-amber"); + view.appendChild(pill); + } + + // ── Summary box: seller + date ── + const summaryBox = document.createElement("div"); summaryBox.className = "order-summary-box"; + + const seller = data["seller"]; + if (seller) { + const row = document.createElement("div"); row.className = "order-summary-row"; + row.innerHTML = "Seller" + seller + ""; + summaryBox.appendChild(row); + } + + const orderDate = data["orderDate"]; + if (orderDate) { + const d = formatDate(orderDate); + const row = document.createElement("div"); row.className = "order-summary-row"; + row.innerHTML = "Date" + (d || orderDate) + ""; + summaryBox.appendChild(row); + } + + if (summaryBox.children.length) view.appendChild(summaryBox); + + // Description as a note + const desc = data["description"]; + if (desc) { + const note = document.createElement("div"); note.className = "order-note"; + note.textContent = desc; + view.appendChild(note); + } + + // Track Order CTA + const url = data["url"]; + if (url) { + const cta = document.createElement("a"); cta.className = "order-cta"; + cta.href = url; cta.target = "_blank"; cta.rel = "noopener"; + cta.innerHTML = "Track Order\u2192"; + view.appendChild(cta); + } + + handled.add("name"); handled.add("image"); handled.add("description"); + handled.add("orderNumber"); handled.add("orderStatus"); handled.add("orderDate"); + handled.add("seller"); handled.add("url"); + } + // ── QUESTION (Stack Overflow Q&A card) ── if (cfg.type === "Question") { const card = document.querySelector(".card");