NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Grepolis Anti-Spam Script 2023 // @namespace https://grepodata.com/ // @version 2.4 // @icon https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcTKIH5FdU3ZTYb4Dpe537qSKl1fAgdo6bPtPjr8FJYHWPD0ykkg // @icon64 https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcTKIH5FdU3ZTYb4Dpe537qSKl1fAgdo6bPtPjr8FJYHWPD0ykkg // @include /http[s]{0,1}://[a-z]{2}[0-9]{1,}\.grepolis\.com/* // @description Script that will turn alarm on only for people you want to! // @author Vít Dolínek // @copyright 2023, LG (https://openuserjs.org/users/shigatora) // @licence MIT // @match https://www.tampermonkey.net/ // @require https://raw.githubusercontent.com/kamranahmedse/jquery-toast-plugin/master/dist/jquery.toast.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/iconify/2.0.0/iconify.without-api.min.js // ==/UserScript== const w = unsafeWindow || window; w.LGAntiSpam = { alarm: { soundPlaying: false, alarmNode: null, alarmListener: function (val) {}, activateSound: function () { this.soundPlaying = true; this.alarmListener(true); }, deactivateSound: function () { this.soundPlaying = false; this.alarmListener(false); }, get sound() { return this.soundPlaying; }, set node(val) { this.alarmNode = val; }, get node() { return this.alarmNode; }, registerListener: function (listener) { this.alarmListener = listener; }, }, }; let alarmAudio = $("<audio />", { preload: "auto", id: "spam_alarm", autoplay: false, loop: true, volume: parseInt(localStorage.getItem("antiSpamAlarmVolume")) || 0 / 100, }); let alarmSource = $("<source />", { src: "https://www.zapsplat.com/wp-content/uploads/2015/sound-effects-84577/zapsplat_emergency_siren_beep_warning_nuclear_meltdown_84854.mp3", }); alarmAudio.append(alarmSource); LGAntiSpam.alarm.node = alarmAudio[0]; /** * Injects stylesheet to document head. * * @param {string} url */ function injectStylesheet(url) { $("head").append(url); } function buildGuideTab() { let wrapper = $("<div/>", { style: "display:flex;justify-content: center;padding: 0 25px 25px 25px;flex-direction: column;align-items: center;", }); wrapper.append( $( ` <div style="display: flex;flex-direction:row;justify-content:center;align-items:center;margin:15px;"> <span class="iconify" data-icon="mi:circle-help" data-height="3em" style="margin-right: 10px;" data-inline="false"></span> <h2>Guide</h2> </div> <h3>Mobile alarm</h3> <p>For mobile alarm you will need Catapush Messenger application. The application is available for both <a href="https://apps.apple.com/gb/app/catapush-messenger/id993475148" target="_blank">iOS</a> and <a href="https://play.google.com/store/apps/details?id=com.catapush.app.demo" target="_blank">Android</a>. Once you download and run the application, you will need to complete a few steps to set up your account. With your account set up, the only thing you will need is your number - it reflects your phone number with country code, but without + or 00. The format is {country code}{phone number}, eg. UK phone 44123456789. </p> ` ) ); return wrapper; } /** TODO: No longer exists, rewrite the function so it uses local storage */ // async function buildLogTab() { // const logs = await fetch( // `https://dolinek.dev/alarm/attack/${Game.world_id}/${Game.player_id}/log` // ) // .then((response) => response.json()) // .catch(() => []); // let wrapper = $("<div/>", { // style: "display:block;", // }); // wrapper.append( // $( // ` // <div style="display: flex;flex-direction:row;justify-content:center;align-items:center;margin:15px;"> // <span class="iconify" data-icon="carbon:catalog" data-height="3em" style="margin-right: 10px;" data-inline="false"></span> // <h2>Alarm log</h2> // </div> // <div class="lg_log_container"> // <div class="lg_log_container__head"> // <div class="sender">Attacker</div> // <div class="townTarget">Town</div> // <div class="time">Time</div> // </div> // <div class="logs"> // ${logs // .map( // (log) => ` // <div class="lg_log_container__row"> // <div class="sender">${log.attacker}</div> // <div class="townTarget">${log.city}</div> // <div class="time">${new Date(log.time).toLocaleString("en-GB", { // timeZone: Game.player_timezone, // })}</div> // </div> // ` // ) // .join("")} // </div> // </div> // ` // ) // ); // return wrapper; // } function buildHelpTab() { let wrapper = $("<div/>", { style: "display:block;", }); wrapper.append( $( ` <div style="display: flex;flex-direction:row;justify-content:center;align-items:center;margin:15px;"> <span class="iconify" data-icon="fa-solid:hands-helping" data-height="3em" style="margin-right: 10px;" data-inline="false"></span> <h2>Help</h2> </div> <p style="text-align:center;"> If you need any help, please contact me via e-mail: dolinekvit@gmail.com or Discord: LG#1211 </p> ` ) ); return wrapper; } function buildDonateTab() { let wrapper = $("<div/>", { style: "display:flex;justify-content: center;padding: 0 25px 25px 25px;flex-direction: column;align-items: center;", }); wrapper.append( $( ` <div style="display: flex;flex-direction:row;justify-content:center;align-items:center;margin:15px;"> <span class="iconify" data-icon="bx:bxs-donate-heart" data-height="3em" style="margin-right: 10px;" data-inline="false"></span> <h2>Donation</h2> </div> <form action="https://www.paypal.com/donate" method="post" target="_blank"> <input type="hidden" name="hosted_button_id" value="HMCV2G3ZPMQSA" /> <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" title="PayPal - The safer, easier way to pay online!" alt="Donate with PayPal button" /> <img alt="" border="0" src="https://www.paypal.com/en_CZ/i/scr/pixel.gif" width="1" height="1" /> </form> <p> If you want to show some love! All donations go directly to alarm development and improvement. Thank you! </p> ` ) ); return wrapper; } function buildMobileAlarmTab() { let wrapper = $("<div/>", { style: "display: block;", }); const ignoreButton = $("<div/>", { class: "button_new", id: "save_ignored_players", name: "Save alarm settings", value: "Save alarm settings", style: "margin: 0 auto;", }).button({ caption: "Save", }); wrapper.append( $( ` <div style="display: flex;flex-direction:row;justify-content:center;align-items:center;margin:15px;"> <span class="iconify" data-icon="fa:mobile" data-height="3em" data-inline="false" style="margin-right: 10px;"></span> <h2>Mobile alarm settings</h2> </div> <div class="lg_container"> <div class="lg_container__box"> <div class="lg_container__box_title"> <span class="iconify" data-icon="system-uicons:bell-disabled" data-height="2em" data-inline="false"></span> <span>Catapush user ID</span> </div> <input id="catapush_id" name="antiSpamCatapushId" value="${localStorage.getItem( "antiSpamCatapushId" )}"/> <small class="lg_container__box_subtitle"> ID format is reflecting your phone number </small> </div> </div> <div class="lg_alarm__save_button"> <div class="button_new"> ${ignoreButton[0].innerHTML} </div> </div> ` ) ); return wrapper; } function buildIgnorePlayersTab() { let wrapper = $("<div/>", { style: "display:block;", }); const ignoreButton = $("<div/>", { class: "button_new", id: "save_ignored_players", name: "Save alarm settings", value: "Save alarm settings", style: "margin: 0 auto;", }).button({ caption: "Save", }); wrapper.append( $( ` <div style="display: flex;flex-direction:row;justify-content:center;align-items:center;margin:15px;"> <span class="iconify" data-icon="ri:sword-fill" data-height="3em" data-inline="false" style="margin-right: 10px;"></span> <h2>Attack alarm settings</h2> </div> <div class="lg_container"> <div class="lg_container__box"> <div class="lg_container__box_title"> <span class="iconify" data-icon="system-uicons:bell-disabled" data-height="2em" data-inline="false"></span> <span>Ignored players</span> </div> <textarea id="ignored_players" name="antiSpamIgnoredPlayers">${localStorage.getItem( "antiSpamIgnoredPlayers" )}</textarea> <small class="lg_container__box_subtitle"> Divide each player name with ; </small> </div> <div class="lg_container__box"> <div class="lg_container__box_title"> <span class="iconify" data-icon="tabler:award" data-height="2em" data-inline="false"></span> <span>Minimum player points</span> </div> <input id="ignored_points" name="antiSpamIgnoredPoints" value="${ localStorage.getItem("antiSpamIgnoredPoints") || 0 }" /> <small class="lg_container__box_subtitle"> The alarm won't trigger if the attacker has less points than defined. 0 = not set </small> </div> <div class="lg_container__box"> <div class="lg_container__box_title"> <span class="iconify" data-icon="ic:outline-watch-later" data-height="2em" data-inline="false"></span> <span>Delay</span> </div> <input id="delay" name="antiSpamDelay" value="${ localStorage.getItem("antiSpamAlarmDelay") || 0 }" /> <small class="lg_container__box_subtitle"> Delay is in seconds. 0 = no alarm delay </small> </div> <div class="lg_container__box"> <div class="lg_container__box_title"> <span class="iconify" data-icon="la:soundcloud" data-height="2em" data-inline="false"></span> <span>Custom sound</span> </div> Coming soon... </div> <div class="lg_container__box"> <div class="lg_container__box_title"> <span class="iconify" data-icon="ri:volume-down-fill" data-height="2em" data-inline="false"></span> <span>Volume</span> </div> <div class="volume-wrapper" style="display: flex;width: 100%;align-items: center;justify-content: space-evenly;"> <div class="windowmgr_slider" style="width: 80%"> <div class="grepo_slider" id="alarm_script_volume_slider"> <div class="button_down left js-button-left"></div> <div class="bar js-slider js-slider-handle-container"></div> <div class="button_up right js-button-right"></div> </div> </div> <span id="lg_alarm__volume" class="lg_alarm__volume">${ LGAntiSpam.alarm.node.volume * 100 }%</span> </div> </div> <div class="lg_container__box"> <div class="lg_container__box_title"> <span class="iconify" data-icon="mdi:dev-to" data-height="2em" data-inline="false"></span> <span>Information</span> </div> <span>Version: 2.3</span><br /> <span>Release date: 14/3/2021</span> </div> </div> <div class="lg_alarm__save_button"> <div class="button_new"> ${ignoreButton[0].innerHTML} </div> </div> ` ) ); return wrapper; } (function () { const menu = setInterval(function () { if (document.querySelector("li.main_menu_item.last")) { const iconSpan = document.createElement("span"); iconSpan.setAttribute("class", "icon"); iconSpan.innerHTML = "⚔️"; iconSpan.setAttribute( "style", "font-size: 14px; position: absolute; left: 4px !important;" ); const uiHighlightDiv = document.createElement("div"); uiHighlightDiv.setAttribute("class", "ui_highlight"); uiHighlightDiv.setAttribute("data-type", "main_menu"); const buttonSpan = document.createElement("span"); buttonSpan.setAttribute("class", "button"); buttonSpan.setAttribute("id", "spam_attack_button"); const nameSpan = document.createElement("span"); nameSpan.setAttribute("class", "name"); nameSpan.innerHTML = "Attack alarm"; const buttonWrapperSpan = document.createElement("span"); buttonWrapperSpan.setAttribute("class", "button_wrapper"); const nameWrapperSpan = document.createElement("span"); nameWrapperSpan.setAttribute("class", "name_wrapper"); const contentWrapperSpan = document.createElement("span"); contentWrapperSpan.setAttribute("class", "content_wrapper"); const liItem = document.createElement("li"); liItem.setAttribute("id", "attack_alarm_menu"); liItem.setAttribute("class", "main_menu_item"); liItem.appendChild(contentWrapperSpan); contentWrapperSpan .appendChild(buttonWrapperSpan) .appendChild(nameWrapperSpan); buttonWrapperSpan.appendChild(buttonSpan); buttonSpan.appendChild(iconSpan); buttonSpan.appendChild(uiHighlightDiv); nameWrapperSpan.appendChild(nameSpan); const lastMenuItem = document.querySelector("li.main_menu_item.last"); liItem.addEventListener("click", () => WF.open("antispam")); if (document.getElementsByClassName("main_menu_item").length === 8) { document .querySelector("div.nui_main_menu > div.middle > div.content > ul") .setAttribute("style", "height: 293px"); } else if ( document.getElementsByClassName("main_menu_item").length === 9 ) { document .querySelector("div.nui_main_menu > div.middle > div.content > ul") .setAttribute("style", "height: 330px"); } else { let height; document .querySelector("div.nui_main_menu > div.middle > div.content > ul") .getAttribute("style") .split(";") .forEach((style) => { if (style.search("height") !== -1) { height = style.replace("height: ", ""); } }); height = height.replace("px", ""); document .querySelector("div.nui_main_menu > div.middle > div.content > ul") .setAttribute("style", `height: ${parseInt(height) + 35}px`); } lastMenuItem.parentNode.insertBefore(liItem, lastMenuItem); clearInterval(menu); } }, 100); })(); function setLocalStorageItems() { if (!localStorage.getItem("antiSpamIgnoredPlayers")) localStorage.setItem("antiSpamIgnoredPlayers", ""); if (!localStorage.getItem("antiSpamIgnoredPoints")) localStorage.setItem("antiSpamIgnoredPoints", 0); if (!localStorage.getItem("antiSpamAlarmDelay")) localStorage.setItem("antiSpamAlarmDelay", 0); if (!localStorage.getItem("antiSpamAlarmVolume")) localStorage.setItem("antiSpamAlarmVolume", 50); if (!localStorage.getItem("antiSpamCatapushId")) localStorage.setItem("antiSpamCatapushId", ""); } $(document).on("game:load", function () { injectStylesheet( "https://cdnjs.cloudflare.com/ajax/libs/jquery-toast-plugin/1.3.2/jquery.toast.min.css" ); injectStylesheet( "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.1/css/all.min.css" ); setLocalStorageItems(); LGAntiSpam.alarm.registerListener(function (soundOn) { if (soundOn) { LGAntiSpam.alarm.node.play(); $(".lg_alarm").removeClass("lg__hidden"); $(".lg_alarm").addClass("lg__visible"); } else { LGAntiSpam.alarm.node.pause(); $(".lg_alarm").addClass("lg__hidden"); $(".lg_alarm").removeClass("lg__visible"); } }); const alarmWrapper = $("<div />", { class: "lg_alarm", style: `${$(".notification").length && "margin-right: 41px"};display:none;`, }); alarmWrapper.append(`<div class="lg_alarm__button"> <div class="lg_alarm__button_border"></div> <div style="display: flex;align-items: center;width: 100%;height: 100%;justify-content: center;color: white;"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" > <path fill-rule="evenodd" clip-rule="evenodd" d="M14 3V3.28988C16.8915 4.15043 19 6.82898 19 10V17H20V19H4V17H5V10C5 6.82898 7.10851 4.15043 10 3.28988V3C10 1.89543 10.8954 1 12 1C13.1046 1 14 1.89543 14 3ZM7 17H17V10C17 7.23858 14.7614 5 12 5C9.23858 5 7 7.23858 7 10V17ZM14 21V20H10V21C10 22.1046 10.8954 23 12 23C13.1046 23 14 22.1046 14 21Z" fill="currentColor" /> </svg> </div> </div> `); $("#ui_box").append(alarmWrapper); $(".lg_alarm").on("click", function () { LGAntiSpam.alarm.deactivateSound(); }); (function () { var a = w.GameControllers.TabController.extend({ render: function () { this.getWindowModel().showLoading(); this.$el.html(buildIgnorePlayersTab()); $("#volume_input").val( `${parseInt(localStorage.getItem("antiSpamAlarmVolume"))}%` ); const volume = $("#alarm_script_volume_slider").grepoSlider({ min: 0, max: 100, step: 5, value: LGAntiSpam.alarm.node.volume * 100, }); volume.on("sl:change:value", (e, _sl, v) => { $(".lg_alarm__volume").text(`${v}%`); LGAntiSpam.alarm.node.volume = v / 100; }); setTimeout( () => volume.setValue( parseInt(localStorage.getItem("antiSpamAlarmVolume")) ), 0 ); $(".lg_alarm__save_button").on("click", function () { localStorage.setItem( "antiSpamIgnoredPlayers", $("#ignored_players").val() ); localStorage.setItem( "antiSpamIgnoredPoints", $("#ignored_points").val() ); localStorage.setItem("antiSpamAlarmDelay", $("#delay").val()); localStorage.setItem( "antiSpamAlarmVolume", $("#lg_alarm__volume").text().replace("%", "") ); setTimeout(function () { HumanMessage.success("Attack alarm successfully updated."); }, 0); }); WM.getWindowByType("antispam")[0].hideLoading(); }, }); w.GameViews.LGAttackAlarm = a; })(); (function () { var a = w.GameControllers.TabController.extend({ render: function () { this.getWindowModel().showLoading(); this.$el.html(buildMobileAlarmTab()); $(".lg_alarm__save_button").on("click", function () { localStorage.setItem("antiSpamCatapushId", $("#catapush_id").val()); }); this.getWindowModel().hideLoading(); }, }); w.GameViews.LGMobileAlarm = a; })(); (function () { var a = w.GameControllers.TabController.extend({ render: function () { this.getWindowModel().showLoading(); this.$el.html(buildDonateTab()); this.getWindowModel().hideLoading(); }, }); w.GameViews.LGDonate = a; })(); (function () { var a = w.GameControllers.TabController.extend({ render: function () { this.getWindowModel().showLoading(); this.$el.html(buildGuideTab()); this.getWindowModel().hideLoading(); }, }); w.GameViews.LGGuide = a; })(); // (function () { // var a = w.GameControllers.TabController.extend({ // render: async function () { // this.getWindowModel().showLoading(); // const html = await buildLogTab(); // this.$el.html(html); // this.getWindowModel().hideLoading(); // }, // }); // w.GameViews.LGLog = a; // })(); (function () { var a = w.GameControllers.TabController.extend({ render: function () { this.getWindowModel().showLoading(); this.$el.html(buildHelpTab()); this.getWindowModel().hideLoading(); }, }); w.GameViews.LGHelp = a; })(); (function () { require("game/windows/ids").ANTISPAM = "antispam"; var a = w.GameViews, b = w.WindowFactorySettings, c = require("game/windows/ids"), e = require("game/windows/tabs"), f = c.ANTISPAM; b[f] = function (b) { b = b || {}; return us.extend({ title: "Anti-Spam Script Settings", window_type: f, minheight: 475, maxheight: 630, width: 830, tabs: [ { type: "ignorePlayers", title: "Settings", content_view_constructor: a.LGAttackAlarm, hidden: !1, }, { type: "mobileAlarm", title: "Mobile alarm", content_view_constructor: a.LGMobileAlarm, hidden: !1, }, { type: "guide", title: "Guide", content_view_constructor: a.LGGuide, hidden: !1, }, // { // type: "alarmLog", // title: "Alarm log", // content_view_constructor: a.LGLog, // hidden: !1, // }, { type: "lghelp", title: "Help", content_view_constructor: a.LGHelp, hidden: !1, }, { type: "donate", title: "Donate", content_view_constructor: a.LGDonate, hidden: !1, }, ], max_instances: 1, activepagenr: 0, minimizable: !0, }); }; })(); }); (async function () { "use strict"; if (!Game.world_id && !Game.player_id) return; const movement = []; let commandIdsForAlarm = movement.map((log) => log.movementId); $.Observer("attack:incoming").subscribe(() => { setTimeout(() => { const movements = Object.values(MM.getModels().MovementsUnits); movements.map(async (movement) => { if (!movement.isIncommingAttack()) return; let townData; townData = await fetch( `https://api.grepodata.com/town?world=${ Game.world_id }&id=${movement.getHomeTownId()}` ) .then((response) => response.json()) .catch(() => fetch( `https://dolinek.dev/town/${ Game.world_id }/${movement.getHomeTownId()}/player` ).then((response) => response.json()) ); const homeTown = ITowns.towns[movement.getTargetTownId()]; if (!commandIdsForAlarm.includes(movement.getId())) { if ( !localStorage .getItem("antiSpamIgnoredPlayers") .split(";") .find( (name) => name.toLowerCase() === townData.player_data.name.toLowerCase() ) ) { if ( townData.player_data.points < parseInt(localStorage.getItem("antiSpamIgnoredPoints"), 10) ) return false; if (parseInt(localStorage.getItem("antiSpamAlarmDelay"), 10) > 0) { setTimeout(() => { LGAntiSpam.alarm.activateSound(); }, parseInt(localStorage.getItem("antiSpamAlarmDelay"), 10) * 1000); return false; } if (localStorage.getItem("antiSpamCatapushId")) { console.log("3"); await fetch(`https://api.catapush.com/1/messages`, { method: "post", headers: new Headers({ Authorization: "Bearer 8aead2eef413007e238beb7789c40171d2a298a9", "Content-Type": "application/json", accept: "application/json", }), body: JSON.stringify({ mobileAppId: 318, text: `Your city ${movement.attributes.town_name_destination} is being attacked by ${townData.player_data.name}. Arrival ${movement.attributes.arrived_human}`, recipients: [ { identifier: localStorage.getItem("antiSpamCatapushId"), }, ], }), }); } if (!LGAntiSpam.alarm.sound) LGAntiSpam.alarm.activateSound(); } commandIdsForAlarm.push(movement.getId()); } }); }, 1); }); })(); (function () { $('head').append( `<style> .lg_alarm { position: absolute; bottom: 50px; right: 70px; width: 100px; height: 100px; z-index: 5; } .lg__hidden { display: none !important; } .lg__visible { display: block !important; } .lg_alarm__save_button { display: inline-flex; align-content: center; justify-content: center; width: 100%; margin-top: 20px; } .lg_alarm__button, .lg_alarm__button_border, .lg_alarm__button_bell { cursor: pointer; border-radius: 50%; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .lg_alarm__button { height: 60px; width: 60px; box-shadow: -1px 2px 10px #999; background: #ef7575; animation-name: col; animation-duration: 2s; animation-iteration-count: infinite; } .lg_alarm__button_border { height: 59px; width: 59px; border: 1px solid #ef7575 !important; animation-name: bord-pop; animation-duration: 2s; animation-iteration-count: infinite; box-shadow: 2px 2px 5px #ccc, -2px -2px 5px #ccc; } .lg_alarm__button_bell { color: white; font-size: 20px; animation-name: bell-ring; animation-duration: 2s; animation-iteration-count: infinite; } .lg_alarm__no_animation { animation: none !important; } @keyframes bord-pop { 0% { transform: translate(-50%, -50%); } 50% { transform: translate(-50%, -50%) scale(1.9); opacity: 0.1; } 100% { transform: translate(-50%, -50%) scale(1.9); opacity: 0; } } @keyframes col { 0% { transform: scale(1) translate(0, 0); } 10% { transform: scale(1.1) translate(0, 0); } 75% { transform: scale(1) translate(0, 0); } 100% { transform: scale(1) translate(0, 0); } } @keyframes bell-ring { 0% { transform: translate(-50%, -50%); } 5%, 15% { transform: translate(-50%, -50%) rotate(25deg); } 10%, 20% { transform: translate(-50%, -50%) rotate(-25deg); } 25% { transform: translate(-50%, -50%) rotate(0deg); } 100% { transform: translate(-50%, -50%) rotate(0deg); } } /* GRID */ .lg_container { display: grid; grid-row-gap: 5px; grid-column-gap: 5px; grid-template-columns: repeat(auto-fit, minmax(calc(50% - 5px), 1fr)); } .lg_container__box { background: #f7e4cb; margin: 0 5px; padding: 10px; box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px, rgba(0, 0, 0, 0.08) 0px 0px 0px 1px; } .lg_container__box_title { width: 100%; font-size: 13px; padding-bottom: 10px; display: flex; align-items: center; } .lg_container__box_title span { font-weight: 700; } .lg_container__box_title svg { margin-right: 5px; } .lg_container__box_helper { padding-top: 5px; } .lg_container__box input, .lg_container__box textarea { width: calc(100% - 10px); } .lg_log_container { display: flex; flex-direction: column; width: 100%; height: 100%; } .lg_log_container__head { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; text-align: center; font-weight: 900; font-size: larger; } .lg_log_container__row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; text-align: center; padding: 5px 0; background: #f7e4cb; margin: 5px; } </style>` ) })();