diff --git a/COPILOT_CONTEXT_TR.md b/COPILOT_CONTEXT_TR.md new file mode 100644 index 00000000..2a1301d4 --- /dev/null +++ b/COPILOT_CONTEXT_TR.md @@ -0,0 +1,199 @@ +# EnvelopeGenerator — Copilot Ba?lam Notlar? (Türkçe) + +## Projenin Amac? +Dijital belge imzalama sistemi. Göndericiler PDF yükleyip PSPDFKit üzerinden imza alan? (annotation) yerle?tirir. Al?c?lar Blazor WASM viewer'da belgeyi görür, annotation konumlar?nda checkbox overlay ile imza alanlar?n? onaylar, imzalar?n? olu?turur ve imzal? PDF'i export eder. + +--- + +## Çözüm Yap?s? + +| Proje | Hedef | Aç?klama | +|---|---|---| +| `EnvelopeGenerator.API` | net8.0 | Web API. Receiver auth (cookie), annotation okuma, PDF sunma. | +| `EnvelopeGenerator.ReceiverUI` | net8.0 WASM | Blazor WebAssembly. Al?c? arayüzü. YARP proxy ile API'ye ba?lan?r. | +| `EnvelopeGenerator.Web` | net7/8/9 | Razor Pages. Gönderen UI + PSPDFKit ile annotation yerle?tirme. | +| `EnvelopeGenerator.Application` | multi | MediatR CQRS handler'lar?. | +| `EnvelopeGenerator.Domain` | multi | Domain modelleri, sabitler, arayüzler. | +| `EnvelopeGenerator.Infrastructure` | multi | EF Core repo'lar?, DB context. | +| `EnvelopeGenerator.PdfEditor` | multi | iText7 PDF yard?mc?lar?. ReceiverUI ak???nda KULLANILMIYOR. | +| `EnvelopeGenerator.DependencyInjection` | multi | DI kay?t yard?mc?lar?. | +| VB.NET projeleri (Service/Form/BBTests) | net462 | Eski legacy. DOKUNMA. | + +--- + +## Önemli Dosyalar + +| Dosya | Amaç | +|---|---| +| `ReceiverUI/Pages/ReportViewer.razor` | Ana al?c? sayfas?. Tüm imzalama mant??? burada. | +| `ReceiverUI/wwwroot/js/receiver-signature.js` | JS: checkbox overlay, imza pad (çizim/yaz?/resim). | +| `ReceiverUI/wwwroot/fake-data/annotations.json` | Dev modda sahte annotation konumlar?. YARP proxy bu dosyaya yönlendirir. | +| `ReceiverUI/Models/AnnotationDto.cs` | Annotation pozisyon modeli. Tüm property'ler non-nullable. | +| `ReceiverUI/Services/AnnotationService.cs` | `List` döner; gerçek modda API'den, dev modda fake-data'dan. | +| `ReceiverUI/Services/DocumentService.cs` | PDF byte'lar?n? API'den al?r. | +| `ReceiverUI/Services/AuthService.cs` | Al?c? session cookie'sini yönetir. | +| `ReceiverUI/wwwroot/appsettings.json` | `ForceToUseFakeDocument: true` ? gerçek PDF yüklenmez, ?ablon rapor kullan?l?r. | +| `API/Controllers/AnnotationController.cs` | GET `api/Annotation/{key}` ? annotation listesi. | +| `API/Controllers/DocumentController.cs` | GET `api/Document/{key}` ? PDF byte'lar?. | + +--- + +## AnnotationDto Koordinat Sistemi + +``` +Birim : 1/100 inch (DX units) — DevExpress XtraReports'un yerel koordinat sistemi +Köken : Sol-üst kö?e +X artar : sa?a do?ru +Y artar : a?a??ya do?ru + +A4 boyutlar? DX units cinsinden: Geni?lik = 827, Yükseklik = 1169 + +Dönü?ümler: + PSPDFKit (pt, sol-üst): xDX = xPsPdf * (100/72) + GDPicture (pt, sol-alt): yDX = (pageHeightPt - yGD - elemHeightPt) * (100/72) + DX ? PDF points: pt = dx * (72/100) + PDF Y ekseni çevirme: imgBottomY = sayfaYüksekli?iPt - ann.Y*(72/100) - elemanYüksekli?iPt +``` + +--- + +## ReceiverUI ?mzalama Ak??? (ReportViewer.razor) + +### Sayfa Yüklenince (`OnInitializedAsync`) +1. `AuthService.CheckEnvelopeAccessAsync` ? yetkisizse login sayfas?na yönlendir +2. `AnnotationService.GetAnnotationsAsync` ? `_annotations` listesi dolar +3. `DocumentService.GetDocumentAsync` ? `_basePdfBytes` dolar (gerçek mod) +4. `BuildFreshBaseReport()` ? `XtraReport` olu?turulur, `DxReportViewer`'a verilir + +### `BuildFreshBaseReport()` Mant??? +``` +_basePdfBytes dolu ? XtraReport + DetailBand + XRPdfContent { GenerateOwnPages = true } +_basePdfBytes bo? (ForceToUseFakeDocument=true) ? ReportStorage'dan LargeDatasetReport ?ablonu (XtraReport, designer'dan geldi?i gibi) +``` + +> NOT: `_basePdfBytes` dal? korunur (gerçek PDF modu). Dev ve test sunucusunda `PredefinedReport` (LargeDatasetReport) `XtraReport` olarak do?rudan kullan?l?r — PDF'e export ED?LMEZ. + +### ?mza Popup'? ("Unterschrift erstellen") +- Sekmeler: Çizim / Yaz? / Resim +- Alanlar: Ad soyad (zorunlu), pozisyon (opsiyonel), yer (zorunlu) +- `_capturedSignature` record'una kaydedilir +- Annotation varsa popup kapan?r ? JS checkbox overlay kurulur + +### JS Checkbox Overlay (`receiver-signature.js`) +- `receiverSignature.installAnnotationCheckboxes(annotations, checkedIds, dotNetRef)` C#'tan ça?r?l?r +- Her annotation için `.annot-sig-cb-wrapper` div'i, viewer scroll container'?na absolute olarak yerle?tirilir +- **Koordinat hesab?:** `left = pageRect.left + ann.x * scaleX`, `top = pageRect.top + ann.y * scaleY` + - `scaleX = sayfaPixelGeni?li?i / 827`, `scaleY = sayfaPixelYüksekli?i / 1169` + - Bu koordinatlar sayfa-relatif ve do?ru çal???yor +- T?klan?nca `dotNetRef.invokeMethodAsync('OnAnnotationToggled', id, checked)` ça?r?l?r + +### ?mza Uygulama ("Unterschriften anwenden" — `SubmitSignaturesAsync`) + +**Tek ortak yol (her iki mod):** +- `SubmitSignaturesAsync` ? `BuildFreshBaseReport()` + `WireAnnotationSignatures(report, _capturedSignature)` +- `WireAnnotationSignatures` ? `report.AfterPrint` olay?na abone olur +- Belge olu?unca `report.PrintingSystem.Pages` dolu olur; her annotation için `Pages[ann.Page-1]` sayfas?na: + - `ImageBrick { Image = imza görseli, Rect = (ann.X, ann.Y, 230, 70), SizeMode = ZoomImage }` + - `TextBrick { Text = bilgi metni, Rect = (ann.X, ann.Y+75, 230, 65) }` + - `Page.AddBrick(brick)` ile bas?l?r +- Brick `Rect`'leri annotation `X`/`Y` (1/100 inch) ? checkbox overlay ile birebir ayn? koordinat, do?ru sayfa+konum +- `ViewerKey++` ile viewer yenilenir + +**Gerçek PDF modu (`_basePdfBytes` dolu):** Yukar?daki ile ayn?; rapor `XRPdfContent`'ten olu?ur, brick'ler yine `AfterPrint` ile `PrintingSystem.Pages` üzerine bas?l?r. + +--- + +## ReceiverUI'daki NuGet Paketleri + +| Paket | Versiyon | Amaç | +|---|---|---| +| `DevExpress.Blazor.Reporting.Viewer` | 25.2.3 | DxReportViewer bile?eni | +| `DevExpress.Blazor.PdfViewer` | 25.2.3 | PDF görüntüleyici | +| `DevExpress.Drawing.Skia` | 25.2.3 | Çizim backend'i | +| `SkiaSharp.*` | 3.119.1 | WASM native render | + +> Not: iText7, ReceiverUI imzalama ak???nda KULLANILMIYOR. ?mza yerle?tirme tamamen DevExpress XtraReports brick mekanizmas?yla (`PrintingSystem.Pages[i].AddBrick`) yap?l?r. + +--- + +## GÖREV 1: ?mza Konum Hatas? (BUG) — ÇÖZÜLDÜ + +### Kullan?c?n?n ?ste?i +Annotation'lardan okunan sayfa ve X/Y koordinatlar?na göre, t?pk? checkbox overlay'ler gibi, imzalar do?ru sayfa ve konumda görünsün. `_basePdfBytes` dal? korunsun; dev/test'te designer ile olu?turulan `PredefinedReport` `XtraReport` olarak do?rudan kullan?lmaya devam etsin (PDF'e export yok). + +### ÇÖZÜM (Oturum 12) ? +`WireAnnotationSignatures` metodu, `report.AfterPrint` olay?nda `report.PrintingSystem.Pages[ann.Page-1].AddBrick(...)` ça??rarak imza görselini (`ImageBrick`) ve bilgi metnini (`TextBrick`) do?rudan hedef sayfaya, annotation `X`/`Y` (1/100 inch) konumunda basar. + +**Neden çal???r:** +- `AfterPrint`, belge tamamen olu?tuktan sonra tetiklenir; `PrintingSystem.Pages` art?k gerçek/nihai sayfalar? içerir. +- Sayfa indeksleme (`Pages[ann.Page-1]`) band veya veri-sat?r? tekrar?ndan **ba??ms?zd?r** ? `LargeDatasetReport`'un veri-ba?l? `detailBand1` sorununu tamamen atlar. +- Brick `Rect` koordinatlar? raporun yerel 1/100 inch sistemindedir ? checkbox overlay ile birebir ayn?, do?ru konum. +- Yeni sayfa eklenmedi?i için sayfa say?s? katlanmaz (35 sayfa ? 35 sayfa). + +**Derleme s?ras?nda ö?renilen API gerçekleri:** +- `PrintOnPage` olay? `e.Page` VERMEZ (yaln?zca `PageIndex`/`PageCount`) ? brick eklenemez. Do?ru olay `AfterPrint` + `PrintingSystem.Pages`. +- `Page` tipinde `InsertBrick` YOK; do?ru metot `Page.AddBrick(brick)` (brick'in `Rect`'i konumu belirler). +- `ImageBrick.BorderStyle` tipi `BrickBorderStyle`'dir (`BorderDashStyle` de?il). Border için `Sides` + `BorderColor` kullan?ld?. + +### Denenen Eski Çözümler (ba?ar?s?z — referans) + +| Deneme | Yakla??m | Sonuç | Ba?ar?s?zl?k Sebebi | +|---|---|---|---| +| 1 | `BottomMarginBand` + `XRPictureBox`/`XRLabel` | Her sayfan?n alt?na ç?kt? | Band her sayfada tekrarlan?r, sayfa filtresi yok | +| 2 | `BeforePrint` + `e.Graph?.PrintingSystem` | Derleme hatas? | `CancelEventArgs`'ta `Graph` yok | +| 3–6 | `DetailBand` + `BeforePrint` counter | Yanl?? sayfa/konum | ?ablonun `detailBand1`'i veri sat?r? ba??na tetiklenir, sayfa ba??na de?il | +| 7 | iText7 export/reload döngüsü | 35 ? 70 sayfa | Margin uyu?mazl???, `GenerateOwnPages` sayfalar? böldü | +| 8 | Fake modda `BottomMarginBand` fallback | Her sayfan?n alt?nda | Koordinat yanl?? | + +--- + +## YAPILMAMASI GEREKENLER + +| Hata | Neden Yanl?? | +|---|---| +| `BottomMarginBand`/`DetailBand` ile sayfa-spesifik imza | Band veri-sat?r?/sayfa ba??na tekrarlan?r, koordinat kayar | +| `BeforePrint` counter ile sayfa filtresi | Veri-ba?l? raporda sat?r ba??na tetiklenir, güvenilmez | +| `PrintOnPage` ile brick ekleme | `e.Page` yok; brick eklenemez | +| `Page.InsertBrick(...)` | Yok; do?ru metot `Page.AddBrick(...)` | +| iText7 export+reload döngüsü | Margin uyu?mazl???ndan sayfa say?s? katlan?r | +| ?ablonu PDF'e export edip `XRPdfContent`'e yükleme | ?stenmiyor; designer raporu do?rudan kullan?lmal? | +| Stamplama için API endpoint ekleme | Gereksiz; brick'ler client'ta bas?l?r | + +--- + +## BEKLEYEN D??ER GÖREVLER (Sonraki Chat'te Yap?lacak) + +### 2. ?mza Arka Plan? Özelli?i +?mza görselinin ve bilgilerinin kaplad??? alan kadar, yar? saydam hafif gri opak dikdörtgen arka plan ekle. Böylece imza ve bilgiler arka plandaki metinlerden etkilenmez ve okunur kal?r. (Art?k brick tabanl?: `ImageBrick`/`TextBrick`'in arkas?na bir arka plan `Brick` dikdörtgeni eklenebilir.) + +### 3. Checkbox Renk ve Stil ?yile?tirmesi +Mevcut checkbox'lar?n rengi ve kenarl?klar? çok dikkat çekici. Koyu füme tonlar?nda, desenli, sade ve profesyonel görünümlü bir stil olsun. (`receiver-signature.js` ve ilgili CSS.) + +### 4. Sayfa Aç?l???nda Otomatik ?mza Popup'? +Sayfa aç?l?r aç?lmaz imza popup'? ç?ks?n. "Kay?tl? hiç bir imzan?z yok, tan?mlay?n?z" mesaj? gösterilsin. Kullan?c? imzas?n? tan?mlamadan ilerleyemesin. Mevcut "Unterschrift erstellen" butonu "?mzay? de?i?tir" olarak güncellensin. + +### 5. Otomatik ?mza Uygulama +Kullan?c? tüm checkbox'lar? onaylad??? anda imzalar otomatik olarak uygulanmaya ba?las?n (butona t?klamaya gerek kalmas?n). Sayfan?n üstünde imza say?s? ve imzalanmas? gereken sayfalar hakk?nda bilgi gösterilsin. + +### 6. Checkbox - DevExpress Toolbar Pozisyon Uyumsuzlu?u (BUG) +- Checkbox'lar browser'?n boyut/konumuna neredeyse anl?k tepki veriyor +- DevExpress toolbar de?i?ikliklerine geç tepki gösteriyor +- Zoom de?i?ince bazen 2-3 PDF yan yana gelebiliyor; checkbox o konumda do?ru görünüyor ama iki PDF'in yan yana gelmesi kald?r?lmal? +- `DocumentViewer.razor`'daki `DxDocumentViewer` + `DxDocumentViewerTabPanelSettings` bile?enleri daha uygun olabilir mi? De?erlendirmesi yap?lacak. + +--- + +## De?i?iklik Günlü?ü + +| Oturum | De?i?iklik | +|---|---| +| 1–3 | Temel altyap?: servisler, YARP proxy, JS overlay, imza pad | +| 4 | `AddSignatureAtAnnotation` + BottomMarginBand ? ? her sayfada tekrar | +| 5 | `BeforePrint` + `e.Graph?.PrintingSystem` ? ? derleme hatas? | +| 6 | BeforePrint counter ? ? do?ru yakla??m, yanl?? band | +| 7 | DetailBand'e geçi? ? ? do?ru band, koordinat hâlâ yanl?? | +| 8 | `(page-1)*1169+Y` ? ? sayfa ?i?mesi (35?140) | +| 9 | `BoundsF.Y = ann.Y` + counter ? (?ablon raporda çal??m?yor) | +| 10 | iText7 `StampSignaturesOnPdf` gerçek modda ?, fake modda export+reload ? (35?70), BottomMarginBand fallback ? | +| 11 | COPILOT_CONTEXT_TR.md ve COPILOT_CONTEXT_EN.md ayr? dosyalar olarak yeniden olu?turuldu | +| **12** | **GÖREV 1 ÇÖZÜLDÜ ?** — `WireAnnotationSignatures`: `report.AfterPrint` + `PrintingSystem.Pages[ann.Page-1].AddBrick(ImageBrick/TextBrick)`. Sayfa hedefleme band'dan ba??ms?z, sayfa say?s? katlanm?yor. iText7 ReceiverUI ak???ndan ç?kar?ld?. `AddSignatureAtAnnotation`/`RemoveExistingSignatureById` kald?r?ld?. Derleme ba?ar?l?. | diff --git a/EnvelopeGenerator.ReceiverUI/Pages/ReportViewer.razor b/EnvelopeGenerator.ReceiverUI/Pages/ReportViewer.razor index df82bc34..a0fdaf5c 100644 --- a/EnvelopeGenerator.ReceiverUI/Pages/ReportViewer.razor +++ b/EnvelopeGenerator.ReceiverUI/Pages/ReportViewer.razor @@ -26,6 +26,7 @@ @inject InMemoryReportStorageWebExtension ReportStorage @inject EnvelopeGenerator.ReceiverUI.Services.DocumentService DocumentService @inject EnvelopeGenerator.ReceiverUI.Services.AuthService AuthService +@inject EnvelopeGenerator.ReceiverUI.Services.EnvelopeReceiverService EnvelopeReceiverService @@ -33,75 +34,151 @@
-
-
-
Unterschrift
-

