From 9289bb2efdd3594af1c3a15c93d6e22c3a1ff1cf Mon Sep 17 00:00:00 2001 From: senseiGai Date: Sun, 22 Mar 2026 01:58:13 +0500 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=B2=D0=B5=D0=B4=D0=BE=D0=BC=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=20CarPlay,=20=D0=BF=D0=B0=D0=BD=D0=B5?= =?UTF-8?q?=D0=BB=D1=8C=20=D0=B2=D0=BB=D0=BE=D0=B6=D0=B5=D0=BD=D0=B8=D0=B9?= =?UTF-8?q?=20=D1=81=20Lottie,=20=D1=84=D0=B8=D0=BA=D1=81=20reply=20previe?= =?UTF-8?q?w,=20=D0=BF=D0=BB=D0=B0=D0=B2=D0=BD=D0=B0=D1=8F=20=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=BC=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BA=D0=BB=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=B0=D1=82=D1=83=D1=80=D1=8B,=20=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D0=B1=D0=B8=D0=BB=D1=8C=D0=BD=D0=BE=D1=81=D1=82=D1=8C=20WebSoc?= =?UTF-8?q?ket?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Network/Protocol/ProtocolManager.swift | 13 +- .../Network/Protocol/WebSocketClient.swift | 19 +- Rosetta/Core/Services/SessionManager.swift | 50 +++-- Rosetta/Core/Utils/PerformanceLogger.swift | 2 +- Rosetta/Core/Utils/ReleaseNotes.swift | 28 +-- .../Components/KeyboardSyncedContainer.swift | 124 ++++++++++++ .../Components/KeyboardTracker.swift | 141 ++++++++------ Rosetta/Features/Calls/CallsView.swift | 1 + .../ChatDetail/AttachmentPanelView.swift | 179 ++++++++++++++---- .../Chats/ChatDetail/ChatDetailView.swift | 47 +++-- .../ChatDetail/FullScreenImageViewer.swift | 29 +-- .../Chats/ChatDetail/ImageGalleryViewer.swift | 6 +- .../Chats/ChatDetail/MessageAvatarView.swift | 11 ++ .../Chats/ChatDetail/ZoomableImagePage.swift | 34 ++-- .../Chats/ChatList/ChatListView.swift | 15 ++ Rosetta/Features/MainTabView.swift | 8 + Rosetta/Resources/Lottie/avatar.json | 1 + Rosetta/Resources/Lottie/file_folder.json | 1 + Rosetta/RosettaApp.swift | 43 ++++- .../NotificationService.swift | 65 ++++++- 20 files changed, 645 insertions(+), 172 deletions(-) create mode 100644 Rosetta/DesignSystem/Components/KeyboardSyncedContainer.swift create mode 100644 Rosetta/Resources/Lottie/avatar.json create mode 100644 Rosetta/Resources/Lottie/file_folder.json diff --git a/Rosetta/Core/Network/Protocol/ProtocolManager.swift b/Rosetta/Core/Network/Protocol/ProtocolManager.swift index 62b4d84..2f63b5c 100644 --- a/Rosetta/Core/Network/Protocol/ProtocolManager.swift +++ b/Rosetta/Core/Network/Protocol/ProtocolManager.swift @@ -323,7 +323,9 @@ final class ProtocolManager: @unchecked Sendable { sendPacketDirect(handshake) - // Timeout + // Timeout — force reconnect instead of permanent disconnect. + // `client.disconnect()` sets `isManuallyClosed = true` which kills all + // future reconnection attempts. Use `forceReconnect()` to retry. handshakeTimeoutTask?.cancel() handshakeTimeoutTask = Task { [weak self] in do { @@ -333,8 +335,13 @@ final class ProtocolManager: @unchecked Sendable { } guard let self, !Task.isCancelled else { return } if !self.handshakeComplete { - Self.logger.error("Handshake timeout") - self.client.disconnect() + Self.logger.error("Handshake timeout — forcing reconnect") + self.handshakeComplete = false + self.heartbeatTask?.cancel() + Task { @MainActor in + self.connectionState = .connecting + } + self.client.forceReconnect() } } } diff --git a/Rosetta/Core/Network/Protocol/WebSocketClient.swift b/Rosetta/Core/Network/Protocol/WebSocketClient.swift index a98a539..68eb70b 100644 --- a/Rosetta/Core/Network/Protocol/WebSocketClient.swift +++ b/Rosetta/Core/Network/Protocol/WebSocketClient.swift @@ -126,6 +126,8 @@ final class WebSocketClient: NSObject, @unchecked Sendable, URLSessionWebSocketD } task.send(.data(data)) { [weak self] error in guard let self else { return } + // Ignore errors from old sockets after forceReconnect. + guard task === self.webSocketTask else { return } if let error { Self.logger.error("Send error: \(error.localizedDescription)") onFailure?(error) @@ -138,9 +140,12 @@ final class WebSocketClient: NSObject, @unchecked Sendable, URLSessionWebSocketD func sendText(_ text: String) { guard let task = webSocketTask else { return } task.send(.string(text)) { [weak self] error in + guard let self else { return } + // Ignore errors from old sockets after forceReconnect. + guard task === self.webSocketTask else { return } if let error { Self.logger.error("Send text error: \(error.localizedDescription)") - self?.handleDisconnect(error: error) + self.handleDisconnect(error: error) } } } @@ -162,6 +167,11 @@ final class WebSocketClient: NSObject, @unchecked Sendable, URLSessionWebSocketD nonisolated func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) { Self.logger.info("WebSocket didOpen") + // Ignore callbacks from old (cancelled) sockets after forceReconnect. + guard webSocketTask === self.webSocketTask else { + Self.logger.info("didOpen ignored: stale socket (not current task)") + return + } guard !isManuallyClosed else { return } // Android parity: reset isConnecting on successful open (Protocol.kt onOpen). isConnecting = false @@ -177,6 +187,11 @@ final class WebSocketClient: NSObject, @unchecked Sendable, URLSessionWebSocketD nonisolated func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) { Self.logger.info("WebSocket didClose: \(closeCode.rawValue)") + // Ignore callbacks from old (cancelled) sockets after forceReconnect. + guard webSocketTask === self.webSocketTask else { + Self.logger.info("didClose ignored: stale socket (not current task)") + return + } isConnecting = false isConnected = false handleDisconnect(error: nil) @@ -189,6 +204,8 @@ final class WebSocketClient: NSObject, @unchecked Sendable, URLSessionWebSocketD task.receive { [weak self] result in guard let self, !isManuallyClosed else { return } + // Ignore callbacks from old (cancelled) sockets after forceReconnect. + guard task === self.webSocketTask else { return } switch result { case .success(let message): diff --git a/Rosetta/Core/Services/SessionManager.swift b/Rosetta/Core/Services/SessionManager.swift index 5d78619..920e42c 100644 --- a/Rosetta/Core/Services/SessionManager.swift +++ b/Rosetta/Core/Services/SessionManager.swift @@ -285,16 +285,15 @@ final class SessionManager { password: attachmentPassword ) - // Upload encrypted blob to transport server (desktop: uploadFile) - let tag = try await TransportManager.shared.uploadFile( - id: attachmentId, - content: Data(encryptedBlob.utf8) - ) - - // Desktop parity: preview = "tag::blurhash" (same as IMAGE attachments) + // Cache avatar locally BEFORE upload so outgoing avatar shows instantly + // (same pattern as sendMessageWithAttachments — AttachmentCache.saveImage before upload). let avatarImage = AvatarRepository.shared.loadAvatar(publicKey: currentPublicKey) + if let avatarImage { + AttachmentCache.shared.saveImage(avatarImage, forAttachmentId: attachmentId) + } + + // BlurHash for preview (computed before upload so optimistic UI has it) let blurhash = avatarImage?.blurHash(numberOfComponents: (4, 3)) ?? "" - let preview = "\(tag)::\(blurhash)" // Build aesChachaKey with Latin-1 payload (desktop sync parity) let aesChachaPayload = Data(latin1ForSync.utf8) @@ -303,7 +302,7 @@ final class SessionManager { password: privKey ) - // Build packet with avatar attachment + // Build packet with avatar attachment — preview will be updated with tag after upload var packet = PacketMessage() packet.fromPublicKey = currentPublicKey packet.toPublicKey = toPublicKey @@ -316,8 +315,8 @@ final class SessionManager { packet.attachments = [ MessageAttachment( id: attachmentId, - preview: preview, - blob: "", // Desktop parity: blob cleared after upload + preview: blurhash, // Will be updated with "tag::blurhash" after upload + blob: "", type: .avatar ), ] @@ -333,21 +332,40 @@ final class SessionManager { myPublicKey: currentPublicKey ) - // Optimistic UI - let isConnected = ProtocolManager.shared.connectionState == .authenticated - let offlineAsSend = !isConnected + // Optimistic UI — show message IMMEDIATELY (before upload) MessageRepository.shared.upsertFromMessagePacket( packet, myPublicKey: currentPublicKey, decryptedText: " ", attachmentPassword: "rawkey:" + encrypted.plainKeyAndNonce.hexString, - fromSync: offlineAsSend + fromSync: false ) DialogRepository.shared.updateDialogFromMessages(opponentKey: packet.toPublicKey) - if offlineAsSend { + // Upload encrypted blob to transport server in background (desktop: uploadFile) + let tag: String + do { + tag = try await TransportManager.shared.uploadFile( + id: attachmentId, + content: Data(encryptedBlob.utf8) + ) + } catch { + // Upload failed — mark as error MessageRepository.shared.updateDeliveryStatus(messageId: messageId, status: .error) DialogRepository.shared.updateDeliveryStatus(messageId: messageId, opponentKey: toPublicKey, status: .error) + Self.logger.error("📤 Avatar upload failed: \(error.localizedDescription)") + throw error } + // Update preview with CDN tag (tag::blurhash) + let preview = "\(tag)::\(blurhash)" + packet.attachments = [ + MessageAttachment( + id: attachmentId, + preview: preview, + blob: "", // Desktop parity: blob cleared after upload + type: .avatar + ), + ] + // Saved Messages — mark delivered locally but STILL send to server // for cross-device avatar sync. Other devices receive via sync and // update their local avatar cache. diff --git a/Rosetta/Core/Utils/PerformanceLogger.swift b/Rosetta/Core/Utils/PerformanceLogger.swift index 9c56ed6..40c5314 100644 --- a/Rosetta/Core/Utils/PerformanceLogger.swift +++ b/Rosetta/Core/Utils/PerformanceLogger.swift @@ -22,7 +22,7 @@ final class PerformanceLogger { /// Enable for performance testing. Disabled by default — zero overhead in production. #if DEBUG - var isEnabled = false // Set to `true` to enable perf logging + var isEnabled = true // Set to `true` to enable perf logging #else var isEnabled = false #endif diff --git a/Rosetta/Core/Utils/ReleaseNotes.swift b/Rosetta/Core/Utils/ReleaseNotes.swift index 4cf650e..065d370 100644 --- a/Rosetta/Core/Utils/ReleaseNotes.swift +++ b/Rosetta/Core/Utils/ReleaseNotes.swift @@ -11,26 +11,26 @@ enum ReleaseNotes { Entry( version: appVersion, body: """ - **Доставка сообщений** - Сообщения больше не теряются при потере сети. Автоматический повтор отправки при восстановлении соединения. Отправленные фото отображаются мгновенно. - **Уведомления** - Вибрация и бейдж работают когда приложение закрыто. Счётчик непрочитанных обновляется в фоне. + Поддержка CarPlay и фильтров Focus. Переход в чат по нажатию на уведомление из любого состояния приложения. Автопереключение на вкладку Chats. - **Фото и файлы** - Фото скачиваются только по тапу. Пересланные фото загружаются из кэша. Файлы открываются по тапу. Свайп между фото в полноэкранном просмотре. Плавное закрытие свайпом вниз. + **Сеть** + Автоматический реконнект при таймауте handshake. Защита от дублирования при переподключении. + + **Клавиатура** + Плавная анимация подъёма и опускания инпута. Устранено дёрганье при закрытии клавиатуры. + + **Вложения** + Новый экран отправки аватарки с анимацией. Обновлённый экран выбора файлов. Анимированный индикатор вкладок. + + **Просмотр фото** + Жесты зума и навигации работают по всему экрану. Исправлен double-tap для сброса зума. **Аватарки** - Превью аватарки отображается как размытое изображение до скачивания. Плавная анимация при загрузке. Скачивание по тапу. - - **Шифрование** - Улучшена совместимость шифрования фото и файлов между устройствами. Пересланные фото корректно расшифровываются получателем. - - **Производительность** - Оптимизация FPS клавиатуры и скролла в длинных переписках. Снижен нагрев устройства. + Мгновенное отображение до загрузки на сервер. Корректное отображение отправленных аватарок. **Исправления** - Исправлена отправка фото и аватаров на Desktop. Исправлено шифрование пересланных сообщений. Исправлен бейдж непрочитанных в tab bar. Исправлен счётчик после синхронизации. Исправлены кнопки на iOS 26+. Группировка баблов для фото-сообщений. Saved Messages: иконка закладки. + Отображение типа вложения (Photo, Avatar, File) при ответе на сообщение. Стабильность WebSocket-соединения. """ ) ] diff --git a/Rosetta/DesignSystem/Components/KeyboardSyncedContainer.swift b/Rosetta/DesignSystem/Components/KeyboardSyncedContainer.swift new file mode 100644 index 0000000..2dc8492 --- /dev/null +++ b/Rosetta/DesignSystem/Components/KeyboardSyncedContainer.swift @@ -0,0 +1,124 @@ +import SwiftUI +import UIKit + +/// Wraps SwiftUI content in a UIKit container whose vertical position is animated +/// using the keyboard's exact Core Animation curve. This achieves Telegram-level +/// keyboard sync because both the keyboard and this container are animated by the +/// render server in the same Core Animation transaction — zero relative movement. +/// +/// On iOS 26+, SwiftUI handles keyboard natively — this wrapper is a no-op passthrough. +struct KeyboardSyncedContainer: View { + let content: Content + + init(@ViewBuilder content: () -> Content) { + self.content = content() + } + + var body: some View { + if #available(iOS 26, *) { + content + } else { + _KeyboardSyncedRepresentable(content: content) + .ignoresSafeArea(.keyboard) + } + } +} + +// MARK: - UIViewControllerRepresentable bridge + +private struct _KeyboardSyncedRepresentable: UIViewControllerRepresentable { + let content: Content + + func makeUIViewController(context: Context) -> _KeyboardSyncedVC { + _KeyboardSyncedVC(rootView: content) + } + + func updateUIViewController(_ vc: _KeyboardSyncedVC, context: Context) { + vc.hostingController.rootView = content + } + + func sizeThatFits( + _ proposal: ProposedViewSize, + uiViewController vc: _KeyboardSyncedVC, + context: Context + ) -> CGSize? { + let width = proposal.width ?? UIScreen.main.bounds.width + let fittingSize = vc.hostingController.sizeThatFits( + in: CGSize(width: width, height: UIView.layoutFittingCompressedSize.height) + ) + return CGSize(width: width, height: fittingSize.height) + } +} + +// MARK: - UIKit view controller that animates with the keyboard + +final class _KeyboardSyncedVC: UIViewController { + + let hostingController: UIHostingController + private var bottomInset: CGFloat = 34 + + init(rootView: Content) { + hostingController = UIHostingController(rootView: rootView) + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { fatalError() } + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .clear + + addChild(hostingController) + hostingController.view.backgroundColor = .clear + view.addSubview(hostingController.view) + hostingController.view.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), + hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), + hostingController.view.topAnchor.constraint(equalTo: view.topAnchor), + hostingController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), + ]) + hostingController.didMove(toParent: self) + + // Read safe area bottom inset + if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene, + let window = scene.keyWindow ?? scene.windows.first { + let bottom = window.safeAreaInsets.bottom + bottomInset = bottom < 50 ? bottom : 34 + } + + NotificationCenter.default.addObserver( + self, + selector: #selector(keyboardWillChangeFrame), + name: UIResponder.keyboardWillChangeFrameNotification, + object: nil + ) + } + + @objc private func keyboardWillChangeFrame(_ notification: Notification) { + guard let info = notification.userInfo, + let endFrame = info[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect, + let duration = info[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval, + let curveRaw = info[UIResponder.keyboardAnimationCurveUserInfoKey] as? Int + else { return } + + let screenHeight = UIScreen.main.bounds.height + let keyboardTop = endFrame.origin.y + let isVisible = keyboardTop < screenHeight + let endHeight = isVisible ? (screenHeight - keyboardTop) : 0 + let padding = isVisible ? max(0, endHeight - bottomInset) : 0 + + // Animate with the KEYBOARD'S EXACT curve in the SAME Core Animation transaction. + // The render server interpolates both the keyboard position and our transform + // together for each frame — pixel-perfect sync, zero gap variation. + let options = UIView.AnimationOptions(rawValue: UInt(curveRaw) << 16) + UIView.animate(withDuration: duration, delay: 0, options: [options, .beginFromCurrentState]) { + self.view.transform = CGAffineTransform(translationX: 0, y: -padding) + } + } + + deinit { + NotificationCenter.default.removeObserver(self) + } +} diff --git a/Rosetta/DesignSystem/Components/KeyboardTracker.swift b/Rosetta/DesignSystem/Components/KeyboardTracker.swift index 4b268bd..a1a0c2e 100644 --- a/Rosetta/DesignSystem/Components/KeyboardTracker.swift +++ b/Rosetta/DesignSystem/Components/KeyboardTracker.swift @@ -40,14 +40,11 @@ final class KeyboardTracker: ObservableObject { private var animTickCount = 0 private var animationNumber = 0 private var lastTickTime: CFTimeInterval = 0 + /// Monotonic guard — sync view can give non-monotonic eased values + /// in the first 2 ticks while Core Animation commits the animation. + private var lastEased: CGFloat = 0 - // Presentation-layer sync: a hidden UIView animated with the keyboard's - // exact curve. Reading its presentation layer on each CADisplayLink tick - // gives us the real easing value — no guessing control points. - private var syncView: UIView? - private var usingSyncAnimation = false - - // Cubic bezier fallback (used when syncView is unavailable) + // Cubic bezier control points — deterministic keyboard curve approximation. private var bezierP1x: CGFloat = 0.25 private var bezierP1y: CGFloat = 0.1 private var bezierP2x: CGFloat = 0.25 @@ -86,6 +83,12 @@ final class KeyboardTracker: ObservableObject { if #available(iOS 26, *) { return } PerformanceLogger.shared.track("keyboard.kvo") guard !isAnimating else { return } + #if DEBUG + let rawPad = max(0, keyboardHeight - bottomInset) + if abs(rawPad - keyboardPadding) > 4 { + print("⌨️ 👆 KVO | height=\(Int(keyboardHeight)) → pad=\(Int(rawPad)) | current=\(Int(keyboardPadding))") + } + #endif if keyboardHeight <= 0 { // Flush any pending KVO value and stop coalescing @@ -183,9 +186,19 @@ final class KeyboardTracker: ObservableObject { lastNotificationPadding = targetPadding PerformanceLogger.shared.track("keyboard.notification") - // CADisplayLink at 30fps — smooth interpolation synced with keyboard curve. - // BubbleContextMenuOverlay.updateUIView is SKIPPED during animation - // (isAnimatingKeyboard flag) — eliminates 40+ UIKit bridge operations per tick. + #if DEBUG + let direction = targetPadding > keyboardPadding ? "⬆️ SHOW" : "⬇️ HIDE" + print("⌨️ \(direction) | current=\(Int(keyboardPadding)) → target=\(Int(targetPadding)) | delta=\(Int(delta)) | duration=\(String(format: "%.3f", duration))s | curve=\(curveRaw)") + #endif + // Filter spurious notifications (delta=0, duration=0) — keyboard frame + // didn't actually change. Happens on some iOS versions during text input. + guard abs(delta) > 1 || targetPadding != keyboardPadding else { return } + + // Animate with sync view (keyboard's exact CA curve) — no pre-apply. + // Pre-apply caused a visible "jump" because input moved 16pt instantly + // before the keyboard had started moving. Without pre-apply, there may + // be ~1 frame of slight overlap (keyboard ahead of input) but it's + // imperceptible at 60Hz (16ms) and provides a much smoother feel. if abs(delta) > 1, targetPadding != keyboardPadding { isAnimatingKeyboard = true startPaddingAnimation(to: targetPadding, duration: duration, curveRaw: curveRaw) @@ -238,31 +251,32 @@ final class KeyboardTracker: ObservableObject { } } - // MARK: - CADisplayLink animation (legacy, kept for reference) + // MARK: - CADisplayLink animation (sync view + bezier fallback) + + // Sync view: hidden UIView animated with keyboard's exact curve. + // Reading its presentation layer gives the real easing value — no guessing. + private var syncView: UIView? private func startPaddingAnimation(to target: CGFloat, duration: CFTimeInterval, curveRaw: Int) { animationNumber += 1 animStartPadding = keyboardPadding animTargetPadding = target - animStartTime = 0 + animStartTime = CACurrentMediaTime() animDuration = max(duration, 0.05) animTickCount = 0 + lastEased = 0 - // Primary: sync with the keyboard's exact curve via presentation layer. - // UIView.animate called HERE lands in the same Core Animation transaction - // as the keyboard notification — identical timing function and start time. - usingSyncAnimation = setupSyncAnimation(duration: duration, curveRaw: curveRaw) + // Primary: sync view matches keyboard's exact curve (same CA transaction). + // No lead, no pre-apply — the natural ~1 frame SwiftUI processing delay + // creates the "keyboard pushes input" effect (like Telegram). + let syncOK = setupSyncAnimation(duration: duration, curveRaw: curveRaw) - // Fallback: cubic bezier approximation (CSS "ease"). - if !usingSyncAnimation { + // Fallback: cubic bezier (only if sync view can't be created). + if !syncOK { configureBezier(curveRaw: curveRaw) } // Reuse existing display link to preserve vsync phase alignment. - // Creating a new CADisplayLink on each animation resets the phase, - // causing alternating frame intervals (15/18ms instead of steady 16.6ms). - // No FPS cap — runs at device native rate (120Hz ProMotion, 60Hz standard). - // BubbleContextMenuOverlay.updateUIView is a no-op, so per-tick cost is trivial. if let proxy = displayLinkProxy { proxy.isPaused = false } else { @@ -272,18 +286,13 @@ final class KeyboardTracker: ObservableObject { } } - /// Starts a hidden UIView animation matching the keyboard's exact curve. - /// Returns true if sync animation was set up successfully. + /// Creates a hidden UIView animated with the keyboard's exact curve. private func setupSyncAnimation(duration: CFTimeInterval, curveRaw: Int) -> Bool { guard let window = UIApplication.shared.connectedScenes .compactMap({ $0 as? UIWindowScene }).first?.keyWindow else { return false } - // Remove previous sync view completely — a fresh view - // guarantees clean presentation layer with no animation history. - // Reusing the same view causes Core Animation to coalesce/skip - // repeated 0→1 opacity animations on subsequent calls. syncView?.layer.removeAllAnimations() syncView?.removeFromSuperview() @@ -292,15 +301,8 @@ final class KeyboardTracker: ObservableObject { window.addSubview(view) syncView = view - // UIView.AnimationOptions encodes the curve in bits 16-19. - // For rawValue 7 (private keyboard curve), this passes through to - // Core Animation with Apple's exact timing function. let options = UIView.AnimationOptions(rawValue: UInt(curveRaw) << 16) - UIView.animate( - withDuration: duration, - delay: 0, - options: [options] - ) { + UIView.animate(withDuration: duration, delay: 0, options: [options]) { view.alpha = 1 } @@ -308,33 +310,42 @@ final class KeyboardTracker: ObservableObject { } private func animationTick() { - let tickStart = CACurrentMediaTime() PerformanceLogger.shared.track("keyboard.animTick") animTickCount += 1 let now = CACurrentMediaTime() lastTickTime = now - // Get eased fraction — either from presentation layer (exact) or bezier (fallback). - let eased: CGFloat - var isComplete = false + let elapsed = now - animStartTime + let timeComplete = elapsed >= animDuration - if usingSyncAnimation, let presentation = syncView?.layer.presentation() { - let fraction = CGFloat(presentation.opacity) - eased = fraction - isComplete = fraction >= 0.999 + // Read eased fraction: sync view (exact) or bezier (fallback). + var eased: CGFloat + if let presentation = syncView?.layer.presentation() { + eased = min(max(CGFloat(presentation.opacity), 0), 1) } else { - // Bezier fallback - if animStartTime == 0 { animStartTime = now } - let elapsed = now - animStartTime let t = min(elapsed / animDuration, 1.0) eased = cubicBezierEase(t) - isComplete = t >= 1.0 } - // Guard: presentation layer can return NaN opacity during edge cases - // (window transition, sync view removed). NaN propagating to keyboardPadding - // causes `Color.clear.frame(height: NaN)` → CoreGraphics NaN errors → FPS freeze. + // Enforce monotonic progress — sync view's presentation layer can give + // non-monotonic values in the first 2 ticks while Core Animation commits + // the animation to the render server. Without this guard, padding oscillates + // (e.g. 312 → 306 → 310 → 302) causing a visible "jerk". + if eased < lastEased { + eased = lastEased + } + lastEased = eased + + // SHOW only: lead by ~1 frame to prevent keyboard overlapping input. + // The value we publish NOW is rendered by SwiftUI NEXT frame. + // Without lead, rendered input position is 1 frame behind keyboard → overlap. + // 0.065 ≈ 1 frame at 60Hz (16.6ms / 250ms). Continuous, not a jump. + // HIDE doesn't need lead — natural 1-frame delay creates acceptable gap. + if animTargetPadding > animStartPadding { + eased = min(eased + 0.065, 1.0) + } + guard eased.isFinite else { displayLinkProxy?.isPaused = true lastTickTime = 0 @@ -342,27 +353,32 @@ final class KeyboardTracker: ObservableObject { return } - // Round to nearest 1pt — now that BubbleContextMenuOverlay.updateUIView - // is a no-op and rows don't re-evaluate during keyboard animation, - // per-tick cost is trivial (just spacer + padded view). 1pt = maximum smoothness. let raw = animStartPadding + (animTargetPadding - animStartPadding) * eased let rounded = max(0, round(raw)) - if isComplete || animTickCount > 30 { + if timeComplete || animTickCount > 30 { + let prevPadding = keyboardPadding keyboardPadding = max(0, animTargetPadding) // Pause instead of invalidate — preserves vsync phase for next animation. displayLinkProxy?.isPaused = true lastTickTime = 0 isAnimatingKeyboard = false + #if DEBUG + let elapsedMs = elapsed * 1000 + print("⌨️ ✅ DONE | ticks=\(animTickCount) | final=\(Int(animTargetPadding)) | lastDelta=\(Int(animTargetPadding - prevPadding))pt | elapsed=\(String(format: "%.0f", elapsedMs))ms") + #endif } else if rounded != keyboardPadding { + #if DEBUG + let prevPad = keyboardPadding + #endif keyboardPadding = rounded + #if DEBUG + if animTickCount <= 5 { + let delta = Int(rounded - prevPad) + print("⌨️ TICK #\(animTickCount) | eased=\(String(format: "%.3f", eased)) | pad=\(Int(rounded)) | delta=\(delta)pt | elapsed=\(String(format: "%.1f", elapsed * 1000))ms") + } + #endif } - #if DEBUG - let tickMs = (CACurrentMediaTime() - tickStart) * 1000 - if tickMs > 16 { - PerformanceLogger.shared.track("keyboard.slowTick") - } - #endif } // MARK: - Cubic bezier fallback @@ -380,7 +396,8 @@ final class KeyboardTracker: ObservableObject { bezierP1x = 0; bezierP1y = 0 bezierP2x = 1.0; bezierP2y = 1.0 default: - // CSS "ease" — closest known approximation of curve 7. + // CSS "ease" — approximation of iOS keyboard curve 7. + // Only used as fallback when sync view is unavailable. bezierP1x = 0.25; bezierP1y = 0.1 bezierP2x = 0.25; bezierP2y = 1.0 } diff --git a/Rosetta/Features/Calls/CallsView.swift b/Rosetta/Features/Calls/CallsView.swift index cd44b91..5da2f61 100644 --- a/Rosetta/Features/Calls/CallsView.swift +++ b/Rosetta/Features/Calls/CallsView.swift @@ -113,6 +113,7 @@ struct CallsView: View { Text("Edit") .font(.system(size: 17, weight: .medium)) .foregroundStyle(RosettaColors.Adaptive.text) + .fixedSize(horizontal: true, vertical: false) .frame(height: 44) .padding(.horizontal, 12) } diff --git a/Rosetta/Features/Chats/ChatDetail/AttachmentPanelView.swift b/Rosetta/Features/Chats/ChatDetail/AttachmentPanelView.swift index 2d9f901..f5fc898 100644 --- a/Rosetta/Features/Chats/ChatDetail/AttachmentPanelView.swift +++ b/Rosetta/Features/Chats/ChatDetail/AttachmentPanelView.swift @@ -1,6 +1,7 @@ import SwiftUI import Photos import PhotosUI +import Lottie // MARK: - AttachmentPanelView @@ -33,6 +34,9 @@ struct AttachmentPanelView: View { @State private var capturedImage: UIImage? @State private var captionText: String = "" @State private var previewAsset: IdentifiableAsset? + /// Tab widths/origins for sliding selection indicator (RosettaTabBar parity). + @State private var tabWidths: [AttachmentTab: CGFloat] = [:] + @State private var tabOrigins: [AttachmentTab: CGFloat] = [:] private var hasSelection: Bool { !selectedAssets.isEmpty } @@ -60,8 +64,7 @@ struct AttachmentPanelView: View { case .file: fileTabContent case .avatar: - // Avatar is an action tab — handled in tabButton tap - Spacer() + avatarTabContent } Spacer(minLength: 0) @@ -104,6 +107,8 @@ struct AttachmentPanelView: View { ) .background(TransparentFullScreenBackground()) } + .onPreferenceChange(AttachTabWidthKey.self) { tabWidths.merge($0) { _, new in new } } + .onPreferenceChange(AttachTabOriginKey.self) { tabOrigins.merge($0) { _, new in new } } .presentationDetents([.medium, .large]) .presentationDragIndicator(.hidden) .attachmentCornerRadius(20) @@ -200,19 +205,85 @@ struct AttachmentPanelView: View { } } + // MARK: - Avatar Tab Content (Android parity: AttachAlertAvatarLayout) + + /// Dedicated avatar tab with Lottie animation and "Send Avatar" button. + /// Android: `AttachAlertAvatarLayout.kt` — looping Lottie + title + subtitle + button. + private var avatarTabContent: some View { + VStack(spacing: 16) { + Spacer() + + // Lottie animation (Android: avatar.json, 100×100dp, infinite loop) + LottieView( + animationName: "avatar", + loopMode: .loop, + animationSpeed: 1.0 + ) + .frame(width: 100, height: 100) + + // Title + Text("Send Avatar") + .font(.system(size: 20, weight: .semibold)) + .foregroundStyle(.white) + + // Subtitle + Text("Share your profile avatar\nwith this contact") + .font(.system(size: 14)) + .foregroundStyle(.white.opacity(0.5)) + .multilineTextAlignment(.center) + + // Send button (capsule style matching File tab's "Browse Files" button) + Button { + if hasAvatar { + onSendAvatar() + dismiss() + } else { + dismiss() + onSetAvatar?() + } + } label: { + Text(hasAvatar ? "Send Avatar" : "Set Avatar") + .font(.system(size: 15, weight: .medium)) + .foregroundStyle(.white) + .padding(.horizontal, 24) + .padding(.vertical, 10) + .background(Color(hex: 0x008BFF), in: Capsule()) + } + + Spacer() + + // Reserve space for tab bar so content doesn't get clipped + Color.clear.frame(height: 70) + } + .frame(maxWidth: .infinity) + } + // MARK: - File Tab Content private var fileTabContent: some View { - VStack(spacing: 20) { + VStack(spacing: 16) { Spacer() - Image(systemName: "doc.fill") - .font(.system(size: 48)) - .foregroundStyle(.white.opacity(0.3)) + // Lottie animation (matching avatar tab layout) + LottieView( + animationName: "file_folder", + loopMode: .loop, + animationSpeed: 1.0 + ) + .frame(width: 100, height: 100) + + // Title + Text("Send File") + .font(.system(size: 20, weight: .semibold)) + .foregroundStyle(.white) + + // Subtitle Text("Select a file to send") - .font(.system(size: 16)) + .font(.system(size: 14)) .foregroundStyle(.white.opacity(0.5)) + .multilineTextAlignment(.center) + // Browse button (capsule style matching avatar tab) Button { showFilePicker = true } label: { @@ -225,6 +296,9 @@ struct AttachmentPanelView: View { } Spacer() + + // Reserve space for tab bar so content doesn't get clipped + Color.clear.frame(height: 70) } .frame(maxWidth: .infinity) .task { @@ -315,28 +389,66 @@ struct AttachmentPanelView: View { TelegramGlassRoundedRect(cornerRadius: 21) } - // MARK: - Tab Bar (Figma: glass capsule, 3 tabs) + // MARK: - Tab Bar (RosettaTabBar parity: glass capsule + sliding indicator) - /// Glass capsule tab bar matching RosettaTabBar pattern. + /// Glass capsule tab bar matching RosettaTabBar pattern exactly. /// Tabs: Gallery | File | Avatar. - /// Colors from RosettaTabBar: selected=#008BFF, unselected=white. - /// Background: .regularMaterial (iOS < 26) / .glassEffect (iOS 26+). + /// Selection indicator: sliding glass pill behind selected tab (spring animation). private var tabBar: some View { HStack(spacing: 0) { tabButton(.gallery, icon: "photo.fill", label: "Gallery") + .background(tabWidthReader(.gallery)) tabButton(.file, icon: "doc.fill", label: "File") + .background(tabWidthReader(.file)) tabButton(.avatar, icon: "person.crop.circle.fill", label: "Avatar") + .background(tabWidthReader(.avatar)) } .padding(4) - .background { tabBarBackground } + .coordinateSpace(name: "attachTabBar") + .background(alignment: .leading) { + // Sliding selection indicator (RosettaTabBar parity) + attachmentSelectionIndicator + } + .background { TelegramGlassCapsule() } .clipShape(Capsule()) .contentShape(Capsule()) .tabBarShadow() } - /// Glass background matching RosettaTabBar (lines 136–149). - private var tabBarBackground: some View { - TelegramGlassCapsule() + /// Reads tab width and origin for selection indicator positioning. + private func tabWidthReader(_ tab: AttachmentTab) -> some View { + GeometryReader { geo in + Color.clear.preference( + key: AttachTabOriginKey.self, + value: [tab: geo.frame(in: .named("attachTabBar")).origin.x] + ) + .preference( + key: AttachTabWidthKey.self, + value: [tab: geo.size.width] + ) + } + } + + /// Sliding glass pill behind the selected tab (matches RosettaTabBar). + @ViewBuilder + private var attachmentSelectionIndicator: some View { + let width = tabWidths[selectedTab] ?? 0 + let xOffset = tabOrigins[selectedTab] ?? 0 + + if #available(iOS 26, *) { + Capsule().fill(.clear) + .glassEffect(.regular, in: .capsule) + .allowsHitTesting(false) + .frame(width: width) + .offset(x: xOffset) + .animation(.spring(response: 0.34, dampingFraction: 0.82), value: selectedTab) + } else { + Capsule().fill(.thinMaterial) + .frame(width: max(0, width - 4)) + .padding(.vertical, 4) + .offset(x: xOffset + 2) + .animation(.spring(response: 0.34, dampingFraction: 0.82), value: selectedTab) + } } /// Individual tab button matching RosettaTabBar dimensions exactly. @@ -345,20 +457,8 @@ struct AttachmentPanelView: View { let isSelected = selectedTab == tab return Button { - if tab == .avatar { - if hasAvatar { - onSendAvatar() - dismiss() - } else { - // No avatar set — offer to set one - dismiss() - onSetAvatar?() - } - return - } else { - withAnimation(.easeInOut(duration: 0.2)) { - selectedTab = tab - } + withAnimation(.spring(response: 0.34, dampingFraction: 0.82)) { + selectedTab = tab } } label: { VStack(spacing: 2) { @@ -372,11 +472,6 @@ struct AttachmentPanelView: View { .foregroundStyle(isSelected ? Color(hex: 0x008BFF) : .white) .frame(minWidth: 66, maxWidth: .infinity) .padding(.vertical, 6) - .background { - if isSelected { - TelegramGlassCapsule() - } - } } .buttonStyle(.plain) } @@ -457,6 +552,22 @@ private enum AttachmentTab: Hashable { case avatar } +// MARK: - Tab Bar Preference Keys (selection indicator positioning) + +private struct AttachTabWidthKey: PreferenceKey { + static var defaultValue: [AttachmentTab: CGFloat] = [:] + static func reduce(value: inout [AttachmentTab: CGFloat], nextValue: () -> [AttachmentTab: CGFloat]) { + value.merge(nextValue()) { _, new in new } + } +} + +private struct AttachTabOriginKey: PreferenceKey { + static var defaultValue: [AttachmentTab: CGFloat] = [:] + static func reduce(value: inout [AttachmentTab: CGFloat], nextValue: () -> [AttachmentTab: CGFloat]) { + value.merge(nextValue()) { _, new in new } + } +} + // MARK: - IdentifiableAsset /// Wrapper to make PHAsset usable with SwiftUI `.fullScreenCover(item:)`. diff --git a/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift b/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift index 8bb0308..9d7177e 100644 --- a/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift +++ b/Rosetta/Features/Chats/ChatDetail/ChatDetailView.swift @@ -181,7 +181,7 @@ struct ChatDetailView: View { } .overlay { chatEdgeGradients } // FPS overlay — uncomment for performance testing: - // .overlay { FPSOverlayView() } + .overlay { FPSOverlayView() } .overlay(alignment: .bottom) { if !route.isSystemAccount { KeyboardPaddedView { @@ -466,10 +466,11 @@ private extension ChatDetailView { color: .white ) .frame(width: 11, height: 20) - .allowsHitTesting(false) + .frame(width: 36, height: 36) + .contentShape(Circle()) + .background { glass(shape: .circle, strokeOpacity: 0.22, strokeColor: .white) } } - .frame(width: 36, height: 36) - .background { glass(shape: .circle, strokeOpacity: 0.22, strokeColor: .white) } + .buttonStyle(.plain) .accessibilityLabel("Back") } @@ -479,6 +480,7 @@ private extension ChatDetailView { .padding(.horizontal, 12) .frame(minWidth: 120) .frame(height: 44) + .contentShape(Capsule()) .background { glass(shape: .capsule, strokeOpacity: 0.22, strokeColor: .white) } @@ -490,6 +492,7 @@ private extension ChatDetailView { Button { openProfile() } label: { ChatDetailToolbarAvatar(route: route, size: 35) .frame(width: 36, height: 36) + .contentShape(Circle()) .background { glass(shape: .circle, strokeOpacity: 0.22, strokeColor: .white) } } .buttonStyle(.plain) @@ -508,6 +511,7 @@ private extension ChatDetailView { .padding(.horizontal, 16) .frame(minWidth: 120) .frame(height: 44) + .contentShape(Capsule()) .background { glass(shape: .capsule, strokeOpacity: 0.22, strokeColor: .white) } @@ -519,6 +523,7 @@ private extension ChatDetailView { Button { openProfile() } label: { ChatDetailToolbarAvatar(route: route, size: 38) .frame(width: 44, height: 44) + .contentShape(Circle()) .background { glass(shape: .circle, strokeOpacity: 0.22, strokeColor: .white) } } .buttonStyle(.plain) @@ -533,10 +538,10 @@ private extension ChatDetailView { color: .white ) .frame(width: 11, height: 20) - .allowsHitTesting(false) .frame(width: 36, height: 36) .frame(height: 44) .padding(.horizontal, 4) + .contentShape(Capsule()) .background { glass(shape: .capsule, strokeOpacity: 0.22, strokeColor: .white) } @@ -790,9 +795,8 @@ private extension ChatDetailView { scroll .scrollIndicators(.hidden) .overlay(alignment: .bottom) { - KeyboardPaddedView(extraPadding: composerHeight + 4) { - scrollToBottomButton(proxy: proxy) - } + scrollToBottomButton(proxy: proxy) + .padding(.bottom, composerHeight + 4) } } } @@ -1766,6 +1770,13 @@ private extension ChatDetailView { func openProfile() { guard !route.isSavedMessages, !route.isSystemAccount else { return } isInputFocused = false + // Force-dismiss keyboard at UIKit level immediately. + // On iOS 26+, the async resignFirstResponder via syncFocus races with + // the navigation transition — the system may re-focus the text view + // when returning from the profile, causing a ghost keyboard. + UIApplication.shared.sendAction( + #selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil + ) showOpponentProfile = true } @@ -2109,17 +2120,31 @@ private extension ChatDetailView { func replyBar(for message: ChatMessage) -> some View { let senderName = senderDisplayName(for: message.fromPublicKey) let previewText: String = { - let trimmed = message.text.trimmingCharacters(in: .whitespaces) - if !trimmed.isEmpty { return message.text } - if message.attachments.contains(where: { $0.type == .image }) { return "Photo" } + // Attachment type labels — check BEFORE text so photo/avatar messages + // always show their type even if text contains invisible characters. + if message.attachments.contains(where: { $0.type == .image }) { + let caption = message.text.trimmingCharacters(in: .whitespacesAndNewlines) + return caption.isEmpty ? "Photo" : caption + } if let file = message.attachments.first(where: { $0.type == .file }) { + let caption = message.text.trimmingCharacters(in: .whitespacesAndNewlines) + if !caption.isEmpty { return caption } + // Parse filename from preview (tag::fileSize::fileName) + let parts = file.preview.components(separatedBy: "::") + if parts.count >= 3 { return parts[2] } return file.id.isEmpty ? "File" : file.id } if message.attachments.contains(where: { $0.type == .avatar }) { return "Avatar" } if message.attachments.contains(where: { $0.type == .messages }) { return "Forwarded message" } + // No known attachment type — fall back to text + let trimmed = message.text.trimmingCharacters(in: .whitespacesAndNewlines) + if !trimmed.isEmpty { return message.text } if !message.attachments.isEmpty { return "Attachment" } return "" }() + #if DEBUG + let _ = print("📋 REPLY: preview='\(previewText.prefix(30))' text='\(message.text.prefix(30))' textHex=\(Array(message.text.utf8).prefix(16).map { String(format: "%02x", $0) }.joined(separator: " ")) atts=\(message.attachments.count) types=\(message.attachments.map { $0.type.rawValue })") + #endif HStack(spacing: 0) { RoundedRectangle(cornerRadius: 1.5) diff --git a/Rosetta/Features/Chats/ChatDetail/FullScreenImageViewer.swift b/Rosetta/Features/Chats/ChatDetail/FullScreenImageViewer.swift index 6c9caf3..0b4eee4 100644 --- a/Rosetta/Features/Chats/ChatDetail/FullScreenImageViewer.swift +++ b/Rosetta/Features/Chats/ChatDetail/FullScreenImageViewer.swift @@ -37,24 +37,15 @@ struct FullScreenImageViewer: View { .opacity(backgroundOpacity) .ignoresSafeArea() - // Zoomable image + // Zoomable image (visual only — no gestures here) Image(uiImage: image) .resizable() .scaledToFit() .scaleEffect(scale) .offset(x: offset.width, y: offset.height + dismissOffset) - .gesture(dragGesture) - .gesture(pinchGesture) - .onTapGesture(count: 2) { - doubleTap() - } - .onTapGesture(count: 1) { - withAnimation(.easeInOut(duration: 0.2)) { - showControls.toggle() - } - } + .allowsHitTesting(false) - // Close button + // Close button (above gesture layer so it stays tappable) if showControls { VStack { HStack { @@ -77,6 +68,20 @@ struct FullScreenImageViewer: View { .transition(.opacity) } } + // Gestures on the full-screen ZStack — not on the Image. + // scaleEffect is visual-only and doesn't expand the Image's hit-test area, + // so when zoomed to 2.5x, taps outside the original frame were lost. + .contentShape(Rectangle()) + .onTapGesture(count: 2) { + doubleTap() + } + .onTapGesture(count: 1) { + withAnimation(.easeInOut(duration: 0.2)) { + showControls.toggle() + } + } + .simultaneousGesture(pinchGesture) + .simultaneousGesture(dragGesture) } // MARK: - Background Opacity diff --git a/Rosetta/Features/Chats/ChatDetail/ImageGalleryViewer.swift b/Rosetta/Features/Chats/ChatDetail/ImageGalleryViewer.swift index 25b53af..6913391 100644 --- a/Rosetta/Features/Chats/ChatDetail/ImageGalleryViewer.swift +++ b/Rosetta/Features/Chats/ChatDetail/ImageGalleryViewer.swift @@ -134,7 +134,11 @@ struct ImageGalleryViewer: View { } } .tabViewStyle(.page(indexDisplayMode: .never)) - .disabled(currentZoomScale > 1.05 || isDismissing) + // Block TabView page swipe when zoomed or dismissing, + // but ONLY the scroll — NOT all user interaction. + // .disabled() kills ALL gestures (taps, pinch, etc.) which prevents + // double-tap zoom out. .scrollDisabled() only blocks the page swipe. + .scrollDisabled(currentZoomScale > 1.05 || isDismissing) .opacity(presentationAlpha) // Controls overlay diff --git a/Rosetta/Features/Chats/ChatDetail/MessageAvatarView.swift b/Rosetta/Features/Chats/ChatDetail/MessageAvatarView.swift index 0f014d2..f9e8795 100644 --- a/Rosetta/Features/Chats/ChatDetail/MessageAvatarView.swift +++ b/Rosetta/Features/Chats/ChatDetail/MessageAvatarView.swift @@ -199,6 +199,17 @@ struct MessageAvatarView: View { if let cached = AttachmentCache.shared.loadImage(forAttachmentId: attachment.id) { avatarImage = cached showAvatar = true // No animation for cached — show immediately + return + } + // Outgoing avatar: sender is me — load from AvatarRepository (always available locally) + if outgoing { + let myKey = SessionManager.shared.currentPublicKey + if let myAvatar = AvatarRepository.shared.loadAvatar(publicKey: myKey) { + avatarImage = myAvatar + showAvatar = true + // Backfill AttachmentCache so next render hits fast path + AttachmentCache.shared.saveImage(myAvatar, forAttachmentId: attachment.id) + } } } diff --git a/Rosetta/Features/Chats/ChatDetail/ZoomableImagePage.swift b/Rosetta/Features/Chats/ChatDetail/ZoomableImagePage.swift index 1e251df..6c9162f 100644 --- a/Rosetta/Features/Chats/ChatDetail/ZoomableImagePage.swift +++ b/Rosetta/Features/Chats/ChatDetail/ZoomableImagePage.swift @@ -35,6 +35,23 @@ struct ZoomableImagePage: View { .scaleEffect(effectiveScale) .offset(x: effectiveScale > 1.05 ? zoomOffset.width : 0, y: (effectiveScale > 1.05 ? zoomOffset.height : 0) + dismissDragOffset) + // Expand hit-test area to full screen — scaleEffect is visual-only + // and doesn't grow the Image's gesture frame. Without this, + // double-tap to zoom out doesn't work on zoomed-in edges. + .frame(maxWidth: .infinity, maxHeight: .infinity) + .contentShape(Rectangle()) + // Double tap: zoom to 2.5x or reset (MUST be before single tap) + .onTapGesture(count: 2) { + withAnimation(.spring(response: 0.35, dampingFraction: 0.9)) { + if zoomScale > 1.1 { + zoomScale = 1.0 + zoomOffset = .zero + } else { + zoomScale = 2.5 + } + currentScale = zoomScale + } + } // Single tap: toggle controls / edge navigation .onTapGesture { location in let width = UIScreen.main.bounds.width @@ -47,20 +64,8 @@ struct ZoomableImagePage: View { showControls.toggle() } } - // Double tap: zoom to 2.5x or reset - .onTapGesture(count: 2) { - withAnimation(.spring(response: 0.35, dampingFraction: 0.9)) { - if zoomScale > 1.1 { - zoomScale = 1.0 - zoomOffset = .zero - } else { - zoomScale = 2.5 - } - currentScale = zoomScale - } - } // Pinch zoom - .gesture( + .simultaneousGesture( MagnifyGesture() .updating($pinchScale) { value, state, _ in state = value.magnification @@ -78,7 +83,7 @@ struct ZoomableImagePage: View { } ) // Pan when zoomed - .gesture( + .simultaneousGesture( zoomScale > 1.05 ? DragGesture() .onChanged { value in @@ -96,7 +101,6 @@ struct ZoomableImagePage: View { .simultaneousGesture( zoomScale <= 1.05 ? dismissDragGesture : nil ) - .contentShape(Rectangle()) } else { placeholder } diff --git a/Rosetta/Features/Chats/ChatList/ChatListView.swift b/Rosetta/Features/Chats/ChatList/ChatListView.swift index 64b2d2a..9c95300 100644 --- a/Rosetta/Features/Chats/ChatList/ChatListView.swift +++ b/Rosetta/Features/Chats/ChatList/ChatListView.swift @@ -106,6 +106,21 @@ struct ChatListView: View { guard let route = notification.object as? ChatRoute else { return } // Navigate to the chat from push notification tap navigationState.path = [route] + // Clear pending route — consumed by onReceive (fast path) + AppDelegate.pendingChatRoute = nil + } + .onAppear { + // Fallback: consume pending route if .onReceive missed it. + // Handles terminated app (ChatListView didn't exist when notification was posted) + // and background app (Combine subscription may not fire during app resume). + if let route = AppDelegate.pendingChatRoute { + AppDelegate.pendingChatRoute = nil + // Small delay to let NavigationStack settle after view creation + Task { @MainActor in + try? await Task.sleep(nanoseconds: 100_000_000) // 100ms + navigationState.path = [route] + } + } } } diff --git a/Rosetta/Features/MainTabView.swift b/Rosetta/Features/MainTabView.swift index 6afaedb..770f4a3 100644 --- a/Rosetta/Features/MainTabView.swift +++ b/Rosetta/Features/MainTabView.swift @@ -64,6 +64,14 @@ struct MainTabView: View { // never observes the dialogs dictionary directly. UnreadCountObserver(count: $cachedUnreadCount) } + // Switch to Chats tab when user taps a push notification. + // Without this, the navigation happens in the Chats NavigationStack + // but the user stays on whatever tab they were on (e.g., Settings). + .onReceive(NotificationCenter.default.publisher(for: .openChatFromNotification)) { _ in + if selectedTab != .chats { + selectedTab = .chats + } + } } // MARK: - iOS 26+ (native TabView with liquid glass tab bar) diff --git a/Rosetta/Resources/Lottie/avatar.json b/Rosetta/Resources/Lottie/avatar.json new file mode 100644 index 0000000..4f7408a --- /dev/null +++ b/Rosetta/Resources/Lottie/avatar.json @@ -0,0 +1 @@ +{"tgs":1,"v":"5.5.2","fr":60,"ip":60,"op":240,"w":512,"h":512,"nm":"Bust in Silhouette","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"silhouette 4","parent":8,"sr":1,"ks":{"o":{"a":1,"k":[{"t":121,"s":[0],"h":1},{"t":126.641,"s":[33],"h":1},{"t":135.664,"s":[0],"h":1},{"t":141.305,"s":[33],"h":1},{"t":149.19921875,"s":[0],"h":1}]},"p":{"a":0,"k":[52,201,0]},"a":{"a":0,"k":[7,201,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0.005,0.035],[35.501,24.537],[8.215,26.577],[0.826,6.722],[-5.945,6.687],[3.082,26.383],[15.735,2.271],[0.263,-0.04],[0,0],[-14.389,-57.101],[-6.535,-12.977],[-32.401,-10.32],[0.438,-8.43],[4.435,-14.348],[34.127,-20.758],[0.966,-6.765]],"o":[[-0.966,-6.765],[-32.859,-22.711],[-4.435,-14.348],[-1.099,-8.942],[27.225,-30.621],[-14.397,-123.256],[0.264,-0.04],[0,0],[-43.311,12.04],[-0.389,1.398],[0,0],[3.984,1.269],[-0.352,6.764],[-8.215,26.577],[-31.767,19.322],[-0.01,0.071]],"v":[[213.566,193.248],[175.425,134.08],[66.801,90.573],[62.599,60.942],[67.775,33.121],[94.897,-74.244],[12.265,-192.771],[-11.342,-192.422],[-30.363,-185.479],[-70.111,-50.898],[-62.465,-22.023],[-9.599,44.32],[-5.938,72.43],[-11.713,96.569],[-116.233,137.678],[-153.389,191.248]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[-70,4]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 5","bm":0,"hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"smile","parent":3,"sr":1,"ks":{"p":{"a":0,"k":[7,-1,0]},"a":{"a":0,"k":[7,-1,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":107,"s":[{"i":[[14.664,2.379],[-18.353,-0.556],[5.448,0.001]],"o":[[12.424,1.113],[-15.665,2.621],[-9.617,-0.002]],"v":[[-20.533,-8.443],[32.268,-7.887],[6.23,-5.802]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":117,"s":[{"i":[[3.395,12.564],[-20.059,-0.483],[26.517,0]],"o":[[13.578,0.966],[-6.172,17.879],[-22.682,0]],"v":[[-22.987,-14.027],[34.721,-13.544],[3.21,15.933]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":124,"s":[{"i":[[5.315,12.564],[-31.409,-0.483],[19.801,1.165]],"o":[[21.262,0.966],[-9.664,17.879],[-16.43,-0.966]],"v":[[-39.314,-14.027],[51.048,-13.544],[3.21,15.933]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":179,"s":[{"i":[[5.315,12.564],[-31.409,-0.483],[19.801,1.165]],"o":[[21.262,0.966],[-9.664,17.879],[-16.43,-0.966]],"v":[[-39.314,-14.027],[51.048,-13.544],[3.21,15.933]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":183,"s":[{"i":[[3.395,12.564],[-20.059,-0.483],[26.517,0]],"o":[[13.578,0.966],[-6.172,17.879],[-22.682,0]],"v":[[-22.987,-14.027],[34.721,-13.544],[3.21,15.933]],"c":true}]},{"t":189,"s":[{"i":[[14.664,2.379],[-18.353,-0.556],[5.448,0.001]],"o":[[12.424,1.113],[-15.665,2.621],[-9.617,-0.002]],"v":[[-20.533,-8.443],[32.268,-7.887],[6.23,-5.802]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":2},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":107,"op":190,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"top","parent":8,"sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":102,"s":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.31],"y":[0]},"t":112,"s":[-8.674]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":186,"s":[-8.674]},{"t":196,"s":[0]}]},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":102,"s":[10,-130,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":112,"s":[-17.288,-128.488,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":186,"s":[-17.288,-128.488,0],"to":[0,0,0],"ti":[0,0,0]},{"t":196,"s":[10,-130,0]}]},"a":{"a":0,"k":[10,-130,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[4.134,1.538],[62.819,0],[0.266,0],[0,0],[-7.732,-77.315],[1.824,-4.353]],"o":[[-1.88,-3.615],[7.732,-77.315],[0.267,0],[0,0],[-62.819,0],[-4.72,1.756],[0,0]],"v":[[108.578,-63.741],[99.674,-71.772],[4.479,-195.476],[5.143,-195.476],[5.867,-195.476],[-89.327,-71.772],[-98.971,-62.161]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.647058823529,0.839215686275,0.917647058824,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 4","bm":0,"hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"shoulder-R","parent":8,"sr":1,"ks":{"p":{"a":0,"k":[132,153,0]},"a":{"a":0,"k":[132,153,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[35.501,24.538],[24.56,10.99]],"o":[[-0.966,-6.765],[-25.029,-17.299],[-0.961,-0.43]],"v":[[203.566,201.248],[164.425,134.08],[67.94,105.313]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.647058823529,0.839215686275,0.917647058824,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 3","bm":0,"hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"shoulder-L","parent":8,"sr":1,"ks":{"p":{"a":0,"k":[-119,154,0]},"a":{"a":0,"k":[-119,154,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[4.004,-1.735],[24.811,-17.149],[0.966,-6.765]],"o":[[-24.728,10.715],[-35.501,24.537],[0,0]],"v":[[-58.24,105.597],[-154.079,134.08],[-193.22,201.248]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.647058823529,0.839215686275,0.917647058824,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"silhouette 2","parent":8,"sr":1,"ks":{"p":{"a":0,"k":[7,201,0]},"a":{"a":0,"k":[7,201,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0.966,-6.765],[0.005,0.035]],"o":[[-0.01,0.071],[-0.966,-6.765]],"v":[[-193.219,201.248],[203.566,201.248]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.2,0.325490196078,0.458823529412,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 5","bm":0,"hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"silhouette LIGHT","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.15],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":127,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.85],"y":[0]},"t":137,"s":[0]},{"i":{"x":[0.16],"y":[1]},"o":{"x":[0.167],"y":[0.167]},"t":144,"s":[100]},{"t":151,"s":[0]}]},"p":{"a":0,"k":[263,457,0]},"a":{"a":0,"k":[7,201,0]},"s":{"a":0,"k":[106,106,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0.005,0.035],[35.501,24.537],[8.215,26.577],[-1.93,6.492],[-2.38,2.853],[0,0],[21.704,4.521],[62.105,-9.44],[0.263,-0.04],[0,0],[-19.262,-75.276],[-12.276,2.843],[-35.533,-22.975],[-3.371,-5.027],[4.435,-14.348],[32.859,-22.711],[0.966,-6.765]],"o":[[-0.966,-6.765],[-32.859,-22.711],[-4.435,-14.348],[1.725,-5.802],[27.102,-32.495],[12.566,-0.932],[-3.974,-77.599],[0.264,-0.04],[0,0],[-62.105,9.44],[-19.381,10.766],[0,0],[3.12,2.017],[3.772,5.625],[-8.215,26.577],[-35.501,24.537],[-0.01,0.071]],"v":[[203.566,201.248],[164.425,134.08],[52.801,91.074],[52.099,46.942],[59.274,34.121],[85.34,-36.639],[80.467,-84.776],[-32.235,-192.771],[-31.58,-192.871],[-30.863,-192.979],[-106.388,-56.375],[-96.738,-8.965],[-50.825,50.855],[-40.164,60.966],[-42.455,91.074],[-154.079,134.08],[-193.219,201.248]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.654901960784,0.776470588235,0.843137254902,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 5","bm":0,"hd":false}],"ip":126,"op":154,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"silhouette","sr":1,"ks":{"p":{"a":0,"k":[263,457,0]},"a":{"a":0,"k":[7,201,0]},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":97,"s":[106,106,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":106,"s":[109.18,102.82,100]},{"i":{"x":[0.703,0.703,0.703],"y":[1,1,1]},"o":{"x":[0.273,0.273,0.273],"y":[0,0,0]},"t":115,"s":[106,106,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":186,"s":[106,106,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":195,"s":[109.18,102.82,100]},{"t":204,"s":[106,106,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":102,"s":[{"i":[[0.005,0.035],[35.501,24.537],[8.215,26.577],[-2.884,6.128],[-2.782,2.463],[0,0],[20.779,7.732],[62.819,0],[0.266,0],[0,0],[-7.732,-77.315],[-12.564,0.966],[-31.677,-28.053],[-2.577,-5.476],[4.435,-14.348],[32.859,-22.711],[0.966,-6.765]],"o":[[-0.966,-6.765],[-32.859,-22.711],[-4.435,-14.348],[2.577,-5.476],[31.677,-28.053],[12.564,0.966],[7.732,-77.315],[0.267,0],[0,0],[-62.819,0],[-20.778,7.732],[0,0],[2.782,2.463],[2.884,6.128],[-8.215,26.577],[-35.501,24.537],[-0.01,0.071]],"v":[[203.566,201.248],[164.425,134.08],[52.801,91.074],[51.835,54.188],[60.855,42.59],[97.258,-23.45],[99.674,-71.772],[4.479,-195.476],[5.143,-195.476],[5.867,-195.476],[-89.327,-71.772],[-86.911,-23.45],[-50.508,42.59],[-41.488,54.188],[-42.455,91.074],[-154.079,134.08],[-193.219,201.248]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":112,"s":[{"i":[[0.005,0.035],[35.501,24.537],[8.215,26.577],[-1.93,6.492],[-2.38,2.853],[0,0],[21.704,4.521],[62.105,-9.44],[0.263,-0.04],[0,0],[-19.262,-75.276],[-12.276,2.843],[-35.533,-22.975],[-3.371,-5.027],[4.435,-14.348],[32.859,-22.711],[0.966,-6.765]],"o":[[-0.966,-6.765],[-32.859,-22.711],[-4.435,-14.348],[1.725,-5.802],[27.102,-32.495],[12.566,-0.932],[-3.974,-77.599],[0.264,-0.04],[0,0],[-62.105,9.44],[-19.381,10.766],[0,0],[3.12,2.017],[3.772,5.625],[-8.215,26.577],[-35.501,24.537],[-0.01,0.071]],"v":[[203.566,201.248],[164.425,134.08],[52.801,91.074],[52.099,46.942],[59.274,34.121],[85.34,-36.639],[80.467,-84.776],[-32.235,-192.771],[-31.58,-192.871],[-30.863,-192.979],[-106.388,-56.375],[-96.738,-8.965],[-50.825,50.855],[-40.164,60.966],[-42.455,91.074],[-154.079,134.08],[-193.219,201.248]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":186,"s":[{"i":[[0.005,0.035],[35.501,24.537],[8.215,26.577],[-1.93,6.492],[-2.38,2.853],[0,0],[21.704,4.521],[62.105,-9.44],[0.263,-0.04],[0,0],[-19.262,-75.276],[-12.276,2.843],[-35.533,-22.975],[-3.371,-5.027],[4.435,-14.348],[32.859,-22.711],[0.966,-6.765]],"o":[[-0.966,-6.765],[-32.859,-22.711],[-4.435,-14.348],[1.725,-5.802],[27.102,-32.495],[12.566,-0.932],[-3.974,-77.599],[0.264,-0.04],[0,0],[-62.105,9.44],[-19.381,10.766],[0,0],[3.12,2.017],[3.772,5.625],[-8.215,26.577],[-35.501,24.537],[-0.01,0.071]],"v":[[203.566,201.248],[164.425,134.08],[52.801,91.074],[52.099,46.942],[59.274,34.121],[85.34,-36.639],[80.467,-84.776],[-32.235,-192.771],[-31.58,-192.871],[-30.863,-192.979],[-106.388,-56.375],[-96.738,-8.965],[-50.825,50.855],[-40.164,60.966],[-42.455,91.074],[-154.079,134.08],[-193.219,201.248]],"c":true}]},{"t":196,"s":[{"i":[[0.005,0.035],[35.501,24.537],[8.215,26.577],[-2.884,6.128],[-2.782,2.463],[0,0],[20.779,7.732],[62.819,0],[0.266,0],[0,0],[-7.732,-77.315],[-12.564,0.966],[-31.677,-28.053],[-2.577,-5.476],[4.435,-14.348],[32.859,-22.711],[0.966,-6.765]],"o":[[-0.966,-6.765],[-32.859,-22.711],[-4.435,-14.348],[2.577,-5.476],[31.677,-28.053],[12.564,0.966],[7.732,-77.315],[0.267,0],[0,0],[-62.819,0],[-20.778,7.732],[0,0],[2.782,2.463],[2.884,6.128],[-8.215,26.577],[-35.501,24.537],[-0.01,0.071]],"v":[[203.566,201.248],[164.425,134.08],[52.801,91.074],[51.835,54.188],[60.855,42.59],[97.258,-23.45],[99.674,-71.772],[4.479,-195.476],[5.143,-195.476],[5.867,-195.476],[-89.327,-71.772],[-86.911,-23.45],[-50.508,42.59],[-41.488,54.188],[-42.455,91.074],[-154.079,134.08],[-193.219,201.248]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.2,0.325490196078,0.458823529412,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":8},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"gf","o":{"a":0,"k":100},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.267,0.408,0.545,0.587,0.375,0.524,0.655,1,0.482,0.639,0.765]}},"s":{"a":0,"k":[-16.126,196.797]},"e":{"a":0,"k":[-25.412,-152.981]},"t":1,"nm":"gtjl34","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 5","bm":0,"hd":false}],"ip":0,"op":300,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"silhouette 3","parent":8,"sr":1,"ks":{"o":{"a":1,"k":[{"t":121,"s":[0],"h":1},{"t":126.641,"s":[33],"h":1},{"t":135.664,"s":[0],"h":1},{"t":141.305,"s":[33],"h":1},{"t":149.19921875,"s":[0],"h":1}]},"p":{"a":0,"k":[32,201,0]},"a":{"a":0,"k":[7,201,0]},"s":{"a":0,"k":[105.096,105.096,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":102,"s":[{"i":[[0.005,0.035],[35.501,24.537],[8.215,26.577],[-2.884,6.128],[-2.782,2.463],[0,0],[20.779,7.732],[62.819,0],[0.266,0],[0,0],[-7.732,-77.315],[-12.564,0.966],[-31.677,-28.053],[-2.577,-5.476],[4.435,-14.348],[32.859,-22.711],[0.966,-6.765]],"o":[[-0.966,-6.765],[-32.859,-22.711],[-4.435,-14.348],[2.577,-5.476],[31.677,-28.053],[12.564,0.966],[7.732,-77.315],[0.267,0],[0,0],[-62.819,0],[-20.778,7.732],[0,0],[2.782,2.463],[2.884,6.128],[-8.215,26.577],[-35.501,24.537],[-0.01,0.071]],"v":[[203.566,201.248],[164.425,134.08],[52.801,91.074],[51.835,54.188],[60.855,42.59],[97.258,-23.45],[99.674,-71.772],[4.479,-195.476],[5.143,-195.476],[5.867,-195.476],[-89.327,-71.772],[-86.911,-23.45],[-50.508,42.59],[-41.488,54.188],[-42.455,91.074],[-154.079,134.08],[-193.219,201.248]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":112,"s":[{"i":[[0.005,0.035],[35.501,24.537],[8.215,26.577],[-1.93,6.492],[-2.38,2.853],[0,0],[21.704,4.521],[62.105,-9.44],[0.263,-0.04],[0,0],[-19.262,-75.276],[-12.276,2.843],[-35.533,-22.975],[-3.371,-5.027],[4.435,-14.348],[32.859,-22.711],[0.966,-6.765]],"o":[[-0.966,-6.765],[-32.859,-22.711],[-4.435,-14.348],[1.725,-5.802],[27.102,-32.495],[12.566,-0.932],[-3.974,-77.599],[0.264,-0.04],[0,0],[-62.105,9.44],[-19.381,10.766],[0,0],[3.12,2.017],[3.772,5.625],[-8.215,26.577],[-35.501,24.537],[-0.01,0.071]],"v":[[203.566,201.248],[164.425,134.08],[52.801,91.074],[52.099,46.942],[59.274,34.121],[85.34,-36.639],[80.467,-84.776],[-32.235,-192.771],[-31.58,-192.871],[-30.863,-192.979],[-106.388,-56.375],[-96.738,-8.965],[-50.825,50.855],[-40.164,60.966],[-42.455,91.074],[-154.079,134.08],[-193.219,201.248]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":186,"s":[{"i":[[0.005,0.035],[35.501,24.537],[8.215,26.577],[-1.93,6.492],[-2.38,2.853],[0,0],[21.704,4.521],[62.105,-9.44],[0.263,-0.04],[0,0],[-19.262,-75.276],[-12.276,2.843],[-35.533,-22.975],[-3.371,-5.027],[4.435,-14.348],[32.859,-22.711],[0.966,-6.765]],"o":[[-0.966,-6.765],[-32.859,-22.711],[-4.435,-14.348],[1.725,-5.802],[27.102,-32.495],[12.566,-0.932],[-3.974,-77.599],[0.264,-0.04],[0,0],[-62.105,9.44],[-19.381,10.766],[0,0],[3.12,2.017],[3.772,5.625],[-8.215,26.577],[-35.501,24.537],[-0.01,0.071]],"v":[[203.566,201.248],[164.425,134.08],[52.801,91.074],[52.099,46.942],[59.274,34.121],[85.34,-36.639],[80.467,-84.776],[-32.235,-192.771],[-31.58,-192.871],[-30.863,-192.979],[-106.388,-56.375],[-96.738,-8.965],[-50.825,50.855],[-40.164,60.966],[-42.455,91.074],[-154.079,134.08],[-193.219,201.248]],"c":true}]},{"t":196,"s":[{"i":[[0.005,0.035],[35.501,24.537],[8.215,26.577],[-2.884,6.128],[-2.782,2.463],[0,0],[20.779,7.732],[62.819,0],[0.266,0],[0,0],[-7.732,-77.315],[-12.564,0.966],[-31.677,-28.053],[-2.577,-5.476],[4.435,-14.348],[32.859,-22.711],[0.966,-6.765]],"o":[[-0.966,-6.765],[-32.859,-22.711],[-4.435,-14.348],[2.577,-5.476],[31.677,-28.053],[12.564,0.966],[7.732,-77.315],[0.267,0],[0,0],[-62.819,0],[-20.778,7.732],[0,0],[2.782,2.463],[2.884,6.128],[-8.215,26.577],[-35.501,24.537],[-0.01,0.071]],"v":[[203.566,201.248],[164.425,134.08],[52.801,91.074],[51.835,54.188],[60.855,42.59],[97.258,-23.45],[99.674,-71.772],[4.479,-195.476],[5.143,-195.476],[5.867,-195.476],[-89.327,-71.772],[-86.911,-23.45],[-50.508,42.59],[-41.488,54.188],[-42.455,91.074],[-154.079,134.08],[-193.219,201.248]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.034645940743,0.173816680908,0.34128370098,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,4]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 5","bm":0,"hd":false}],"ip":0,"op":300,"st":0,"bm":0}]} \ No newline at end of file diff --git a/Rosetta/Resources/Lottie/file_folder.json b/Rosetta/Resources/Lottie/file_folder.json new file mode 100644 index 0000000..dc1171d --- /dev/null +++ b/Rosetta/Resources/Lottie/file_folder.json @@ -0,0 +1 @@ +{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":120,"w":512,"h":512,"nm":"13_FOLDER_EMOJI_closed","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":3,"nm":"NULL CONTROL","sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":0,"k":[244.941,205.599,0]},"a":{"a":0,"k":[60,60,0]}},"ao":0,"ip":0,"op":85,"st":5,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Layer 64","parent":1,"sr":1,"ks":{"r":{"a":0,"k":77.3},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":5,"s":[56.544,13.819,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":25,"s":[76.544,73.819,0],"to":[0,0,0],"ti":[-2.515,-0.112,0]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":45,"s":[76.544,73.819,0],"to":[1.015,-117.388,0],"ti":[0,0,0]},{"t":73,"s":[59.552,234.809,0]}]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":5,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":25,"s":[110,110,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":35,"s":[100,100,100]},{"t":73,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[102.41,175.367],[-58.067,139.083],[-58.067,-165.374],[102.41,-129.09]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.403921568627,0.462745098039,0.513725490196,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":5,"s":[20]},{"t":25,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.462745127958,0.513725490196,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":5,"op":69,"st":5,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Layer 63","parent":1,"sr":1,"ks":{"r":{"a":0,"k":77.3},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":8,"s":[49.949,0.843,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":28,"s":[59.96,60.851,0],"to":[0,0,0],"ti":[-2.515,-0.112,0]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":56,"s":[59.96,60.851,0],"to":[1.015,-117.388,0],"ti":[0,0,0]},{"t":84,"s":[60.923,235.808,0]}]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":8,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":28,"s":[110,110,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":38,"s":[100,100,100]},{"t":76,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[102.41,175.367],[-58.067,139.083],[-58.067,-165.374],[102.41,-129.09]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.403921568627,0.462745098039,0.513725490196,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":8,"s":[20]},{"t":28,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.462745127958,0.513725490196,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":80,"st":8,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Layer 62","parent":1,"sr":1,"ks":{"r":{"a":0,"k":77.3},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":11,"s":[33.63,-13.14,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":31,"s":[53.676,46.687,0],"to":[0,0,0],"ti":[-2.515,-0.112,0]},{"t":41,"s":[43.676,47.169,0],"h":1},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":61,"s":[43.676,47.169,0],"to":[1.015,-117.388,0],"ti":[0,0,0]},{"t":89,"s":[61.423,235.808,0]}]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":11,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":31,"s":[110,110,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":41,"s":[100,100,100]},{"t":79,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":31,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[102.41,175.367],[-58.067,139.083],[-58.067,-165.374],[102.41,-129.09]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":41,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[102.41,175.367],[-58.067,139.083],[-58.067,-165.374],[102.41,-129.09]],"c":true}]},{"i":{"x":0.715,"y":0.702},"o":{"x":1,"y":0},"t":61,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[102.41,175.367],[-58.067,139.083],[-58.067,-165.374],[102.41,-129.09]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.174},"t":76,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[102.41,175.367],[-39.143,143.347],[-39.143,-161.109],[102.41,-129.09]],"c":true}]},{"t":89,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[102.41,175.367],[-136.159,121.714],[-136.159,-182.742],[102.41,-129.09]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.403921568627,0.462745098039,0.513725490196,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":11,"s":[20]},{"t":31,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.403921598547,0.462745127958,0.513725490196,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":3,"op":85,"st":11,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Layer 48","parent":2,"sr":1,"ks":{"p":{"a":0,"k":[-0.691,0.492,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.163,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-2.182,42.422],[4.413,-1.098],[0,0],[2.126,-8.116],[-0.161,-50.934],[11.242,-3.498],[0,0],[-0.633,7.945]],"o":[[0.407,-7.916],[0,0],[-9.908,2.181],[-3.809,14.544],[0.239,75.331],[0,0],[7.725,-1.963],[5.48,-68.814]],"v":[[436.556,187.086],[427.403,178.357],[134.665,242.788],[111.874,260.616],[109.261,390.419],[98.758,502.748],[407.551,424.311],[421.71,407.822]],"c":true}]},{"i":{"x":0.301,"y":1},"o":{"x":0.951,"y":0},"t":40,"s":[{"i":[[-24.961,37.768],[7.741,-1.702],[0,0],[5.867,-8.277],[6.179,-33.41],[14.688,-5.293],[0,0],[-0.782,7.932]],"o":[[4.37,-6.613],[0,0],[-9.908,2.181],[-12.684,17.892],[-13.064,70.634],[0,0],[7.725,-1.963],[6.775,-68.707]],"v":[[478.123,232.836],[468.903,219.357],[176.165,283.788],[153.374,301.616],[115.511,390.419],[98.758,502.748],[407.551,424.311],[421.711,407.822]],"c":true}]},{"i":{"x":0.223,"y":1},"o":{"x":0.476,"y":0},"t":80,"s":[{"i":[[1.26,40.797],[4.413,-1.098],[0,0],[1.086,-8.319],[-4.321,-69.16],[6.933,-2.99],[0,0],[-0.201,7.968]],"o":[[-0.245,-7.922],[0,0],[-9.908,2.181],[-1.684,12.892],[4.392,70.299],[0,0],[7.725,-1.963],[1.98,-78.314]],"v":[[413.431,180.211],[404.278,171.482],[114.665,235.288],[91.874,253.116],[99.261,385.419],[98.758,502.748],[409.301,423.561],[417.21,401.322]],"c":true}]},{"t":120,"s":[{"i":[[-2.182,42.422],[4.413,-1.098],[0,0],[2.126,-8.116],[-0.161,-50.934],[11.242,-3.498],[0,0],[-0.633,7.945]],"o":[[0.407,-7.916],[0,0],[-9.908,2.181],[-3.809,14.544],[0.239,75.331],[0,0],[7.725,-1.963],[5.48,-68.814]],"v":[[436.556,187.086],[427.403,178.357],[134.665,242.788],[111.874,260.616],[109.261,390.419],[98.758,502.748],[407.551,424.311],[421.71,407.822]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Layer 47","parent":39,"sr":1,"ks":{"p":{"a":0,"k":[42.742,39.17,0]},"a":{"a":0,"k":[284.188,355.943,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0]],"o":[[0,0]],"v":[[433,204.5]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.666666666667,0.486274539723,0.294117647059,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Shape 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.163,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-177.869,1.216],[-31.5,-31],[114.869,-63.216]],"c":false}]},{"i":{"x":0.301,"y":1},"o":{"x":0.951,"y":0},"t":40,"s":[{"i":[[-5.295,2.647],[0,0],[0,0]],"o":[[5.295,-2.647],[0,0],[0,0]],"v":[[-143.119,44.466],[-122.747,39.134],[156.369,-22.216]],"c":false}]},{"i":{"x":0.223,"y":1},"o":{"x":0.476,"y":0},"t":80,"s":[{"i":[[-2.587,8.5],[0,0],[0,0]],"o":[[3.545,-11.647],[0,0],[0,0]],"v":[[-216.369,7.216],[-197.497,-6.366],[94.869,-70.716]],"c":false}]},{"t":120,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-177.869,1.216],[-31.5,-31],[114.869,-63.216]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[313.824,247.432]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.163,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[156.65,-39.791],[-156.65,39.791]],"c":false}]},{"i":{"x":0.301,"y":1},"o":{"x":0.951,"y":0},"t":40,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[156.65,-39.791],[-156.65,39.791]],"c":false}]},{"i":{"x":0.223,"y":1},"o":{"x":0.476,"y":0},"t":80,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[151.65,-39.291],[-159.4,40.291]],"c":false}]},{"t":120,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[156.65,-39.791],[-156.65,39.791]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.929411768913,0.933333337307,0.945098042488,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[262.449,454.978]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":50},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.163,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[-0.117,2.624],[-0.436,26.693],[-5.511,17.741],[-8.212,1.808],[0,0]],"o":[[0.118,-2.391],[0.781,-17.567],[0.617,-37.781],[2.471,-7.955],[0,0],[0,0]],"v":[[-172.883,127.709],[-172.537,120.187],[-172.873,48.383],[-170.048,-79.666],[-151.354,-94.277],[141.383,-158.709]],"c":false}]},{"i":{"x":0.301,"y":1},"o":{"x":0.951,"y":0},"t":40,"s":[{"i":[[0,0],[-0.117,2.624],[-4.814,30.119],[-10.636,21.678],[-8.212,1.808],[0,0]],"o":[[0.118,-2.391],[0.781,-17.567],[5.964,-37.312],[3.669,-7.479],[0,0],[0,0]],"v":[[-172.883,127.709],[-172.537,120.187],[-165.873,48.441],[-128.548,-38.666],[-109.854,-53.277],[182.883,-117.709]],"c":false}]},{"i":{"x":0.223,"y":1},"o":{"x":0.476,"y":0},"t":80,"s":[{"i":[[0,0],[-0.117,2.624],[1.314,24.693],[-0.511,15.491],[-8.212,1.808],[0,0]],"o":[[0.118,-2.391],[0.781,-17.567],[-2.009,-37.733],[0.275,-8.325],[0,0],[0,0]],"v":[[-179.633,122.646],[-179.287,115.125],[-182.873,43.383],[-190.048,-87.166],[-171.354,-101.777],[121.383,-166.209]],"c":false}]},{"t":120,"s":[{"i":[[0,0],[-0.117,2.624],[-0.436,26.693],[-5.511,17.741],[-8.212,1.808],[0,0]],"o":[[0.118,-2.391],[0.781,-17.567],[0.617,-37.781],[2.471,-7.955],[0,0],[0,0]],"v":[[-172.883,127.709],[-172.537,120.187],[-172.873,48.383],[-170.048,-79.666],[-151.354,-94.277],[141.383,-158.709]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.900359509038,0.900359509038,0.904794730392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[287.309,342.925]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.163,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[0,0],[4.321,-1.557],[0,0],[-1.467,7.009],[0,0],[0,0]],"o":[[-1.309,6.993],[0,0],[7.055,-1.793],[0,0],[0,0],[0,0]],"v":[[-153.798,36.65],[-161.674,49.427],[147.119,-29.01],[160.954,-43.472],[161.674,-49.427],[-152.905,30.234]],"c":true}]},{"i":{"x":0.301,"y":1},"o":{"x":0.951,"y":0},"t":40,"s":[{"i":[[0,0],[4.321,-1.557],[0,0],[-1.467,7.009],[0,0],[0,0]],"o":[[-1.309,6.993],[0,0],[7.055,-1.793],[0,0],[0,0],[0,0]],"v":[[-153.798,36.65],[-161.674,49.427],[147.119,-29.01],[160.954,-43.472],[161.674,-49.427],[-152.905,30.234]],"c":true}]},{"i":{"x":0.223,"y":1},"o":{"x":0.476,"y":0},"t":80,"s":[{"i":[[0,0],[4.321,-1.557],[0,0],[-1.467,7.009],[0,0],[0,0]],"o":[[-1.309,6.993],[0,0],[7.055,-1.793],[0,0],[0,0],[0,0]],"v":[[-157.548,34.15],[-161.674,49.427],[149.494,-30.51],[156.454,-49.972],[156.674,-50.677],[-157.155,29.234]],"c":true}]},{"t":120,"s":[{"i":[[0,0],[4.321,-1.557],[0,0],[-1.467,7.009],[0,0],[0,0]],"o":[[-1.309,6.993],[0,0],[7.055,-1.793],[0,0],[0,0],[0,0]],"v":[[-153.798,36.65],[-161.674,49.427],[147.119,-29.01],[160.954,-43.472],[161.674,-49.427],[-152.905,30.234]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[260.432,453.321]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":20},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 3","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.163,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[{"i":[[-2.182,42.422],[4.413,-1.098],[0,0],[2.126,-8.116],[-0.161,-50.934],[11.242,-3.498],[0,0],[-0.633,7.945]],"o":[[0.407,-7.916],[0,0],[-9.908,2.181],[-3.809,14.544],[0.239,75.331],[0,0],[7.725,-1.963],[5.48,-68.814]],"v":[[152.367,-168.857],[143.214,-177.586],[-149.524,-113.155],[-172.314,-95.327],[-174.927,34.476],[-185.431,146.805],[123.363,68.368],[137.522,51.88]],"c":true}]},{"i":{"x":0.301,"y":1},"o":{"x":0.951,"y":0},"t":40,"s":[{"i":[[-24.961,37.768],[7.741,-1.702],[0,0],[5.867,-8.277],[6.179,-33.41],[14.688,-5.293],[0,0],[-0.782,7.932]],"o":[[4.37,-6.613],[0,0],[-9.908,2.181],[-12.684,17.892],[-13.064,70.634],[0,0],[7.725,-1.963],[6.775,-68.707]],"v":[[193.935,-123.107],[184.714,-136.586],[-108.024,-72.155],[-130.814,-54.327],[-168.677,34.476],[-185.431,146.805],[123.363,68.368],[137.522,51.88]],"c":true}]},{"i":{"x":0.223,"y":1},"o":{"x":0.476,"y":0},"t":80,"s":[{"i":[[1.26,40.797],[4.413,-1.098],[0,0],[1.086,-8.319],[-4.321,-69.16],[6.933,-2.99],[0,0],[-0.201,7.968]],"o":[[-0.245,-7.922],[0,0],[-9.908,2.181],[-1.684,12.892],[4.392,70.299],[0,0],[7.725,-1.963],[1.98,-78.314]],"v":[[129.242,-175.732],[120.089,-184.461],[-169.524,-120.655],[-192.314,-102.827],[-184.927,29.476],[-185.431,146.805],[125.113,67.618],[133.022,45.38]],"c":true}]},{"t":120,"s":[{"i":[[-2.182,42.422],[4.413,-1.098],[0,0],[2.126,-8.116],[-0.161,-50.934],[11.242,-3.498],[0,0],[-0.633,7.945]],"o":[[0.407,-7.916],[0,0],[-9.908,2.181],[-3.809,14.544],[0.239,75.331],[0,0],[7.725,-1.963],[5.48,-68.814]],"v":[[152.367,-168.857],[143.214,-177.586],[-149.524,-113.155],[-172.314,-95.327],[-174.927,34.476],[-185.431,146.805],[123.363,68.368],[137.522,51.88]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"gf","o":{"a":0,"k":100},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.808,0.808,0.82,0.5,0.869,0.871,0.882,1,0.929,0.933,0.945]}},"s":{"a":0,"k":[-6.189,-17.943]},"e":{"a":0,"k":[23.15,93.055]},"t":1,"nm":"gr1","hd":false},{"ty":"tr","p":{"a":0,"k":[284.189,355.943]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 4","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[284.189,355.943]},"a":{"a":0,"k":[284.189,355.943]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Layer 18","parent":10,"sr":1,"ks":{"p":{"a":0,"k":[8.495,-103.329,0]},"a":{"a":0,"k":[-118.75,-113.365,0]},"s":{"a":0,"k":[100.066,100.066,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-197.651,-119.273],[-98.468,-96.704]],"c":false}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-130.739,-76.795],[-86.967,-66.859]],"c":false}},"nm":"Path 2","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-85.976,-93.598],[-69.013,-89.703]],"c":false}},"nm":"Path 4","hd":false},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-197.553,-153.061],[-169.918,-146.519]],"c":false}},"nm":"Path 5","hd":false},{"ind":4,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-158.063,-144.19],[-142.115,-140.339]],"c":false}},"nm":"Path 6","hd":false},{"ind":5,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198.365,-92.046],[-142.739,-79.388]],"c":false}},"nm":"Path 3","hd":false},{"ty":"st","c":{"a":0,"k":[0.227450980392,0.227450980392,0.227450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"tm","s":{"a":0,"k":0},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":12,"s":[0]},{"t":32,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false}],"ip":0,"op":64,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Layer 17","parent":10,"sr":1,"ks":{"p":{"a":0,"k":[-7.676,-53.727,0]},"a":{"a":0,"k":[-134.851,-63.744,0]},"s":{"a":0,"k":[100.554,100.554,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-199.035,-64.993],[-164.637,-57.166]],"c":false}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-152.965,-54.551],[-99.241,-42.008]],"c":false}},"nm":"Path 2","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-70.097,-35.502],[-87.166,-39.046]],"c":false}},"nm":"Path 3","hd":false},{"ty":"st","c":{"a":0,"k":[0.227450980392,0.227450980392,0.227450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"tm","s":{"a":0,"k":0},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14,"s":[0]},{"t":33,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false}],"ip":0,"op":64,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Layer 16","parent":10,"sr":1,"ks":{"p":{"a":0,"k":[59.195,1.838,0]},"a":{"a":0,"k":[-69.217,-2.818,0]},"s":{"a":0,"k":[101.431,101.431,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-149.992,-2.801],[-130.224,1.697]],"c":false}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-118.266,4.406],[-61.984,17.29]],"c":false}},"nm":"Path 3","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-196.825,-13.458],[-163.525,-5.88]],"c":false}},"nm":"Path 2","hd":false},{"ty":"st","c":{"a":0,"k":[0.227450980392,0.227450980392,0.227450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"tm","s":{"a":0,"k":0},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":16,"s":[0]},{"t":34,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false}],"ip":0,"op":64,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"Layer 15","parent":10,"sr":1,"ks":{"p":{"a":0,"k":[9.798,35.695,0]},"a":{"a":0,"k":[-118.75,31.037,0]},"s":{"a":0,"k":[102.593,102.593,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-116.556,31.536],[-85.45,38.614]],"c":false}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-198.61,12.808],[-128.548,28.807]],"c":false}},"nm":"Path 2","hd":false},{"ty":"st","c":{"a":0,"k":[0.227450980392,0.227450980392,0.227450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"tm","s":{"a":0,"k":0},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":18,"s":[0]},{"t":36,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false}],"ip":0,"op":64,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Layer 14","parent":10,"sr":1,"ks":{"p":{"a":0,"k":[58.348,103.598,0]},"a":{"a":0,"k":[-69.96,99.113,0]},"s":{"a":0,"k":[103.936,103.936,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-193.145,44.222],[-66.91,72.947]],"c":false}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-136.87,85.82],[-67.558,101.592]],"c":false}},"nm":"Path 2","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-193.793,72.866],[-160.626,80.414]],"c":false}},"nm":"Path 3","hd":false},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-194.087,97.608],[-130.97,111.971]],"c":false}},"nm":"Path 4","hd":false},{"ty":"st","c":{"a":0,"k":[0.227450980392,0.227450980392,0.227450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"tm","s":{"a":0,"k":0},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":20,"s":[0]},{"t":38,"s":[100]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false}],"ip":0,"op":64,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Layer 46","parent":10,"sr":1,"ks":{},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-90.992,120.841],[-91.798,-159.461]],"c":false}]},{"t":20,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-95.972,124.393],[-96.542,-165.204]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[20]},{"t":20,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":64,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Layer 45","parent":10,"sr":1,"ks":{},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":5,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[97.781,163.499],[-90.55,120.941]],"c":false}]},{"t":20,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[96.779,167.947],[-95.972,124.393]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.960784373564,0.956862804936,0.921568687289,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[20]},{"t":20,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":64,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Layer 4","sr":1,"ks":{"r":{"a":0,"k":77.3},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[255.079,83.533,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[276.879,203.433,0],"to":[0,0,0],"ti":[-2.741,-0.122,0]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":40,"s":[276.879,203.433,0],"to":[1.106,-127.953,0],"ti":[0,0,0]},{"t":68,"s":[258.357,304.412,0]}]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[119.9,119.9,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":30,"s":[109,109,100]},{"t":68,"s":[109,109,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[102.41,175.367],[-102.41,129.09],[-102.41,-175.367],[13.512,-149.175],[102.41,-73.516]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.741176486015,0.713725507259,0.611764729023,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[20]},{"t":20,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"gf","o":{"a":0,"k":100},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.879,0.867,0.787,0.5,0.928,0.922,0.87,1,0.976,0.976,0.953]}},"s":{"a":0,"k":[86.456,2.068]},"e":{"a":0,"k":[-81.267,-33.093]},"t":1,"nm":"gr4","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":64,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"Layer 60","sr":1,"ks":{"o":{"a":0,"k":40},"r":{"a":0,"k":77.3},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[255.079,83.533,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":20,"s":[270.339,199.073,0],"to":[0,0,0],"ti":[-2.741,-0.122,0]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":40,"s":[270.339,199.073,0],"to":[1.106,-127.953,0],"ti":[0,0,0]},{"t":68,"s":[251.817,300.052,0]}]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[119.9,119.9,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":30,"s":[109,109,100]},{"t":68,"s":[109,109,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":11,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[96.472,168.849],[-96.862,125.16],[-96.581,-172.104],[96.753,-128.416]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":20,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[90.925,172.779],[-102.41,129.09],[-102.129,-168.174],[91.205,-124.486]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":23,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[93.363,173.328],[-102.41,129.09],[-102.129,-168.174],[93.644,-123.936]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":1,"y":0},"t":40,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[91.412,172.888],[-102.41,129.09],[-102.387,-163.62],[91.435,-119.821]],"c":true}]},{"t":52,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[91.412,172.888],[-95.093,130.738],[-95.07,-161.971],[91.435,-119.821]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.741176486015,0.713725507259,0.611764729023,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[20]},{"t":20,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176470588,0.713725490196,0.611764705882,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":64,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Shape Layer 1","parent":13,"sr":1,"ks":{"r":{"a":0,"k":-77.3},"p":{"a":0,"k":[178.713,-41.935,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[-6.561,1.072],[-0.037,-8.4],[6.561,-1.072],[0.037,8.4]],"o":[[6.561,-1.072],[0.037,8.4],[-6.561,1.072],[-0.037,-8.4]],"v":[[2.364,-10.864],[14.311,2.403],[2.498,19.554],[-9.448,6.287]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.929411764706,0.941176470588,0.972549019608,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[88.456,-102.634]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Ellipse 1","bm":0,"hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"Layer 34","parent":17,"sr":1,"ks":{"p":{"a":0,"k":[-88.559,61.757,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[6.969,-6.098],[0,0],[0,0],[7.933,13.239],[6.573,-4.904],[0,0],[4.521,8.17]],"o":[[-12.223,10.694],[0,0],[0,0],[-4.454,-7.433],[-4.236,3.16],[0,0],[-6.581,-11.893]],"v":[[54.556,-138.999],[18.71,-106],[134.767,-80.26],[111.622,-116.743],[97.519,-119.838],[86.398,-111.393],[73.105,-136.05]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.41568627451,0.556862745098,0.713725490196,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Shape 2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[16.506,-194.983],[18.463,-105.872],[132.113,-80.26],[131.131,-169.151]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.643137254902,0.752941176471,0.870588235294,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Shape 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[75.989,-128.127]},"a":{"a":0,"k":[75.989,-128.127]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[131.57,-52.059],[100.455,-59.14]],"c":false}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[43.898,-45.114],[19.622,-50.704]],"c":false}},"nm":"Path 4","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[77.406,-64.385],[19.826,-77.474]],"c":false}},"nm":"Path 2","hd":false},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[129.151,-25.782],[76.189,-37.834],[62.774,-40.996]],"c":false}},"nm":"Path 3","hd":false},{"ty":"st","c":{"a":0,"k":[0.229365808824,0.229365808824,0.229365808824,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":20,"op":75,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"Layer 31","parent":17,"sr":1,"ks":{"p":{"a":0,"k":[-86.594,3.002,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[129.842,67.39],[103.128,61.025]],"c":false}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[91.806,58.773],[19.471,41.638]],"c":false}},"nm":"Path 6","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[149.073,99.904],[127.811,95.066]],"c":false}},"nm":"Path 2","hd":false},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[56.432,78.863],[19.051,70.501]],"c":false}},"nm":"Path 5","hd":false},{"ind":4,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[106.797,90.284],[67.485,81.303]],"c":false}},"nm":"Path 3","hd":false},{"ind":5,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[57.925,111.513],[19.084,102.608]],"c":false}},"nm":"Path 4","hd":false},{"ty":"st","c":{"a":0,"k":[0.227450980392,0.227450980392,0.227450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":20,"op":75,"st":0,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"Layer 51","parent":17,"sr":1,"ks":{},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-90.992,120.841],[-91.798,-159.461]],"c":false}]},{"t":23,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-95.972,124.393],[-96.542,-165.204]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":3,"s":[20]},{"t":23,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":75,"st":3,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"Layer 50","parent":17,"sr":1,"ks":{},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[97.781,163.499],[-90.55,120.941]],"c":false}]},{"t":23,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[96.779,167.947],[-95.972,124.393]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.921568687289,0.937031525257,0.960784373564,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":3,"s":[20]},{"t":23,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":75,"st":3,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"Layer 49","sr":1,"ks":{"r":{"a":0,"k":77.3},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":3,"s":[247.89,69.389,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":23,"s":[258.802,189.298,0],"to":[0,0,0],"ti":[-2.741,-0.122,0]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":51,"s":[258.802,189.298,0],"to":[-0.394,-91.453,0],"ti":[0,0,0]},{"t":79,"s":[259.851,305.501,0]}]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":3,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":23,"s":[119.9,119.9,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":33,"s":[109,109,100]},{"t":71,"s":[109,109,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[102.41,175.367],[-102.41,129.09],[-102.41,-175.367],[102.41,-129.09]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.6,0.686274509804,0.800000059838,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":3,"s":[20]},{"t":23,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"gf","o":{"a":0,"k":100},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0.001,0.788,0.824,0.878,0.5,0.871,0.893,0.927,1,0.953,0.963,0.976]}},"s":{"a":0,"k":[86.456,2.068]},"e":{"a":0,"k":[-81.267,-33.093]},"t":1,"nm":"gr7","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":75,"st":3,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"Layer 61","sr":1,"ks":{"o":{"a":0,"k":40},"r":{"a":0,"k":77.3},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":3,"s":[242.44,65.574,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":23,"s":[253.352,185.483,0],"to":[0,0,0],"ti":[-2.741,-0.122,0]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":51,"s":[253.352,185.483,0],"to":[1.606,-93.453,0],"ti":[0,0,0]},{"t":79,"s":[254.401,301.686,0]}]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":3,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":23,"s":[119.9,119.9,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":33,"s":[109,109,100]},{"t":71,"s":[109,109,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":9,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[95.059,162.515],[-96.457,119.236],[-96.295,-173.535],[95.221,-130.256]],"c":true}]},{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":16,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[76.302,158.288],[-96.457,119.236],[-96.295,-173.535],[76.464,-134.483]],"c":true}]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.333,"y":0},"t":23,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[89.107,172.369],[-102.41,129.09],[-102.247,-163.681],[89.269,-120.402]],"c":true}]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":33,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[91.546,172.919],[-102.41,129.09],[-103.051,-166.938],[90.904,-123.109]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":1,"y":0},"t":51,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[91.546,172.919],[-102.41,129.09],[-103.051,-166.938],[90.904,-123.109]],"c":true}]},{"t":64,"s":[{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[91.546,172.918],[-95.093,130.738],[-95.734,-165.289],[90.904,-123.109]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.741176486015,0.713725507259,0.611764729023,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":3,"s":[20]},{"t":23,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.741176470588,0.713725490196,0.611764705882,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":75,"st":3,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"Layer 67","parent":28,"sr":1,"ks":{"r":{"a":0,"k":180},"p":{"a":0,"k":[4.547,-123.569,0]},"a":{"a":0,"k":[4.375,-163.721,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[37.675,-156.144],[17.388,-160.76]],"c":false}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-28.925,-171.299],[7.395,-163.034]],"c":false}},"nm":"Path 2","hd":false},{"ty":"st","c":{"a":0,"k":[0.227450980392,0.227450980392,0.227450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[19.511,4.397]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":20,"op":80,"st":0,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"Layer 66","parent":28,"sr":1,"ks":{"r":{"a":0,"k":180},"p":{"a":0,"k":[38.023,-74.437,0]},"a":{"a":0,"k":[83.735,-60.578,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[148.224,-45.978],[112.308,-54.151]],"c":false}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[85.702,-60.206],[19.246,-75.179]],"c":false}},"nm":"Path 2","hd":false},{"ty":"st","c":{"a":0,"k":[0.227450980392,0.227450980392,0.227450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[19.511,4.397]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":20,"op":80,"st":0,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"Layer 68","parent":28,"sr":1,"ks":{"p":{"a":0,"k":[-94.853,6.949,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"st","c":{"a":0,"k":[0.227450980392,0.227450980392,0.227450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[19.511,4.397]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":20,"op":80,"st":0,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":"Layer 69","parent":28,"sr":1,"ks":{"p":{"a":0,"k":[-89.975,8.049,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[11.506,-112.142],[-1.453,-100.806],[-8.125,-109.485]],"c":false}},"nm":"Path 5","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[9.919,1.797],[-3.041,13.132],[-9.713,4.454]],"c":false}},"nm":"Path 6","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[10.119,-156.02],[-2.841,-144.685],[-9.512,-153.363]],"c":false}},"nm":"Path 7","hd":false},{"ty":"st","c":{"a":0,"k":[0.831862745098,0.533829573089,0.533829573089,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[19.511,4.397]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":20,"op":80,"st":0,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":"Layer 65","parent":28,"sr":1,"ks":{"p":{"a":0,"k":[-85.098,9.148,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[131.57,-52.059],[100.455,-59.14]],"c":false}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[43.898,-45.114],[19.622,-50.704]],"c":false}},"nm":"Path 4","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[77.406,-64.385],[19.826,-77.474]],"c":false}},"nm":"Path 2","hd":false},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[129.151,-25.782],[76.189,-37.834],[62.774,-40.996]],"c":false}},"nm":"Path 3","hd":false},{"ty":"st","c":{"a":0,"k":[0.227450980392,0.227450980392,0.227450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[19.511,4.397]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":20,"op":80,"st":0,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":"Layer 63","parent":28,"sr":1,"ks":{"p":{"a":0,"k":[8.961,32.654,0]},"a":{"a":0,"k":[92.361,28.762,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[165.159,45.294],[124.787,36.046]],"c":false}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[110.532,32.802],[19.563,12.23]],"c":false}},"nm":"Path 2","hd":false},{"ty":"st","c":{"a":0,"k":[0.227450980392,0.227450980392,0.227450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[19.511,4.397]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":20,"op":80,"st":0,"bm":0},{"ddd":0,"ind":25,"ty":4,"nm":"Layer 62","parent":28,"sr":1,"ks":{"p":{"a":0,"k":[-83.401,3.892,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[129.842,67.39],[103.128,61.025]],"c":false}},"nm":"Path 1","hd":false},{"ind":1,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[91.806,58.773],[19.471,41.638]],"c":false}},"nm":"Path 6","hd":false},{"ind":2,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[149.073,99.904],[127.811,95.066]],"c":false}},"nm":"Path 2","hd":false},{"ind":3,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[56.432,78.863],[19.051,70.501]],"c":false}},"nm":"Path 5","hd":false},{"ind":4,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[106.797,90.284],[67.485,81.303]],"c":false}},"nm":"Path 3","hd":false},{"ind":5,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[57.925,111.513],[19.084,102.608]],"c":false}},"nm":"Path 4","hd":false},{"ty":"st","c":{"a":0,"k":[0.227450980392,0.227450980392,0.227450980392,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[19.511,4.397]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":20,"op":80,"st":0,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":"Layer 54","parent":28,"sr":1,"ks":{},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":11,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-90.992,120.841],[-91.798,-159.461]],"c":false}]},{"t":26,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-95.972,124.393],[-96.542,-165.204]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":6,"s":[20]},{"t":26,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":80,"st":6,"bm":0},{"ddd":0,"ind":27,"ty":4,"nm":"Layer 53","parent":28,"sr":1,"ks":{},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.167,"y":0.167},"t":11,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[97.781,163.499],[-90.55,120.941]],"c":false}]},{"t":26,"s":[{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[96.779,167.947],[-95.972,124.393]],"c":false}]}]},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.960784373564,0.921568687289,0.921568687289,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":6,"s":[20]},{"t":26,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":80,"st":6,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":"Layer 52","sr":1,"ks":{"r":{"a":0,"k":77.3},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.333,"y":0},"t":6,"s":[230.102,54.148,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":26,"s":[241.052,173.86,0],"to":[0,0,0],"ti":[-2.741,-0.122,0]},{"i":{"x":0.667,"y":1},"o":{"x":1,"y":0},"t":56,"s":[241.052,174.385,0],"to":[-2.439,-87.778,0],"ti":[0,0,0]},{"t":84,"s":[260.396,305.501,0]}]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":6,"s":[0,0,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":26,"s":[119.9,119.9,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[1,1,1],"y":[0,0,0]},"t":36,"s":[109,109,100]},{"t":74,"s":[109,109,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[102.41,175.367],[-102.41,129.09],[-102.41,-175.367],[102.41,-129.09]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.859941789216,0.652359667011,0.652359667011,1]},"o":{"a":0,"k":100},"w":{"a":1,"k":[{"i":{"x":[0],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":6,"s":[20]},{"t":26,"s":[6]}]},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"gf","o":{"a":0,"k":100},"r":1,"bm":0,"g":{"p":3,"k":{"a":0,"k":[0,0.851,0.772,0.772,0.354,0.914,0.863,0.863,1,0.976,0.953,0.953]}},"s":{"a":0,"k":[86.456,2.068]},"e":{"a":0,"k":[-81.267,-33.093]},"t":1,"nm":"gr10","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":80,"st":6,"bm":0},{"ddd":0,"ind":29,"ty":0,"nm":"Pre-comp 2","parent":39,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":40},"p":{"a":0,"k":[22.553,-31.924,0]},"a":{"a":0,"k":[256,256,0]}},"ao":0,"w":512,"h":512,"ip":0,"op":80,"st":-5,"bm":0},{"ddd":0,"ind":30,"ty":4,"nm":"p_str","parent":39,"sr":1,"ks":{},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-0.258,6.055],[0,0],[6.31,-1.446],[0,0],[-0.073,5.112],[0,0],[6.333,-1.453],[0,0],[-0.058,-6.533],[0,0],[-7.065,-16.479],[-5.052,1.289]],"o":[[6.051,-1.378],[0,0],[0.28,-6.551],[0,0],[-4.812,1.103],[0,0],[0.093,-6.574],[0,0],[-6.323,1.45],[0,0],[0.043,4.885],[7.129,16.628],[0,0]],"v":[[144.859,112.603],[156.283,99.152],[166.7,-176.707],[155.78,-185.961],[-9.063,-148.178],[-18.293,-155.977],[-18.097,-169.807],[-29.394,-179.09],[-155.367,-149.84],[-166.711,-135.375],[-164.411,125.639],[-158.249,172.856],[-139.624,185.173]],"c":true}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":31,"ty":4,"nm":"p_bl4","parent":39,"sr":1,"ks":{"p":{"a":0,"k":[-94.374,-154.336,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-3.595,0.824],[0,0]],"o":[[-0.033,-3.738],[0,0],[0,0]],"v":[[-66.337,18.909],[-59.652,10.344],[66.338,-18.909]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.929411768913,0.933333337307,0.945098042488,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":32,"ty":4,"nm":"p_sh2","parent":39,"sr":1,"ks":{"o":{"a":0,"k":33},"p":{"a":0,"k":[-140.348,177.818,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-4.572,7.663]],"o":[[10.337,14.81],[0,0]],"v":[[-14.261,0.419],[14.261,-6.841]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":33,"ty":4,"nm":"p_shad 2","parent":39,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.163],"y":[1]},"o":{"x":[0.951],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.301],"y":[1]},"o":{"x":[0.951],"y":[0]},"t":40,"s":[0]},{"i":{"x":[0.163],"y":[1]},"o":{"x":[0.476],"y":[0]},"t":80,"s":[100]},{"t":120,"s":[0]}]},"p":{"a":1,"k":[{"i":{"x":0.163,"y":0.163},"o":{"x":0.333,"y":0.333},"t":0,"s":[3.569,12.387,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.301,"y":1},"o":{"x":0.951,"y":0},"t":40,"s":[3.569,12.387,0],"to":[-0.25,-0.583,0],"ti":[0,0,0]},{"i":{"x":0.223,"y":1},"o":{"x":0.476,"y":0},"t":80,"s":[2.069,8.887,0],"to":[0,0,0],"ti":[-0.25,-0.583,0]},{"t":120,"s":[3.569,12.387,0]}]},"a":{"a":0,"k":[245.016,323.16,0]},"s":{"a":1,"k":[{"i":{"x":[0.163,0.163,0.163],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,97,100]},{"i":{"x":[0.301,0.301,0.301],"y":[1,1,1]},"o":{"x":[0.951,0.951,0.951],"y":[0,0,0]},"t":40,"s":[100,97,100]},{"i":{"x":[0.223,0.223,0.223],"y":[1,1,1]},"o":{"x":[0.476,0.476,0.476],"y":[0,0,0]},"t":80,"s":[100,100,100]},{"t":120,"s":[100,97,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.262,-1.435],[0,0],[1.345,-0.897],[-0.044,-4.985],[0,0],[-9.197,-14.632],[-1.207,0.307]],"o":[[0,0],[0.278,-6.501],[0,0],[-1.557,0.357],[-4.148,2.766],[0,0],[0.045,5.059],[1.147,0.153],[0,0]],"v":[[135.781,91.782],[145.245,-150.133],[134.41,-159.315],[-134.447,-97.21],[-138.828,-95.291],[-145.256,-82.737],[-143.551,110.732],[-135.398,159.515],[-131.847,159.315]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[249.77,335.347]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":80},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.351,-1.456],[0,0],[1.364,-0.909],[-0.045,-5.056],[0,0],[-9.328,-14.84],[-1.224,0.311]],"o":[[0,0],[0.282,-6.594],[0,0],[-1.579,0.362],[-4.207,2.805],[0,0],[0.045,5.131],[1.163,0.155],[0,0]],"v":[[137.715,93.09],[147.314,-152.271],[136.324,-161.584],[-136.362,-98.595],[-140.805,-96.649],[-147.325,-83.915],[-145.596,112.309],[-137.327,161.787],[-133.725,161.584]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[249.175,333.823]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":70},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.44,-1.476],[0,0],[1.383,-0.922],[-0.045,-5.127],[0,0],[-9.459,-15.049],[-1.241,0.316]],"o":[[0,0],[0.285,-6.686],[0,0],[-1.601,0.367],[-4.266,2.845],[0,0],[0.046,5.203],[1.179,0.158],[0,0]],"v":[[139.649,94.397],[149.383,-154.41],[138.239,-163.853],[-138.277,-99.979],[-142.783,-98.006],[-149.394,-85.094],[-147.64,113.886],[-139.255,164.059],[-135.603,163.853]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[248.581,332.3]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":60},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 3","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.529,-1.497],[0,0],[1.402,-0.935],[-0.046,-5.198],[0,0],[-9.59,-15.257],[-1.259,0.32]],"o":[[0,0],[0.289,-6.779],[0,0],[-1.623,0.372],[-4.325,2.884],[0,0],[0.046,5.275],[1.196,0.16],[0,0]],"v":[[141.583,95.704],[151.452,-156.548],[140.153,-166.122],[-140.192,-101.364],[-144.76,-99.363],[-151.463,-86.272],[-149.685,115.463],[-141.184,166.331],[-137.481,166.122]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[247.987,330.777]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":50},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 4","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.618,-1.517],[0,0],[1.421,-0.948],[-0.046,-5.269],[0,0],[-9.721,-15.465],[-1.276,0.324]],"o":[[0,0],[0.293,-6.871],[0,0],[-1.646,0.378],[-4.384,2.924],[0,0],[0.047,5.348],[1.212,0.162],[0,0]],"v":[[143.517,97.012],[153.521,-158.686],[142.068,-168.391],[-142.107,-102.748],[-146.738,-100.72],[-153.532,-87.451],[-151.73,117.041],[-143.113,168.604],[-139.359,168.392]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[247.393,329.253]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":40},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 5","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.708,-1.537],[0,0],[1.44,-0.961],[-0.047,-5.34],[0,0],[-9.852,-15.674],[-1.293,0.329]],"o":[[0,0],[0.297,-6.964],[0,0],[-1.668,0.383],[-4.443,2.963],[0,0],[0.048,5.42],[1.228,0.164],[0,0]],"v":[[145.451,98.319],[155.589,-160.825],[143.982,-170.661],[-144.022,-104.133],[-148.715,-102.078],[-155.601,-88.629],[-153.774,118.618],[-145.041,170.876],[-141.237,170.661]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[246.799,327.73]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":30},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 6","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.797,-1.558],[0,0],[1.46,-0.973],[-0.048,-5.411],[0,0],[-9.983,-15.882],[-1.31,0.333]],"o":[[0,0],[0.301,-7.056],[0,0],[-1.69,0.388],[-4.502,3.002],[0,0],[0.048,5.492],[1.245,0.166],[0,0]],"v":[[147.385,99.626],[157.658,-162.963],[145.897,-172.93],[-145.937,-105.518],[-150.693,-103.435],[-157.67,-89.807],[-155.819,120.195],[-146.97,173.148],[-143.115,172.93]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[246.204,326.207]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":20},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 7","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.886,-1.578],[0,0],[1.479,-0.986],[-0.048,-5.482],[0,0],[-10.114,-16.091],[-1.327,0.337]],"o":[[0,0],[0.305,-7.149],[0,0],[-1.712,0.393],[-4.561,3.042],[0,0],[0.049,5.564],[1.261,0.168],[0,0]],"v":[[149.319,100.934],[159.727,-165.102],[147.811,-175.199],[-147.852,-106.902],[-152.67,-104.792],[-159.739,-90.986],[-157.864,121.772],[-148.898,175.42],[-144.993,175.199]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[245.61,324.684]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":10},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 8","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.975,-1.599],[0,0],[1.498,-0.999],[-0.049,-5.553],[0,0],[-10.245,-16.299],[-1.345,0.342]],"o":[[0,0],[0.309,-7.242],[0,0],[-1.734,0.398],[-4.62,3.081],[0,0],[0.05,5.636],[1.277,0.171],[0,0]],"v":[[151.253,102.241],[161.796,-167.24],[149.726,-177.468],[-149.767,-108.287],[-154.647,-106.15],[-161.808,-92.164],[-159.909,123.349],[-150.827,177.692],[-146.871,177.468]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[245.016,323.16]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":0},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 9","bm":0,"hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":34,"ty":4,"nm":"p_shad","parent":39,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.163],"y":[1]},"o":{"x":[0.951],"y":[0]},"t":0,"s":[90]},{"i":{"x":[0.301],"y":[1]},"o":{"x":[0.951],"y":[0]},"t":40,"s":[85]},{"i":{"x":[0.163],"y":[1]},"o":{"x":[0.476],"y":[0]},"t":80,"s":[100]},{"t":120,"s":[90]}]},"p":{"a":1,"k":[{"i":{"x":0.163,"y":0.163},"o":{"x":0.333,"y":0.333},"t":0,"s":[3.569,12.387,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.301,"y":1},"o":{"x":0.951,"y":0},"t":40,"s":[3.569,12.387,0],"to":[-0.25,-0.583,0],"ti":[0,0,0]},{"i":{"x":0.223,"y":1},"o":{"x":0.476,"y":0},"t":80,"s":[2.069,8.887,0],"to":[0,0,0],"ti":[-0.25,-0.583,0]},{"t":120,"s":[3.569,12.387,0]}]},"a":{"a":0,"k":[245.016,323.16,0]},"s":{"a":1,"k":[{"i":{"x":[0.163,0.163,0.163],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,97,100]},{"i":{"x":[0.301,0.301,0.301],"y":[1,1,1]},"o":{"x":[0.951,0.951,0.951],"y":[0,0,0]},"t":40,"s":[100,97,100]},{"i":{"x":[0.223,0.223,0.223],"y":[1,1,1]},"o":{"x":[0.476,0.476,0.476],"y":[0,0,0]},"t":80,"s":[100,100,100]},{"t":120,"s":[100,97,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.262,-1.435],[0,0],[1.345,-0.897],[-0.044,-4.985],[0,0],[-9.197,-14.632],[-1.207,0.307]],"o":[[0,0],[0.278,-6.501],[0,0],[-1.557,0.357],[-4.148,2.766],[0,0],[0.045,5.059],[1.147,0.153],[0,0]],"v":[[135.781,91.782],[145.245,-150.133],[134.41,-159.315],[-134.447,-97.21],[-138.828,-95.291],[-145.256,-82.737],[-143.551,110.732],[-135.398,159.515],[-131.847,159.315]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[249.77,335.347]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":80},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.351,-1.456],[0,0],[1.364,-0.909],[-0.045,-5.056],[0,0],[-9.328,-14.84],[-1.224,0.311]],"o":[[0,0],[0.282,-6.594],[0,0],[-1.579,0.362],[-4.207,2.805],[0,0],[0.045,5.131],[1.163,0.155],[0,0]],"v":[[137.715,93.09],[147.314,-152.271],[136.324,-161.584],[-136.362,-98.595],[-140.805,-96.649],[-147.325,-83.915],[-145.596,112.309],[-137.327,161.787],[-133.725,161.584]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[249.175,333.823]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":70},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.44,-1.476],[0,0],[1.383,-0.922],[-0.045,-5.127],[0,0],[-9.459,-15.049],[-1.241,0.316]],"o":[[0,0],[0.285,-6.686],[0,0],[-1.601,0.367],[-4.266,2.845],[0,0],[0.046,5.203],[1.179,0.158],[0,0]],"v":[[139.649,94.397],[149.383,-154.41],[138.239,-163.853],[-138.277,-99.979],[-142.783,-98.006],[-149.394,-85.094],[-147.64,113.886],[-139.255,164.059],[-135.603,163.853]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[248.581,332.3]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":60},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 3","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.529,-1.497],[0,0],[1.402,-0.935],[-0.046,-5.198],[0,0],[-9.59,-15.257],[-1.259,0.32]],"o":[[0,0],[0.289,-6.779],[0,0],[-1.623,0.372],[-4.325,2.884],[0,0],[0.046,5.275],[1.196,0.16],[0,0]],"v":[[141.583,95.704],[151.452,-156.548],[140.153,-166.122],[-140.192,-101.364],[-144.76,-99.363],[-151.463,-86.272],[-149.685,115.463],[-141.184,166.331],[-137.481,166.122]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[247.987,330.777]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":50},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 4","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.618,-1.517],[0,0],[1.421,-0.948],[-0.046,-5.269],[0,0],[-9.721,-15.465],[-1.276,0.324]],"o":[[0,0],[0.293,-6.871],[0,0],[-1.646,0.378],[-4.384,2.924],[0,0],[0.047,5.348],[1.212,0.162],[0,0]],"v":[[143.517,97.012],[153.521,-158.686],[142.068,-168.391],[-142.107,-102.748],[-146.738,-100.72],[-153.532,-87.451],[-151.73,117.041],[-143.113,168.604],[-139.359,168.392]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[247.393,329.253]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":40},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 5","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.708,-1.537],[0,0],[1.44,-0.961],[-0.047,-5.34],[0,0],[-9.852,-15.674],[-1.293,0.329]],"o":[[0,0],[0.297,-6.964],[0,0],[-1.668,0.383],[-4.443,2.963],[0,0],[0.048,5.42],[1.228,0.164],[0,0]],"v":[[145.451,98.319],[155.589,-160.825],[143.982,-170.661],[-144.022,-104.133],[-148.715,-102.078],[-155.601,-88.629],[-153.774,118.618],[-145.041,170.876],[-141.237,170.661]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[246.799,327.73]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":30},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 6","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.797,-1.558],[0,0],[1.46,-0.973],[-0.048,-5.411],[0,0],[-9.983,-15.882],[-1.31,0.333]],"o":[[0,0],[0.301,-7.056],[0,0],[-1.69,0.388],[-4.502,3.002],[0,0],[0.048,5.492],[1.245,0.166],[0,0]],"v":[[147.385,99.626],[157.658,-162.963],[145.897,-172.93],[-145.937,-105.518],[-150.693,-103.435],[-157.67,-89.807],[-155.819,120.195],[-146.97,173.148],[-143.115,172.93]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[246.204,326.207]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":20},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 7","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.886,-1.578],[0,0],[1.479,-0.986],[-0.048,-5.482],[0,0],[-10.114,-16.091],[-1.327,0.337]],"o":[[0,0],[0.305,-7.149],[0,0],[-1.712,0.393],[-4.561,3.042],[0,0],[0.049,5.564],[1.261,0.168],[0,0]],"v":[[149.319,100.934],[159.727,-165.102],[147.811,-175.199],[-147.852,-106.902],[-152.67,-104.792],[-159.739,-90.986],[-157.864,121.772],[-148.898,175.42],[-144.993,175.199]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[245.61,324.684]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":10},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 8","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0],[6.975,-1.599],[0,0],[1.498,-0.999],[-0.049,-5.553],[0,0],[-10.245,-16.299],[-1.345,0.342]],"o":[[0,0],[0.309,-7.242],[0,0],[-1.734,0.398],[-4.62,3.081],[0,0],[0.05,5.636],[1.277,0.171],[0,0]],"v":[[151.253,102.241],[161.796,-167.24],[149.726,-177.468],[-149.767,-108.287],[-154.647,-106.15],[-161.808,-92.164],[-159.909,123.349],[-150.827,177.692],[-146.871,177.468]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.396078437567,0.458823531866,0.509803950787,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[245.016,323.16]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":0},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 9","bm":0,"hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":35,"ty":4,"nm":"p_bl3","parent":39,"sr":1,"ks":{"p":{"a":0,"k":[74.699,-161.22,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-82.422,18.892],[82.422,-18.892]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.929411768913,0.933333337307,0.945098042488,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":36,"ty":4,"nm":"p_bl2","parent":39,"sr":1,"ks":{"o":{"a":0,"k":40},"p":{"a":0,"k":[-158.75,9.661,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0.076,8.644],[0,0]],"o":[[-1.278,-9.446],[0,0],[0,0]],"v":[[1.961,145.088],[0.34,115.925],[-1.961,-145.088]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.929411768913,0.933333337307,0.945098042488,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":6},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":37,"ty":4,"nm":"p_bl1","parent":39,"sr":1,"ks":{"o":{"a":0,"k":25},"p":{"a":0,"k":[-10.378,135.807,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[145.485,-37.034],[-145.485,37.034]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[0.929411768913,0.933333337307,0.945098042488,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":10},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":38,"ty":3,"nm":"NULL CONTROL","sr":1,"ks":{"o":{"a":0,"k":0},"p":{"a":1,"k":[{"i":{"x":0.163,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[240.867,414.113,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.301,"y":1},"o":{"x":0.951,"y":0},"t":54,"s":[240.867,403.213,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.223,"y":1},"o":{"x":0.476,"y":0},"t":80,"s":[240.867,422.833,0],"to":[0,0,0],"ti":[0,0,0]},{"t":119,"s":[240.867,414.113,0]}]},"a":{"a":0,"k":[60,60,0]},"s":{"a":0,"k":[109,109,100]}},"ao":0,"ip":0,"op":120,"st":0,"bm":0},{"ddd":0,"ind":39,"ty":4,"nm":"papk1","parent":38,"sr":1,"ks":{"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":64,"s":[60,60,0],"to":[0.303,12.479,0],"ti":[0.428,9.354,0]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":71.084,"s":[60,60,0],"to":[0.447,12.021,0],"ti":[0.322,8.271,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77.5,"s":[60,60,0],"to":[0.303,12.479,0],"ti":[0.178,3.979,0]},{"t":83,"s":[60,60,0]}]},"a":{"a":0,"k":[0,144.248,0]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[-0.258,6.055],[0,0],[6.31,-1.446],[0,0],[-0.073,5.112],[0,0],[6.333,-1.453],[0,0],[-0.058,-6.533],[0,0],[-7.065,-16.479],[-5.052,1.289]],"o":[[6.051,-1.378],[0,0],[0.28,-6.551],[0,0],[-4.812,1.103],[0,0],[0.093,-6.574],[0,0],[-6.323,1.45],[0,0],[0.043,4.885],[7.129,16.628],[0,0]],"v":[[144.859,112.603],[156.283,99.152],[166.7,-176.707],[155.78,-185.961],[-9.063,-148.178],[-18.293,-155.977],[-18.097,-169.807],[-29.394,-179.09],[-155.367,-149.84],[-166.711,-135.375],[-164.411,125.639],[-158.249,172.856],[-139.624,185.173]],"c":true}},"nm":"Path 1","hd":false},{"ty":"fl","c":{"a":0,"k":[0.705882370472,0.721568644047,0.741176486015,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":120,"st":0,"bm":0}]} \ No newline at end of file diff --git a/Rosetta/RosettaApp.swift b/Rosetta/RosettaApp.swift index 430ad3c..00ce0d3 100644 --- a/Rosetta/RosettaApp.swift +++ b/Rosetta/RosettaApp.swift @@ -1,6 +1,7 @@ import FirebaseCore import FirebaseCrashlytics import FirebaseMessaging +import Intents import SwiftUI import UserNotifications @@ -9,6 +10,11 @@ import UserNotifications final class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate { + /// Pending chat route from notification tap — consumed by ChatListView on appear. + /// Handles both terminated app (notification posted before ChatListView exists) + /// and background app (fallback if .onReceive misses the synchronous post). + static var pendingChatRoute: ChatRoute? + func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil @@ -137,9 +143,40 @@ final class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCent // Always set sender_public_key in userInfo for notification tap navigation. content.userInfo = ["sender_public_key": senderKey, "sender_name": senderName] + // Communication Notification via INSendMessageIntent (CarPlay + Focus parity). + let handle = INPersonHandle(value: senderKey, type: .unknown) + let sender = INPerson( + personHandle: handle, + nameComponents: nil, + displayName: senderName.isEmpty ? "Rosetta" : senderName, + image: nil, + contactIdentifier: nil, + customIdentifier: senderKey + ) + let intent = INSendMessageIntent( + recipients: nil, + outgoingMessageType: .outgoingMessageText, + content: messageText, + speakableGroupName: nil, + conversationIdentifier: senderKey, + serviceName: "Rosetta", + sender: sender, + attachments: nil + ) + let interaction = INInteraction(intent: intent, response: nil) + interaction.direction = .incoming + interaction.donate(completion: nil) + + let finalContent: UNNotificationContent + if let updated = try? content.updating(from: intent) { + finalContent = updated + } else { + finalContent = content + } + let request = UNNotificationRequest( identifier: "msg_\(senderKey)_\(Int(now))", - content: content, + content: finalContent, trigger: nil ) UNUserNotificationCenter.current().add(request) { _ in @@ -212,6 +249,10 @@ final class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCent username: "", verified: 0 ) + // Store pending route BEFORE posting — handles terminated app case + // where ChatListView doesn't exist yet, and background app case + // where .onReceive might miss the synchronous post. + Self.pendingChatRoute = route NotificationCenter.default.post( name: .openChatFromNotification, object: route diff --git a/RosettaNotificationService/NotificationService.swift b/RosettaNotificationService/NotificationService.swift index a91e9e5..cd648ca 100644 --- a/RosettaNotificationService/NotificationService.swift +++ b/RosettaNotificationService/NotificationService.swift @@ -1,4 +1,5 @@ import UserNotifications +import Intents /// Notification Service Extension — runs as a separate process even when the main app /// is terminated. Intercepts push notifications with `mutable-content: 1` and: @@ -6,6 +7,7 @@ import UserNotifications /// 2. Increments the app icon badge from shared App Group storage /// 3. Normalizes sender_public_key in userInfo (Android parity: multi-key fallback) /// 4. Filters muted chats +/// 5. Creates Communication Notification via INSendMessageIntent (CarPlay + Focus parity) final class NotificationService: UNNotificationServiceExtension { private static let appGroupID = "group.com.rosetta.dev" @@ -76,7 +78,18 @@ final class NotificationService: UNNotificationServiceExtension { content.categoryIdentifier = "message" } - contentHandler(content) + // 6. Create Communication Notification via INSendMessageIntent. + // This makes the notification appear on CarPlay and work with Focus filters. + // Apple requires INSendMessageIntent for messaging notifications on CarPlay (iOS 15+). + let senderName = Self.firstNonBlank(content.userInfo, keys: Self.senderNameKeyNames) + ?? content.title + let finalContent = Self.makeCommunicationNotification( + content: content, + senderName: senderName, + senderKey: senderKey + ) + + contentHandler(finalContent) } override func serviceExtensionTimeWillExpire() { @@ -86,6 +99,56 @@ final class NotificationService: UNNotificationServiceExtension { } } + // MARK: - Communication Notification (CarPlay + Focus) + + /// Wraps the notification content with an INSendMessageIntent so iOS treats it + /// as a Communication Notification. This enables: + /// - Display on CarPlay + /// - Proper grouping in Focus modes + /// - Sender name/avatar in notification UI + private static func makeCommunicationNotification( + content: UNMutableNotificationContent, + senderName: String, + senderKey: String + ) -> UNNotificationContent { + let handle = INPersonHandle(value: senderKey, type: .unknown) + let sender = INPerson( + personHandle: handle, + nameComponents: nil, + displayName: senderName.isEmpty ? "Rosetta" : senderName, + image: nil, + contactIdentifier: nil, + customIdentifier: senderKey + ) + + let intent = INSendMessageIntent( + recipients: nil, + outgoingMessageType: .outgoingMessageText, + content: content.body, + speakableGroupName: nil, + conversationIdentifier: senderKey, + serviceName: "Rosetta", + sender: sender, + attachments: nil + ) + + // Donate the intent so Siri can learn communication patterns. + let interaction = INInteraction(intent: intent, response: nil) + interaction.direction = .incoming + interaction.donate(completion: nil) + + // Update the notification content with the intent. + // This returns a new content object that iOS recognizes as a Communication Notification. + do { + let updatedContent = try content.updating(from: intent) + return updatedContent + } catch { + // If updating fails, return original content — notification still works, + // just without CarPlay / Communication Notification features. + return content + } + } + // MARK: - Helpers /// Android parity: extract sender key from multiple possible key names.