- @if(SignatureApplied) { - Die Unterschrift wurde dem Bericht hinzugefuegt. Sie koennen die Unterschrift erneuern oder das signierte PDF exportieren. - } else { - Bitte fuegen Sie vor dem PDF-Export Ihre Unterschrift hinzu. - } -

- @if(!string.IsNullOrWhiteSpace(SignatureValidationMessage)) { -
@SignatureValidationMessage
- } -
- - @if (_annotations.Count > 0 && !SignatureApplied) { -
- - @_annotations.Count @(_annotations.Count == 1 ? "Unterschriftsfeld" : "Unterschriftsfelder") -  ·  - Seite@(AnnotationPages.Count() == 1 ? "" : "n"): @string.Join(", ", AnnotationPages) - - @if (_capturedSignature is not null) { -
-
-
-
- - @_checkedAnnotations.Count / @_annotations.Count @(_annotations.Count == 1 ? "Feld" : "Felder") markiert - -
- @if (_checkedAnnotations.Count == _annotations.Count) { - ✓ Alle Felder markiert – Unterschriften werden angewendet… + +@* ?? Envelope info header ???????????????????????????????????????????????? *@ +@if (_envelopeReceiver is not null) { +
+
+
+ + + +
+
@(_envelopeReceiver.Envelope?.Title ?? "Dokument")
+ @if (!string.IsNullOrWhiteSpace(_envelopeReceiver.Envelope?.User?.FullName) || !string.IsNullOrWhiteSpace(_envelopeReceiver.Envelope?.User?.Email)) { +
+ Von + @if (!string.IsNullOrWhiteSpace(_envelopeReceiver.Envelope?.User?.FullName)) { + @_envelopeReceiver.Envelope!.User!.FullName } - } else { - - Bitte zuerst eine Unterschrift erstellen, dann die Felder im Dokument markieren. - - } -
+ @if (!string.IsNullOrWhiteSpace(_envelopeReceiver.Envelope?.User?.Email)) { + <@_envelopeReceiver.Envelope!.User!.Email> + } +  ·  @_envelopeReceiver.Envelope?.AddedWhen.ToString("dd.MM.yyyy") +
+ } +
+
+
+ @if (!string.IsNullOrWhiteSpace(_envelopeReceiver.Name)) { + + + + + @_envelopeReceiver.Name + } - + @if (!string.IsNullOrWhiteSpace(_envelopeReceiver.CompanyName)) { + + + + + @_envelopeReceiver.CompanyName + + } + @if (!string.IsNullOrWhiteSpace(_envelopeReceiver.JobTitle)) { + @_envelopeReceiver.JobTitle + } + @{ + var docElements = _envelopeReceiver.Envelope?.Documents?.FirstOrDefault()?.Elements; + int sigCount = docElements?.Count() ?? _annotations.Count; + } + @if (sigCount > 0) { + + + + + @sigCount @(sigCount == 1 ? "Unterschriftsfeld" : "Unterschriftsfelder") + + } +
+
+ @if (!string.IsNullOrWhiteSpace(_envelopeReceiver.Envelope?.Message)) { +
@_envelopeReceiver.Envelope!.Message
+ } + @if (!string.IsNullOrWhiteSpace(_envelopeReceiver.PrivateMessage)) { +
+ + + + @_envelopeReceiver.PrivateMessage +
+ } +
+} + +@* ?? Signature action bar ???????????????????????????????????????????????? *@ +
+
+ + + @if (_annotations.Count > 0 && !SignatureApplied) { +
+
+
+
+ + @_checkedAnnotations.Count / @_annotations.Count + Seite@(AnnotationPages.Count() == 1 ? "" : "n") @string.Join(",", AnnotationPages) + + @if (_capturedSignature is null) { + Zuerst Unterschrift erstellen + } else if (_checkedAnnotations.Count == _annotations.Count) { + ✓ Wird angewendet… + } +
+ } + + @if (!string.IsNullOrWhiteSpace(SignatureValidationMessage)) { + @SignatureValidationMessage + } + @if (SignatureApplied) { + + + + + Unterschrift angewendet + + } + +
+ @if (!string.IsNullOrWhiteSpace(EnvelopeKey)) { - }
+
IReadOnlyList _annotations = []; IEnumerable AnnotationPages => _annotations.Select(a => a.Page).Distinct().OrderBy(p => p); + EnvelopeReceiverDto? _envelopeReceiver; record SignatureCapture(string DataUrl, string FullName, string Position, string Place); SignatureCapture? _capturedSignature; byte[]? _basePdfBytes; @@ -259,6 +337,7 @@ Shown="OnPopupShownAsync"> } _annotations = await AnnotationService.GetAnnotationsAsync(EnvelopeKey ?? "fake"); + _envelopeReceiver = await EnvelopeReceiverService.GetAsync(EnvelopeKey ?? "fake"); if (!AppOptions.Value.ForceToUseFakeDocument && !string.IsNullOrWhiteSpace(EnvelopeKey)) { var (pdfBytes, _) = await DocumentService.GetDocumentAsync(EnvelopeKey); diff --git a/EnvelopeGenerator.ReceiverUI/Shared/MainLayout.razor b/EnvelopeGenerator.ReceiverUI/Shared/MainLayout.razor index c14b9ff2..16fd71b5 100644 --- a/EnvelopeGenerator.ReceiverUI/Shared/MainLayout.razor +++ b/EnvelopeGenerator.ReceiverUI/Shared/MainLayout.razor @@ -3,10 +3,6 @@
-
- About -
-
@Body
diff --git a/EnvelopeGenerator.ReceiverUI/wwwroot/css/app.css b/EnvelopeGenerator.ReceiverUI/wwwroot/css/app.css index 571c8844..eca33d5b 100644 --- a/EnvelopeGenerator.ReceiverUI/wwwroot/css/app.css +++ b/EnvelopeGenerator.ReceiverUI/wwwroot/css/app.css @@ -125,4 +125,121 @@ article { text-overflow: ellipsis; pointer-events: none; letter-spacing: 0.01em; +} + +/* ── Envelope info header ────────────────────────────────────────────────── */ +.receiver-info-header { + border-bottom: 1px solid rgba(0,0,0,.08); +} + +.receiver-info-header__gradient { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 8px; + padding: 10px 16px 8px; + background: linear-gradient(135deg, #2c3e50 0%, #3498db 100%); + color: #fff; +} + +.receiver-info-header__left { + display: flex; + align-items: center; + gap: 10px; + min-width: 0; +} + +.receiver-info-header__icon { + flex-shrink: 0; + opacity: .85; +} + +.receiver-info-header__title { + font-size: 0.92rem; + font-weight: 600; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 340px; +} + +.receiver-info-header__sender { + font-size: 0.72rem; + opacity: .8; + margin-top: 1px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 400px; +} + +.receiver-info-header__badges { + display: flex; + flex-wrap: wrap; + gap: 5px; + align-items: center; +} + +.receiver-info-badge { + display: inline-flex; + align-items: center; + background: rgba(255,255,255,.18); + color: #fff; + border-radius: 20px; + padding: 2px 9px; + font-size: 0.70rem; + font-weight: 500; + white-space: nowrap; +} + +.receiver-info-badge--muted { + background: rgba(255,255,255,.10); + opacity: .8; +} + +.receiver-info-badge--accent { + background: rgba(39,174,96,.35); + border: 1px solid rgba(39,174,96,.5); +} + +.receiver-info-message { + padding: 7px 16px; + font-size: 0.78rem; + color: #444; + border-bottom: 1px solid rgba(0,0,0,.05); + white-space: pre-wrap; + line-height: 1.45; +} + +.receiver-info-private-message { + display: flex; + align-items: flex-start; + gap: 4px; + padding: 5px 16px 6px; + font-size: 0.75rem; + color: #5a5a72; + background: #f8f7ff; + border-top: 1px solid rgba(90,80,180,.12); +} + +/* ── Signature action bar ────────────────────────────────────────────────── */ +.receiver-action-bar { + border-bottom: 1px solid rgba(0,0,0,.08); + background: #fff; +} + +.receiver-action-bar__inner { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 10px; + padding: 7px 14px; +} + +.receiver-action-bar__progress { + display: flex; + align-items: center; + gap: 7px; + flex-wrap: wrap; } \ No newline at end of file